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

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

Icons in pager.

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