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

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

Misc fixes.

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