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

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

More fixes.

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