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

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

More pager fixes.

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