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

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

Tons of updates for turbo folders and replacement icons.

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