source: trunk/src/helpers/gpih.c@ 238

Last change on this file since 238 was 238, checked in by umoeller, 23 years ago

Misc fixes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 106.2 KB
Line 
1
2/*
3 *@@sourcefile gpih.c:
4 * contains GPI (graphics) helper functions.
5 *
6 * Usage: All PM programs.
7 *
8 * Function prefixes (new with V0.81):
9 * -- gpih* GPI helper functions
10 *
11 * Note: Version numbering in this file relates to XWorkplace version
12 * numbering.
13 *
14 *@@header "helpers\gpih.h"
15 */
16
17/*
18 * Copyright (C) 1997-2002 Ulrich M”ller.
19 * This file is part of the "XWorkplace helpers" source package.
20 * This is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published
22 * by the Free Software Foundation, in version 2 as it comes in the
23 * "COPYING" file of the XWorkplace main distribution.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 */
29
30#define OS2EMX_PLAIN_CHAR
31 // this is needed for "os2emx.h"; if this is defined,
32 // emx will define PSZ as _signed_ char, otherwise
33 // as unsigned char
34
35#define INCL_DOSSEMAPHORES
36#define INCL_DOSMODULEMGR
37#define INCL_DOSRESOURCES
38#define INCL_DOSERRORS
39
40#define INCL_WINWINDOWMGR
41#define INCL_WINMESSAGEMGR
42#define INCL_WINPOINTERS
43#define INCL_WINSYS
44
45#define INCL_GPIPRIMITIVES
46#define INCL_GPIBITMAPS
47#define INCL_GPILOGCOLORTABLE
48#define INCL_GPILCIDS
49#include <os2.h>
50
51#include <stdlib.h>
52#include <string.h>
53#include <stdio.h>
54#include <sys/types.h>
55#include <sys/stat.h>
56
57#include "setup.h" // code generation and debugging options
58
59#ifdef WINH_STANDARDWRAPPERS
60#undef WINH_STANDARDWRAPPERS
61#endif
62#include "helpers\dosh.h"
63#include "helpers\winh.h"
64#include "helpers\gpih.h"
65
66#pragma hdrstop
67
68// array for querying device capabilities (gpihQueryDisplayCaps)
69LONG DisplayCaps[CAPS_DEVICE_POLYSET_POINTS] = {0};
70BOOL G_fCapsQueried = FALSE;
71
72/*
73 *@@category: Helpers\PM helpers\GPI helpers
74 * See gpih.c.
75 */
76
77/*
78 *@@category: Helpers\PM helpers\GPI helpers\Devices
79 */
80
81/*
82 *@@gloss: GPI_rectangles GPI rectangles
83 * OS/2 PM (and GPI) uses two types of rectangles. This is rarely
84 * mentioned in the documentation, so a word is in order here.
85 *
86 * In general, graphics operations involving device coordinates
87 * (such as regions, bit maps and bit blts, and window management)
88 * use inclusive-exclusive rectangles. In other words, with
89 * those rectangles, xRight - xLeft is the same as the width
90 * of the rectangle (and yTop - yBottom = height).
91 *
92 * All other graphics operations, such as GPI functions that
93 * define paths, use inclusive-inclusive rectangles.
94 *
95 * This can be a problem with mixing Win and Gpi functions. For
96 * example, WinQueryWindowRect returns an inclusive-exclusive
97 * rectangle (tested V0.9.7 (2000-12-20) [umoeller]).
98 *
99 * WinFillRect expects an inclusive-exclusive rectangle, so it
100 * will work with a rectangle from WinQueryWindowRect directly.
101 *
102 * By contrast, the GpiBox expects an inclusive-inclusive rectangle.
103 */
104
105/* ******************************************************************
106 *
107 * Global variables
108 *
109 ********************************************************************/
110
111STATIC HMTX G_hmtxLCIDs = NULLHANDLE;
112
113/* ******************************************************************
114 *
115 * Rectangle helpers
116 *
117 ********************************************************************/
118
119/*
120 *@@ gpihIsPointInRect:
121 * like WinPtInRect, but doesn't need a HAB.
122 *
123 * NOTE: as opposed to WinPtInRect, prcl is
124 * considered inclusive, that is, TRUE is
125 * returned even if x or y are exactly
126 * the same as prcl->xRight or prcl->yTop.
127 *
128 *@@added V0.9.9 (2001-02-28) [umoeller]
129 */
130
131BOOL gpihIsPointInRect(PRECTL prcl,
132 LONG x,
133 LONG y)
134{
135 if (prcl)
136 return ( (x >= prcl->xLeft)
137 && (x <= prcl->xRight)
138 && (y >= prcl->yBottom)
139 && (y <= prcl->yTop)
140 );
141
142 return FALSE;
143}
144
145/*
146 *@@ gpihInflateRect:
147 * Positive l will make the rectangle larger.
148 * Negative l will make the rectangle smaller.
149 *
150 *@@added V0.9.9 (2001-02-28) [umoeller]
151 */
152
153VOID gpihInflateRect(PRECTL prcl,
154 LONG l)
155{
156 if (prcl && l)
157 {
158 prcl->xLeft -= l;
159 prcl->yBottom -= l;
160 prcl->xRight += l;
161 prcl->yTop += l;
162 }
163}
164
165/*
166 *@@ gpihCopyRectIncl:
167 * copies prclWin to prclGpi, making it
168 * inclusive-inclusive for use with GPI
169 * functions.
170 *
171 *@@added V1.0.1 (2002-11-30) [umoeller]
172 */
173
174VOID gpihCopyRectIncl(PRECTL prclGpi, // out: GPI rectangle
175 PRECTL prclWin) // in: WIN rectangle
176{
177 prclGpi->xLeft = prclWin->xLeft;
178 prclGpi->yBottom = prclWin->yBottom;
179 prclGpi->xRight = prclWin->xRight - 1;
180 prclGpi->yTop = prclWin->yTop - 1;
181}
182
183/* ******************************************************************
184 *
185 * Device helpers
186 *
187 ********************************************************************/
188
189/*
190 *@@ gpihQueryDisplayCaps:
191 * this returns certain device capabilities of
192 * the display device. ulIndex must be one of
193 * the indices as described in DevQueryCaps.
194 *
195 * This function will load all the device capabilities
196 * only once into a global array and re-use them afterwards.
197 *
198 *@@changed V0.9.16 (2001-12-18) [umoeller]: fixed multiple loads
199 */
200
201ULONG gpihQueryDisplayCaps(ULONG ulIndex)
202{
203 if (!G_fCapsQueried)
204 {
205 HPS hps = WinGetScreenPS(HWND_DESKTOP);
206 HDC hdc = GpiQueryDevice(hps);
207 DevQueryCaps(hdc, 0, CAPS_DEVICE_POLYSET_POINTS, &DisplayCaps[0]);
208 G_fCapsQueried = TRUE; // was missing V0.9.16 (2001-12-18) [umoeller]
209 }
210
211 return DisplayCaps[ulIndex];
212}
213
214/*
215 *@@category: Helpers\PM helpers\GPI helpers\Colors
216 */
217
218/* ******************************************************************
219 *
220 * Color helpers
221 *
222 ********************************************************************/
223
224/*
225 * HackColor:
226 *
227 */
228
229STATIC VOID HackColor(PBYTE pb, double dFactor)
230{
231 ULONG ul = (ULONG)((double)(*pb) * dFactor);
232 if (ul > 255)
233 *pb = 255;
234 else
235 *pb = (BYTE)ul;
236}
237
238/*
239 *@@ gpihManipulateRGB:
240 * this changes an RGB color value
241 * by multiplying each color component
242 * (red, green, blue) with dFactor.
243 *
244 * Each color component is treated separately,
245 * so if overflows occur (because dFactor
246 * is > 1), this does not affect the other
247 * components.
248 *
249 *@@changed V0.9.11 (2001-04-25) [umoeller]: changed prototype to use a double now
250 */
251
252VOID gpihManipulateRGB(PLONG plColor, // in/out: RGB color
253 double dFactor) // in: factor (> 1 makes brigher, < 1 makes darker)
254{
255 PBYTE pb = (PBYTE)plColor;
256
257 // in memory, the bytes are blue, green, red, unused
258
259 // blue
260 ULONG ul = (ULONG)( (double)(*pb) * dFactor
261 );
262 if (ul > 255)
263 *pb = 255;
264 else
265 *pb = (BYTE)ul;
266
267 // green
268 ul = (ULONG)( (double)(*(++pb)) * dFactor
269 );
270 if (ul > 255)
271 *pb = 255;
272 else
273 *pb = (BYTE)ul;
274
275 // red
276 ul = (ULONG)( (double)(*(++pb)) * dFactor
277 );
278 if (ul > 255)
279 *pb = 255;
280 else
281 *pb = (BYTE)ul;
282}
283
284#define MEDIATE(a, b) (LONG)(a) + (((LONG)(b) - (LONG)(a)) / 2)
285
286/*
287 *@@ gpihMediumRGB:
288 * returns the arithmetic medium color between
289 * lcol1 and lcol2.
290 *
291 *@@added V0.9.19 (2002-05-28) [umoeller]
292 */
293
294LONG gpihMediumRGB(LONG lcol1, LONG lcol2)
295{
296 return MAKE_RGB(
297 MEDIATE(GET_RED(lcol1),
298 GET_RED(lcol2)),
299 MEDIATE(GET_GREEN(lcol1),
300 GET_GREEN(lcol2)),
301 MEDIATE(GET_BLUE(lcol1),
302 GET_BLUE(lcol2))
303 );
304}
305
306/*
307 *@@ gpihSwitchToRGB:
308 * this switches the given HPS into RGB mode. You should
309 * always use this if you are operating with RGB colors.
310 *
311 * This is just a shortcut to calling
312 *
313 + GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
314 *
315 *@@changed V0.9.7 (2001-01-15) [umoeller]: turned macro into function to reduce fixups
316 */
317
318BOOL gpihSwitchToRGB(HPS hps)
319{
320 return GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
321}
322
323/*
324 *@@category: Helpers\PM helpers\GPI helpers\Drawing primitives
325 */
326
327/* ******************************************************************
328 *
329 * Drawing primitives helpers
330 *
331 ********************************************************************/
332
333/*
334 *@@ gpihDrawRect:
335 * this draws a simple rectangle with the current
336 * color (use GpiSetColor before calling this function).
337 *
338 * The specified rectangle is inclusive, that is, the top
339 * right corner specifies the top right pixel to be drawn
340 * (see @GPI_rectangles).
341 *
342 * This sets the current position to the bottom left corner
343 * of prcl.
344 *
345 *@added V0.9.0
346 */
347
348VOID gpihDrawRect(HPS hps, // in: presentation space for output
349 PRECTL prcl) // in: rectangle to draw (inclusive)
350{
351 POINTL ptl1;
352
353 ptl1.x = prcl->xLeft;
354 ptl1.y = prcl->yBottom;
355 GpiMove(hps, &ptl1);
356 ptl1.y = prcl->yTop - 1;
357 GpiLine(hps, &ptl1);
358 ptl1.x = prcl->xRight - 1;
359 GpiLine(hps, &ptl1);
360 ptl1.y = prcl->yBottom;
361 GpiLine(hps, &ptl1);
362 ptl1.x = prcl->xLeft;
363 GpiLine(hps, &ptl1);
364}
365
366/*
367 *@@ gpihBox:
368 * this is a shortcurt to GpiBox, using the specified
369 * rectangle.
370 *
371 * As opposed to WinFillRect, this works with memory
372 * (bitmap) PS's also.
373 *
374 * The specified rectangle is inclusive, that is, the top
375 * right corner specifies the top right pixel to be drawn.
376 * This is different from WinFillRect (see @GPI_rectangles).
377 *
378 * Changes to the HPS:
379 *
380 * -- the current position is moved to the lower left
381 * corner of *prcl.
382 *
383 *@@changed V0.9.0 [umoeller]: renamed from gpihFillRect
384 *@@changed V0.9.0 [umoeller]: modified function prototype to support lControl
385 *@@changed V0.9.7 (2001-01-17) [umoeller]: removed lColor
386 */
387
388VOID gpihBox(HPS hps, // in: presentation space for output
389 LONG lControl, // in: one of DRO_OUTLINE, DRO_FILL, DRO_OUTLINEFILL
390 PRECTL prcl) // in: rectangle to draw (inclusive)
391{
392 POINTL ptl;
393
394 ptl.x = prcl->xLeft;
395 ptl.y = prcl->yBottom;
396 GpiMove(hps, &ptl);
397 ptl.x = prcl->xRight;
398 ptl.y = prcl->yTop;
399 GpiBox(hps,
400 lControl, // DRO_*
401 &ptl,
402 0, 0); // no corner rounding
403}
404
405/*
406 *@@ gpihMarker:
407 * this draws a quick marker (a filled
408 * rectangle) at the specified position.
409 * The rectangle will be drawn so that
410 * the specified point is in its center.
411 *
412 * No PS data is changed.
413 *
414 *@@changed V0.9.7 (2001-01-17) [umoeller]: removed lColor
415 */
416
417VOID gpihMarker(HPS hps,
418 LONG x, // in: x-center of rectangle
419 LONG y, // in: y-center of rectangle
420 ULONG ulWidth) // in: rectangle width and height
421{
422 POINTL ptlSave;
423 RECTL rclTemp;
424 ULONG ulWidth2 = ulWidth / 2;
425 rclTemp.xLeft = x - ulWidth2;
426 rclTemp.xRight = x + ulWidth2;
427 rclTemp.yBottom = y - ulWidth2;
428 rclTemp.yTop = y + ulWidth2;
429
430 GpiQueryCurrentPosition(hps, &ptlSave);
431 gpihBox(hps,
432 DRO_FILL,
433 &rclTemp);
434 GpiMove(hps, &ptlSave);
435}
436
437/*
438 *@@ gpihThickBox:
439 * draws a box from the specified rectangle with the
440 * specified width.
441 *
442 * The specified rectangle is inclusive, that is, the top
443 * right corner specifies the top right pixel to be drawn.
444 * This is different from WinFillRect
445 * (see @GPI_rectangles).
446 *
447 * If usWidth > 1, the additional pixels will be drawn towards
448 * the _center_ of the rectangle. prcl thus always specifies
449 * the bottom left and top right pixels to be drawn.
450 *
451 * This is different from using GpiSetLineWidth, with which
452 * I was unable to find out in which direction lines are
453 * extended.
454 *
455 * This is similar to gpihDraw3DFrame, except that everything
456 * is painted in the current color.
457 *
458 *@@added V0.9.7 (2000-12-06) [umoeller]
459 */
460
461VOID gpihDrawThickFrame(HPS hps, // in: presentation space for output
462 PRECTL prcl, // in: rectangle to draw (inclusive)
463 ULONG ulWidth) // in: line width (>= 1)
464{
465 ULONG ul = 0;
466 for (;
467 ul < ulWidth;
468 ul++)
469 {
470 GpiMove(hps, (PPOINTL)prcl);
471 GpiBox(hps,
472 DRO_OUTLINE,
473 (PPOINTL)&(prcl->xRight),
474 0,
475 0);
476
477 // and one more to the outside
478 prcl->xLeft++;
479 prcl->yBottom++;
480 prcl->xRight--;
481 prcl->yTop--;
482 }
483}
484
485/*
486 *@@ gpihDraw3DFrame2:
487 * this draws a rectangle in 3D style with a given line width
488 * and the given colors.
489 *
490 * The specified rectangle is inclusive, that is, the top
491 * right corner specifies the top right pixel to be drawn.
492 * This is different from WinFillRect
493 * (see @GPI_rectangles).
494 *
495 * If usWidth > 1, the additional pixels will be drawn towards
496 * the _center_ of the rectangle. prcl thus always specifies
497 * the bottom left and top right pixels to be drawn.
498 *
499 * Note: With V1.0.0, this now modifies prcl to be smaller towards
500 * the center of the rectangle by usWidth so you can call this several
501 * times with different colors.
502 *
503 *@@changed V0.9.0 [umoeller]: changed function prototype to have colors specified
504 *@@changed V0.9.7 (2000-12-20) [umoeller]: now really using inclusive rectangle...
505 *@@changed V1.0.0 (2002-08-24) [umoeller]: renamed, now modifying prcl on output
506 */
507
508VOID gpihDraw3DFrame2(HPS hps,
509 PRECTL prcl, // in: rectangle (inclusive)
510 USHORT usWidth, // in: line width (>= 1)
511 LONG lColorLeft, // in: color to use for left and top; e.g. SYSCLR_BUTTONLIGHT
512 LONG lColorRight) // in: color to use for right and bottom; e.g. SYSCLR_BUTTONDARK
513{
514 USHORT us;
515 POINTL ptl1;
516
517 for (us = 0;
518 us < usWidth;
519 us++)
520 {
521 GpiSetColor(hps, lColorLeft);
522 // draw left line
523 ptl1.x = prcl->xLeft;
524 ptl1.y = prcl->yBottom;
525 GpiMove(hps, &ptl1);
526 ptl1.y = prcl->yTop; // V0.9.7 (2000-12-20) [umoeller]
527 GpiLine(hps, &ptl1);
528 // go right -> draw top
529 ptl1.x = prcl->xRight; // V0.9.7 (2000-12-20) [umoeller]
530 GpiLine(hps, &ptl1);
531 // go down -> draw right
532 GpiSetColor(hps, lColorRight);
533 ptl1.y = prcl->yBottom;
534 GpiLine(hps, &ptl1);
535 // go left -> draw bottom
536 ptl1.x = prcl->xLeft;
537 GpiLine(hps, &ptl1);
538
539 prcl->xLeft++;
540 prcl->yBottom++;
541 prcl->xRight--;
542 prcl->yTop--;
543 }
544}
545
546/*
547 *@@ gpihDraw3DFrame:
548 * compatibility function for those who used the
549 * export. As opposed to gpihDraw3DFrame2, this
550 * does not modify prcl.
551 *
552 *@@added V1.0.0 (2002-08-24) [umoeller]
553 */
554
555VOID gpihDraw3DFrame(HPS hps,
556 PRECTL prcl, // in: rectangle (inclusive)
557 USHORT usWidth, // in: line width (>= 1)
558 LONG lColorLeft, // in: color to use for left and top; e.g. SYSCLR_BUTTONLIGHT
559 LONG lColorRight) // in: color to use for right and bottom; e.g. SYSCLR_BUTTONDARK
560{
561 RECTL rcl2 = *prcl;
562 gpihDraw3DFrame2(hps, &rcl2, usWidth, lColorLeft, lColorRight);
563}
564
565/*
566 *@@ gpihCharStringPosAt:
567 * wrapper for GpiCharStringPosAt.
568 * Since that function is limited to 512 characters
569 * (according to GPIREF; on my Warp 4 FP13, I actually
570 * get some 3000 characters... whatever this is),
571 * this splits the string into 512 byte chunks and
572 * calls GpiCharStringPosAt accordingly.
573 *
574 *@@added V0.9.3 (2000-05-06) [umoeller]
575 *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed underline for bitmap fonts, which never worked
576 */
577
578LONG gpihCharStringPosAt(HPS hps,
579 PPOINTL pptlStart,
580 PRECTL prclRect,
581 ULONG flOptions,
582 LONG lCount,
583 PCH pchString)
584{
585 LONG lHits = 0,
586 lCountLeft = lCount;
587 PCH pchThis = pchString;
588
589 GpiMove(hps, pptlStart);
590
591 if (lCount)
592 {
593 do
594 {
595 LONG lCountThis = lCountLeft;
596 if (lCountLeft >= 512)
597 lCountThis = 512;
598
599 lHits = GpiCharStringPos(hps,
600 prclRect,
601 0, // flOptions,
602 lCountThis,
603 pchThis,
604 0);
605
606 pchThis += 512;
607 lCountLeft -= 512;
608 } while (lCountLeft > 0);
609 }
610
611 // I can't get underscore to work with bitmap fonts,
612 // so I'm doing it manually always now
613 // V0.9.20 (2002-08-10) [umoeller]
614 if (flOptions & CHS_UNDERSCORE)
615 {
616 POINTL ptl2, ptlSave;
617 GpiQueryCurrentPosition(hps, &ptlSave);
618 ptl2.x = pptlStart->x;
619 ptl2.y = pptlStart->y - 2;
620 GpiMove(hps, &ptl2);
621 ptl2.x = ptlSave.x;
622 GpiLine(hps, &ptl2);
623 GpiMove(hps, &ptlSave);
624 }
625
626 return lHits;
627}
628
629/*
630 *@@ gpihFillBackground:
631 * fills the specified rectangle in the way
632 * that is specified by the given BKGNDINFO
633 * structure. This way one can either use
634 * a solid color, a color fade, a bitmap,
635 * or a combination of those.
636 *
637 * See BKGNDINFO for the various parameters.
638 *
639 * Since this can potentially be expensive,
640 * it is strongly recommended to use a buffer
641 * bitmap for painting with the size of the
642 * window and bitblt that bitmap into the
643 * window on repaints. This way the background
644 * only has to be recreated on window resize.
645 *
646 *@@added V0.9.19 (2002-05-07) [umoeller]
647 */
648
649VOID gpihFillBackground(HPS hps, // in: PS to paint into
650 PRECTL prcl, // in: rectangle (inclusive!)
651 PBKGNDINFO pInfo) // in: background into
652{
653 LONG l;
654 POINTL ptl;
655
656 switch (pInfo->flPaintMode & PMOD_COLORMASK)
657 {
658 case PMOD_SOLID:
659 // fill with background color
660 GpiSetColor(hps,
661 pInfo->lcol1);
662 ptl.x = prcl->xLeft;
663 ptl.y = prcl->yBottom;
664 GpiMove(hps,
665 &ptl);
666 ptl.x = prcl->xRight;
667 ptl.y = prcl->yTop;
668 GpiBox(hps,
669 DRO_FILL,
670 &ptl,
671 0,
672 0);
673 break;
674
675 case PMOD_TOPBOTTOM:
676 {
677 LONG lDiffRed = (LONG)GET_RED(pInfo->lcol2) - (LONG)GET_RED(pInfo->lcol1);
678 LONG lDiffGreen = (LONG)GET_GREEN(pInfo->lcol2) - (LONG)GET_GREEN(pInfo->lcol1);
679 LONG lDiffBlue = (LONG)GET_BLUE(pInfo->lcol2) - (LONG)GET_BLUE(pInfo->lcol1);
680
681 LONG lMax = prcl->yTop - prcl->yBottom;
682
683 // start at top
684 ptl.y = prcl->yTop;
685
686 for (l = 0;
687 l <= lMax;
688 ++l)
689 {
690 // compose RGB color for this line;
691 // lcol1 is top, lcol2 is bottom
692 LONG lRed = GET_RED(pInfo->lcol1)
693 + ( lDiffRed
694 * l
695 / lMax
696 );
697 LONG lGreen = GET_GREEN(pInfo->lcol1)
698 + ( lDiffGreen
699 * l
700 / lMax
701 );
702 LONG lBlue = GET_BLUE(pInfo->lcol1)
703 + ( lDiffBlue
704 * l
705 / lMax
706 );
707
708 GpiSetColor(hps, MAKE_RGB(lRed, lGreen, lBlue));
709 ptl.x = prcl->xLeft;
710 GpiMove(hps, &ptl);
711 ptl.x = prcl->xRight;
712 GpiLine(hps, &ptl);
713
714 // next line below
715 --(ptl.y);
716 }
717 }
718 break;
719
720 case PMOD_LEFTRIGHT:
721 {
722 LONG lDiffRed = (LONG)GET_RED(pInfo->lcol2) - (LONG)GET_RED(pInfo->lcol1);
723 LONG lDiffGreen = (LONG)GET_GREEN(pInfo->lcol2) - (LONG)GET_GREEN(pInfo->lcol1);
724 LONG lDiffBlue = (LONG)GET_BLUE(pInfo->lcol2) - (LONG)GET_BLUE(pInfo->lcol1);
725
726 LONG lMax = prcl->xRight - prcl->xLeft;
727
728 // start at left
729 ptl.x = prcl->xLeft;
730
731 for (l = 0;
732 l <= lMax;
733 ++l)
734 {
735 // compose RGB color for this line;
736 // lcol1 is top, lcol2 is bottom
737 LONG lRed = GET_RED(pInfo->lcol1)
738 + ( lDiffRed
739 * l
740 / lMax
741 );
742 LONG lGreen = GET_GREEN(pInfo->lcol1)
743 + ( lDiffGreen
744 * l
745 / lMax
746 );
747 LONG lBlue = GET_BLUE(pInfo->lcol1)
748 + ( lDiffBlue
749 * l
750 / lMax
751 );
752
753 GpiSetColor(hps, MAKE_RGB(lRed, lGreen, lBlue));
754 ptl.y = prcl->yBottom;
755 GpiMove(hps, &ptl);
756 ptl.y = prcl->yTop;
757 GpiLine(hps, &ptl);
758
759 // next line to the right
760 ++(ptl.x);
761 }
762 }
763 break;
764 }
765}
766
767/*
768 *@@category: Helpers\PM helpers\GPI helpers\Fonts
769 */
770
771/* ******************************************************************
772 *
773 * Font helpers
774 *
775 ********************************************************************/
776
777/*
778 *@@ gpihMatchFont:
779 * attempts to find a font matching the specified
780 * data and fills the specified FATTRS structure
781 * accordingly.
782 *
783 * This function performs the insane "11-step process" to
784 * match a font, as described in the GPI reference.
785 *
786 * This function can operate in two modes:
787 *
788 * -- "Family" mode. In that case, specify the font family name
789 * with pszName and set fFamily to TRUE. This is useful for
790 * WYSIWYG text viewing if you need several font faces for
791 * the same family, such as Courier Bold, Bold Italics, etc.
792 * You can specify those attributes with usFormat then.
793 *
794 * -- "Face" mode. In that case, specify the full font face name
795 * with pszName and set fFamily to FALSE. This is useful for
796 * font presentation parameters which use the "WarpSans Bold"
797 * format. In that case, set usFormat to 0.
798 *
799 * Returns TRUE if a "true" match was found, FALSE
800 * otherwise. In both cases, *pfa receives data
801 * which will allow GpiCreateLogFont to work; however,
802 * if FALSE is returned, GpiCreateLogFont will most
803 * likely find the default font (System Proportional)
804 * only.
805 *
806 * If (pFontMetrics != NULL), *pFontMetrics receives the
807 * FONTMETRICS of the font which was found. If an outline
808 * font has been found (instead of a bitmap font),
809 * FONTMETRICS.fsDefn will have the FM_DEFN_OUTLINE bit set.
810 *
811 * This function was extracted from gpihFindFont with
812 * 0.9.14 to allow for caching the font search results,
813 * which is most helpful for memory device contexts,
814 * where gpihFindFont can be inefficient.
815 *
816 *@@added V0.9.14 (2001-08-03) [umoeller]
817 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed a few weirdos with outline fonts
818 */
819
820BOOL gpihMatchFont(HPS hps,
821 LONG lSize, // in: font point size
822 BOOL fFamily, // in: if TRUE, pszName specifies font family;
823 // if FALSE, pszName specifies font face
824 const char *pcszName, // in: font family or face name (without point size)
825 USHORT usFormat, // in: none, one or several of:
826 // -- FATTR_SEL_ITALIC
827 // -- FATTR_SEL_UNDERSCORE (underline)
828 // -- FATTR_SEL_BOLD
829 // -- FATTR_SEL_STRIKEOUT
830 // -- FATTR_SEL_OUTLINE (hollow)
831 FATTRS *pfa, // out: font attributes if found
832 PFONTMETRICS pFontMetrics) // out: font metrics of created font (optional)
833{
834 // first find out how much memory we need to allocate
835 // for the FONTMETRICS structures
836 ULONG ul = 0;
837 LONG lTemp = 0;
838 LONG cFonts = GpiQueryFonts(hps,
839 QF_PUBLIC | QF_PRIVATE,
840 NULL, // pszFaceName,
841 &lTemp,
842 sizeof(FONTMETRICS),
843 NULL);
844 PFONTMETRICS pfm = (PFONTMETRICS)malloc(cFonts * sizeof(FONTMETRICS)),
845 pfm2 = pfm,
846 pfmFound = NULL;
847
848 BOOL fQueriedDevice = FALSE; // V0.9.14 (2001-08-01) [umoeller]
849 LONG alDevRes[2]; // device resolution
850
851 // _Pmpf(("gpihFindFont: enumerating for %s, %d points", pcszName, lSize));
852
853 GpiQueryFonts(hps,
854 QF_PUBLIC | QF_PRIVATE,
855 NULL, // pszFaceName,
856 &cFonts,
857 sizeof(FONTMETRICS), // length of each metrics structure
858 // -- _not_ total buffer size!
859 pfm);
860
861 // now we have an array of FONTMETRICS
862 // for EVERY font that is installed on the system...
863 // these things are completely unsorted, so there's
864 // nothing we can rely on, we have to check them all.
865
866 // fill in some default values for FATTRS,
867 // in case we don't find something better
868 // in the loop below; these values will be
869 // applied if
870 // a) an outline font has been found;
871 // b) bitmap fonts have been found, but
872 // none for the current device resolution
873 // exists;
874 // c) no font has been found at all.
875 // In all cases, GpiCreateLogFont will do
876 // a "close match" resolution (at the bottom).
877 pfa->usRecordLength = sizeof(FATTRS);
878 pfa->fsSelection = usFormat; // changed later if better font is found
879 pfa->lMatch = 0L; // closest match
880 strcpy(pfa->szFacename, pcszName);
881 pfa->idRegistry = 0; // default registry
882 pfa->usCodePage = 0; // default codepage
883 // the following two must be zero, or outline fonts
884 // will not be found; if a bitmap font has been passed
885 // to us, we'll modify these two fields later
886 pfa->lMaxBaselineExt = 0; // font size (height)
887 pfa->lAveCharWidth = 0; // font size (width)
888 pfa->fsType = 0; // default type
889 pfa->fsFontUse = FATTR_FONTUSE_NOMIX;
890
891 // now go thru the array of FONTMETRICS
892 // to check if we have a bitmap font
893 // pszFaceName; the default WPS behavior
894 // is that bitmap fonts appear to take
895 // priority over outline fonts of the
896 // same name, so we check these first
897 pfm2 = pfm;
898 for (ul = 0;
899 ul < cFonts;
900 ul++)
901 {
902 const char *pcszCompare = (fFamily)
903 ? pfm2->szFamilyname
904 : pfm2->szFacename;
905
906 /* _Pmpf((" Checking font: %s (Fam: %s), %d, %d, %d",
907 pcszCompare,
908 pfm2->szFamilyname,
909 pfm2->sNominalPointSize,
910 pfm2->lMaxBaselineExt,
911 pfm2->lAveCharWidth)); */
912
913 if (!strcmp(pcszCompare, pcszName))
914 {
915 /* _Pmpf((" Found font %s; slope %d, usWeightClass %d",
916 pfm2->szFacename,
917 pfm2->sCharSlope,
918 pfm2->usWeightClass)); */
919
920 if ((pfm2->fsDefn & FM_DEFN_OUTLINE) == 0)
921 {
922 // image (bitmap) font:
923 // check point size
924 if (pfm2->sNominalPointSize == lSize * 10)
925 {
926 // OK: check device resolutions, because
927 // normally, there are always several image
928 // fonts for different resolutions
929 // for bitmap fonts, there are normally two versions:
930 // one for low resolutions, one for high resolutions
931 if (!fQueriedDevice)
932 {
933 DevQueryCaps(GpiQueryDevice(hps),
934 CAPS_HORIZONTAL_FONT_RES,
935 2L,
936 alDevRes);
937 fQueriedDevice = TRUE;
938 }
939
940 if ( (pfm2->sXDeviceRes == alDevRes[0])
941 && (pfm2->sYDeviceRes == alDevRes[1])
942 )
943 {
944 // OK: use this for GpiCreateLogFont
945 pfa->lMaxBaselineExt = pfm2->lMaxBaselineExt;
946 pfa->lAveCharWidth = pfm2->lAveCharWidth;
947 // pfa->lMatch = pfm2->lMatch;
948
949 pfmFound = pfm2;
950 break;
951 }
952 }
953 }
954 else
955 // outline font:
956 if (pfmFound == NULL)
957 {
958 // no bitmap font found yet:
959
960 /*
961 #define FATTR_SEL_ITALIC 0x0001
962 #define FATTR_SEL_UNDERSCORE 0x0002
963 #define FATTR_SEL_OUTLINE 0x0008
964 #define FATTR_SEL_STRIKEOUT 0x0010
965 #define FATTR_SEL_BOLD 0x0020
966 */
967
968 if ( (!fFamily) // face mode is OK always
969 // V0.9.14 (2001-08-03) [umoeller]
970 || ( ( ( (usFormat & FATTR_SEL_BOLD)
971 && (pfm2->usWeightClass == 7) // bold
972 )
973 || ( (!(usFormat & FATTR_SEL_BOLD))
974 && (pfm2->usWeightClass == 5) // regular
975 )
976 )
977 && ( ( (usFormat & FATTR_SEL_ITALIC)
978 && (pfm2->sCharSlope != 0) // italics
979 )
980 || ( (!(usFormat & FATTR_SEL_ITALIC))
981 && (pfm2->sCharSlope == 0) // regular
982 )
983 )
984 )
985 )
986 {
987 // yes, we found a true font for that face:
988 pfmFound = pfm2;
989
990 // use this exact font for GpiCreateLogFont
991 pfa->lMatch = pfm2->lMatch;
992
993 // the following two might have been set
994 // for a bitmap font above
995 // V0.9.14 (2001-08-03) [umoeller]
996 pfa->lMaxBaselineExt = pfm2->lMaxBaselineExt;
997 pfa->lAveCharWidth = pfm2->lAveCharWidth;
998
999 pfa->idRegistry = pfm2->idRegistry;
1000
1001 // override NOMIX // V0.9.14 (2001-08-03) [umoeller]
1002 pfa->fsFontUse = FATTR_FONTUSE_OUTLINE;
1003
1004 // according to GPIREF, we must also specify
1005 // the full face name... geese!
1006 strcpy(pfa->szFacename, pfm2->szFacename);
1007 // unset flag in FATTRS, because this would
1008 // duplicate bold or italic
1009 pfa->fsSelection = 0;
1010
1011 // _Pmpf((" --> using it"));
1012 // but loop on, because we might have a bitmap
1013 // font which should take priority
1014 }
1015 }
1016 }
1017
1018 pfm2++;
1019 }
1020
1021 if (pfmFound)
1022 // FONTMETRICS found:
1023 // copy font metrics?
1024 if (pFontMetrics)
1025 memcpy(pFontMetrics, pfmFound, sizeof(FONTMETRICS));
1026
1027 // free the FONTMETRICS array
1028 free(pfm);
1029
1030 return (pfmFound != NULL);
1031}
1032
1033/*
1034 *@@ gpihSplitPresFont:
1035 * splits a presentation parameter font
1036 * string into the point size and face
1037 * name so that it can be passed to
1038 * gpihFindFont more easily.
1039 *
1040 *@@added V0.9.1 (2000-02-15) [umoeller]
1041 *@@changed V0.9.20 (2002-07-19) [umoeller]: optimized
1042 */
1043
1044BOOL gpihSplitPresFont(PSZ pszFontNameSize, // in: e.g. "12.Courier"
1045 PULONG pulSize, // out: integer point size (e.g. 12);
1046 // ptr must be specified
1047 PSZ *ppszFaceName) // out: ptr into pszFontNameSize
1048 // (e.g. "Courier")
1049{
1050 PCHAR pcDot;
1051 if ( (pszFontNameSize)
1052 && (pcDot = strchr(pszFontNameSize, '.'))
1053 )
1054 {
1055 // _Pmpf(("Found font PP: %s", pszFontFound));
1056 // sscanf(pszFontNameSize, "%lu", pulSize);
1057 *pulSize = atoi(pszFontNameSize); // V0.9.20 (2002-07-19) [umoeller]
1058 *ppszFaceName = pcDot + 1;
1059 return TRUE;
1060 }
1061
1062 return FALSE;
1063}
1064
1065/*
1066 *@@ gpihLockLCIDs:
1067 * requests the mutex for serializing the
1068 * lcids.
1069 *
1070 * With GPI, lcids are a process-wide resource and not
1071 * guaranteed to be unique. In the worst case, while your
1072 * font routines are running, another thread modifies the
1073 * lcids and you get garbage. If your fonts suddenly
1074 * turn to "System Proportional", you know this has
1075 * happened.
1076 *
1077 * As a result, whenever you work on lcids, request this
1078 * mutex during your processing. If you do this consistently
1079 * across all your code, you should be safe.
1080 *
1081 * gpihFindFont uses this mutex. If you call GpiCreateLogFont
1082 * yourself somewhere, you should do this under the protection
1083 * of this function.
1084 *
1085 * Call gpihUnlockLCIDs to unlock.
1086 *
1087 *@@added V0.9.9 (2001-04-01) [umoeller]
1088 */
1089
1090BOOL gpihLockLCIDs(VOID)
1091{
1092 if (!G_hmtxLCIDs)
1093 // first call: create
1094 return !DosCreateMutexSem(NULL,
1095 &G_hmtxLCIDs,
1096 0,
1097 TRUE); // request!
1098
1099 // subsequent calls: request
1100 return !DosRequestMutexSem(G_hmtxLCIDs, SEM_INDEFINITE_WAIT);
1101}
1102
1103/*
1104 *@@ UnlockLCIDs:
1105 * releases the mutex for serializing the
1106 * lcids.
1107 *
1108 *@@added V0.9.9 (2001-04-01) [umoeller]
1109 */
1110
1111VOID gpihUnlockLCIDs(VOID)
1112{
1113 DosReleaseMutexSem(G_hmtxLCIDs);
1114}
1115
1116/*
1117 *@@ gpihQueryNextLCID:
1118 * returns the next available lcid for the given HPS.
1119 * Actually, it's the next available lcid for the
1120 * entire process, since there can be only 255 altogether.
1121 * Gets called by gpihFindFont automatically.
1122 *
1123 * WARNING: This function by itself is not thread-safe.
1124 * See gpihLockLCIDs for how to serialize this.
1125 *
1126 * Code was extensively re-tested, works (V0.9.12 (2001-05-31) [umoeller]).
1127 *
1128 *@@added V0.9.3 (2000-05-06) [umoeller]
1129 *@@changed V0.9.9 (2001-04-01) [umoeller]: removed all those sick sub-allocs
1130 */
1131
1132LONG gpihQueryNextFontID(HPS hps)
1133{
1134 LONG lcidNext = -1;
1135
1136 LONG lCount = GpiQueryNumberSetIds(hps);
1137 // the number of local identifiers
1138 // (lcids) currently in use, and
1139 // therefore the maximum number
1140 // of objects for which information
1141 // can be returned
1142
1143 // _Pmpf((__FUNCTION__ ": Entering"));
1144
1145 if (lCount == 0)
1146 {
1147 // none in use yet:
1148 lcidNext = 15;
1149
1150 // _Pmpf((" no lcids in use"));
1151 }
1152 else
1153 {
1154 // #define GQNCL_BLOCK_SIZE 400*sizeof(LONG)
1155
1156 PLONG alTypes = NULL; // object types
1157 PSTR8 aNames = NULL; // font names
1158 PLONG allcids = NULL; // local identifiers
1159
1160 if ( (alTypes = (PLONG)malloc(lCount * sizeof(LONG)))
1161 && (aNames = (PSTR8)malloc(lCount * sizeof(STR8)))
1162 && (allcids = (PLONG)malloc(lCount * sizeof(LONG)))
1163 )
1164 {
1165 if (GpiQuerySetIds(hps,
1166 lCount,
1167 alTypes,
1168 aNames,
1169 allcids))
1170 {
1171 // FINALLY we have all the lcids in use.
1172 BOOL fContinue = TRUE;
1173 lcidNext = 15;
1174
1175 // _Pmpf((" %d fonts in use, browsing...", lCount));
1176
1177 // now, check if this lcid is in use already:
1178 while (fContinue)
1179 {
1180 BOOL fFound = FALSE;
1181 ULONG ul;
1182 fContinue = FALSE;
1183 for (ul = 0;
1184 ul < lCount;
1185 ul++)
1186 {
1187 if (allcids[ul] == lcidNext)
1188 {
1189 fFound = TRUE;
1190 break;
1191 }
1192 }
1193
1194 if (fFound)
1195 {
1196 // lcid found:
1197 // try next higher one
1198
1199 // _Pmpf((" %d is busy...", lcidNext));
1200
1201 lcidNext++;
1202 fContinue = TRUE;
1203 }
1204 // else
1205 // else: return that one
1206 // _Pmpf((" %d is free", lcidNext));
1207 }
1208 }
1209 }
1210
1211 if (alTypes)
1212 free(alTypes);
1213 if (aNames)
1214 free(aNames);
1215 if (allcids)
1216 free(allcids);
1217 }
1218
1219 // _Pmpf((__FUNCTION__ ": Returning lcid %d", lcidNext));
1220
1221 return lcidNext;
1222}
1223
1224/*
1225 *@@ gpihCreateFont:
1226 *
1227 *@@added V0.9.14 (2001-08-03) [umoeller]
1228 */
1229
1230LONG gpihCreateFont(HPS hps,
1231 FATTRS *pfa)
1232{
1233 LONG lLCIDReturn = 0;
1234
1235 if (gpihLockLCIDs()) // V0.9.9 (2001-04-01) [umoeller]
1236 {
1237 // new logical font ID: last used plus one
1238 lLCIDReturn = gpihQueryNextFontID(hps);
1239
1240 GpiCreateLogFont(hps,
1241 NULL, // don't create "logical font name" (STR8)
1242 lLCIDReturn,
1243 pfa);
1244
1245 gpihUnlockLCIDs();
1246 }
1247
1248 return lLCIDReturn;
1249}
1250
1251/*
1252 *@@ gpihFindFont:
1253 * this returns a new logical font ID (LCID) for the specified
1254 * font by calling gpihMatchFont first and then
1255 * GpiCreateLogFont to create a logical font from the
1256 * data returned.
1257 *
1258 * See gpihMatchFont for additional explanations.
1259 *
1260 * To then use the font whose LCID has been returned by this
1261 * function for text output, call:
1262 + GpiSetCharSet(hps, lLCIDReturned);
1263 *
1264 * <B>Font Point Sizes:</B>
1265 *
1266 * 1) For image (bitmap) fonts, the size is fixed, and
1267 * you can directly draw after the font has been
1268 * selected. GpiSetCharBox has no effect on image
1269 * fonts unless you switch to character mode 2
1270 * (if you care for that: GpiSetCharMode).
1271 *
1272 * 2) For outline fonts however, you need to define a
1273 * character box if you want to output text in a
1274 * size other than the default size. This is almost
1275 * as bad a mess as this function, so gpihSetPointSize
1276 * has been provided for this. See remarks there.
1277 *
1278 * <B>Example:</B>
1279 *
1280 * This example prints text in "24.Courier".
1281 *
1282 + PSZ pszOutput = "Test output";
1283 + FONTMETRICS FontMetrics;
1284 + LONG lLCID = gpihFindFont(hps,
1285 + 24, // point size
1286 + FALSE, // face, not family
1287 + "Courier",
1288 + 0,
1289 + &FontMetrics);
1290 + if (lLCID)
1291 + {
1292 + // no error:
1293 + GpiSetCharSet(hps, lLCID);
1294 + if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
1295 + // outline font found (should be the case):
1296 + gpihSetPointSize(hps, 24);
1297 + }
1298 + GpiCharString(hps, strlen(pszOutput), pszOutput);
1299 *
1300 * <B>Details:</B>
1301 *
1302 * First, GpiQueryFonts is called to enumerate all the fonts on
1303 * the system. We then evaluate the awful FONTMETRICS data
1304 * of those fonts to perform a "real" match.
1305 *
1306 * If that fails, we allow GPI to do a "close" match based
1307 * on the input values. This might result in the system
1308 * default font (System Proportional) to be found, in the
1309 * worst case. But even then, a new LCID is returned.
1310 *
1311 * The "close match" comes in especially when using the
1312 * font attributes (bold, italics) and we are unable to
1313 * find the correct outline font for that. Unfortunately,
1314 * the information in FONTMETRICS.fsSelection is wrong,
1315 * wrong, wrong for the large majority of fonts. (For
1316 * example, I get the "bold" flag set for regular fonts,
1317 * and vice versa.) So we attempt to use the other fields,
1318 * but this doesn't always help. Not even Netscape gets
1319 * this right.
1320 *
1321 * <B>Font faces:</B>
1322 *
1323 * This is terribly complicated as well. You need to
1324 * differentiate between "true" emphasis faces (that
1325 * is, bold and italics are implemented thru separate
1326 * font files) and the ugly GPI simulation, which simply
1327 * makes the characters wider or shears them.
1328 *
1329 * This function even finds true "bold" and "italic" faces
1330 * for outline fonts. To do this, always specify the "family"
1331 * name as pszFaceName (e.g. "Courier" instead of "Courier
1332 * Bold") and set the flags for usFormat (e.g. FATTR_SEL_BOLD).
1333 * Note that this implies that you need call this function
1334 * twice to create two logical fonts for regular and bold faces.
1335 *
1336 * If a "true" emphasis font is not found, the GPI simulation
1337 * is enabled.
1338 *
1339 * <B>Remarks:</B>
1340 *
1341 * 1) This function _always_ creates a new logical font,
1342 * whose ID is returned, even if the specified font
1343 * was not found or a "close match" was performed by
1344 * GPI. As a result, do not call this function twice
1345 * for the same font specification, because there are
1346 * only 255 logical font IDs for each process.
1347 *
1348 * 2) Since this function always creates an LCID,
1349 * you should _always_ free the LCID later.
1350 * This is only valid if the font is no longer selected
1351 * into any presentation space. So use these calls:
1352 + GpiSetCharSet(hps, LCID_DEFAULT);
1353 + GpiDeleteSetId(hps, lLCIDReturnedByThis);
1354 *
1355 * 3) Using this function, bitmap fonts will have priority
1356 * over outline fonts of the same face name. This is
1357 * how the WPS does it too. This is most obvious with
1358 * the "Helv" font, which exists as a bitmap font for
1359 * certain point sizes only.
1360 *
1361 * 4) Since logical font IDs are shared across the
1362 * process, a mutex is requested while the lcids are
1363 * being queried and/or manipulated. In other words,
1364 * this func is now thread-safe (V0.9.9).
1365 *
1366 * This calls gpihQueryNextFontID in turn to find the
1367 * next free lcid. See remarks there.
1368 *
1369 * <B>Font metrics:</B>
1370 *
1371 * The important values in the returned FONTMETRICS are
1372 * like this (according to PMREF):
1373 *
1374 + ÉÍ ________________________________________________
1375 + º
1376 + º lExternalLeading, according to font designer.
1377 + º ________________________________________________ Í»
1378 + ÈÍ º
1379 + # # º
1380 + ## ## º lMaxAscender (of entire;
1381 + ÉÍ _______________ # # # # º font); this can be > capital
1382 + º #### # # # # º letters because miniscules
1383 + º # # # # # º can exceed that.
1384 + º lXHeight # # # # º
1385 + º # # # # º
1386 + º # # # # º
1387 + º _______________#####________#_______#___ baseline Í»
1388 + ÈÍ # º
1389 + # º lMaxDescender
1390 + ______________ ####______________________________ ͌
1391 +
1392 *
1393 * In turn, lMaxBaselineExt is lMaxAscender + lMaxDescender.
1394 *
1395 * Soooo... to find out about the optimal line spacing, GPIREF
1396 * recommends to use lMaxBaselineExt + lExternalLeading.
1397 *
1398 *@@added V0.9.0 [umoeller]
1399 *@@changed V0.9.3 (2000-05-06) [umoeller]: didn't work for more than one font; now using gpihQueryNextFontID
1400 *@@changed V0.9.3 (2000-05-06) [umoeller]: usFormat didn't work; fixed
1401 *@@changed V0.9.4 (2000-08-08) [umoeller]: added fFamily
1402 *@@changed V0.9.9 (2001-04-01) [umoeller]: made this thread-safe, finally
1403 *@@changed V0.9.14 (2001-08-01) [umoeller]: some optimizations
1404 */
1405
1406LONG gpihFindFont(HPS hps, // in: HPS for font selection
1407 LONG lSize, // in: font point size
1408 BOOL fFamily, // in: if TRUE, pszName specifies font family;
1409 // if FALSE, pszName specifies font face
1410 const char *pcszName, // in: font family or face name (without point size)
1411 USHORT usFormat, // in: none, one or several of:
1412 // -- FATTR_SEL_ITALIC
1413 // -- FATTR_SEL_UNDERSCORE (underline)
1414 // -- FATTR_SEL_BOLD
1415 // -- FATTR_SEL_STRIKEOUT
1416 // -- FATTR_SEL_OUTLINE (hollow)
1417 PFONTMETRICS pFontMetrics) // out: font metrics of created font (optional)
1418{
1419 FATTRS FontAttrs;
1420
1421 gpihMatchFont(hps,
1422 lSize,
1423 fFamily,
1424 pcszName,
1425 usFormat,
1426 &FontAttrs,
1427 pFontMetrics);
1428
1429 return gpihCreateFont(hps,
1430 &FontAttrs);
1431
1432 // _Pmpf((__FUNCTION__ ": returning lcid %d", lLCIDReturn));
1433}
1434
1435/*
1436 *@@ gpihFindPresFont:
1437 * similar to gpihFindFont, but this one evaluates
1438 * the PP_FONTNAMESIZE presentation parameter of the
1439 * specified window instead. If that one is not set,
1440 * the specified default font is used instead.
1441 *
1442 * Note that as opposed to gpihFindFont, this one
1443 * takes a "8.Helv"-type string as input.
1444 *
1445 * See gpihFindFont for additional remarks, which
1446 * gets called by this function.
1447 *
1448 * Again, if an outline font has been returned, you
1449 * must also set the "character box" for the HPS, or
1450 * your text will always have the same point size.
1451 * Use gpihSetPointSize for that, using the presparam's
1452 * point size, which is returned by this function
1453 * into *plSize, if (plSize != NULL):
1454 +
1455 + FONTMETRICS FontMetrics;
1456 + LONG lPointSize;
1457 + LONG lLCID = gpihFindPresFont(hwnd, hps, "8.Helv",
1458 + &FontMetrics,
1459 + &lPointSize);
1460 + GpiSetCharSet(hps, lLCID);
1461 + if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
1462 + gpihSetPointSize(hps, lPointSize);
1463 *
1464 * If errors occur, e.g. if the font string does not
1465 * conform to the "size.face" format, null is returned.
1466 *
1467 *@@added V0.9.0 [umoeller]
1468 *@@changed V0.9.9 (2001-04-01) [umoeller]: now supporting NULLHANDLE hwnd
1469 */
1470
1471LONG gpihFindPresFont(HWND hwnd, // in: window to search for presparam or NULLHANDLE
1472 BOOL fInherit, // in: search parent windows too?
1473 HPS hps, // in: HPS for font selection
1474 const char *pcszDefaultFont, // in: default font if not found (i.e. "8.Helv")
1475 PFONTMETRICS pFontMetrics, // out: font metrics of created font (optional)
1476 PLONG plSize) // out: presparam's point size (optional)
1477{
1478 CHAR szPPFont[200] = "";
1479 const char *pcszFontFound = 0;
1480 CHAR szFaceName[300] = "";
1481 ULONG ulFontSize = 0;
1482
1483 if ( (hwnd) // V0.9.9 (2001-04-01) [umoeller]
1484 && (WinQueryPresParam(hwnd,
1485 PP_FONTNAMESIZE, // first PP to query
1486 0, // second PP to query
1487 NULL, // out: which one is returned
1488 (ULONG)sizeof(szPPFont), // in: buffer size
1489 (PVOID)&szPPFont, // out: PP value returned
1490 (fInherit)
1491 ? 0
1492 : QPF_NOINHERIT))
1493 )
1494 // PP found:
1495 pcszFontFound = szPPFont;
1496 else
1497 pcszFontFound = pcszDefaultFont;
1498
1499 if (pcszFontFound)
1500 {
1501 const char *pcDot = strchr(pcszFontFound, '.');
1502 if (pcDot)
1503 {
1504 // _Pmpf(("Found font PP: %s", pszFontFound));
1505 sscanf(pcszFontFound, "%lu", &ulFontSize);
1506 if (plSize)
1507 *plSize = ulFontSize;
1508 strcpy(szFaceName, pcDot + 1);
1509 return gpihFindFont(hps,
1510 ulFontSize,
1511 FALSE, // face, not family name
1512 szFaceName,
1513 0,
1514 pFontMetrics);
1515 }
1516 }
1517
1518 return 0;
1519}
1520
1521/*
1522 *@@ gpihSetPointSize:
1523 * this invokes GpiSetCharBox on the given HPS to
1524 * set the correct "character box" with the proper
1525 * parameters.
1526 *
1527 * This is necessary for text output with outline
1528 * fonts. Bitmap fonts have a fixed size, and
1529 * calling this function is not necessary for them.
1530 *
1531 * Unfortunately, IBM has almost not documented
1532 * how to convert nominal point sizes to the
1533 * correct character cell values. This involves
1534 * querying the output device (DevQueryCaps).
1535 *
1536 * I have found one hint for this procedure in GPIREF
1537 * (chapter "Character string primitives", "Using...",
1538 * "Drawing text"), but the example code given
1539 * there is buggy, because it must be "72" instead
1540 * of "720". #%(%!"õ!!.
1541 *
1542 * So here we go. If you want to output text in,
1543 * say, "Courier" and 24 points (nominal point size),
1544 * select the font into your HPS and call
1545 + gpihSetPointSize(hps, 24)
1546 * and you're done. See gpihFindFont for a complete
1547 * example.
1548 *
1549 * Call this function as many times as needed. This
1550 * consumes no resources at all.
1551 *
1552 * This returns the return value of GpiSetCharBox.
1553 *
1554 *@@added V0.9.0 [umoeller]
1555 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed bad rounding errors
1556 */
1557
1558BOOL gpihSetPointSize(HPS hps, // in: presentation space for output
1559 LONG lPointSize) // in: desired nominal point size
1560{
1561 SIZEF box;
1562 HDC hdc = GpiQueryDevice(hps); // get the HDC from the HPS
1563 LONG alDevRes[2];
1564 DevQueryCaps(GpiQueryDevice(hps), // get the HDC from the HPS
1565 CAPS_HORIZONTAL_FONT_RES,
1566 2L,
1567 alDevRes);
1568
1569 // V0.9.14: this code didn't work... it produced rounding
1570 // errors which set the font size different from what
1571 // it should be according to the WPS font palette
1572 /* box.cx = MAKEFIXED((lPointSize * alDevRes[0]) / 72, 0);
1573 box.cy = MAKEFIXED((lPointSize * alDevRes[1]) / 72, 0); */
1574
1575 // V0.9.14 (2001-08-03) [umoeller]: now using this one
1576 // instead
1577 lPointSize *= 65536;
1578 box.cx = (FIXED)(lPointSize / 72 * alDevRes[0]);
1579 box.cy = (FIXED)(lPointSize / 72 * alDevRes[1]);
1580
1581 return GpiSetCharBox(hps, &box);
1582}
1583
1584/*
1585 *@@ gpihQueryLineSpacing:
1586 * this returns the optimal line spacing for text
1587 * output with the current HPS; this is computed
1588 * by evaluating those incredible FONTMETRICS.
1589 *
1590 * This might be helpful if you write text to the
1591 * screen yourself and need the height of a text
1592 * line to advance to the next.
1593 *
1594 *@@changed V0.9.7 (2000-12-20) [umoeller]: removed psz param
1595 */
1596
1597LONG gpihQueryLineSpacing(HPS hps)
1598{
1599 FONTMETRICS fm;
1600
1601 if (GpiQueryFontMetrics(hps, sizeof(FONTMETRICS), &fm))
1602 return ( ( fm.lMaxBaselineExt // max vertical font space
1603 +fm.lExternalLeading) // space advised by font designer
1604 );
1605
1606 return 15;
1607}
1608
1609/*
1610 *@@category: Helpers\PM helpers\GPI helpers\Bitmaps/Icons
1611 */
1612
1613/* ******************************************************************
1614 *
1615 * Bitmap helpers
1616 *
1617 ********************************************************************/
1618
1619/*
1620 *@@ gpihCreateMemPS:
1621 * creates a memory device context and presentation space so
1622 * that they are compatible with the screen device context and
1623 * presentation space. These are stored in *hdcMem and *hpsMem.
1624 *
1625 * This is a one-shot function for the standard code that is
1626 * always needed when working with bitmaps in a memory device
1627 * context.
1628 *
1629 * psizlPage must point to a SIZEL structure containing the
1630 * width and height for the memory PS. Specify the size of
1631 * the future bitmap here. Specify {0, 0} to get a PS with
1632 * the size of the full screen, which consumes quite a bit
1633 * of memory though.
1634 *
1635 * Returns FALSE upon errors. In that case, both hdcMem and
1636 * hpsMem are set to NULLHANDLE.
1637 *
1638 * To cleanup after this function has returned TRUE, use the
1639 * following:
1640 + GpiDestroyPS(hpsMem);
1641 + DevCloseDC(hdcMem);
1642 *
1643 *@@changed V0.9.3 (2000-05-18) [umoeller]: added psiszlPage
1644 */
1645
1646BOOL gpihCreateMemPS(HAB hab, // in: anchor block
1647 PSIZEL psizlPage, // in: width and height for mem PS
1648 HDC *hdcMem, // out: memory DC or NULLHANDLE upon errors
1649 HPS *hpsMem) // out: memory PS or NULLHANDLE upon errors
1650{
1651 BOOL brc = FALSE;
1652 PSZ pszData[4] = { "Display", NULL, NULL, NULL };
1653
1654 // create new memory DC
1655 if ((*hdcMem = DevOpenDC(hab,
1656 OD_MEMORY, // create memory DC
1657 "*", // token: do not take INI info
1658 4, // item count in pszData
1659 (PDEVOPENDATA)pszData,
1660 NULLHANDLE))) // compatible with screen
1661 {
1662 // memory DC created successfully:
1663 // create compatible PS
1664 if ((*hpsMem = GpiCreatePS(hab,
1665 *hdcMem, // HDC to associate HPS with (GPIA_ASSOC);
1666 // mandatory for GPIT_MICRO
1667 psizlPage, // is (0, 0) == screen size
1668 PU_PELS // presentation page units: pixels
1669 | GPIA_ASSOC // associate with hdcMem (req. for GPIT_MICRO)
1670 | GPIT_MICRO))) // micro presentation space
1671 brc = TRUE;
1672 else
1673 {
1674 // error (hpsMem == NULLHANDLE):
1675 // close memory DC again
1676 DevCloseDC(*hdcMem);
1677 *hdcMem = NULLHANDLE;
1678 }
1679 }
1680
1681 return brc;
1682}
1683
1684/*
1685 *@@ gpihCreateBitmap:
1686 * calls gpihCreateBitmap2 with cPlanes and cBitCount == 0
1687 * for compatibility with exports. Widgets might
1688 * have used this func.
1689 *
1690 *@@changed V0.9.0 [umoeller]: function prototype changed to cx and cy
1691 *@@changed V0.9.16 (2001-12-18) [umoeller]: now using optimized gpihCreateBitmap2
1692 */
1693
1694HBITMAP gpihCreateBitmap(HPS hpsMem, // in: memory DC
1695 ULONG cx, // in: width of new bitmap
1696 ULONG cy) // in: height of new bitmap
1697{
1698 return gpihCreateBitmap2(hpsMem,
1699 cx,
1700 cy,
1701 0,
1702 0); // current screen bit count
1703}
1704
1705static const BITMAPINFOHEADER2 G_bih2Template[] =
1706 {
1707 sizeof(BITMAPINFOHEADER2), // ULONG cbFix;
1708 0, // ULONG cx; // tbr
1709 0, // ULONG cy; // tbr
1710 0, // USHORT cPlanes; // tbr
1711 0, // USHORT cBitCount; // tbr
1712 BCA_UNCOMP, // ULONG ulCompression;
1713 0, // ULONG cbImage;
1714 70, // ULONG cxResolution;
1715 70, // ULONG cyResolution;
1716 2, // ULONG cclrUsed;
1717 0, // ULONG cclrImportant;
1718 BRU_METRIC, // USHORT usUnits;
1719 // measure units for cxResolution/cyResolution: pels per meter
1720 0, // USHORT usReserved;
1721 BRA_BOTTOMUP, // USHORT usRecording;
1722 // scan lines are bottom to top (default)
1723
1724 BRH_NOTHALFTONED, // USHORT usRendering;
1725 // other algorithms aren't documented anyway
1726 0, // ULONG cSize1;
1727 // parameter for halftoning (undocumented anyway)
1728 0, // ULONG cSize2;
1729 // parameter for halftoning (undocumented anyway)
1730 BCE_RGB, // ULONG ulColorEncoding;
1731 // only possible value
1732 0 // ULONG ulIdentifier;
1733 // application-specific data
1734 };
1735
1736/*
1737 *@@ gpihCreateBitmap2:
1738 * creates a new bitmap for a given memory PS.
1739 * This bitmap will have the cPlanes and bitcount
1740 * which are found in the memory PS.
1741 * For all the mysterious other values, we use
1742 * fixed default values, this doesn't seem to hurt.
1743 *
1744 * Note that the bitmap is _not_ yet selected into
1745 * the specified memory PS. You must call
1746 + GpiSetBitmap(hpsMem, hbm)
1747 * to do this.
1748 *
1749 * Returns the bitmap handle or NULLHANDLE upon errors.
1750 *
1751 *@@added V0.9.16 (2001-12-18) [umoeller]
1752 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
1753 */
1754
1755HBITMAP gpihCreateBitmap2(HPS hpsMem, // in: memory DC
1756 ULONG cx, // in: width of new bitmap
1757 ULONG cy, // in: height of new bitmap
1758 ULONG cPlanes, // in: color planes (usually 1); if 0, current screen is used
1759 ULONG cBitCount) // in: either 1, 4, or 24; if 0, current screen value
1760{
1761 LONG alData[2];
1762 BITMAPINFOHEADER2 bih2;
1763
1764 // determine the device's plane/bit-count format;
1765 // alData[0] then has cPlanes,
1766 // alData[1] has cBitCount
1767 if (GpiQueryDeviceBitmapFormats(hpsMem, 2, alData))
1768 {
1769 // copy values from global template V1.0.1 (2002-11-30) [umoeller]
1770 memcpy(&bih2, &G_bih2Template, sizeof(bih2));
1771
1772 // fix variable fields V1.0.1 (2002-11-30) [umoeller]
1773 bih2.cx = cx;
1774 bih2.cy = cy;
1775 bih2.cPlanes = (cPlanes) ? cPlanes : alData[0];
1776 bih2.cBitCount = (cBitCount) ? cBitCount : alData[1];
1777 bih2.cbImage = ( ( (bih2.cx
1778 * (1 << bih2.cPlanes)
1779 * (1 << bih2.cBitCount)
1780 ) + 31
1781 ) / 32
1782 ) * bih2.cy;
1783
1784 // create a bit map that is compatible with the display
1785 return GpiCreateBitmap(hpsMem,
1786 &bih2,
1787 0, // do not initialize
1788 NULL, // init data
1789 NULL);
1790 }
1791
1792 return NULLHANDLE;
1793}
1794
1795/*
1796 *@@ gpihQueryBitmapSize:
1797 * returns the width and height of the given bitmap.
1798 *
1799 *@@added V1.0.1 (2002-11-30) [umoeller]
1800 */
1801
1802BOOL gpihQueryBitmapSize(HBITMAP hbm, // in: bitmap handle for query
1803 PSIZEL pszl) // out: size (cx, cy) of bitmap
1804{
1805 BITMAPINFOHEADER2 bmi2;
1806 // query bitmap info
1807 bmi2.cbFix = sizeof(bmi2);
1808 if ( (hbm)
1809 && (GpiQueryBitmapInfoHeader(hbm, &bmi2))
1810 )
1811 {
1812 pszl->cx = bmi2.cx;
1813 pszl->cy = bmi2.cy;
1814 return TRUE;
1815 }
1816
1817 return FALSE;
1818}
1819
1820/*
1821 *@@ gpihCreateHalftonedBitmap:
1822 * this creates a half-toned copy of the
1823 * input bitmap by doing the following:
1824 *
1825 * 1) create a new bitmap with the size of hbmSource;
1826 * 2) copy hbmSource to the new bitmap (using GpiWCBitBlt);
1827 * 3) overpaint every other pixel with lColorGray.
1828 *
1829 * Note that the memory DC is switched to RGB mode, so
1830 * lColorGray better be an RGB color.
1831 *
1832 * Note: hbmSource must _not_ be selected into any device
1833 * context, or otherwise GpiWCBitBlt will fail.
1834 *
1835 * This returns the new bitmap or NULLHANDLE upon errors.
1836 *
1837 *@added V0.9.0
1838 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
1839 */
1840
1841HBITMAP gpihCreateHalftonedBitmap(HAB hab, // in: anchor block
1842 HBITMAP hbmSource, // in: source bitmap
1843 LONG lColorGray) // in: color used for gray
1844{
1845 HBITMAP hbmReturn = NULLHANDLE;
1846
1847 HDC hdcMem;
1848 HPS hpsMem;
1849 SIZEL szlPage;
1850
1851 if (gpihQueryBitmapSize(hbmSource, &szlPage)) // V1.0.1 (2002-11-30) [umoeller]
1852 {
1853 if (gpihCreateMemPS(hab, &szlPage, &hdcMem, &hpsMem))
1854 {
1855 if ((hbmReturn = gpihCreateBitmap(hpsMem,
1856 szlPage.cx,
1857 szlPage.cy)))
1858 {
1859 if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
1860 {
1861 POINTL aptl[4];
1862
1863 // step 1: copy bitmap
1864 memset(aptl, 0, sizeof(POINTL) * 4);
1865 // aptl[0]: target bottom-left, is all 0
1866 // aptl[1]: target top-right (inclusive!)
1867 aptl[1].x = szlPage.cx - 1;
1868 aptl[1].y = szlPage.cy - 1;
1869 // aptl[2]: source bottom-left, is all 0
1870
1871 // aptl[3]: source top-right (exclusive!)
1872 aptl[3].x = szlPage.cx;
1873 aptl[3].y = szlPage.cy;
1874 GpiWCBitBlt(hpsMem, // target HPS (bmp selected)
1875 hbmSource,
1876 4L, // must always be 4
1877 &aptl[0], // points array
1878 ROP_SRCCOPY,
1879 BBO_IGNORE);
1880 // doesn't matter here, because we're not stretching
1881
1882 // step 2: overpaint bitmap
1883 // with half-toned pattern
1884
1885 gpihSwitchToRGB(hpsMem);
1886
1887 GpiMove(hpsMem, &aptl[0]); // still 0, 0
1888 aptl[0].x = szlPage.cx - 1;
1889 aptl[0].y = szlPage.cy - 1;
1890 GpiSetColor(hpsMem, lColorGray);
1891 GpiSetPattern(hpsMem, PATSYM_HALFTONE);
1892 GpiBox(hpsMem,
1893 DRO_FILL, // interior only
1894 &aptl[0],
1895 0, 0); // no corner rounding
1896
1897 // unselect bitmap
1898 GpiSetBitmap(hpsMem, NULLHANDLE);
1899 } // end if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
1900 else
1901 {
1902 // error selecting bitmap:
1903 GpiDeleteBitmap(hbmReturn);
1904 hbmReturn = NULLHANDLE;
1905 }
1906 } // end if (hbmReturn = gpihCreateBitmap...
1907
1908 GpiDestroyPS(hpsMem);
1909 DevCloseDC(hdcMem);
1910 } // end if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
1911 } // end if (hbmSource)
1912
1913 return hbmReturn;
1914}
1915
1916/*
1917typedef struct _BITMAPARRAYFILEHEADER {
1918 USHORT usType; // Type of structure.
1919 ULONG cbSize; // Size of the BITMAPARRAYFILEHEADER structure in bytes.
1920 ULONG offNext; // Offset of the next BITMAPARRAYFILEHEADER structure from the start of the file.
1921 USHORT cxDisplay; // Device width, in pels.
1922 USHORT cyDisplay; // Device height, in pels.
1923 BITMAPFILEHEADER bfh; // Bitmap file header structure.
1924} BITMAPARRAYFILEHEADER;
1925
1926typedef struct _BITMAPARRAYFILEHEADER2 {
1927 USHORT usType; // Type of structure.
1928 ULONG cbSize; // Size of the BITMAPARRAYFILEHEADER2 structure in bytes.
1929 ULONG offNext; // Offset of the next BITMAPARRAYFILEHEADER2 structure from the start of the file.
1930 USHORT cxDisplay; // Device width, in pels.
1931 USHORT cyDisplay; // Device height, in pels.
1932 BITMAPFILEHEADER2 bfh2; // Bitmap file header structure.
1933} BITMAPARRAYFILEHEADER2;
1934
1935These two are binarily the same, except for the file header that is contained.
1936*/
1937
1938/* OLD FORMAT
1939
1940typedef struct _BITMAPFILEHEADER {
1941 USHORT usType; // Type of resource the file contains.
1942 ULONG cbSize; // Size of the BITMAPFILEHEADER structure in bytes.
1943 SHORT xHotspot; // Width of hotspot for icons and pointers.
1944 SHORT yHotspot; // Height of hotspot for icons and pointers.
1945 USHORT offBits; // Offset in bytes.
1946 BITMAPINFOHEADER bmp; // Bitmap information header structure.
1947
1948 typedef struct _BITMAPINFOHEADER {
1949 ULONG cbFix; // Length of structure.
1950 USHORT cx; // Bitmap width in pels.
1951 USHORT cy; // Bitmap height in pels.
1952 USHORT cPlanes; // Number of bit planes.
1953 USHORT cBitCount; // Number of bits per pel within a plane.
1954 } BITMAPINFOHEADER;
1955
1956} BITMAPFILEHEADER;
1957*/
1958
1959/* NEW FORMAT
1960
1961typedef struct _BITMAPFILEHEADER2 {
1962 USHORT usType; // Type of resource the file contains.
1963 ULONG cbSize; // Size of the BITMAPFILEHEADER2 structure in bytes.
1964 SHORT xHotspot; // Width of hotspot for icons and pointers.
1965 SHORT yHotspot; // Height of hotspot for icons and pointers.
1966 USHORT offBits; // Offset in bytes.
1967 BITMAPINFOHEADER2 bmp2; // Bitmap information header structure.
1968
1969 typedef struct _BITMAPINFOHEADER2 {
1970 ULONG cbFix; // Length of structure.
1971 ULONG cx; // Bitmap width in pels.
1972 ULONG cy; // Bitmap height in pels.
1973 USHORT cPlanes; // Number of bit planes.
1974 USHORT cBitCount; // Number of bits per pel within a plane.
1975 ULONG ulCompression; // Compression scheme used to store the bit map.
1976 ULONG cbImage; // Length of bitmap storage data, in bytes.
1977 ULONG cxResolution; // Horizontal component of the resolution of target device.
1978 ULONG cyResolution; // Vertical component of the resolution of target device.
1979 ULONG cclrUsed; // Number of color indexes used.
1980 ULONG cclrImportant; // Minimum number of color indexes for satisfactory appearance of the bit map.
1981 USHORT usUnits; // Units of measure.
1982 USHORT usReserved; // Reserved.
1983 USHORT usRecording; // Recording algorithm.
1984 USHORT usRendering; // Halftoning algorithm.
1985 ULONG cSize1; // Size value 1.
1986 ULONG cSize2; // Size value 2.
1987 ULONG ulColorEncoding; // Color encoding.
1988 ULONG ulIdentifier; // Reserved for application use.
1989 } BITMAPINFOHEADER2;
1990
1991 Because of the unfortunate replacement of USHORTs with ULONGs for
1992 cx and cy in the info header, the cx, cy, and cBitCount data is
1993 NOT the same between old and new formats. Well, IBM, good job.
1994 And ICONEDIT _still_ writes the old format, too.
1995
1996} BITMAPFILEHEADER2;
1997
1998*/
1999
2000/*
2001 *@@ gpihCreateBitmapFromFile:
2002 * creates a new HBITMAP from the given bitmap data,
2003 * which is assumed to be in a standard OS/2 1.3 or
2004 * 2.0 bitmap file format. That is, it should start
2005 * with either a BITMAPFILEHEADER or BITMAPFILEHEADER2
2006 * (if single bitmap) or a BITMAPARRAYFILEHEADER or
2007 * BITMAPARRAYFILEHEADER2 (if bitmap array) and all
2008 * the bitmap bits following. This format is also
2009 * used by bitmap resources.
2010 *
2011 * If the file contains only a single bitmap,
2012 * this bitmap is used.
2013 *
2014 * If it contains a bitmap array, we use the
2015 * "best bitmap" in the array, which is determined
2016 * from the following criteria (in this order):
2017 *
2018 * -- a device-dependent bitmap, if its device
2019 * resolution is not too large and the given
2020 * HPS can display all its colors;
2021 *
2022 * -- a device-dependent bitmap, if its device
2023 * resolution is not too large, even if the
2024 * given HPS cannot display all its colors;
2025 *
2026 * -- a device-independent bitmap, if the given
2027 * HPS can display all its colors;
2028 *
2029 * -- the first device-independent bitmap in
2030 * the file;
2031 *
2032 * -- the first bitmap in the file.
2033 *
2034 * Support for bitmap arrays was added with V0.9.19.
2035 * I'm not quite sure if the above is the same way
2036 * of selecting the "best bitmap" that GpiLoadBitmap
2037 * would do, but without any documentation, who is
2038 * supposed to know. It appears to me however that
2039 * GpiLoadBitmap always does an _exact_ match of
2040 * device resolutions (in contrast to this function).
2041 * In other words, if you're running at 1152x864
2042 * and the device-dependent bitmap is for 1024x768,
2043 * _this_ function would use it, while GpiLoadBitmap
2044 * might load a device-independent VGA bitmap
2045 * instead. (Looks broken, if you ask me.)
2046 *
2047 * Returns:
2048 *
2049 * -- NO_ERROR
2050 *
2051 * -- ERROR_INVALID_DATA: we can't understand this format.
2052 *
2053 *@@added V0.9.20 (2002-07-19) [umoeller]
2054 */
2055
2056APIRET gpihCreateBitmapFromFile(HBITMAP *phbm, // out: bitmap if NO_ERROR
2057 HPS hps, // in: HPS for bmp
2058 PBYTE pData) // in: bitmap data
2059{
2060 APIRET arc = NO_ERROR;
2061
2062 PBITMAPFILEHEADER2 pbfh = (PBITMAPFILEHEADER2)pData;
2063
2064 // check bitmap magic codes
2065 switch (pbfh->usType)
2066 {
2067 case BFT_BMAP: // "BM"
2068 // single bitmap in file (no array):
2069 if (!(*phbm = GpiCreateBitmap(hps,
2070 &pbfh->bmp2,
2071 CBM_INIT,
2072 (PBYTE)pbfh + pbfh->offBits,
2073 (PBITMAPINFO2)&pbfh->bmp2)))
2074 arc = ERROR_INVALID_DATA;
2075 break;
2076
2077 case BFT_BITMAPARRAY: // "BA"
2078 {
2079 // define a handy union for all the above crap
2080 typedef union
2081 {
2082 BITMAPFILEHEADER Old;
2083 BITMAPFILEHEADER2 New;
2084 } BMPUNION, *PBMPUNION;
2085
2086 PBMPUNION
2087 puFirstDI = NULL, // first device-independent bitmap
2088 puBestDI = NULL, // best device-independent bitmap
2089 puFirstDD = NULL, // first device-dependent bitmap
2090 puBestDD = NULL, // best device-dependent bitmap
2091 puFirstAny = NULL, // first bitmap of any type
2092 puUse;
2093
2094 // get device resolution for this HPS
2095 // so we can select the "best bitmap"
2096 #define GET_CAPS_FIRST CAPS_WIDTH
2097 #define GET_CAPS_LAST CAPS_COLOR_BITCOUNT
2098 #define GET_NO_CAPS GET_CAPS_LAST - GET_CAPS_FIRST + 1
2099
2100 LONG alCaps[GET_NO_CAPS];
2101 PBITMAPARRAYFILEHEADER2 pba = (PBITMAPARRAYFILEHEADER2)pData;
2102
2103 DevQueryCaps(GpiQueryDevice(hps),
2104 GET_CAPS_FIRST,
2105 GET_NO_CAPS,
2106 alCaps);
2107
2108 #define BITCOUNT alCaps[CAPS_COLOR_BITCOUNT - GET_CAPS_FIRST]
2109 #define WIDTH alCaps[CAPS_WIDTH - GET_CAPS_FIRST]
2110 #define HEIGHT alCaps[CAPS_HEIGHT - GET_CAPS_FIRST]
2111
2112 // for-all-bitmaps-in-array loop
2113 while (pba)
2114 {
2115 PBMPUNION puThis = (PBMPUNION)&pba->bfh2;
2116
2117 LONG cx = 0,
2118 cy,
2119 cBitCount;
2120
2121 // ignore this if the type isn't "BM"
2122 if (puThis->Old.usType == BFT_BMAP)
2123 {
2124 // fill the three, but watch out, the offsets are
2125 // different between old and new formats
2126 if (puThis->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER))
2127 {
2128 // old format:
2129 cx = puThis->Old.bmp.cx;
2130 cy = puThis->Old.bmp.cy;
2131 cBitCount = puThis->Old.bmp.cBitCount;
2132 }
2133 else if (puThis->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER2))
2134 {
2135 // new format:
2136 cx = puThis->New.bmp2.cx;
2137 cy = puThis->New.bmp2.cy;
2138 cBitCount = puThis->New.bmp2.cBitCount;
2139 }
2140 }
2141
2142 if (cx)
2143 {
2144 // remember the first bitmap from all that we see
2145 if (!puFirstAny)
2146 puFirstAny = puThis;
2147
2148 // check device resolution... device-independent
2149 // one has cxDisplay and cyDisplay set to 0
2150 if ( (!pba->cxDisplay)
2151 && (!pba->cyDisplay)
2152 )
2153 {
2154 // device-independent:
2155
2156 // remember the first device-independent bitmap
2157 if (!puFirstDI)
2158 puFirstDI = puThis;
2159
2160 if (cBitCount <= BITCOUNT)
2161 // we can display all the colors:
2162 puBestDI = puThis;
2163 }
2164 else
2165 {
2166 // device-dependent:
2167 // ignore if device resolution is too large
2168 if ( (pba->cxDisplay <= WIDTH)
2169 && (pba->cyDisplay <= HEIGHT)
2170 )
2171 {
2172 // remember first matching device-dependent bitmap
2173 if (!puFirstDD)
2174 puFirstDD = puThis;
2175
2176 if (cBitCount <= BITCOUNT)
2177 puBestDD = puThis;
2178 }
2179 }
2180 } // end if cx
2181
2182 // go for next bmp in array
2183 if (pba->offNext)
2184 // another one coming up:
2185 // this ofs is from the beginning of the file
2186 pba = (PBITMAPARRAYFILEHEADER2)(pData + pba->offNext);
2187 else
2188 // no more bitmaps:
2189 break;
2190 } // end while (pba)
2191
2192 if ( (puUse = puBestDD)
2193 || (puUse = puFirstDD)
2194 || (puUse = puBestDI)
2195 || (puUse = puFirstDI)
2196 || (puUse = puFirstAny)
2197 )
2198 {
2199 PBITMAPINFOHEADER2 pbih2;
2200 PBYTE pbInitData;
2201
2202 if (puUse->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER))
2203 {
2204 // old format:
2205 pbih2 = (PBITMAPINFOHEADER2)&puUse->Old.bmp;
2206 pbInitData = (PBYTE)pData + puUse->Old.offBits;
2207 }
2208 else
2209 {
2210 // new format:
2211 pbih2 = &puUse->New.bmp2;
2212 pbInitData = (PBYTE)pData + puUse->New.offBits;
2213 }
2214
2215 if (!(*phbm = GpiCreateBitmap(hps,
2216 pbih2,
2217 CBM_INIT,
2218 pbInitData,
2219 (PBITMAPINFO2)pbih2)))
2220 arc = ERROR_INVALID_DATA;
2221 }
2222 else
2223 arc = ERROR_INVALID_DATA;
2224 }
2225 break;
2226 }
2227
2228 return arc;
2229}
2230
2231/*
2232 *@@ gpihLoadBitmap:
2233 * creates a new HBITMAP from the given resource.
2234 *
2235 * This is a replacement for GpiLoadBitmap which handles
2236 * device-dependent bitmaps in bitmap arrays in a
2237 * non-brain-dead way.
2238 *
2239 * Calls gpihCreateBitmapFromFile for handling the resource
2240 * data. See remarks there for how we select a bitmap from
2241 * bitmap arrays, which is _different_ from GpiLoadBitmap.
2242 *
2243 * Returns:
2244 *
2245 * -- NO_ERROR: *phbm has received new HBITMAP,
2246 * to be freed with GpiDeleteBitmap.
2247 *
2248 * -- ERROR_INVALID_DATA: resource exists, but we
2249 * can't understand its format.
2250 *
2251 * plus the error codes from DosGetResource.
2252 *
2253 *@@added V0.9.20 (2002-07-19) [umoeller]
2254 */
2255
2256APIRET gpihLoadBitmap(HBITMAP *phbm, // out: bitmap if NO_ERROR
2257 HPS hps, // in: HPS for bmp
2258 HMODULE hmodResource, // in: module to load bitmap from
2259 ULONG idBitmap) // in: resource ID for bitmap
2260{
2261 APIRET arc;
2262
2263 PBYTE pbData;
2264
2265 if (!(arc = DosGetResource(hmodResource,
2266 RT_BITMAP,
2267 idBitmap,
2268 (PVOID*)&pbData)))
2269 {
2270 arc = gpihCreateBitmapFromFile(phbm,
2271 hps,
2272 pbData);
2273
2274 DosFreeResource(pbData);
2275 }
2276
2277 return arc;
2278}
2279
2280/*
2281 *@@ gpihLoadBitmapFile:
2282 * this loads the specified bitmap file into
2283 * the given HPS. Note that the bitmap is _not_
2284 * yet selected into the HPS.
2285 *
2286 * Calls gpihCreateBitmapFromFile for handling the resource
2287 * data. See remarks there for how we select a bitmap from
2288 * bitmap arrays, which is _different_ from GpiLoadBitmap.
2289 *
2290 * Returns:
2291 *
2292 * -- NO_ERROR: *phbm has received new HBITMAP,
2293 * to be freed with GpiDeleteBitmap.
2294 *
2295 * -- ERROR_INVALID_PARAMETER
2296 *
2297 * -- ERROR_INVALID_DATA: file exists, but we
2298 * can't understand its format.
2299 *
2300 * plus the error codes from doshOpen and DosRead.
2301 *
2302 *@@changed V0.9.4 (2000-08-03) [umoeller]: this didn't return NULLHANDLE on errors
2303 *@@changed V0.9.19 (2002-04-14) [umoeller]: rewritten to support bitmap arrays, prototype changed
2304 *@@changed V0.9.20 (2002-07-19) [umoeller]: extracted bitmap selection into gpihCreateBitmapFromFile
2305 */
2306
2307APIRET gpihLoadBitmapFile(HBITMAP *phbm, // out: bitmap if NO_ERROR
2308 HPS hps, // in: HPS for bmp
2309 PCSZ pcszBmpFile) // in: bitmap filename
2310{
2311 APIRET arc;
2312 PXFILE pFile;
2313 ULONG cbFile = 0;
2314
2315 if (!hps || !pcszBmpFile || !phbm)
2316 return ERROR_INVALID_PARAMETER;
2317
2318 if (!(arc = doshOpen(pcszBmpFile,
2319 XOPEN_READ_EXISTING | XOPEN_BINARY,
2320 &cbFile,
2321 &pFile)))
2322 {
2323 PBYTE pData;
2324 if (!(pData = (PBYTE)malloc(cbFile)))
2325 arc = ERROR_NOT_ENOUGH_MEMORY;
2326 else
2327 {
2328 // read in the ENTIRE file
2329 if (!(arc = DosRead(pFile->hf,
2330 pData,
2331 cbFile,
2332 &cbFile)))
2333 // extracted all the code into this extra func
2334 // V0.9.20 (2002-07-19) [umoeller]
2335 arc = gpihCreateBitmapFromFile(phbm,
2336 hps,
2337 (PBYTE)pData);
2338
2339 free(pData);
2340 }
2341
2342 doshClose(&pFile);
2343 }
2344
2345 return arc;
2346}
2347
2348/*
2349 *@@ gpihStretchBitmap:
2350 * this copies hbmSource to the bitmap selected
2351 * into hpsTarget, which must be a memory PS.
2352 *
2353 * The source size is the whole size of hbmSource,
2354 * the target size is specified in prclTarget
2355 * (which is exclusive, meaning that the top right
2356 * corner of that rectangle lies _outside_ the target).
2357 *
2358 * This uses GpiWCBitBlt to stretch the bitmap.
2359 * hbmSource therefore must _not_ be selected
2360 * into any presentation space, or GpiWCBitBlt will
2361 * fail.
2362 *
2363 * If (fPropotional == TRUE), the target size is
2364 * modified so that the proportions of the bitmap
2365 * are preserved. The bitmap data will then be
2366 * copied to a subrectangle of the target bitmap:
2367 * there will be extra space either to the left
2368 * and right of the bitmap data or to the bottom
2369 * and top.
2370 * The outside areas of the target bitmap are
2371 * not changed then, so you might want to fill
2372 * the bitmap with some color first.
2373 *
2374 * This returns the return value of GpiWCBitBlt,
2375 * which can be:
2376 * -- GPI_OK
2377 * -- GPI_HITS: correlate hits
2378 * -- GPI_ERROR: error occured (probably either hbmSource not free
2379 * or no bitmap selected into hpsTarget)
2380 *
2381 *@added V0.9.0
2382 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
2383 */
2384
2385LONG gpihStretchBitmap(HPS hpsTarget, // in: memory PS to copy bitmap to
2386 HBITMAP hbmSource, // in: bitmap to be copied into hpsTarget (must be free)
2387 PRECTL prclSource, // in: source rectangle -- if NULL, use size of bitmap
2388 PRECTL prclTarget, // in: target rectangle (req.)
2389 BOOL fProportional) // in: preserve proportions when stretching?
2390{
2391 SIZEL szlBmp;
2392 POINTL aptl[4];
2393 BOOL fCalculated = FALSE;
2394
2395 if (!gpihQueryBitmapSize(hbmSource, &szlBmp)) // V1.0.1 (2002-11-30) [umoeller]
2396 return GPI_ERROR;
2397
2398 memset(aptl, 0, sizeof(POINTL) * 4);
2399
2400 // aptl[2]: source bottom-left, is all 0
2401 // aptl[3]: source top-right (exclusive!)
2402 aptl[3].x = szlBmp.cx;
2403 aptl[3].y = szlBmp.cy;
2404
2405 if (fProportional)
2406 {
2407 // proportional mode:
2408
2409 // 1) find out whether cx or cy is too
2410 // large
2411
2412 ULONG ulPropSource = (szlBmp.cx * 1000)
2413 / szlBmp.cy;
2414 // e.g. if the bmp is 200 x 100, we now have 2000
2415 ULONG ulPropTarget = ((prclTarget->xRight - prclTarget->xLeft) * 1000)
2416 / (prclTarget->yTop - prclTarget->yBottom);
2417 // case 1: if prclTarget is 300 x 100, we now have 3000 (> ulPropSource)
2418 // case 2: if prclTarget is 150 x 100, we now have 1500 (< ulPropSource)
2419
2420 // case 1:
2421 if (ulPropTarget > ulPropSource)
2422 {
2423 // prclTarget is too wide (horizontally):
2424 // decrease width, keep height
2425
2426 ULONG cx = (prclTarget->xRight - prclTarget->xLeft);
2427 ULONG cxNew = (cx * ulPropSource) / ulPropTarget;
2428
2429 // aptl[0]: target bottom-left
2430 // move left right (towards center)
2431 aptl[0].x = prclTarget->xLeft + ((cx - cxNew) / 2);
2432 aptl[0].y = prclTarget->yBottom;
2433
2434 // aptl[1]: target top-right (inclusive!)
2435 aptl[1].x = aptl[0].x + cxNew;
2436 aptl[1].y = prclTarget->yTop;
2437
2438 fCalculated = TRUE;
2439 }
2440 else
2441 {
2442 // prclTarget is too high (vertically):
2443 // keep width, decrease height
2444
2445 ULONG cy = (prclTarget->yTop - prclTarget->yBottom);
2446 ULONG cyNew = (cy * ulPropTarget) / ulPropSource;
2447
2448 // aptl[0]: target bottom-left
2449 aptl[0].x = prclTarget->xLeft;
2450 // move bottom up (towards center)
2451 aptl[0].y = prclTarget->yBottom + ((cy - cyNew) / 2);
2452
2453 // aptl[1]: target top-right (inclusive!)
2454 aptl[1].x = prclTarget->xRight;
2455 aptl[1].y = aptl[0].y + cyNew;
2456 // (prclTarget->yTop * ulPropSource) / ulPropTarget;
2457
2458 fCalculated = TRUE;
2459 }
2460 } // end if (pa->ulFlags & ANF_PROPORTIONAL)
2461
2462 if (!fCalculated)
2463 {
2464 // non-proportional mode or equal proportions:
2465 // stretch to whole size of prclTarget
2466
2467 // aptl[0]: target bottom-left
2468 aptl[0].x = prclTarget->xLeft;
2469 aptl[0].y = prclTarget->yBottom;
2470 // aptl[1]: target top-right (inclusive!)
2471 aptl[1].x = prclTarget->xRight;
2472 aptl[1].y = prclTarget->yTop;
2473 }
2474
2475 return GpiWCBitBlt(hpsTarget, // target HPS (bmp selected)
2476 hbmSource,
2477 4L, // must always be 4
2478 &aptl[0], // points array
2479 ROP_SRCCOPY,
2480 BBO_IGNORE);
2481 // ignore eliminated rows or
2482 // columns; useful for color
2483}
2484
2485/*
2486 *@@ gpihDrawPointer:
2487 * replacement for WinDrawPointer that can do clipping.
2488 *
2489 * Normally, to do clipping with WinDrawPointer, one
2490 * would have to alter the clip rectangle for the current
2491 * HPS, which requires creating regions and is thus quite
2492 * expensive.
2493 *
2494 * Instead, this function allows for specifying a clip
2495 * rectangle directly. It blits the icon bitmaps directly
2496 * without calling WinDrawPointer.
2497 * Besides, since it uses GpiWCBitBlt, it should probably
2498 * work with all types of device contexts.
2499 *
2500 * This also replaces gpihIcon2Bitmap, which wasn't quite
2501 * working in the first place and couldn't to clipping
2502 * either.
2503 *
2504 * If you don't need clipping and are drawing to the
2505 * screen only, this function has no advantage over
2506 * WinDrawPointer because it's presumably a bit slower.
2507 *
2508 * Flags presently supported in fl:
2509 *
2510 * -- DP_MINI (not DP_MINIICON, as stated in PMREF):
2511 * use mini-icon.
2512 *
2513 * -- DP_HALFTONED (V0.9.20)
2514 *
2515 * Preconditions:
2516 *
2517 * -- The hps is assumed to be in RGB mode.
2518 *
2519 * Post conditions:
2520 *
2521 * -- This uses GpiSet(Back)Color, so the foreground
2522 * and background colors are undefined after the
2523 * call.
2524 *
2525 *@@added V0.9.19 (2002-06-18) [umoeller]
2526 *@@changed V0.9.20 (2002-07-31) [umoeller]: optimized, saved one GpiQueryBitmapInfoHeader
2527 *@@changed V0.9.20 (2002-08-04) [umoeller]: added DP_HALFTONED
2528 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
2529 */
2530
2531BOOL gpihDrawPointer(HPS hps, // in: target presentation space
2532 LONG x, // in: lower left target position of icon
2533 LONG y, // in: lower left target position of icon
2534 HPOINTER hptr, // in: icon to be drawn
2535 PSIZEL pszlIcon, // in: icon size (req., should be sysvalues SV_CXICON, SV_CYICON always)
2536 PRECTL prclClip, // in: clip rectangle (inclusive!) or NULL
2537 ULONG fl) // in: DP_* flags
2538{
2539 POINTERINFO pi;
2540
2541 if ( (pszlIcon)
2542 && (hptr)
2543 && (WinQueryPointerInfo(hptr, &pi))
2544 )
2545 {
2546 POINTL aptl[4];
2547 HBITMAP hbmThis;
2548 SIZEL szlAndXor, // V1.0.1 (2002-11-30) [umoeller]
2549 szlColor;
2550
2551 // A HPOINTER really consists of two bitmaps,
2552 // one monochrome bitmap that has twice the icon
2553 // height and contains an AND mask in the upper
2554 // half and an XOR mask in the lower half, and
2555 // a (probably color) bitmap with the regular
2556 // icon height.
2557
2558 // Drawing the icon means (1) blitting the AND
2559 // mask with ROP_SRCAND, (2) blitting the color
2560 // bitmap with ROP_SRCPAINT, (3) blitting the
2561 // XOR mask with ROP_SRCINVERT. Hence the slightly
2562 // complicated code that follows.
2563
2564 /*
2565 * 0) preparations
2566 */
2567
2568 // set up a bunch of variables that are used
2569 // by the below calculations. We use cx|yIcon
2570 // to quickly get the system icon dimensions
2571 // and set up the clip offsets here too,
2572 // if a clip rectangle is specified.
2573
2574 LONG cxIcon = pszlIcon->cx,
2575 cyIcon = pszlIcon->cy,
2576 cySrc,
2577 xRight,
2578 yTop,
2579 // clip "rectangle"... this is not really a
2580 // recangle because it specifies _offsets_
2581 // towards the center of the icon to be
2582 // clipped
2583 lClipLeft = 0,
2584 lClipBottom = 0,
2585 lClipRight = 0,
2586 lClipTop = 0;
2587
2588 BOOL fMini;
2589
2590 if (fMini = !!(fl & DP_MINI))
2591 {
2592 cxIcon /= 2;
2593 cyIcon /= 2;
2594 }
2595
2596 // target top right (inclusive)
2597 xRight = x + cxIcon - 1;
2598 yTop = y + cyIcon - 1;
2599
2600 if (prclClip)
2601 {
2602 // we have a clip rectangle:
2603 // set up the clip offsets that are used
2604 // in both the target and source coordinates
2605 // for blitting
2606 if (x < prclClip->xLeft)
2607 lClipLeft = prclClip->xLeft - x;
2608 if (xRight > prclClip->xRight)
2609 lClipRight = xRight - prclClip->xRight;
2610 if (y < prclClip->yBottom)
2611 lClipBottom = prclClip->yBottom - y;
2612 if (yTop > prclClip->yTop)
2613 lClipTop = yTop - prclClip->yTop;
2614 }
2615
2616 // set up target coordinates that are constant
2617 // for all the three blits
2618
2619 // aptl[0]: target bottom-left
2620 aptl[0].x = x + lClipLeft;
2621 aptl[0].y = y + lClipBottom;
2622
2623 // aptl[1]: target top-right (inclusive!)
2624 aptl[1].x = xRight - lClipRight;
2625 aptl[1].y = yTop - lClipTop;
2626
2627 if ( (aptl[0].x < aptl[1].x)
2628 && (aptl[0].y < aptl[1].y)
2629 )
2630 {
2631 LONG lPatternOld = -1; // mark as "not changed" for now
2632 LONG lAndROP = ROP_SRCAND, // 0x0088 = 10001000
2633 lPaintROP = ROP_SRCPAINT, // 0x00EE = 11101110
2634 lInvertROP = ROP_SRCINVERT; // 0x0066 = 01100110
2635
2636 // colors are constant too
2637 GpiSetColor(hps, RGBCOL_WHITE);
2638 GpiSetBackColor(hps, RGBCOL_BLACK);
2639
2640 if (fl & DP_HALFTONED) // V0.9.20 (2002-08-04) [umoeller]
2641 {
2642 lPatternOld = GpiQueryPattern(hps);
2643 GpiSetPattern(hps, PATSYM_HALFTONE);
2644
2645 lAndROP = 0x00A8; // 10101000
2646 lInvertROP = 0x00A6; // 10100110
2647 }
2648
2649 /*
2650 * 1) work on the AND image
2651 * (upper part of the monochrome image)
2652 */
2653
2654 if ( ( (fMini)
2655 && (hbmThis = pi.hbmMiniPointer)
2656 )
2657 || (hbmThis = pi.hbmPointer)
2658 )
2659 {
2660 gpihQueryBitmapSize(hbmThis, &szlAndXor); // V1.0.1 (2002-11-30) [umoeller]
2661
2662 // use only half the bitmap height
2663 cySrc = szlAndXor.cy / 2;
2664
2665 // aptl[2]: source bottom-left
2666 aptl[2].x = 0
2667 + lClipLeft * szlAndXor.cx / cxIcon;
2668 aptl[2].y = cySrc
2669 + lClipBottom * cySrc / cyIcon;
2670
2671 // aptl[3]: source top-right (exclusive!)
2672 aptl[3].x = szlAndXor.cx
2673 - lClipRight * szlAndXor.cx / cxIcon;
2674 aptl[3].y = szlAndXor.cy
2675 - lClipTop * cySrc / cyIcon;
2676
2677 GpiWCBitBlt(hps, // target
2678 hbmThis, // src bmp
2679 4L, // must always be 4
2680 aptl, // point array
2681 lAndROP, // ROP_SRCAND, // source AND target
2682 BBO_IGNORE);
2683 }
2684
2685 /*
2686 * 2) paint the color image; the parts that
2687 * are to be transparent are black
2688 */
2689
2690 if ( ( (fMini)
2691 && (hbmThis = pi.hbmMiniColor)
2692 )
2693 || (hbmThis = pi.hbmColor)
2694 )
2695 {
2696 gpihQueryBitmapSize(hbmThis, &szlColor); // V1.0.1 (2002-11-30) [umoeller]
2697
2698 // aptl[2]: source bottom-left
2699 aptl[2].x = 0
2700 + lClipLeft * szlColor.cx / cxIcon;
2701 aptl[2].y = 0
2702 + lClipBottom * szlColor.cy / cyIcon;
2703
2704 // aptl[3]: source top-right (exclusive!)
2705 aptl[3].x = szlColor.cx
2706 - lClipRight * szlColor.cx / cxIcon;
2707 aptl[3].y = szlColor.cy
2708 - lClipTop * szlColor.cy / cyIcon;
2709
2710 GpiWCBitBlt(hps, // target
2711 hbmThis, // src bmp
2712 4L, // must always be 4
2713 aptl, // point array
2714 lPaintROP, // ROP_SRCPAINT,
2715 BBO_IGNORE);
2716 }
2717
2718 /*
2719 * 3) work on the XOR image:
2720 * (lower part of monochrome bitmap)
2721 */
2722
2723 if ( ( (fMini)
2724 && (hbmThis = pi.hbmMiniPointer)
2725 )
2726 || (hbmThis = pi.hbmPointer)
2727 )
2728 {
2729 // we queried the size of this one above V0.9.20 (2002-07-31) [umoeller]
2730
2731 // use only half the bitmap height
2732 cySrc = szlAndXor.cy / 2;
2733
2734 // aptl[2]: source bottom-left
2735 aptl[2].x = 0
2736 + lClipLeft * szlAndXor.cx / cxIcon;
2737 aptl[2].y = 0
2738 + lClipBottom * cySrc / cyIcon;
2739
2740 // aptl[3]: source top-right (exclusive!)
2741 aptl[3].x = szlAndXor.cx
2742 - lClipRight * szlAndXor.cx / cxIcon;
2743 aptl[3].y = cySrc
2744 - lClipTop * cySrc / cyIcon;
2745
2746 GpiWCBitBlt(hps, // target
2747 hbmThis, // src bmp
2748 4L, // must always be 4
2749 aptl, // point array
2750 lInvertROP, // ROP_SRCINVERT, // source XOR target
2751 BBO_IGNORE);
2752 }
2753
2754 // reset old pattern, if changed
2755 if (lPatternOld != -1)
2756 GpiSetPattern(hps, lPatternOld);
2757
2758 return TRUE;
2759 }
2760 }
2761
2762 return FALSE;
2763}
2764
2765/*
2766 *@@category: Helpers\PM helpers\GPI helpers\XBitmaps
2767 * Extended bitmaps. See gpihCreateXBitmap for an introduction.
2768 */
2769
2770/* ******************************************************************
2771 *
2772 * XBitmap functions
2773 *
2774 ********************************************************************/
2775
2776/*
2777 *@@ gpihCreateXBitmap:
2778 * calls gpihCreateXBitmap2 with cPlanes and cBitCount == 0
2779 * for compatibility with exports. Widgets might
2780 * have used this func.
2781 *
2782 *@@added V0.9.12 (2001-05-20) [umoeller]
2783 *@@changed V0.9.16 (2001-12-18) [umoeller]: now using optimized gpihCreateXBitmap2
2784 */
2785
2786PXBITMAP gpihCreateXBitmap(HAB hab, // in: anchor block
2787 LONG cx, // in: bitmap width
2788 LONG cy) // in: bitmap height
2789{
2790 return gpihCreateXBitmap2(hab,
2791 cx,
2792 cy,
2793 0,
2794 0);
2795}
2796
2797/*
2798 *@@ gpihCreateXBitmap:
2799 * creates an XBitmap, which is returned in an
2800 * XBITMAP structure.
2801 *
2802 * The problem with all the GPI bitmap functions
2803 * is that they are quite complex and it is easy
2804 * to forget one of the "disassociate" and "deselect"
2805 * functions, which then simply leads to enormous
2806 * resource leaks in the application.
2807 *
2808 * This function may relieve this a bit. This
2809 * creates a memory DC, an memory PS, and a bitmap,
2810 * and selects the bitmap into the memory PS.
2811 * You can then use any GPI function on the memory
2812 * PS to draw into the bitmap. Use the fields from
2813 * XBITMAP for that.
2814 *
2815 * The bitmap is created in RGB mode.
2816 *
2817 * Use gpihDestroyXBitmap to destroy the XBitmap
2818 * again.
2819 *
2820 * Example:
2821 *
2822 + PXBITMAP pbmp = gpihCreateXBitmap(hab, 100, 100);
2823 + if (pbmp)
2824 + {
2825 + GpiMove(pbmp->hpsMem, ...);
2826 + GpiBox(pbmp->hpsMem, ...);
2827 +
2828 + WinDrawBitmap(hpsScreen,
2829 + pbmp->hbm, // bitmap handle
2830 + ...);
2831 + gpihDestroyXBitmap(&pbmp);
2832 + }
2833 *
2834 * Without the gpih* functions, the above would expand
2835 * to more than 100 lines.
2836 *
2837 *@@added V0.9.16 (2001-12-18) [umoeller]
2838 */
2839
2840PXBITMAP gpihCreateXBitmap2(HAB hab, // in: anchor block
2841 LONG cx, // in: bitmap width
2842 LONG cy, // in: bitmap height
2843 ULONG cPlanes, // in: color planes (usually 1); if 0, current screen is used
2844 ULONG cBitCount) // in: either 1, 4, or 24; if 0, current screen value
2845{
2846 BOOL fOK = FALSE;
2847 PXBITMAP pbmp = (PXBITMAP)malloc(sizeof(XBITMAP));
2848 if (pbmp)
2849 {
2850 memset(pbmp, 0, sizeof(XBITMAP));
2851
2852 // create memory PS for bitmap
2853 pbmp->szl.cx = cx;
2854 pbmp->szl.cy = cy;
2855 if (gpihCreateMemPS(hab,
2856 &pbmp->szl,
2857 &pbmp->hdcMem,
2858 &pbmp->hpsMem))
2859 {
2860 if (cBitCount != 1)
2861 // not monochrome bitmap:
2862 gpihSwitchToRGB(pbmp->hpsMem);
2863
2864 if (pbmp->hbm = gpihCreateBitmap2(pbmp->hpsMem,
2865 cx,
2866 cy,
2867 cPlanes,
2868 cBitCount))
2869 {
2870 if (GpiSetBitmap(pbmp->hpsMem,
2871 pbmp->hbm)
2872 != HBM_ERROR)
2873 fOK = TRUE;
2874 }
2875 }
2876
2877 if (!fOK)
2878 gpihDestroyXBitmap(&pbmp);
2879 }
2880
2881 return pbmp;
2882}
2883
2884/*
2885 *@@ gpihDetachBitmap:
2886 * "detaches" the bitmap from the given XBITMAP.
2887 * This will deselect the bitmap from the internal
2888 * memory PS so it can be used with many OS/2 APIs
2889 * that require that the bitmap not be selected
2890 * into any memory PS.
2891 *
2892 * Note that it then becomes the responsibility
2893 * of the caller to explicitly call GpiDeleteBitmap
2894 * because it will not be deleted by gpihDestroyXBitmap.
2895 *
2896 *@@added V0.9.16 (2001-12-18) [umoeller]
2897 */
2898
2899HBITMAP gpihDetachBitmap(PXBITMAP pbmp)
2900{
2901 HBITMAP hbm = pbmp->hbm;
2902 pbmp->hbm = NULLHANDLE;
2903 GpiSetBitmap(pbmp->hpsMem, NULLHANDLE);
2904
2905 return hbm;
2906}
2907
2908/*
2909 *@@ gpihDestroyXBitmap:
2910 * destroys an XBitmap created with gpihCreateXBitmap.
2911 *
2912 * To be on the safe side, this sets the
2913 * given XBITMAP pointer to NULL as well.
2914 *
2915 *@@added V0.9.12 (2001-05-20) [umoeller]
2916 */
2917
2918VOID gpihDestroyXBitmap(PXBITMAP *ppbmp)
2919{
2920 if (ppbmp)
2921 {
2922 PXBITMAP pbmp;
2923 if (pbmp = *ppbmp)
2924 {
2925 if (pbmp->hbm)
2926 {
2927 if (pbmp->hpsMem)
2928 GpiSetBitmap(pbmp->hpsMem, NULLHANDLE);
2929 GpiDeleteBitmap(pbmp->hbm);
2930 }
2931 if (pbmp->hpsMem)
2932 {
2933 GpiAssociate(pbmp->hpsMem, NULLHANDLE);
2934 GpiDestroyPS(pbmp->hpsMem);
2935 }
2936 if (pbmp->hdcMem)
2937 DevCloseDC(pbmp->hdcMem);
2938
2939 free(pbmp);
2940
2941 *ppbmp = NULL;
2942 }
2943 }
2944}
2945
2946/*
2947 *@@ gpihCreateBmpFromPS:
2948 * this creates a new bitmap and copies a screen rectangle
2949 * into it. Consider this a "screen capture" function.
2950 *
2951 * The new bitmap (which is returned) is compatible with the
2952 * device associated with hpsScreen. This function calls
2953 * gpihCreateMemPS and gpihCreateBitmap to have it created.
2954 * The memory PS is only temporary and freed again.
2955 *
2956 * This returns the handle of the new bitmap,
2957 * which can then be used for WinDrawBitmap and such, or
2958 * NULLHANDLE upon errors.
2959 *
2960 *@@changed V0.9.12 (2001-05-20) [umoeller]: fixed excessive mem PS size
2961 *@@changed V0.9.16 (2001-01-04) [umoeller]: now creating XBITMAP
2962 */
2963
2964PXBITMAP gpihCreateBmpFromPS(HAB hab, // in: anchor block
2965 HPS hpsScreen, // in: screen PS to copy from
2966 PRECTL prcl) // in: rectangle to copy
2967{
2968
2969 /* To copy an image from a display screen to a bit map:
2970 1. Associate the memory device context with a presentation space.
2971 2. Create a bit map.
2972 3. Select the bit map into the memory device context by calling GpiSetBitmap.
2973 4. Determine the location (in device coordinates) of the image.
2974 5. Call GpiBitBlt and copy the image to the bit map. */
2975
2976 PXBITMAP pBmp;
2977
2978 if (pBmp = gpihCreateXBitmap(hab,
2979 prcl->xRight - prcl->xLeft,
2980 prcl->yTop - prcl->yBottom))
2981 {
2982 POINTL aptl[3];
2983 // Copy the screen to the bit map.
2984 aptl[0].x = 0; // lower-left corner of destination rectangle
2985 aptl[0].y = 0;
2986 aptl[1].x = prcl->xRight; // upper-right corner for both
2987 aptl[1].y = prcl->yTop;
2988 aptl[2].x = prcl->xLeft; // lower-left corner of source rectangle
2989 aptl[2].y = prcl->yBottom;
2990
2991 if (GPI_ERROR == GpiBitBlt(pBmp->hpsMem,
2992 hpsScreen,
2993 sizeof(aptl) / sizeof(POINTL), // Number of points in aptl
2994 aptl,
2995 ROP_SRCCOPY,
2996 BBO_IGNORE))
2997 {
2998 // error during bitblt:
2999 gpihDestroyXBitmap(&pBmp);
3000 }
3001 }
3002
3003 return pBmp;
3004}
3005
Note: See TracBrowser for help on using the repository browser.