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

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

Major work on textview control and dialogs.

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