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

Last change on this file since 93 was 91, checked in by umoeller, 24 years ago

Misc changes

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 71.9 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-2000 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#include "helpers\winh.h"
58#include "helpers\gpih.h"
59
60#pragma hdrstop
61
62// array for querying device capabilities (gpihQueryDisplayCaps)
63LONG DisplayCaps[CAPS_DEVICE_POLYSET_POINTS] = {0};
64BOOL fCapsQueried = FALSE;
65
66/*
67 *@@category: Helpers\PM helpers\GPI helpers
68 * See gpih.c.
69 */
70
71/*
72 *@@category: Helpers\PM helpers\GPI helpers\Devices
73 */
74
75/*
76 *@@gloss: GPI_rectangles GPI rectangles
77 * OS/2 PM (and GPI) uses two types of rectangles. This is rarely
78 * mentioned in the documentation, so a word is in order here.
79 *
80 * In general, graphics operations
81 * involving device coordinates (such as regions, bit maps and
82 * bit blts, and window management) use inclusive-exclusive
83 * rectangles. All other graphics operations, such as GPI
84 * functions that define paths, use inclusive-inclusive rectangles.
85 *
86 * This can be a problem with mixing Win and Gpi functions. For
87 * example, WinQueryWindowRect returns an inclusive-exclusive
88 * rectangle (so that the xRight value is the same as the window
89 * width -- tested V0.9.7 (2000-12-20) [umoeller]).
90 *
91 * WinFillRect expects an inclusive-exclusive rectangle, so it
92 * will work with a rectangle from WinQueryWindowRect directly.
93 *
94 * By contrast, the GpiBox expects an inclusive-inclusive rectangle.
95 */
96
97/* ******************************************************************
98 *
99 * Global variables
100 *
101 ********************************************************************/
102
103static HMTX G_hmtxLCIDs = NULLHANDLE;
104
105/* ******************************************************************
106 *
107 * Rectangle helpers
108 *
109 ********************************************************************/
110
111/*
112 *@@ gpihIsPointInRect:
113 * like WinPtInRect, but doesn't need a HAB.
114 *
115 * NOTE: as opposed to WinPtInRect, prcl is
116 * considered inclusive, that is, TRUE is
117 * returned even if x or y are exactly
118 * the same as prcl->xRight or prcl->yTop.
119 *
120 *@@added V0.9.9 (2001-02-28) [umoeller]
121 */
122
123BOOL gpihIsPointInRect(PRECTL prcl,
124 LONG x,
125 LONG y)
126{
127 if (prcl)
128 {
129 return ( (x >= prcl->xLeft)
130 && (x <= prcl->xRight)
131 && (y >= prcl->yBottom)
132 && (y <= prcl->yTop)
133 );
134 }
135
136 return (FALSE);
137}
138
139/*
140 *@@ gpihInflateRect:
141 * Positive l will make the rectangle larger.
142 * Negative l will make the rectangle smaller.
143 *
144 *@@added V0.9.9 (2001-02-28) [umoeller]
145 */
146
147VOID gpihInflateRect(PRECTL prcl,
148 LONG l)
149{
150 if (prcl && l)
151 {
152 prcl->xLeft -= l;
153 prcl->yBottom -= l;
154 prcl->xRight += l;
155 prcl->yTop += l;
156 }
157}
158
159/* ******************************************************************
160 *
161 * Device helpers
162 *
163 ********************************************************************/
164
165/*
166 *@@ gpihQueryDisplayCaps:
167 * this returns certain device capabilities of
168 * the Display device. ulIndex must be one of
169 * the indices as described in DevQueryCaps.
170 *
171 * This function will load all the device capabilities
172 * only once into a global array and re-use them afterwards.
173 */
174
175ULONG gpihQueryDisplayCaps(ULONG ulIndex)
176{
177 if (!fCapsQueried)
178 {
179 HPS hps = WinGetScreenPS(HWND_DESKTOP);
180 HDC hdc = GpiQueryDevice(hps);
181 DevQueryCaps(hdc, 0, CAPS_DEVICE_POLYSET_POINTS, &DisplayCaps[0]);
182 }
183
184 return (DisplayCaps[ulIndex]);
185}
186
187/*
188 *@@category: Helpers\PM helpers\GPI helpers\Colors
189 */
190
191/* ******************************************************************
192 *
193 * Color helpers
194 *
195 ********************************************************************/
196
197/*
198 * HackColor:
199 *
200 */
201
202VOID HackColor(PBYTE pb, double dFactor)
203{
204 ULONG ul = (ULONG)((double)(*pb) * dFactor);
205 if (ul > 255)
206 *pb = 255;
207 else
208 *pb = (BYTE)ul;
209}
210
211/*
212 *@@ gpihManipulateRGB:
213 * this changes an RGB color value
214 * by multiplying each color component
215 * (red, green, blue) with dFactor.
216 *
217 * Each color component is treated separately,
218 * so if overflows occur (because dFactor
219 * is > 1), this does not affect the other
220 * components.
221 *
222 *@@changed V0.9.11 (2001-04-25) [umoeller]: changed prototype to use a double now
223 */
224
225VOID gpihManipulateRGB(PLONG plColor, // in/out: RGB color
226 double dFactor) // in: factor (> 1 makes brigher, < 1 makes darker)
227{
228 PBYTE pb = (PBYTE)plColor;
229
230 // in memory, the bytes are blue, green, red, unused
231
232 // blue
233 ULONG ul = (ULONG)( (double)(*pb) * dFactor
234 );
235 if (ul > 255)
236 *pb = 255;
237 else
238 *pb = (BYTE)ul;
239
240 // green
241 ul = (ULONG)( (double)(*(++pb)) * dFactor
242 );
243 if (ul > 255)
244 *pb = 255;
245 else
246 *pb = (BYTE)ul;
247
248 // red
249 ul = (ULONG)( (double)(*(++pb)) * dFactor
250 );
251 if (ul > 255)
252 *pb = 255;
253 else
254 *pb = (BYTE)ul;
255}
256
257/*
258 *@@ gpihSwitchToRGB:
259 * this switches the given HPS into RGB mode. You should
260 * always use this if you are operating with RGB colors.
261 *
262 * This is just a shortcut to calling
263 *
264 + GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
265 *
266 *@@changed V0.9.7 (2001-01-15) [umoeller]: turned macro into function
267 */
268
269BOOL gpihSwitchToRGB(HPS hps)
270{
271 return (GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL));
272}
273
274/*
275 *@@category: Helpers\PM helpers\GPI helpers\Drawing primitives
276 */
277
278/* ******************************************************************
279 *
280 * Drawing primitives helpers
281 *
282 ********************************************************************/
283
284/*
285 *@@ gpihDrawRect:
286 * this draws a simple rectangle with the current
287 * color (use GpiSetColor before calling this function).
288 *
289 * The specified rectangle is inclusive, that is, the top
290 * right corner specifies the top right pixel to be drawn
291 * (see @GPI_rectangles).
292 *
293 * This sets the current position to the bottom left corner
294 * of prcl.
295 *
296 *@added V0.9.0
297 */
298
299VOID gpihDrawRect(HPS hps, // in: presentation space for output
300 PRECTL prcl) // in: rectangle to draw (inclusive)
301{
302 POINTL ptl1;
303
304 ptl1.x = prcl->xLeft;
305 ptl1.y = prcl->yBottom;
306 GpiMove(hps, &ptl1);
307 ptl1.y = prcl->yTop-1;
308 GpiLine(hps, &ptl1);
309 ptl1.x = prcl->xRight-1;
310 GpiLine(hps, &ptl1);
311 ptl1.y = prcl->yBottom;
312 GpiLine(hps, &ptl1);
313 ptl1.x = prcl->xLeft;
314 GpiLine(hps, &ptl1);
315}
316
317/*
318 *@@ gpihBox:
319 * this is a shortcurt to GpiBox, using the specified
320 * rectangle.
321 *
322 * As opposed to WinFillRect, this works with memory
323 * (bitmap) PS's also.
324 *
325 * The specified rectangle is inclusive, that is, the top
326 * right corner specifies the top right pixel to be drawn.
327 * This is different from WinFillRect
328 * (see @GPI_rectangles).
329 *
330 * If (lColor != -1), the HPS's current foreground color
331 * is changed to that color.
332 *
333 * Changes to the HPS:
334 *
335 * -- the current position is moved to the lower left
336 * corner of *prcl.
337 *
338 *@@changed V0.9.0 [umoeller]: renamed from gpihFillRect
339 *@@changed V0.9.0 [umoeller]: modified function prototype to support lControl
340 *@@changed V0.9.7 (2001-01-17) [umoeller]: removed lColor
341 */
342
343VOID gpihBox(HPS hps, // in: presentation space for output
344 LONG lControl, // in: one of DRO_OUTLINE, DRO_FILL, DRO_OUTLINEFILL
345 PRECTL prcl) // in: rectangle to draw (exclusive)
346{
347 POINTL ptl;
348
349 ptl.x = prcl->xLeft;
350 ptl.y = prcl->yBottom;
351 GpiMove(hps, &ptl);
352 ptl.x = prcl->xRight;
353 ptl.y = prcl->yTop;
354 GpiBox(hps,
355 lControl, // DRO_*
356 &ptl,
357 0, 0); // no corner rounding
358}
359
360/*
361 *@@ gpihMarker:
362 * this draws a quick marker (a filled
363 * rectangle) at the specified position.
364 * The rectangle will be drawn so that
365 * the specified point is in its center.
366 *
367 * No PS data is changed.
368 *
369 *@@changed V0.9.7 (2001-01-17) [umoeller]: removed lColor
370 */
371
372VOID gpihMarker(HPS hps,
373 LONG x, // in: x-center of rectangle
374 LONG y, // in: y-center of rectangle
375 ULONG ulWidth) // in: rectangle width and height
376{
377 POINTL ptlSave;
378 RECTL rclTemp;
379 ULONG ulWidth2 = ulWidth / 2;
380 rclTemp.xLeft = x - ulWidth2;
381 rclTemp.xRight = x + ulWidth2;
382 rclTemp.yBottom = y - ulWidth2;
383 rclTemp.yTop = y + ulWidth2;
384
385 GpiQueryCurrentPosition(hps, &ptlSave);
386 gpihBox(hps,
387 DRO_FILL,
388 &rclTemp);
389 GpiMove(hps, &ptlSave);
390}
391
392/*
393 *@@ gpihThickBox:
394 * draws a box from the specified rectangle with the
395 * specified width.
396 *
397 * The specified rectangle is inclusive, that is, the top
398 * right corner specifies the top right pixel to be drawn.
399 * This is different from WinFillRect
400 * (see @GPI_rectangles).
401 *
402 * If usWidth > 1, the additional pixels will be drawn towards
403 * the _center_ of the rectangle. prcl thus always specifies
404 * the bottom left and top right pixels to be drawn.
405 *
406 * This is different from using GpiSetLineWidth, with which
407 * I was unable to find out in which direction lines are
408 * extended.
409 *
410 * This is similar to gpihDraw3DFrame, except that everything
411 * is painted in the current color.
412 *
413 *@@added V0.9.7 (2000-12-06) [umoeller]
414 */
415
416VOID gpihDrawThickFrame(HPS hps, // in: presentation space for output
417 PRECTL prcl, // in: rectangle to draw (inclusive)
418 ULONG ulWidth) // in: line width (>= 1)
419{
420 ULONG ul = 0;
421 for (;
422 ul < ulWidth;
423 ul++)
424 {
425 GpiMove(hps, (PPOINTL)prcl);
426 GpiBox(hps,
427 DRO_OUTLINE,
428 (PPOINTL)&(prcl->xRight),
429 0,
430 0);
431
432 // and one more to the outside
433 prcl->xLeft++;
434 prcl->yBottom++;
435 prcl->xRight--;
436 prcl->yTop--;
437 }
438}
439
440/*
441 *@@ gpihDraw3DFrame:
442 * this draws a rectangle in 3D style with a given line width
443 * and the given colors.
444 *
445 * The specified rectangle is inclusive, that is, the top
446 * right corner specifies the top right pixel to be drawn.
447 * This is different from WinFillRect
448 * (see @GPI_rectangles).
449 *
450 * If usWidth > 1, the additional pixels will be drawn towards
451 * the _center_ of the rectangle. prcl thus always specifies
452 * the bottom left and top right pixels to be drawn.
453 *
454 *@@changed V0.9.0 [umoeller]: changed function prototype to have colors specified
455 *@@changed V0.9.7 (2000-12-20) [umoeller]: now really using inclusive rectangle...
456 */
457
458VOID gpihDraw3DFrame(HPS hps,
459 PRECTL prcl, // in: rectangle (inclusive)
460 USHORT usWidth, // in: line width (>= 1)
461 LONG lColorLeft, // in: color to use for left and top; e.g. SYSCLR_BUTTONLIGHT
462 LONG lColorRight) // in: color to use for right and bottom; e.g. SYSCLR_BUTTONDARK
463{
464 RECTL rcl2 = *prcl;
465 USHORT us;
466 POINTL ptl1;
467
468 for (us = 0;
469 us < usWidth;
470 us++)
471 {
472 GpiSetColor(hps, lColorLeft);
473 // draw left line
474 ptl1.x = rcl2.xLeft;
475 ptl1.y = rcl2.yBottom;
476 GpiMove(hps, &ptl1);
477 ptl1.y = rcl2.yTop; // V0.9.7 (2000-12-20) [umoeller]
478 GpiLine(hps, &ptl1);
479 // go right -> draw top
480 ptl1.x = rcl2.xRight; // V0.9.7 (2000-12-20) [umoeller]
481 GpiLine(hps, &ptl1);
482 // go down -> draw right
483 GpiSetColor(hps, lColorRight);
484 ptl1.y = rcl2.yBottom;
485 GpiLine(hps, &ptl1);
486 // go left -> draw bottom
487 ptl1.x = rcl2.xLeft;
488 GpiLine(hps, &ptl1);
489
490 rcl2.xLeft++;
491 rcl2.yBottom++;
492 rcl2.xRight--;
493 rcl2.yTop--;
494 }
495}
496
497/*
498 *@@ gpihCharStringPosAt:
499 * wrapper for GpiCharStringPosAt.
500 * Since that function is limited to 512 characters
501 * (according to GPIREF; on my Warp 4 FP13, I actually
502 * get some 3000 characters... whatever this is),
503 * this splits the string into 512 byte chunks and
504 * calls GpiCharStringPosAt accordingly.
505 *
506 *@@added V0.9.3 (2000-05-06) [umoeller]
507 */
508
509LONG gpihCharStringPosAt(HPS hps,
510 PPOINTL pptlStart,
511 PRECTL prclRect,
512 ULONG flOptions,
513 LONG lCount,
514 PCH pchString)
515{
516 LONG lHits = 0,
517 lCountLeft = lCount;
518 PCH pchThis = pchString;
519
520 GpiMove(hps, pptlStart);
521
522 if (lCount)
523 {
524 do
525 {
526 LONG lCountThis = lCountLeft;
527 if (lCountLeft >= 512)
528 lCountThis = 512;
529
530 lHits = GpiCharStringPos(hps,
531 prclRect,
532 flOptions,
533 lCountThis,
534 pchThis,
535 0);
536
537 pchThis += 512;
538 lCountLeft -= 512;
539 } while (lCountLeft > 0);
540 }
541
542 return (lHits);
543}
544
545/*
546 *@@category: Helpers\PM helpers\GPI helpers\Fonts
547 */
548
549/* ******************************************************************
550 *
551 * Font helpers
552 *
553 ********************************************************************/
554
555/*
556 *@@ gpihSplitPresFont:
557 * splits a presentation parameter font
558 * string into the point size and face
559 * name so that it can be passed to
560 * gpihFindFont more easily.
561 *
562 *@@added V0.9.1 (2000-02-15) [umoeller]
563 */
564
565BOOL gpihSplitPresFont(PSZ pszFontNameSize, // in: e.g. "12.Courier"
566 PULONG pulSize, // out: integer point size (e.g. 12);
567 // ptr must be specified
568 PSZ *ppszFaceName) // out: ptr into pszFontNameSize
569 // (e.g. "Courier")
570{
571 BOOL brc = FALSE;
572
573 if (pszFontNameSize)
574 {
575 PCHAR pcDot = strchr(pszFontNameSize, '.');
576 if (pcDot)
577 {
578 // _Pmpf(("Found font PP: %s", pszFontFound));
579 sscanf(pszFontNameSize, "%lu", pulSize);
580 *ppszFaceName = pcDot + 1;
581 brc = TRUE;
582 }
583 }
584
585 return (brc);
586}
587
588/*
589 *@@ gpihLockLCIDs:
590 * requests the mutex for serializing the
591 * lcids.
592 *
593 * With GPI, lcids are a process-wide resource and not
594 * guaranteed to be unique. In the worst case, while your
595 * font routines are running, another thread modifies the
596 * lcids and you get garbage. If your fonts suddenly
597 * turn to "System Proportional", you know this has
598 * happened.
599 *
600 * As a result, whenever you work on lcids, request this
601 * mutex during your processing. If you do this consistently
602 * across all your code, you should be safe.
603 *
604 * gpihFindFont uses this mutex. If you call GpiCreateLogFont
605 * yourself somewhere, you should do this under the protection
606 * of this function.
607 *
608 * Call gpihUnlockLCIDs to unlock.
609 *
610 *@@added V0.9.9 (2001-04-01) [umoeller]
611 */
612
613BOOL gpihLockLCIDs(VOID)
614{
615 if (!G_hmtxLCIDs)
616 // first call: create
617 return (!DosCreateMutexSem(NULL,
618 &G_hmtxLCIDs,
619 0,
620 TRUE)); // request!
621
622 // subsequent calls: request
623 return (!WinRequestMutexSem(G_hmtxLCIDs, SEM_INDEFINITE_WAIT));
624}
625
626/*
627 *@@ UnlockLCIDs:
628 * releases the mutex for serializing the
629 * lcids.
630 *
631 *@@added V0.9.9 (2001-04-01) [umoeller]
632 */
633
634VOID gpihUnlockLCIDs(VOID)
635{
636 DosReleaseMutexSem(G_hmtxLCIDs);
637}
638
639/*
640 *@@ gpihQueryNextLCID:
641 * returns the next available lcid for the given HPS.
642 * Actually, it's the next available lcid for the
643 * entire process, since there can be only 255 altogether.
644 * Gets called by gpihFindFont automatically.
645 *
646 * WARNING: This function by itself is not thread-safe.
647 * See gpihLockLCIDs for how to serialize this.
648 *
649 * Code was extensively re-tested, works (V0.9.12 (2001-05-31) [umoeller]).
650 *
651 *@@added V0.9.3 (2000-05-06) [umoeller]
652 *@@changed V0.9.9 (2001-04-01) [umoeller]: removed all those sick sub-allocs
653 */
654
655LONG gpihQueryNextFontID(HPS hps)
656{
657 LONG lcidNext = -1;
658
659 LONG lCount = GpiQueryNumberSetIds(hps);
660 // the number of local identifiers
661 // (lcids) currently in use, and
662 // therefore the maximum number
663 // of objects for which information
664 // can be returned
665
666 // _Pmpf((__FUNCTION__ ": Entering"));
667
668 if (lCount == 0)
669 {
670 // none in use yet:
671 lcidNext = 15;
672
673 // _Pmpf((" no lcids in use"));
674 }
675 else
676 {
677 // #define GQNCL_BLOCK_SIZE 400*sizeof(LONG)
678
679 PLONG alTypes = NULL; // object types
680 PSTR8 aNames = NULL; // font names
681 PLONG allcids = NULL; // local identifiers
682
683 if ( (alTypes = (PLONG)malloc(lCount * sizeof(LONG)))
684 && (aNames = (PSTR8)malloc(lCount * sizeof(STR8)))
685 && (allcids = (PLONG)malloc(lCount * sizeof(LONG)))
686 )
687 {
688 if (GpiQuerySetIds(hps,
689 lCount,
690 alTypes,
691 aNames,
692 allcids))
693 {
694 // FINALLY we have all the lcids in use.
695 BOOL fContinue = TRUE;
696 lcidNext = 15;
697
698 // _Pmpf((" %d fonts in use, browsing...", lCount));
699
700 // now, check if this lcid is in use already:
701 while (fContinue)
702 {
703 BOOL fFound = FALSE;
704 ULONG ul;
705 fContinue = FALSE;
706 for (ul = 0;
707 ul < lCount;
708 ul++)
709 {
710 if (allcids[ul] == lcidNext)
711 {
712 fFound = TRUE;
713 break;
714 }
715 }
716
717 if (fFound)
718 {
719 // lcid found:
720 // try next higher one
721
722 // _Pmpf((" %d is busy...", lcidNext));
723
724 lcidNext++;
725 fContinue = TRUE;
726 }
727 // else
728 // else: return that one
729 // _Pmpf((" %d is free", lcidNext));
730 }
731 }
732 }
733
734 if (alTypes)
735 free(alTypes);
736 if (aNames)
737 free(aNames);
738 if (allcids)
739 free(allcids);
740 }
741
742 // _Pmpf((__FUNCTION__ ": Returning lcid %d", lcidNext));
743
744 return (lcidNext);
745}
746
747/*
748 *@@ gpihFindFont:
749 * this returns a new logical font ID (LCID) for the specified
750 * font by calling GpiCreateLogFont.
751 * This function performs the insane "11-step process" to
752 * match a font, as described in the GPI reference.
753 *
754 * This function can operate in two modes:
755 *
756 * -- "Family" mode. In that case, specify the font family name
757 * with pszName and set fFamily to TRUE. This is useful for
758 * WYSIWYG text viewing if you need several font faces for
759 * the same family, such as Courier Bold, Bold Italics, etc.
760 * You can specify those attributes with usFormat then.
761 *
762 * -- "Face" mode. In that case, specify the full font face name
763 * with pszName and set fFamily to FALSE. This is useful for
764 * font presentation parameters which use the "WarpSans Bold"
765 * format. In that case, set usFormat to 0.
766 *
767 * After the font has been created, if (pFontMetrics != NULL),
768 * *pFontMetrics receives the FONTMETRICS of the font which
769 * has been created. If an outline font has been created
770 * (instead of a bitmap font), FONTMETRICS.fsDefn will have
771 * the FM_DEFN_OUTLINE bit set.
772 *
773 * To then use the font whose LCID has been returned by this
774 * function for text output, call:
775 + GpiSetCharSet(hps, lLCIDReturned);
776 *
777 * <B>Font Point Sizes:</B>
778 *
779 * 1) For image (bitmap) fonts, the size is fixed, and
780 * you can directly draw after the font has been
781 * selected. GpiSetCharBox has no effect on image
782 * fonts unless you switch to character mode 2
783 * (if you care for that: GpiSetCharMode).
784 *
785 * 2) For outline fonts however, you need to define a
786 * character box if you want to output text in a
787 * size other than the default size. This is almost
788 * as bad a mess as this function, so gpihSetPointSize
789 * has been provided for this. See remarks there.
790 *
791 * <B>Example:</B>
792 *
793 * This example prints text in "24.Courier".
794 *
795 + PSZ pszOutput = "Test output";
796 + FONTMETRICS FontMetrics;
797 + LONG lLCID = gpihFindFont(hps,
798 + 24, // point size
799 + FALSE, // face, not family
800 + "Courier",
801 + 0,
802 + &FontMetrics);
803 + if (lLCID)
804 + {
805 + // no error:
806 + GpiSetCharSet(hps, lLCID);
807 + if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
808 + // outline font found (should be the case):
809 + gpihSetPointSize(hps, 24);
810 + }
811 + GpiCharString(hps, strlen(pszOutput), pszOutput);
812 *
813 * <B>Details:</B>
814 *
815 * First, GpiQueryFonts is called to enumerate all the fonts on
816 * the system. We then evaluate the awful FONTMETRICS data
817 * of those fonts to perform a "real" match.
818 *
819 * If that fails, we allow GPI to do a "close" match based
820 * on the input values. This might result in the system
821 * default font (System Proportional) to be found, in the
822 * worst case. But even then, a new LCID is returned.
823 *
824 * The "close match" comes in especially when using the
825 * font attributes (bold, italics) and we are unable to
826 * find the correct outline font for that. Unfortunately,
827 * the information in FONTMETRICS.fsSelection is wrong,
828 * wrong, wrong for the large majority of fonts. (For
829 * example, I get the "bold" flag set for regular fonts,
830 * and vice versa.) So we attempt to use the other fields,
831 * but this doesn't always help. Not even Netscape gets
832 * this right.
833 *
834 * <B>Font faces:</B>
835 *
836 * This is terribly complicated as well. You need to
837 * differentiate between "true" emphasis faces (that
838 * is, bold and italics are implemented thru separate
839 * font files) and the ugly GPI simulation, which simply
840 * makes the characters wider or shears them.
841 *
842 * This function even finds true "bold" and "italic" faces
843 * for outline fonts. To do this, always specify the "family"
844 * name as pszFaceName (e.g. "Courier" instead of "Courier
845 * Bold") and set the flags for usFormat (e.g. FATTR_SEL_BOLD).
846 * Note that this implies that you need call this function
847 * twice to create two logical fonts for regular and bold faces.
848 *
849 * If a "true" emphasis font is not found, the GPI simulation
850 * is enabled.
851 *
852 * <B>Remarks:</B>
853 *
854 * 1) This function _always_ creates a new logical font,
855 * whose ID is returned, even if the specified font
856 * was not found or a "close match" was performed by
857 * GPI. As a result, do not call this function twice
858 * for the same font specification, because there are
859 * only 255 logical font IDs for each process.
860 *
861 * 2) Since this function always creates an LCID,
862 * you should _always_ free the LCID later.
863 * This is only valid if the font is no longer selected
864 * into any presentation space. So use these calls:
865 + GpiSetCharSet(hps, LCID_DEFAULT);
866 + GpiDeleteSetId(hps, lLCIDReturnedByThis);
867 *
868 * 3) Using this function, bitmap fonts will have priority
869 * over outline fonts of the same face name. This is
870 * how the WPS does it too. This is most obvious with
871 * the "Helv" font, which exists as a bitmap font for
872 * certain point sizes only.
873 *
874 * 4) Since logical font IDs are shared across the
875 * process, a mutex is requested while the lcids are
876 * being queried and/or manipulated. In other words,
877 * this func is now thread-safe (V0.9.9).
878 *
879 * This calls gpihQueryNextFontID in turn to find the
880 * next free lcid. See remarks there.
881 *
882 * <B>Font metrics:</B>
883 *
884 * The important values in the returned FONTMETRICS are
885 * like this (according to PMREF):
886 *
887 + ÉÍ ________________________________________________
888 + º
889 + º lExternalLeading, according to font designer.
890 + º ________________________________________________ Í»
891 + ÈÍ º
892 + # # º
893 + ## ## º lMaxAscender (of entire;
894 + ÉÍ _______________ # # # # º font); this can be > capital
895 + º #### # # # # º letters because miniscules
896 + º # # # # # º can exceed that.
897 + º lXHeight # # # # º
898 + º # # # # º
899 + º # # # # º
900 + º _______________#####________#_______#___ baseline Í»
901 + ÈÍ # º
902 + # º lMaxDescender
903 + ______________ ####______________________________ ͌
904 +
905 *
906 * In turn, lMaxBaselineExt is lMaxAscender + lMaxDescender.
907 *
908 * Soooo... to find out about the optimal line spacing, GPIREF
909 * recommends to use lMaxBaselineExt + lExternalLeading.
910 *
911 *@@added V0.9.0 [umoeller]
912 *@@changed V0.9.3 (2000-05-06) [umoeller]: didn't work for more than one font; now using gpihQueryNextFontID
913 *@@changed V0.9.3 (2000-05-06) [umoeller]: usFormat didn't work; fixed
914 *@@changed V0.9.4 (2000-08-08) [umoeller]: added fFamily
915 *@@changed V0.9.9 (2001-04-01) [umoeller]: made this thread-safe, finally
916 *@@changed V0.9.14 (2001-08-01) [umoeller]: some optimizations
917 */
918
919LONG gpihFindFont(HPS hps, // in: HPS for font selection
920 LONG lSize, // in: font point size
921 BOOL fFamily, // in: if TRUE, pszName specifies font family;
922 // if FALSE, pszName specifies font face
923 const char *pcszName, // in: font family or face name (without point size)
924 USHORT usFormat, // in: none, one or several of:
925 // -- FATTR_SEL_ITALIC
926 // -- FATTR_SEL_UNDERSCORE (underline)
927 // -- FATTR_SEL_BOLD
928 // -- FATTR_SEL_STRIKEOUT
929 // -- FATTR_SEL_OUTLINE (hollow)
930 PFONTMETRICS pFontMetrics) // out: font metrics of created font (optional)
931{
932 LONG lLCIDReturn = 0;
933 ULONG ul = 0;
934 FATTRS FontAttrs;
935
936 // first find out how much memory we need to allocate
937 // for the FONTMETRICS structures
938 LONG lTemp = 0;
939 LONG cFonts = GpiQueryFonts(hps,
940 QF_PUBLIC | QF_PRIVATE,
941 NULL, // pszFaceName,
942 &lTemp,
943 sizeof(FONTMETRICS),
944 NULL);
945 PFONTMETRICS pfm = (PFONTMETRICS)malloc(cFonts * sizeof(FONTMETRICS)),
946 pfm2 = pfm,
947 pfmFound = NULL;
948
949 BOOL fQueriedDevice = FALSE; // V0.9.14 (2001-08-01) [umoeller]
950 LONG alDevRes[2]; // device resolution
951
952 // _Pmpf(("gpihFindFont: enumerating for %s, %d points", pszFaceName, lSize));
953
954 GpiQueryFonts(hps,
955 QF_PUBLIC | QF_PRIVATE,
956 NULL, // pszFaceName,
957 &cFonts,
958 sizeof(FONTMETRICS), // length of each metrics structure
959 // -- _not_ total buffer size!
960 pfm);
961
962 // now we have an array of FONTMETRICS
963 // for EVERY font that is installed on the system...
964 // these things are completely unsorted, so there's
965 // nothing we can rely on, we have to check them all.
966
967 // fill in some default values for FATTRS,
968 // in case we don't find something better
969 // in the loop below; these values will be
970 // applied if
971 // a) an outline font has been found;
972 // b) bitmap fonts have been found, but
973 // none for the current device resolution
974 // exists;
975 // c) no font has been found at all.
976 // In all cases, GpiCreateLogFont will do
977 // a "close match" resolution (at the bottom).
978 FontAttrs.usRecordLength = sizeof(FATTRS);
979 FontAttrs.fsSelection = usFormat; // changed later if better font is found
980 FontAttrs.lMatch = 0L; // closest match
981 strcpy(FontAttrs.szFacename, pcszName);
982 FontAttrs.idRegistry = 0; // default registry
983 FontAttrs.usCodePage = 0; // default codepage
984 // the following two must be zero, or outline fonts
985 // will not be found; if a bitmap font has been passed
986 // to us, we'll modify these two fields later
987 FontAttrs.lMaxBaselineExt = 0; // font size (height)
988 FontAttrs.lAveCharWidth = 0; // font size (width)
989 FontAttrs.fsType = 0; // default type
990 FontAttrs.fsFontUse = FATTR_FONTUSE_NOMIX;
991
992 // now go thru the array of FONTMETRICS
993 // to check if we have a bitmap font
994 // pszFaceName; the default WPS behavior
995 // is that bitmap fonts appear to take
996 // priority over outline fonts of the
997 // same name, so we check these first
998 pfm2 = pfm;
999 for (ul = 0;
1000 ul < cFonts;
1001 ul++)
1002 {
1003 /* _Pmpf((" Checking font: %s (Fam: %s), %d, %d, %d",
1004 pszFaceName,
1005 pfm2->szFamilyname,
1006 pfm2->sNominalPointSize,
1007 pfm2->lMaxBaselineExt,
1008 pfm2->lAveCharWidth)); */
1009
1010 const char *pcszCompare = (fFamily)
1011 ? pfm2->szFamilyname
1012 : pfm2->szFacename;
1013
1014 if (!strcmp(pcszCompare, pcszName))
1015 {
1016 /* _Pmpf((" Found font %s; slope %d, usWeightClass %d",
1017 pfm2->szFacename,
1018 pfm2->sCharSlope,
1019 pfm2->usWeightClass)); */
1020
1021 if ((pfm2->fsDefn & FM_DEFN_OUTLINE) == 0)
1022 {
1023 // image (bitmap) font:
1024 // check point size
1025 if (pfm2->sNominalPointSize == lSize * 10)
1026 {
1027 // OK: check device resolutions, because
1028 // normally, there are always several image
1029 // fonts for different resolutions
1030 // for bitmap fonts, there are always two versions:
1031 // one for low resolutions, one for high resolutions
1032 if (!fQueriedDevice)
1033 {
1034 DevQueryCaps(GpiQueryDevice(hps),
1035 CAPS_HORIZONTAL_FONT_RES,
1036 2L,
1037 alDevRes);
1038 fQueriedDevice = TRUE;
1039 }
1040
1041 if ( (pfm2->sXDeviceRes == alDevRes[0])
1042 && (pfm2->sYDeviceRes == alDevRes[1])
1043 )
1044 {
1045 // OK: use this for GpiCreateLogFont
1046 FontAttrs.lMaxBaselineExt = pfm2->lMaxBaselineExt;
1047 FontAttrs.lAveCharWidth = pfm2->lAveCharWidth;
1048 FontAttrs.lMatch = pfm2->lMatch;
1049
1050 pfmFound = pfm2;
1051 break;
1052 }
1053 }
1054 }
1055 else
1056 // outline font:
1057 if (pfmFound == NULL)
1058 {
1059 /*
1060 #define FATTR_SEL_ITALIC 0x0001
1061 #define FATTR_SEL_UNDERSCORE 0x0002
1062 #define FATTR_SEL_OUTLINE 0x0008
1063 #define FATTR_SEL_STRIKEOUT 0x0010
1064 #define FATTR_SEL_BOLD 0x0020
1065 */
1066 // no bitmap font found yet:
1067 if ( ( ( (usFormat & FATTR_SEL_BOLD)
1068 && (pfm2->usWeightClass == 7) // bold
1069 )
1070 || ( (!(usFormat & FATTR_SEL_BOLD))
1071 && (pfm2->usWeightClass == 5) // regular
1072 )
1073 )
1074 && ( ( (usFormat & FATTR_SEL_ITALIC)
1075 && (pfm2->sCharSlope != 0) // italics
1076 )
1077 || ( (!(usFormat & FATTR_SEL_ITALIC))
1078 && (pfm2->sCharSlope == 0) // regular
1079 )
1080 )
1081 )
1082 {
1083 // yes, we found a true font for that face:
1084 pfmFound = pfm2;
1085 // use this exact font for GpiCreateLogFont
1086 FontAttrs.lMatch = pfm2->lMatch;
1087 // according to GPIREF, we must also specify
1088 // the full face name... Jesus!
1089 strcpy(FontAttrs.szFacename, pfm2->szFacename);
1090 // unset flag in FATTRS, because this would
1091 // duplicate bold or italic
1092 FontAttrs.fsSelection = 0;
1093
1094 // _Pmpf((" --> using it"));
1095 // but loop on, because we might have a bitmap
1096 // font which should take priority
1097 }
1098 }
1099 }
1100
1101 pfm2++;
1102 }
1103
1104 if (pfmFound)
1105 // FONTMETRICS found:
1106 // copy font metrics?
1107 if (pFontMetrics)
1108 memcpy(pFontMetrics, pfmFound, sizeof(FONTMETRICS));
1109
1110 // free the FONTMETRICS array
1111 free(pfm);
1112
1113 if (gpihLockLCIDs()) // V0.9.9 (2001-04-01) [umoeller]
1114 {
1115 // new logical font ID: last used plus one
1116 lLCIDReturn = gpihQueryNextFontID(hps);
1117
1118 GpiCreateLogFont(hps,
1119 NULL, // don't create "logical font name" (STR8)
1120 lLCIDReturn,
1121 &FontAttrs);
1122
1123 gpihUnlockLCIDs();
1124 }
1125
1126 // _Pmpf((__FUNCTION__ ": returning lcid %d", lLCIDReturn));
1127
1128 return (lLCIDReturn);
1129}
1130
1131/*
1132 *@@ gpihFindPresFont:
1133 * similar to gpihFindFont, but this one evaluates
1134 * the PP_FONTNAMESIZE presentation parameter of the
1135 * specified window instead. If that one is not set,
1136 * the specified default font is used instead.
1137 *
1138 * Note that as opposed to gpihFindFont, this one
1139 * takes a "8.Helv"-type string as input.
1140 *
1141 * See gpihFindFont for additional remarks, which
1142 * gets called by this function.
1143 *
1144 * Again, if an outline font has been returned, you
1145 * must also set the "character box" for the HPS, or
1146 * your text will always have the same point size.
1147 * Use gpihSetPointSize for that, using the presparam's
1148 * point size, which is returned by this function
1149 * into *plSize, if (plSize != NULL):
1150 +
1151 + FONTMETRICS FontMetrics;
1152 + LONG lPointSize;
1153 + LONG lLCID = gpihFindPresFont(hwnd, hps, "8.Helv",
1154 + &FontMetrics,
1155 + &lPointSize);
1156 + GpiSetCharSet(hps, lLCID);
1157 + if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
1158 + gpihSetPointSize(hps, lPointSize);
1159 *
1160 * If errors occur, e.g. if the font string does not
1161 * conform to the "size.face" format, null is returned.
1162 *
1163 *@@added V0.9.0 [umoeller]
1164 *@@changed V0.9.9 (2001-04-01) [umoeller]: now supporting NULLHANDLE hwnd
1165 */
1166
1167LONG gpihFindPresFont(HWND hwnd, // in: window to search for presparam or NULLHANDLE
1168 BOOL fInherit, // in: search parent windows too?
1169 HPS hps, // in: HPS for font selection
1170 const char *pcszDefaultFont, // in: default font if not found (i.e. "8.Helv")
1171 PFONTMETRICS pFontMetrics, // out: font metrics of created font (optional)
1172 PLONG plSize) // out: presparam's point size (optional)
1173{
1174 CHAR szPPFont[200] = "";
1175 const char *pcszFontFound = 0;
1176 CHAR szFaceName[300] = "";
1177 ULONG ulFontSize = 0;
1178
1179 if ( (hwnd) // V0.9.9 (2001-04-01) [umoeller]
1180 && (WinQueryPresParam(hwnd,
1181 PP_FONTNAMESIZE, // first PP to query
1182 0, // second PP to query
1183 NULL, // out: which one is returned
1184 (ULONG)sizeof(szPPFont), // in: buffer size
1185 (PVOID)&szPPFont, // out: PP value returned
1186 (fInherit)
1187 ? 0
1188 : QPF_NOINHERIT))
1189 )
1190 // PP found:
1191 pcszFontFound = szPPFont;
1192 else
1193 pcszFontFound = pcszDefaultFont;
1194
1195 if (pcszFontFound)
1196 {
1197 const char *pcDot = strchr(pcszFontFound, '.');
1198 if (pcDot)
1199 {
1200 // _Pmpf(("Found font PP: %s", pszFontFound));
1201 sscanf(pcszFontFound, "%lu", &ulFontSize);
1202 if (plSize)
1203 *plSize = ulFontSize;
1204 strcpy(szFaceName, pcDot + 1);
1205 return (gpihFindFont(hps,
1206 ulFontSize,
1207 FALSE, // face, not family name
1208 szFaceName,
1209 0,
1210 pFontMetrics));
1211 }
1212 }
1213
1214 return (0);
1215}
1216
1217/*
1218 *@@ gpihSetPointSize:
1219 * this invokes GpiSetCharBox on the given HPS to
1220 * set the correct "character box" with the proper
1221 * parameters.
1222 *
1223 * This is necessary for text output with outline
1224 * fonts. Bitmap fonts have a fixed size, and
1225 * calling this function is not necessary for them.
1226 *
1227 * Unfortunately, IBM has almost not documented
1228 * how to convert nominal point sizes to the
1229 * correct character cell values. This involves
1230 * querying the output device (DevQueryCaps).
1231 *
1232 * I have found one hint for this procedure in GPIREF
1233 * (chapter "Character string primitives", "Using...",
1234 * "Drawing text"), but the example code given
1235 * there is buggy, because it must be "72" instead
1236 * of "720". #%(%!"õ!!.
1237 *
1238 * So here we go. If you want to output text in,
1239 * say, "Courier" and 24 points (nominal point size),
1240 * select the font into your HPS and call
1241 + gpihSetPointSize(hps, 24)
1242 * and you're done. See gpihFindFont for a complete
1243 * example.
1244 *
1245 * Call this function as many times as needed. This
1246 * consumes no resources at all.
1247 *
1248 * This returns the return value of GpiSetCharBox.
1249 *
1250 *@@added V0.9.0 [umoeller]
1251 */
1252
1253BOOL gpihSetPointSize(HPS hps, // in: presentation space for output
1254 LONG lPointSize) // in: desired nominal point size
1255{
1256 SIZEF box;
1257 LONG alDevRes[2];
1258 DevQueryCaps(GpiQueryDevice(hps), // get the HDC from the HPS
1259 CAPS_HORIZONTAL_FONT_RES,
1260 2L,
1261 alDevRes);
1262 box.cx = MAKEFIXED((lPointSize * alDevRes[0]) / 72, 0);
1263 box.cy = MAKEFIXED((lPointSize * alDevRes[1]) / 72, 0);
1264 return (GpiSetCharBox(hps, &box));
1265}
1266
1267/*
1268 *@@ gpihQueryLineSpacing:
1269 * this returns the optimal line spacing for text
1270 * output with the current HPS; this is computed
1271 * by evaluating those incredible FONTMETRICS.
1272 *
1273 * This might be helpful if you write text to the
1274 * screen yourself and need the height of a text
1275 * line to advance to the next.
1276 *
1277 *@@changed V0.9.7 (2000-12-20) [umoeller]: removed psz param
1278 */
1279
1280LONG gpihQueryLineSpacing(HPS hps)
1281{
1282 FONTMETRICS fm;
1283
1284 if (GpiQueryFontMetrics(hps, sizeof(FONTMETRICS), &fm))
1285 return ( ( fm.lMaxBaselineExt // max vertical font space
1286 +fm.lExternalLeading) // space advised by font designer
1287 );
1288 else
1289 return (15);
1290}
1291
1292/*
1293 *@@category: Helpers\PM helpers\GPI helpers\Bitmaps/Icons
1294 */
1295
1296/* ******************************************************************
1297 *
1298 * Bitmap helpers
1299 *
1300 ********************************************************************/
1301
1302/*
1303 *@@ gpihCreateMemPS:
1304 * creates a memory device context and presentation space so
1305 * that they are compatible with the screen device context and
1306 * presentation space. These are stored in *hdcMem and *hpsMem.
1307 *
1308 * This is a one-shot function for the standard code that is
1309 * always needed when working with bitmaps in a memory device
1310 * context.
1311 *
1312 * psizlPage must point to a SIZEL structure containing the
1313 * width and height for the memory PS. Specify the size of
1314 * the future bitmap here. Specify {0, 0} to get a PS with
1315 * the size of the full screen, which consumes quite a bit
1316 * of memory though.
1317 *
1318 * Returns FALSE upon errors. In that case, both hdcMem and
1319 * hpsMem are set to NULLHANDLE.
1320 *
1321 * To cleanup after this function has returned TRUE, use the
1322 * following:
1323 + GpiDestroyPS(hpsMem);
1324 + DevCloseDC(hdcMem);
1325 *
1326 *@@changed V0.9.3 (2000-05-18) [umoeller]: added psiszlPage
1327 */
1328
1329BOOL gpihCreateMemPS(HAB hab, // in: anchor block
1330 PSIZEL psizlPage, // in: width and height for mem PS
1331 HDC *hdcMem, // out: memory DC or NULLHANDLE upon errors
1332 HPS *hpsMem) // out: memory PS or NULLHANDLE upon errors
1333{
1334 BOOL brc = FALSE;
1335 PSZ pszData[4] = { "Display", NULL, NULL, NULL };
1336
1337 // create new memory DC
1338 if ((*hdcMem = DevOpenDC(hab,
1339 OD_MEMORY, // create memory DC
1340 "*", // token: do not take INI info
1341 4, // item count in pszData
1342 (PDEVOPENDATA)pszData,
1343 NULLHANDLE))) // compatible with screen
1344 {
1345 // memory DC created successfully:
1346 // create compatible PS
1347 if ((*hpsMem = GpiCreatePS(hab,
1348 *hdcMem, // HDC to associate HPS with (GPIA_ASSOC);
1349 // mandatory for GPIT_MICRO
1350 psizlPage, // is (0, 0) == screen size
1351 PU_PELS // presentation page units: pixels
1352 | GPIA_ASSOC // associate with hdcMem (req. for GPIT_MICRO)
1353 | GPIT_MICRO))) // micro presentation space
1354 brc = TRUE;
1355 else
1356 {
1357 // error (hpsMem == NULLHANDLE):
1358 // close memory DC again
1359 DevCloseDC(*hdcMem);
1360 *hdcMem = NULLHANDLE;
1361 }
1362 }
1363
1364 return (brc);
1365}
1366
1367/*
1368 *@@ gpihCreateBitmap:
1369 * creates a new bitmap for a given memory PS.
1370 * This bitmap will have the cPlanes and bitcount
1371 * which are found in the memory PS.
1372 * For all the mysterious other values, we use
1373 * fixed default values, this doesn't seem to hurt.
1374 *
1375 * Note that the bitmap is _not_ yet selected into
1376 * the specified memory PS. You must call
1377 + GpiSetBitmap(hpsMem, hbm)
1378 * to do this.
1379 *
1380 * Returns the bitmap handle or NULLHANDLE upon errors.
1381 *
1382 *@@changed V0.9.0 [umoeller]: function prototype changed to cx and cy
1383 */
1384
1385HBITMAP gpihCreateBitmap(HPS hpsMem, // in: memory DC
1386 ULONG cx, // in: width of new bitmap
1387 ULONG cy) // in: height of new bitmap
1388{
1389 HBITMAP hbm = NULLHANDLE;
1390 LONG alData[2];
1391 BITMAPINFOHEADER2 bih2;
1392 PBITMAPINFO2 pbmi = NULL;
1393
1394 // determine the device's plane/bit-count format;
1395 // alData[0] then has cPlanes,
1396 // alData[1] has cBitCount
1397 if (GpiQueryDeviceBitmapFormats(hpsMem, 2, alData))
1398 {
1399 // set up the BITMAPINFOHEADER2 and BITMAPINFO2 structures
1400 bih2.cbFix = (ULONG)sizeof(BITMAPINFOHEADER2);
1401 bih2.cx = cx; // (prcl->xRight - prcl->xLeft); changed V0.9.0
1402 bih2.cy = cy; // (prcl->yTop - prcl->yBottom); changed V0.9.0
1403 bih2.cPlanes = alData[0];
1404 bih2.cBitCount = alData[1];
1405 bih2.ulCompression = BCA_UNCOMP;
1406 bih2.cbImage = ( ( (bih2.cx
1407 * (1 << bih2.cPlanes)
1408 * (1 << bih2.cBitCount)
1409 ) + 31
1410 ) / 32
1411 ) * bih2.cy;
1412 bih2.cxResolution = 70;
1413 bih2.cyResolution = 70;
1414 bih2.cclrUsed = 2;
1415 bih2.cclrImportant = 0;
1416 bih2.usUnits = BRU_METRIC; // measure units for cxResolution/cyResolution: pels per meter
1417 bih2.usReserved = 0;
1418 bih2.usRecording = BRA_BOTTOMUP; // scan lines are bottom to top (default)
1419 bih2.usRendering = BRH_NOTHALFTONED; // other algorithms aren't documented anyway
1420 bih2.cSize1 = 0; // parameter for halftoning (undocumented anyway)
1421 bih2.cSize2 = 0; // parameter for halftoning (undocumented anyway)
1422 bih2.ulColorEncoding = BCE_RGB; // only possible value
1423 bih2.ulIdentifier = 0; // application-specific data
1424
1425 // allocate memory for info header
1426 if (DosAllocMem((PPVOID)&pbmi,
1427 sizeof(BITMAPINFO2) +
1428 (sizeof(RGB2)
1429 * (1 << bih2.cPlanes)
1430 * (1 << bih2.cBitCount)
1431 ),
1432 PAG_COMMIT | PAG_READ | PAG_WRITE)
1433 == NO_ERROR)
1434 {
1435 pbmi->cbFix = bih2.cbFix;
1436 pbmi->cx = bih2.cx;
1437 pbmi->cy = bih2.cy;
1438 pbmi->cPlanes = bih2.cPlanes;
1439 pbmi->cBitCount = bih2.cBitCount;
1440 pbmi->ulCompression = BCA_UNCOMP;
1441 pbmi->cbImage = ((bih2.cx+31)/32) * bih2.cy;
1442 pbmi->cxResolution = 70;
1443 pbmi->cyResolution = 70;
1444 pbmi->cclrUsed = 2;
1445 pbmi->cclrImportant = 0;
1446 pbmi->usUnits = BRU_METRIC;
1447 pbmi->usReserved = 0;
1448 pbmi->usRecording = BRA_BOTTOMUP;
1449 pbmi->usRendering = BRH_NOTHALFTONED;
1450 pbmi->cSize1 = 0;
1451 pbmi->cSize2 = 0;
1452 pbmi->ulColorEncoding = BCE_RGB;
1453 pbmi->ulIdentifier = 0;
1454
1455 // create a bit map that is compatible with the display
1456 hbm = GpiCreateBitmap(hpsMem,
1457 &bih2,
1458 FALSE,
1459 NULL,
1460 pbmi);
1461
1462 // free the memory we allocated previously; GPI has
1463 // allocated all the resources it needs itself, so
1464 // we can release this
1465 DosFreeMem(pbmi);
1466 }
1467 }
1468
1469 return (hbm);
1470}
1471
1472/*
1473 *@@ gpihCreateBmpFromPS:
1474 * this creates a new bitmap and copies a screen rectangle
1475 * into it. Consider this a "screen capture" function.
1476 *
1477 * The new bitmap (which is returned) is compatible with the
1478 * device associated with hpsScreen. This function calls
1479 * gpihCreateMemPS and gpihCreateBitmap to have it created.
1480 * The memory PS is only temporary and freed again.
1481 *
1482 * This returns the handle of the new bitmap,
1483 * which can then be used for WinDrawBitmap and such, or
1484 * NULLHANDLE upon errors.
1485 *
1486 *@@changed V0.9.12 (2001-05-20) [umoeller]: fixed excessive mem PS size
1487 */
1488
1489HBITMAP gpihCreateBmpFromPS(HAB hab, // in: anchor block
1490 HPS hpsScreen, // in: screen PS to copy from
1491 PRECTL prcl) // in: rectangle to copy
1492{
1493
1494 /* To copy an image from a display screen to a bit map:
1495 1. Associate the memory device context with a presentation space.
1496 2. Create a bit map.
1497 3. Select the bit map into the memory device context by calling GpiSetBitmap.
1498 4. Determine the location (in device coordinates) of the image.
1499 5. Call GpiBitBlt and copy the image to the bit map. */
1500
1501 HDC hdcMem;
1502 HPS hpsMem;
1503 HBITMAP hbm = NULLHANDLE;
1504 POINTL aptl[3];
1505
1506 SIZEL szlPage = {prcl->xRight - prcl->xLeft,
1507 prcl->yTop - prcl->yBottom}; // fixed V0.9.12 (2001-05-20) [umoeller]
1508 if (gpihCreateMemPS(hab,
1509 &szlPage,
1510 &hdcMem,
1511 &hpsMem))
1512 {
1513 if ((hbm = gpihCreateBitmap(hpsMem,
1514 szlPage.cx,
1515 szlPage.cy)))
1516 {
1517 // Associate the bit map and the memory presentation space.
1518 if (GpiSetBitmap(hpsMem, hbm)
1519 != HBM_ERROR)
1520 {
1521 // Copy the screen to the bit map.
1522 aptl[0].x = 0; // lower-left corner of destination rectangle
1523 aptl[0].y = 0;
1524 aptl[1].x = prcl->xRight; // upper-right corner for both
1525 aptl[1].y = prcl->yTop;
1526 aptl[2].x = prcl->xLeft; // lower-left corner of source rectangle
1527 aptl[2].y = prcl->yBottom;
1528
1529 if (GpiBitBlt(hpsMem,
1530 hpsScreen,
1531 sizeof(aptl) / sizeof(POINTL), // Number of points in aptl
1532 aptl,
1533 ROP_SRCCOPY,
1534 BBO_IGNORE)
1535 == GPI_ERROR)
1536 {
1537 // error during bitblt:
1538 GpiDeleteBitmap(hbm);
1539 hbm = NULLHANDLE; // for return code
1540 }
1541 }
1542 else
1543 {
1544 // error selecting bitmap for hpsMem:
1545 GpiDeleteBitmap(hbm);
1546 hbm = NULLHANDLE; // for return code
1547 }
1548 }
1549
1550 GpiDestroyPS(hpsMem);
1551 DevCloseDC(hdcMem);
1552 } // end if (hdcMem = DevOpenDC())
1553
1554 return (hbm);
1555}
1556
1557/*
1558 *@@ gpihCreateHalftonedBitmap:
1559 * this creates a half-toned copy of the
1560 * input bitmap by doing the following:
1561 *
1562 * 1) create a new bitmap with the size of hbmSource;
1563 * 2) copy hbmSource to the new bitmap (using GpiWCBitBlt);
1564 * 3) overpaint every other pixel with lColorGray.
1565 *
1566 * Note that the memory DC is switched to RGB mode, so
1567 * lColorGray better be an RGB color.
1568 *
1569 * Note: hbmSource must _not_ be selected into any device
1570 * context, or otherwise GpiWCBitBlt will fail.
1571 *
1572 * This returns the new bitmap or NULLHANDLE upon errors.
1573 *
1574 *@added V0.9.0
1575 */
1576
1577HBITMAP gpihCreateHalftonedBitmap(HAB hab, // in: anchor block
1578 HBITMAP hbmSource, // in: source bitmap
1579 LONG lColorGray) // in: color used for gray
1580{
1581 HBITMAP hbmReturn = NULLHANDLE;
1582
1583 HDC hdcMem;
1584 HPS hpsMem;
1585 BITMAPINFOHEADER2 bmi;
1586 // RECTL rclSource;
1587
1588 if (hbmSource)
1589 {
1590 SIZEL szlPage;
1591 // query bitmap info
1592 bmi.cbFix = sizeof(bmi);
1593 GpiQueryBitmapInfoHeader(hbmSource, &bmi);
1594
1595 szlPage.cx = bmi.cx;
1596 szlPage.cy = bmi.cy;
1597 if (gpihCreateMemPS(hab, &szlPage, &hdcMem, &hpsMem))
1598 {
1599 if ((hbmReturn = gpihCreateBitmap(hpsMem,
1600 bmi.cx,
1601 bmi.cy)))
1602 {
1603 if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
1604 {
1605 POINTL aptl[4];
1606
1607 // step 1: copy bitmap
1608 memset(aptl, 0, sizeof(POINTL) * 4);
1609 // aptl[0]: target bottom-left, is all 0
1610 // aptl[1]: target top-right (inclusive!)
1611 aptl[1].x = bmi.cx - 1;
1612 aptl[1].y = bmi.cy - 1;
1613 // aptl[2]: source bottom-left, is all 0
1614
1615 // aptl[3]: source top-right (exclusive!)
1616 aptl[3].x = bmi.cx;
1617 aptl[3].y = bmi.cy;
1618 GpiWCBitBlt(hpsMem, // target HPS (bmp selected)
1619 hbmSource,
1620 4L, // must always be 4
1621 &aptl[0], // points array
1622 ROP_SRCCOPY,
1623 BBO_IGNORE);
1624 // doesn't matter here, because we're not stretching
1625
1626 // step 2: overpaint bitmap
1627 // with half-toned pattern
1628
1629 gpihSwitchToRGB(hpsMem);
1630
1631 GpiMove(hpsMem, &aptl[0]); // still 0, 0
1632 aptl[0].x = bmi.cx - 1;
1633 aptl[0].y = bmi.cy - 1;
1634 GpiSetColor(hpsMem, lColorGray);
1635 GpiSetPattern(hpsMem, PATSYM_HALFTONE);
1636 GpiBox(hpsMem,
1637 DRO_FILL, // interior only
1638 &aptl[0],
1639 0, 0); // no corner rounding
1640
1641 // unselect bitmap
1642 GpiSetBitmap(hpsMem, NULLHANDLE);
1643 } // end if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
1644 else
1645 {
1646 // error selecting bitmap:
1647 GpiDeleteBitmap(hbmReturn);
1648 hbmReturn = NULLHANDLE;
1649 }
1650 } // end if (hbmReturn = gpihCreateBitmap...
1651
1652 GpiDestroyPS(hpsMem);
1653 DevCloseDC(hdcMem);
1654 } // end if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
1655 } // end if (hbmSource)
1656
1657 return (hbmReturn);
1658}
1659
1660/*
1661 *@@ gpihLoadBitmapFile:
1662 * this loads the specified bitmap file into
1663 * the given HPS. Note that the bitmap is _not_
1664 * yet selected into the HPS.
1665 *
1666 * This function can currently only handle OS/2 1.3
1667 * bitmaps.
1668 *
1669 * Returns the new bitmap handle or NULL upon errors
1670 * (e.g. if an OS/2 2.0 bitmap was accessed).
1671 *
1672 * In the latter case, *pulError is set to one of
1673 * the following:
1674 * -- -1: file not found
1675 * -- -2: malloc failed
1676 * -- -3: the bitmap data could not be read (fopen failed)
1677 * -- -4: file format not recognized (maybe OS/2 2.0 bitmap)
1678 * -- -5: GpiCreateBitmap error (maybe file corrupt)
1679 *
1680 *@@changed V0.9.4 (2000-08-03) [umoeller]: this didn't return NULLHANDLE on errors
1681 */
1682
1683HBITMAP gpihLoadBitmapFile(HPS hps, // in: HPS for bmp
1684 PSZ pszBmpFile, // in: bitmap filename
1685 PULONG pulError) // out: error code if FALSE is returned
1686{
1687 HBITMAP hbm = NULLHANDLE;
1688 PBITMAPFILEHEADER2 pbfh;
1689
1690 struct stat st;
1691 PBYTE pBmpData;
1692 FILE *BmpFile;
1693
1694 if (stat(pszBmpFile, &st) == 0)
1695 {
1696
1697 if ((pBmpData = (PBYTE)malloc(st.st_size)))
1698 {
1699 // open bmp file
1700 if ((BmpFile = fopen(pszBmpFile, "rb")))
1701 {
1702 // read bmp data
1703 fread(pBmpData, 1, st.st_size, BmpFile);
1704 fclose(BmpFile);
1705
1706 // check bitmap magic codes
1707 if (pBmpData[0] == 'B' && pBmpData[1] == 'M')
1708 {
1709 pbfh = (PBITMAPFILEHEADER2)pBmpData;
1710 hbm = GpiCreateBitmap(hps,
1711 &pbfh->bmp2,
1712 CBM_INIT,
1713 (pBmpData + pbfh->offBits),
1714 (PBITMAPINFO2)&pbfh->bmp2);
1715
1716 if (hbm == NULLHANDLE)
1717 {
1718 if (pulError)
1719 *pulError = -5;
1720 }
1721 }
1722 else if (pulError)
1723 *pulError = -4;
1724
1725 }
1726 else if (pulError)
1727 *pulError = -3;
1728
1729 free(pBmpData);
1730 }
1731 else if (pulError)
1732 *pulError = -2;
1733 }
1734 else if (pulError)
1735 *pulError = -1;
1736
1737 return (hbm);
1738}
1739
1740/*
1741 *@@ gpihStretchBitmap:
1742 * this copies hbmSource to the bitmap selected
1743 * into hpsTarget, which must be a memory PS.
1744 *
1745 * The source size is the whole size of hbmSource,
1746 * the target size is specified in prclTarget
1747 * (which is exclusive, meaning that the top right
1748 * corner of that rectangle lies _outside_ the target).
1749 *
1750 * This uses GpiWCBitBlt to stretch the bitmap.
1751 * hbmSource therefore must _not_ be selected
1752 * into any presentation space, or GpiWCBitBlt will
1753 * fail.
1754 *
1755 * If (fPropotional == TRUE), the target size is
1756 * modified so that the proportions of the bitmap
1757 * are preserved. The bitmap data will then be
1758 * copied to a subrectangle of the target bitmap:
1759 * there will be extra space either to the left
1760 * and right of the bitmap data or to the bottom
1761 * and top.
1762 * The outside areas of the target bitmap are
1763 * not changed then, so you might want to fill
1764 * the bitmap with some color first.
1765 *
1766 * This returns the return value of GpiWCBitBlt,
1767 * which can be:
1768 * -- GPI_OK
1769 * -- GPI_HITS: correlate hits
1770 * -- GPI_ERROR: error occured (probably either hbmSource not free
1771 * or no bitmap selected into hpsTarget)
1772 *
1773 *@added V0.9.0
1774 */
1775
1776LONG gpihStretchBitmap(HPS hpsTarget, // in: memory PS to copy bitmap to
1777 HBITMAP hbmSource, // in: bitmap to be copied into hpsTarget (must be free)
1778 PRECTL prclSource, // in: source rectangle -- if NULL, use size of bitmap
1779 PRECTL prclTarget, // in: target rectangle (req.)
1780 BOOL fProportional) // in: preserve proportions when stretching?
1781{
1782 LONG lHits = 0;
1783 BITMAPINFOHEADER2 bih2;
1784 POINTL aptl[4];
1785 BOOL fCalculated = FALSE;
1786
1787 memset(aptl, 0, sizeof(POINTL) * 4);
1788
1789 bih2.cbFix = sizeof(bih2);
1790 GpiQueryBitmapInfoHeader(hbmSource,
1791 &bih2);
1792
1793 // aptl[2]: source bottom-left, is all 0
1794 // aptl[3]: source top-right (exclusive!)
1795 aptl[3].x = bih2.cx;
1796 aptl[3].y = bih2.cy;
1797
1798 if (fProportional)
1799 {
1800 // proportional mode:
1801
1802 // 1) find out whether cx or cy is too
1803 // large
1804
1805 ULONG ulPropSource = (bih2.cx * 1000)
1806 / bih2.cy;
1807 // e.g. if the bmp is 200 x 100, we now have 2000
1808 ULONG ulPropTarget = ((prclTarget->xRight - prclTarget->xLeft) * 1000)
1809 / (prclTarget->yTop - prclTarget->yBottom);
1810 // case 1: if prclTarget is 300 x 100, we now have 3000 (> ulPropSource)
1811 // case 2: if prclTarget is 150 x 100, we now have 1500 (< ulPropSource)
1812
1813 // case 1:
1814 if (ulPropTarget > ulPropSource)
1815 {
1816 // prclTarget is too wide (horizontally):
1817 // decrease width, keep height
1818
1819 ULONG cx = (prclTarget->xRight - prclTarget->xLeft);
1820 ULONG cxNew = (cx * ulPropSource) / ulPropTarget;
1821
1822 // aptl[0]: target bottom-left
1823 // move left right (towards center)
1824 aptl[0].x = prclTarget->xLeft + ((cx - cxNew) / 2);
1825 aptl[0].y = prclTarget->yBottom;
1826
1827 // aptl[1]: target top-right (inclusive!)
1828 aptl[1].x = aptl[0].x + cxNew;
1829 aptl[1].y = prclTarget->yTop;
1830
1831 fCalculated = TRUE;
1832 }
1833 else
1834 {
1835 // prclTarget is too high (vertically):
1836 // keep width, decrease height
1837
1838 ULONG cy = (prclTarget->yTop - prclTarget->yBottom);
1839 ULONG cyNew = (cy * ulPropTarget) / ulPropSource;
1840
1841 // aptl[0]: target bottom-left
1842 aptl[0].x = prclTarget->xLeft;
1843 // move bottom up (towards center)
1844 aptl[0].y = prclTarget->yBottom + ((cy - cyNew) / 2);
1845
1846 // aptl[1]: target top-right (inclusive!)
1847 aptl[1].x = prclTarget->xRight;
1848 aptl[1].y = aptl[0].y + cyNew;
1849 // (prclTarget->yTop * ulPropSource) / ulPropTarget;
1850
1851 fCalculated = TRUE;
1852 }
1853 } // end if (pa->ulFlags & ANF_PROPORTIONAL)
1854
1855 if (!fCalculated)
1856 {
1857 // non-proportional mode or equal proportions:
1858 // stretch to whole size of prclTarget
1859
1860 // aptl[0]: target bottom-left
1861 aptl[0].x = prclTarget->xLeft;
1862 aptl[0].y = prclTarget->yBottom;
1863 // aptl[1]: target top-right (inclusive!)
1864 aptl[1].x = prclTarget->xRight;
1865 aptl[1].y = prclTarget->yTop;
1866 }
1867
1868 lHits = GpiWCBitBlt(hpsTarget, // target HPS (bmp selected)
1869 hbmSource,
1870 4L, // must always be 4
1871 &aptl[0], // points array
1872 ROP_SRCCOPY,
1873 BBO_IGNORE);
1874 // ignore eliminated rows or
1875 // columns; useful for color
1876
1877 return (lHits);
1878}
1879
1880/*
1881 *@@ gpihIcon2Bitmap:
1882 * this paints the given icon/pointer into
1883 * a bitmap.
1884 *
1885 * Returns FALSE upon errors.
1886 *
1887 *@@added V0.9.0 [umoeller]
1888 */
1889
1890BOOL gpihIcon2Bitmap(HPS hpsMem, // in: target memory PS with bitmap selected into it
1891 HPOINTER hptr, // in: source icon
1892 LONG lBkgndColor, // in: background color for transparent areas
1893 ULONG ulIconSize) // in: icon size (should be the value of WinQuerySysValue(HWND_DESKTOP, SV_CXICON))
1894{
1895 BOOL brc = FALSE;
1896 POINTERINFO pi;
1897
1898 // Each icon consists of two (really three)
1899 // bitmaps, which are stored in the POINTERINFO
1900 // structure:
1901 // pi.hbmColor is the actual bitmap to be
1902 // drawn. The parts that are
1903 // to be transparent or inverted
1904 // are black in this image.
1905 // pi.hbmPointer has twice the height of
1906 // hbmColor. The upper bitmap
1907 // contains an XOR mask (for
1908 // inverting parts), the lower
1909 // bitmap an AND mask (for
1910 // transparent parts).
1911 if (WinQueryPointerInfo(hptr, &pi))
1912 {
1913 POINTL aptl[4];
1914 memset(aptl, 0, sizeof(POINTL) * 4);
1915
1916 // aptl[0]: target bottom-left, is all 0
1917
1918 // aptl[1]: target top-right (inclusive!)
1919 aptl[1].x = ulIconSize;
1920 aptl[1].y = ulIconSize;
1921
1922 // aptl[2]: source bottom-left, is all 0
1923
1924 // aptl[3]: source top-right (exclusive!)
1925 aptl[3].x = ulIconSize + 1;
1926 aptl[3].y = ulIconSize + 1;
1927
1928 GpiSetColor(hpsMem, CLR_WHITE);
1929 GpiSetBackColor(hpsMem, CLR_BLACK);
1930
1931 // GpiErase(hpsMem);
1932
1933 // work on the AND image
1934 GpiWCBitBlt(hpsMem,
1935 pi.hbmPointer,
1936 4L, // must always be 4
1937 &aptl[0], // point array
1938 ROP_SRCAND, // source AND target
1939 BBO_OR);
1940
1941 // paint the real image
1942 if (pi.hbmColor)
1943 GpiWCBitBlt(hpsMem,
1944 pi.hbmColor,
1945 4L, // must always be 4
1946 &aptl[0], // point array
1947 ROP_SRCPAINT, // source OR target
1948 BBO_OR);
1949
1950 GpiSetColor(hpsMem, lBkgndColor);
1951 // work on the XOR image
1952 aptl[2].y = ulIconSize;
1953 aptl[3].y = (ulIconSize * 2) + 1;
1954 GpiWCBitBlt(hpsMem,
1955 pi.hbmPointer,
1956 4L, // must always be 4
1957 &aptl[0], // point array
1958 ROP_SRCINVERT,
1959 BBO_OR);
1960
1961 brc = TRUE;
1962 }
1963
1964 return (brc);
1965}
1966
1967/*
1968 *@@category: Helpers\PM helpers\GPI helpers\XBitmaps
1969 * Extended bitmaps. See gpihCreateXBitmap for an introduction.
1970 */
1971
1972/* ******************************************************************
1973 *
1974 * XBitmap functions
1975 *
1976 ********************************************************************/
1977
1978/*
1979 *@@ gpihCreateXBitmap:
1980 * creates an XBitmap, which is returned in an
1981 * _XBITMAP structure.
1982 *
1983 * The problem with all the GPI bitmap functions
1984 * is that they are quite complex and it is easy
1985 * to forget one of the "disassociate" and "deselect"
1986 * functions, which then simply leads to enormous
1987 * resource leaks in the application.
1988 *
1989 * This function may relieve this a bit. This
1990 * creates a memory DC, an memory PS, and a bitmap,
1991 * and selects the bitmap into the memory PS.
1992 * You can then use any GPI function on the memory
1993 * PS to draw into the bitmap. Use the fields from
1994 * XBITMAP for that.
1995 *
1996 * The bitmap is created in RGB mode.
1997 *
1998 * Use gpihDestroyXBitmap to destroy the XBitmap
1999 * again.
2000 *
2001 * Example:
2002 *
2003 + PXBITMAP pbmp = gpihCreateXBitmap(hab, 100, 100);
2004 + if (pbmp)
2005 + {
2006 + GpiMove(pbmp->hpsMem, ...);
2007 + GpiBox(pbmp->hpsMem, ...);
2008 +
2009 + WinDrawBitmap(hpsScreen,
2010 + pbmp->hbm, // bitmap handle
2011 + ...);
2012 + gpihDestroyXBitmap(&pbmp);
2013 + }
2014 *
2015 * Without the gpih* functions, the above would expand
2016 * to more than 100 lines.
2017 *
2018 *@@added V0.9.12 (2001-05-20) [umoeller]
2019 */
2020
2021PXBITMAP gpihCreateXBitmap(HAB hab, // in: anchor block
2022 LONG cx, // in: bitmap width
2023 LONG cy) // in: bitmap height
2024{
2025 BOOL fOK = FALSE;
2026 PXBITMAP pbmp = (PXBITMAP)malloc(sizeof(XBITMAP));
2027 if (pbmp)
2028 {
2029 memset(pbmp, 0, sizeof(XBITMAP));
2030
2031 // create memory PS for bitmap
2032 pbmp->szl.cx = cx;
2033 pbmp->szl.cy = cy;
2034 if (gpihCreateMemPS(hab,
2035 &pbmp->szl,
2036 &pbmp->hdcMem,
2037 &pbmp->hpsMem))
2038 {
2039 gpihSwitchToRGB(pbmp->hpsMem);
2040 if (pbmp->hbm = gpihCreateBitmap(pbmp->hpsMem,
2041 cx,
2042 cy))
2043 {
2044 if (GpiSetBitmap(pbmp->hpsMem,
2045 pbmp->hbm)
2046 != HBM_ERROR)
2047 fOK = TRUE;
2048 }
2049 }
2050
2051 if (!fOK)
2052 gpihDestroyXBitmap(&pbmp);
2053 }
2054
2055 return (pbmp);
2056}
2057
2058/*
2059 *@@ gpihDestroyXBitmap:
2060 * destroys an XBitmap created with gpihCreateXBitmap.
2061 *
2062 * To be on the safe side, this sets the
2063 * given XBITMAP pointer to NULL as well.
2064 *
2065 *@@added V0.9.12 (2001-05-20) [umoeller]
2066 */
2067
2068VOID gpihDestroyXBitmap(PXBITMAP *ppbmp)
2069{
2070 if (ppbmp)
2071 {
2072 PXBITMAP pbmp;
2073 if (pbmp = *ppbmp)
2074 {
2075 if (pbmp->hbm)
2076 {
2077 if (pbmp->hpsMem)
2078 GpiSetBitmap(pbmp->hpsMem, NULLHANDLE);
2079 GpiDeleteBitmap(pbmp->hbm);
2080 }
2081 if (pbmp->hpsMem)
2082 {
2083 GpiAssociate(pbmp->hpsMem, NULLHANDLE);
2084 GpiDestroyPS(pbmp->hpsMem);
2085 }
2086 if (pbmp->hdcMem)
2087 DevCloseDC(pbmp->hdcMem);
2088
2089 free(pbmp);
2090
2091 *ppbmp = NULL;
2092 }
2093 }
2094}
2095
2096
Note: See TracBrowser for help on using the repository browser.