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

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

Massive pager rework.

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