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

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

First attempt at new container contol.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 111.5 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-2002 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_DOSMODULEMGR
37#define INCL_DOSRESOURCES
38#define INCL_DOSERRORS
39
40#define INCL_WINWINDOWMGR
41#define INCL_WINMESSAGEMGR
42#define INCL_WINPOINTERS
43#define INCL_WINSYS
44
45#define INCL_GPIPRIMITIVES
46#define INCL_GPIBITMAPS
47#define INCL_GPILOGCOLORTABLE
48#define INCL_GPILCIDS
49#include <os2.h>
50
51#include <stdlib.h>
52#include <string.h>
53#include <stdio.h>
54#include <sys/types.h>
55#include <sys/stat.h>
56
57#include "setup.h" // code generation and debugging options
58
59#ifdef WINH_STANDARDWRAPPERS
60#undef WINH_STANDARDWRAPPERS
61#endif
62#include "helpers\dosh.h"
63#include "helpers\gpih.h"
64#include "helpers\winh.h"
65#include "helpers\stringh.h"
66
67#pragma hdrstop
68
69// array for querying device capabilities (gpihQueryDisplayCaps)
70LONG DisplayCaps[CAPS_DEVICE_POLYSET_POINTS] = {0};
71BOOL G_fCapsQueried = FALSE;
72
73/*
74 *@@category: Helpers\PM helpers\GPI helpers
75 * See gpih.c.
76 */
77
78/*
79 *@@category: Helpers\PM helpers\GPI helpers\Devices
80 */
81
82/*
83 *@@gloss: GPI_rectangles GPI rectangles
84 * OS/2 PM (and GPI) uses two types of rectangles. This is rarely
85 * mentioned in the documentation, so a word is in order here.
86 *
87 * In general, graphics operations involving device coordinates
88 * (such as regions, bit maps and bit blts, and window management)
89 * use inclusive-exclusive rectangles. In other words, with
90 * those rectangles, xRight - xLeft is the same as the width
91 * of the rectangle (and yTop - yBottom = height).
92 *
93 * All other graphics operations, such as GPI functions that
94 * define paths, use inclusive-inclusive rectangles.
95 *
96 * This can be a problem with mixing Win and Gpi functions. For
97 * example, WinQueryWindowRect returns an inclusive-exclusive
98 * rectangle (tested V0.9.7 (2000-12-20) [umoeller]).
99 *
100 * WinFillRect expects an inclusive-exclusive rectangle, so it
101 * will work with a rectangle from WinQueryWindowRect directly.
102 *
103 * By contrast, the GpiBox expects an inclusive-inclusive rectangle.
104 */
105
106/* ******************************************************************
107 *
108 * Global variables
109 *
110 ********************************************************************/
111
112STATIC HMTX G_hmtxLCIDs = NULLHANDLE;
113
114/* ******************************************************************
115 *
116 * Rectangle helpers
117 *
118 ********************************************************************/
119
120/*
121 *@@ gpihIsPointInRect:
122 * like WinPtInRect, but doesn't need a HAB.
123 *
124 * NOTE: as opposed to WinPtInRect, prcl is
125 * considered inclusive, that is, TRUE is
126 * returned even if x or y are exactly
127 * the same as prcl->xRight or prcl->yTop.
128 *
129 *@@added V0.9.9 (2001-02-28) [umoeller]
130 */
131
132BOOL gpihIsPointInRect(PRECTL prcl,
133 LONG x,
134 LONG y)
135{
136 if (prcl)
137 return ( (x >= prcl->xLeft)
138 && (x <= prcl->xRight)
139 && (y >= prcl->yBottom)
140 && (y <= prcl->yTop)
141 );
142
143 return FALSE;
144}
145
146/*
147 *@@ gpihInflateRect:
148 * Positive l will make the rectangle larger.
149 * Negative l will make the rectangle smaller.
150 *
151 *@@added V0.9.9 (2001-02-28) [umoeller]
152 */
153
154VOID gpihInflateRect(PRECTL prcl,
155 LONG l)
156{
157 if (prcl && l)
158 {
159 prcl->xLeft -= l;
160 prcl->yBottom -= l;
161 prcl->xRight += l;
162 prcl->yTop += l;
163 }
164}
165
166/*
167 *@@ gpihCopyRectIncl:
168 * copies prclWin to prclGpi, making it
169 * inclusive-inclusive for use with GPI
170 * functions.
171 *
172 *@@added V1.0.1 (2002-11-30) [umoeller]
173 */
174
175VOID gpihCopyRectIncl(PRECTL prclGpi, // out: GPI rectangle
176 PRECTL prclWin) // in: WIN rectangle
177{
178 prclGpi->xLeft = prclWin->xLeft;
179 prclGpi->yBottom = prclWin->yBottom;
180 prclGpi->xRight = prclWin->xRight - 1;
181 prclGpi->yTop = prclWin->yTop - 1;
182}
183
184/* ******************************************************************
185 *
186 * Device helpers
187 *
188 ********************************************************************/
189
190/*
191 *@@ gpihQueryDisplayCaps:
192 * this returns certain device capabilities of
193 * the display device. ulIndex must be one of
194 * the indices as described in DevQueryCaps.
195 *
196 * This function will load all the device capabilities
197 * only once into a global array and re-use them afterwards.
198 *
199 *@@changed V0.9.16 (2001-12-18) [umoeller]: fixed multiple loads
200 */
201
202ULONG gpihQueryDisplayCaps(ULONG ulIndex)
203{
204 if (!G_fCapsQueried)
205 {
206 HPS hps = WinGetScreenPS(HWND_DESKTOP);
207 HDC hdc = GpiQueryDevice(hps);
208 DevQueryCaps(hdc, 0, CAPS_DEVICE_POLYSET_POINTS, &DisplayCaps[0]);
209 G_fCapsQueried = TRUE; // was missing V0.9.16 (2001-12-18) [umoeller]
210 }
211
212 return DisplayCaps[ulIndex];
213}
214
215/*
216 *@@category: Helpers\PM helpers\GPI helpers\Colors
217 */
218
219/* ******************************************************************
220 *
221 * Color helpers
222 *
223 ********************************************************************/
224
225/*
226 * HackColor:
227 *
228 */
229
230STATIC VOID HackColor(PBYTE pb, double dFactor)
231{
232 ULONG ul = (ULONG)((double)(*pb) * dFactor);
233 if (ul > 255)
234 *pb = 255;
235 else
236 *pb = (BYTE)ul;
237}
238
239/*
240 *@@ gpihManipulateRGB:
241 * this changes an RGB color value
242 * by multiplying each color component
243 * (red, green, blue) with dFactor.
244 *
245 * Each color component is treated separately,
246 * so if overflows occur (because dFactor
247 * is > 1), this does not affect the other
248 * components.
249 *
250 *@@changed V0.9.11 (2001-04-25) [umoeller]: changed prototype to use a double now
251 */
252
253VOID gpihManipulateRGB(PLONG plColor, // in/out: RGB color
254 double dFactor) // in: factor (> 1 makes brigher, < 1 makes darker)
255{
256 PBYTE pb = (PBYTE)plColor;
257
258 // in memory, the bytes are blue, green, red, unused
259
260 // blue
261 ULONG ul = (ULONG)( (double)(*pb) * dFactor
262 );
263 if (ul > 255)
264 *pb = 255;
265 else
266 *pb = (BYTE)ul;
267
268 // green
269 ul = (ULONG)( (double)(*(++pb)) * dFactor
270 );
271 if (ul > 255)
272 *pb = 255;
273 else
274 *pb = (BYTE)ul;
275
276 // red
277 ul = (ULONG)( (double)(*(++pb)) * dFactor
278 );
279 if (ul > 255)
280 *pb = 255;
281 else
282 *pb = (BYTE)ul;
283}
284
285#define MEDIATE(a, b) (LONG)(a) + (((LONG)(b) - (LONG)(a)) / 2)
286
287/*
288 *@@ gpihMediumRGB:
289 * returns the arithmetic medium color between
290 * lcol1 and lcol2.
291 *
292 *@@added V0.9.19 (2002-05-28) [umoeller]
293 */
294
295LONG gpihMediumRGB(LONG lcol1, LONG lcol2)
296{
297 return MAKE_RGB(
298 MEDIATE(GET_RED(lcol1),
299 GET_RED(lcol2)),
300 MEDIATE(GET_GREEN(lcol1),
301 GET_GREEN(lcol2)),
302 MEDIATE(GET_BLUE(lcol1),
303 GET_BLUE(lcol2))
304 );
305}
306
307/*
308 *@@ gpihSwitchToRGB:
309 * this switches the given HPS into RGB mode. You should
310 * always use this if you are operating with RGB colors.
311 *
312 * This is just a shortcut to calling
313 *
314 + GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
315 *
316 *@@changed V0.9.7 (2001-01-15) [umoeller]: turned macro into function to reduce fixups
317 */
318
319BOOL gpihSwitchToRGB(HPS hps)
320{
321 return GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
322}
323
324/*
325 *@@category: Helpers\PM helpers\GPI helpers\Drawing primitives
326 */
327
328/* ******************************************************************
329 *
330 * Drawing primitives helpers
331 *
332 ********************************************************************/
333
334/*
335 *@@ gpihDrawRect:
336 * this draws a simple rectangle with the current
337 * color (use GpiSetColor before calling this function).
338 *
339 * The specified rectangle is inclusive, that is, the top
340 * right corner specifies the top right pixel to be drawn
341 * (see @GPI_rectangles).
342 *
343 * This sets the current position to the bottom left corner
344 * of prcl.
345 *
346 *@added V0.9.0
347 */
348
349VOID gpihDrawRect(HPS hps, // in: presentation space for output
350 PRECTL prcl) // in: rectangle to draw (inclusive)
351{
352 POINTL ptl1;
353
354 ptl1.x = prcl->xLeft;
355 ptl1.y = prcl->yBottom;
356 GpiMove(hps, &ptl1);
357 ptl1.y = prcl->yTop - 1;
358 GpiLine(hps, &ptl1);
359 ptl1.x = prcl->xRight - 1;
360 GpiLine(hps, &ptl1);
361 ptl1.y = prcl->yBottom;
362 GpiLine(hps, &ptl1);
363 ptl1.x = prcl->xLeft;
364 GpiLine(hps, &ptl1);
365}
366
367/*
368 *@@ gpihBox:
369 * this is a shortcurt to GpiBox, using the specified
370 * rectangle.
371 *
372 * As opposed to WinFillRect, this works with memory
373 * (bitmap) PS's also.
374 *
375 * The specified rectangle is inclusive, that is, the top
376 * right corner specifies the top right pixel to be drawn.
377 * This is different from WinFillRect (see @GPI_rectangles).
378 *
379 * Changes to the HPS:
380 *
381 * -- the current position is moved to the lower left
382 * corner of *prcl.
383 *
384 *@@changed V0.9.0 [umoeller]: renamed from gpihFillRect
385 *@@changed V0.9.0 [umoeller]: modified function prototype to support lControl
386 *@@changed V0.9.7 (2001-01-17) [umoeller]: removed lColor
387 */
388
389VOID gpihBox(HPS hps, // in: presentation space for output
390 LONG lControl, // in: one of DRO_OUTLINE, DRO_FILL, DRO_OUTLINEFILL
391 PRECTL prcl) // in: rectangle to draw (inclusive)
392{
393 POINTL ptl;
394
395 ptl.x = prcl->xLeft;
396 ptl.y = prcl->yBottom;
397 GpiMove(hps, &ptl);
398 ptl.x = prcl->xRight;
399 ptl.y = prcl->yTop;
400 GpiBox(hps,
401 lControl, // DRO_*
402 &ptl,
403 0, 0); // no corner rounding
404}
405
406/*
407 *@@ gpihMarker:
408 * this draws a quick marker (a filled
409 * rectangle) at the specified position.
410 * The rectangle will be drawn so that
411 * the specified point is in its center.
412 *
413 * No PS data is changed.
414 *
415 *@@changed V0.9.7 (2001-01-17) [umoeller]: removed lColor
416 */
417
418VOID gpihMarker(HPS hps,
419 LONG x, // in: x-center of rectangle
420 LONG y, // in: y-center of rectangle
421 ULONG ulWidth) // in: rectangle width and height
422{
423 POINTL ptlSave;
424 RECTL rclTemp;
425 ULONG ulWidth2 = ulWidth / 2;
426 rclTemp.xLeft = x - ulWidth2;
427 rclTemp.xRight = x + ulWidth2;
428 rclTemp.yBottom = y - ulWidth2;
429 rclTemp.yTop = y + ulWidth2;
430
431 GpiQueryCurrentPosition(hps, &ptlSave);
432 gpihBox(hps,
433 DRO_FILL,
434 &rclTemp);
435 GpiMove(hps, &ptlSave);
436}
437
438/*
439 *@@ gpihThickBox:
440 * draws a box from the specified rectangle with the
441 * specified width.
442 *
443 * The specified rectangle is inclusive, that is, the top
444 * right corner specifies the top right pixel to be drawn.
445 * This is different from WinFillRect
446 * (see @GPI_rectangles).
447 *
448 * If usWidth > 1, the additional pixels will be drawn towards
449 * the _center_ of the rectangle. prcl thus always specifies
450 * the bottom left and top right pixels to be drawn.
451 *
452 * This is different from using GpiSetLineWidth, with which
453 * I was unable to find out in which direction lines are
454 * extended.
455 *
456 * This is similar to gpihDraw3DFrame, except that everything
457 * is painted in the current color.
458 *
459 *@@added V0.9.7 (2000-12-06) [umoeller]
460 */
461
462VOID gpihDrawThickFrame(HPS hps, // in: presentation space for output
463 PRECTL prcl, // in: rectangle to draw (inclusive)
464 ULONG ulWidth) // in: line width (>= 1)
465{
466 ULONG ul = 0;
467 for (;
468 ul < ulWidth;
469 ul++)
470 {
471 GpiMove(hps, (PPOINTL)prcl);
472 GpiBox(hps,
473 DRO_OUTLINE,
474 (PPOINTL)&(prcl->xRight),
475 0,
476 0);
477
478 // and one more to the outside
479 prcl->xLeft++;
480 prcl->yBottom++;
481 prcl->xRight--;
482 prcl->yTop--;
483 }
484}
485
486/*
487 *@@ gpihDraw3DFrame2:
488 * this draws a rectangle in 3D style with a given line width
489 * and the given colors.
490 *
491 * The specified rectangle is inclusive, that is, the top
492 * right corner specifies the top right pixel to be drawn.
493 * This is different from WinFillRect
494 * (see @GPI_rectangles).
495 *
496 * If usWidth > 1, the additional pixels will be drawn towards
497 * the _center_ of the rectangle. prcl thus always specifies
498 * the bottom left and top right pixels to be drawn.
499 *
500 * Note: With V1.0.0, this now modifies prcl to be smaller towards
501 * the center of the rectangle by usWidth so you can call this several
502 * times with different colors.
503 *
504 *@@changed V0.9.0 [umoeller]: changed function prototype to have colors specified
505 *@@changed V0.9.7 (2000-12-20) [umoeller]: now really using inclusive rectangle...
506 *@@changed V1.0.0 (2002-08-24) [umoeller]: renamed, now modifying prcl on output
507 */
508
509VOID gpihDraw3DFrame2(HPS hps, // in: presentation space for output
510 PRECTL prcl, // in: rectangle (inclusive)
511 USHORT usWidth, // in: line width (>= 1)
512 LONG lColorLeft, // in: color to use for left and top; e.g. SYSCLR_BUTTONLIGHT
513 LONG lColorRight) // in: color to use for right and bottom; e.g. SYSCLR_BUTTONDARK
514{
515 USHORT us;
516 POINTL ptl1;
517
518 for (us = 0;
519 us < usWidth;
520 us++)
521 {
522 GpiSetColor(hps, lColorLeft);
523 // draw left line
524 ptl1.x = prcl->xLeft;
525 ptl1.y = prcl->yBottom;
526 GpiMove(hps, &ptl1);
527 ptl1.y = prcl->yTop; // V0.9.7 (2000-12-20) [umoeller]
528 GpiLine(hps, &ptl1);
529 // go right -> draw top
530 ptl1.x = prcl->xRight; // V0.9.7 (2000-12-20) [umoeller]
531 GpiLine(hps, &ptl1);
532 // go down -> draw right
533 GpiSetColor(hps, lColorRight);
534 ptl1.y = prcl->yBottom;
535 GpiLine(hps, &ptl1);
536 // go left -> draw bottom
537 ptl1.x = prcl->xLeft;
538 GpiLine(hps, &ptl1);
539
540 prcl->xLeft++;
541 prcl->yBottom++;
542 prcl->xRight--;
543 prcl->yTop--;
544 }
545}
546
547/*
548 *@@ gpihDraw3DFrame:
549 * compatibility function for those who used the
550 * export. As opposed to gpihDraw3DFrame2, this
551 * does not modify prcl.
552 *
553 *@@added V1.0.0 (2002-08-24) [umoeller]
554 */
555
556VOID gpihDraw3DFrame(HPS hps, // in: presentation space for output
557 PRECTL prcl, // in: rectangle (inclusive)
558 USHORT usWidth, // in: line width (>= 1)
559 LONG lColorLeft, // in: color to use for left and top; e.g. SYSCLR_BUTTONLIGHT
560 LONG lColorRight) // in: color to use for right and bottom; e.g. SYSCLR_BUTTONDARK
561{
562 RECTL rcl2 = *prcl;
563 gpihDraw3DFrame2(hps, &rcl2, usWidth, lColorLeft, lColorRight);
564}
565
566/*
567 *@@ gpihCharStringPosAt:
568 * wrapper for GpiCharStringPosAt.
569 * Since that function is limited to 512 characters
570 * (according to GPIREF; on my Warp 4 FP13, I actually
571 * get some 3000 characters... whatever this is),
572 * this splits the string into 512 byte chunks and
573 * calls GpiCharStringPosAt accordingly.
574 *
575 *@@added V0.9.3 (2000-05-06) [umoeller]
576 *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed underline for bitmap fonts, which never worked
577 */
578
579LONG gpihCharStringPosAt(HPS hps, // in: presentation space for output
580 PPOINTL pptlStart,
581 PRECTL prclRect,
582 ULONG flOptions,
583 LONG lCount,
584 PCH pchString)
585{
586 LONG lHits = 0,
587 lCountLeft = lCount;
588 PCH pchThis = pchString;
589
590 GpiMove(hps, pptlStart);
591
592 if (lCount)
593 {
594 do
595 {
596 LONG lCountThis = lCountLeft;
597 if (lCountLeft >= 512)
598 lCountThis = 512;
599
600 lHits = GpiCharStringPos(hps,
601 prclRect,
602 0, // flOptions,
603 lCountThis,
604 pchThis,
605 0);
606
607 pchThis += 512;
608 lCountLeft -= 512;
609 } while (lCountLeft > 0);
610 }
611
612 // I can't get underscore to work with bitmap fonts,
613 // so I'm doing it manually always now
614 // V0.9.20 (2002-08-10) [umoeller]
615 if (flOptions & CHS_UNDERSCORE)
616 {
617 POINTL ptl2, ptlSave;
618 GpiQueryCurrentPosition(hps, &ptlSave);
619 ptl2.x = pptlStart->x;
620 ptl2.y = pptlStart->y - 2;
621 GpiMove(hps, &ptl2);
622 ptl2.x = ptlSave.x;
623 GpiLine(hps, &ptl2);
624 GpiMove(hps, &ptlSave);
625 }
626
627 return lHits;
628}
629
630/*
631 *@@ gpihCalcTextExtent:
632 *
633 *@@added V1.0.1 (2003-01-17) [umoeller]
634 */
635
636VOID gpihCalcTextExtent(HPS hps, // in: presentation space for output
637 PCSZ pcsz, // in: string to test
638 PLONG pcx, // out: max width occupied by a line in the string
639 PULONG pcLines) // out: no. of lines
640{
641 LONG cxLineThis;
642 PCSZ pThis = pcsz;
643 *pcx = 0;
644
645 *pcLines = 0;
646
647 if (!pThis)
648 return;
649
650 while (*pThis)
651 {
652 ULONG lenThis;
653 PCSZ pNext = strhFindEOL(pThis, &lenThis);
654
655 ++(*pcLines);
656
657 if (lenThis)
658 {
659 POINTL aptl[TXTBOX_COUNT];
660 GpiQueryTextBox(hps,
661 lenThis,
662 (PCH)pThis,
663 TXTBOX_COUNT,
664 aptl);
665
666 cxLineThis = aptl[TXTBOX_TOPRIGHT].x - aptl[TXTBOX_BOTTOMLEFT].x;
667
668 if (cxLineThis > *pcx)
669 *pcx = cxLineThis;
670 }
671
672 if (*pNext == '\r')
673 pNext++;
674 pThis = pNext;
675 }
676}
677
678/*
679 *@@ gpihDrawString:
680 * replacement for WinDrawText that can still align
681 * properly with multi-line strings.
682 *
683 * fl works as with WinDrawText, that is:
684 *
685 * -- specify one of DT_LEFT, DT_CENTER, DT_RIGHT;
686 *
687 * -- specifiy one of DT_TOP, DT_VCENTER, DT_BOTTOM.
688 *
689 * The alignment definitions are:
690 *
691 * -- DT_LEFT 0x00000000
692 * -- DT_CENTER 0x00000100
693 * -- DT_RIGHT 0x00000200
694 * -- DT_TOP 0x00000000
695 * -- DT_VCENTER 0x00000400
696 * -- DT_BOTTOM 0x00000800
697 *
698 * Other flags:
699 *
700 * -- DT_QUERYEXTENT 0x00000002 (not supported)
701 * -- DT_UNDERSCORE 0x00000010 (not supported)
702 * -- DT_STRIKEOUT 0x00000020 (not supported)
703 * -- DT_TEXTATTRS 0x00000040 (always enabled)
704 * -- DT_EXTERNALLEADING 0x00000080 (not supported)
705 * -- DT_HALFTONE 0x00001000 (not supported)
706 * -- DT_MNEMONIC 0x00002000 (not supported)
707 * -- DT_WORDBREAK 0x00004000 (always enabled)
708 * -- DT_ERASERECT 0x00008000 (not supported)
709 *
710 *@@added V1.0.1 (2003-01-17) [umoeller]
711 */
712
713VOID gpihDrawString(HPS hps, // in: presentation space for output
714 PCSZ pcsz, // in: string to test
715 PRECTL prcl, // in: clipping rectangle (inclusive!)
716 ULONG fl, // in: alignment flags
717 PFONTMETRICS pfm)
718{
719 PCSZ pThis = pcsz;
720 POINTL ptlRun,
721 ptlUse;
722 LONG cxRect,
723 cyRect,
724 cyString;
725
726 if (!pThis || !prcl || !pfm)
727 return;
728
729 ptlRun.x = prcl->xLeft;
730 ptlRun.y = prcl->yTop;
731
732 cxRect = prcl->xRight - prcl->xLeft + 1;
733 cyRect = prcl->yTop - prcl->yBottom + 1;
734
735 // vertical alignment:
736 if (fl & (DT_VCENTER | DT_BOTTOM))
737 {
738 ULONG cLines = strhCount(pcsz, '\n') + 1;
739 cyString = cLines * (pfm->lMaxBaselineExt + pfm->lExternalLeading);
740
741 if (fl & DT_VCENTER)
742 ptlRun.y += (cyRect - cyString) / 2;
743 else
744 ptlRun.y += cyRect - cyString;
745 }
746
747 while (*pThis)
748 {
749 ULONG lenThis;
750 PCSZ pNext = strhFindEOL(pThis, &lenThis);
751
752 ptlRun.y -= pfm->lMaxBaselineExt;
753
754 if (lenThis)
755 {
756 // horizontal alignment:
757 if (!(fl & (DT_CENTER | DT_RIGHT)))
758 ptlUse.x = ptlRun.x;
759 else
760 {
761 POINTL aptl[TXTBOX_COUNT];
762 LONG cxString;
763 GpiQueryTextBox(hps,
764 lenThis,
765 (PCH)pThis,
766 TXTBOX_COUNT,
767 aptl);
768
769 cxString = aptl[TXTBOX_TOPRIGHT].x - aptl[TXTBOX_BOTTOMLEFT].x;
770
771 if (fl & DT_CENTER)
772 ptlUse.x = ptlRun.x + (cxRect - cxString) / 2;
773 else
774 // right
775 ptlUse.x = ptlRun.x + cxRect - cxString;
776 }
777
778 ptlUse.y = ptlRun.y + pfm->lMaxDescender;
779
780 GpiCharStringPosAt(hps,
781 &ptlUse,
782 prcl,
783 CHS_CLIP,
784 lenThis,
785 (PCH)pThis,
786 NULL);
787 }
788
789 ptlRun.y -= pfm->lExternalLeading;
790
791 if (*pNext == '\r')
792 pNext++;
793 pThis = pNext;
794 }
795}
796
797/*
798 *@@ gpihFillBackground:
799 * fills the specified rectangle in the way
800 * that is specified by the given BKGNDINFO
801 * structure. This way one can either use
802 * a solid color, a color fade, a bitmap,
803 * or a combination of those.
804 *
805 * See BKGNDINFO for the various parameters.
806 *
807 * Since this can potentially be expensive,
808 * it is strongly recommended to use a buffer
809 * bitmap for painting with the size of the
810 * window and bitblt that bitmap into the
811 * window on repaints. This way the background
812 * only has to be recreated on window resize.
813 *
814 *@@added V0.9.19 (2002-05-07) [umoeller]
815 */
816
817VOID gpihFillBackground(HPS hps, // in: presentation space for output
818 PRECTL prcl, // in: rectangle (inclusive!)
819 PBKGNDINFO pInfo) // in: background into
820{
821 LONG l;
822 POINTL ptl;
823
824 switch (pInfo->flPaintMode & PMOD_COLORMASK)
825 {
826 case PMOD_SOLID:
827 // fill with background color
828 GpiSetColor(hps,
829 pInfo->lcol1);
830 ptl.x = prcl->xLeft;
831 ptl.y = prcl->yBottom;
832 GpiMove(hps,
833 &ptl);
834 ptl.x = prcl->xRight;
835 ptl.y = prcl->yTop;
836 GpiBox(hps,
837 DRO_FILL,
838 &ptl,
839 0,
840 0);
841 break;
842
843 case PMOD_TOPBOTTOM:
844 {
845 LONG lDiffRed = (LONG)GET_RED(pInfo->lcol2) - (LONG)GET_RED(pInfo->lcol1);
846 LONG lDiffGreen = (LONG)GET_GREEN(pInfo->lcol2) - (LONG)GET_GREEN(pInfo->lcol1);
847 LONG lDiffBlue = (LONG)GET_BLUE(pInfo->lcol2) - (LONG)GET_BLUE(pInfo->lcol1);
848
849 LONG lMax = prcl->yTop - prcl->yBottom;
850
851 // start at top
852 ptl.y = prcl->yTop;
853
854 for (l = 0;
855 l <= lMax;
856 ++l)
857 {
858 // compose RGB color for this line;
859 // lcol1 is top, lcol2 is bottom
860 LONG lRed = GET_RED(pInfo->lcol1)
861 + ( lDiffRed
862 * l
863 / lMax
864 );
865 LONG lGreen = GET_GREEN(pInfo->lcol1)
866 + ( lDiffGreen
867 * l
868 / lMax
869 );
870 LONG lBlue = GET_BLUE(pInfo->lcol1)
871 + ( lDiffBlue
872 * l
873 / lMax
874 );
875
876 GpiSetColor(hps, MAKE_RGB(lRed, lGreen, lBlue));
877 ptl.x = prcl->xLeft;
878 GpiMove(hps, &ptl);
879 ptl.x = prcl->xRight;
880 GpiLine(hps, &ptl);
881
882 // next line below
883 --(ptl.y);
884 }
885 }
886 break;
887
888 case PMOD_LEFTRIGHT:
889 {
890 LONG lDiffRed = (LONG)GET_RED(pInfo->lcol2) - (LONG)GET_RED(pInfo->lcol1);
891 LONG lDiffGreen = (LONG)GET_GREEN(pInfo->lcol2) - (LONG)GET_GREEN(pInfo->lcol1);
892 LONG lDiffBlue = (LONG)GET_BLUE(pInfo->lcol2) - (LONG)GET_BLUE(pInfo->lcol1);
893
894 LONG lMax = prcl->xRight - prcl->xLeft;
895
896 // start at left
897 ptl.x = prcl->xLeft;
898
899 for (l = 0;
900 l <= lMax;
901 ++l)
902 {
903 // compose RGB color for this line;
904 // lcol1 is top, lcol2 is bottom
905 LONG lRed = GET_RED(pInfo->lcol1)
906 + ( lDiffRed
907 * l
908 / lMax
909 );
910 LONG lGreen = GET_GREEN(pInfo->lcol1)
911 + ( lDiffGreen
912 * l
913 / lMax
914 );
915 LONG lBlue = GET_BLUE(pInfo->lcol1)
916 + ( lDiffBlue
917 * l
918 / lMax
919 );
920
921 GpiSetColor(hps, MAKE_RGB(lRed, lGreen, lBlue));
922 ptl.y = prcl->yBottom;
923 GpiMove(hps, &ptl);
924 ptl.y = prcl->yTop;
925 GpiLine(hps, &ptl);
926
927 // next line to the right
928 ++(ptl.x);
929 }
930 }
931 break;
932 }
933}
934
935/*
936 *@@category: Helpers\PM helpers\GPI helpers\Fonts
937 */
938
939/* ******************************************************************
940 *
941 * Font helpers
942 *
943 ********************************************************************/
944
945/*
946 *@@ gpihMatchFont:
947 * attempts to find a font matching the specified
948 * data and fills the specified FATTRS structure
949 * accordingly.
950 *
951 * This function performs the insane "11-step process" to
952 * match a font, as described in the GPI reference.
953 *
954 * This function can operate in two modes:
955 *
956 * -- "Family" mode. In that case, specify the font family name
957 * with pszName and set fFamily to TRUE. This is useful for
958 * WYSIWYG text viewing if you need several font faces for
959 * the same family, such as Courier Bold, Bold Italics, etc.
960 * You can specify those attributes with usFormat then.
961 *
962 * -- "Face" mode. In that case, specify the full font face name
963 * with pszName and set fFamily to FALSE. This is useful for
964 * font presentation parameters which use the "WarpSans Bold"
965 * format. In that case, set usFormat to 0.
966 *
967 * Returns TRUE if a "true" match was found, FALSE
968 * otherwise. In both cases, *pfa receives data
969 * which will allow GpiCreateLogFont to work; however,
970 * if FALSE is returned, GpiCreateLogFont will most
971 * likely find the default font (System Proportional)
972 * only.
973 *
974 * If (pFontMetrics != NULL), *pFontMetrics receives the
975 * FONTMETRICS of the font which was found. If an outline
976 * font has been found (instead of a bitmap font),
977 * FONTMETRICS.fsDefn will have the FM_DEFN_OUTLINE bit set.
978 *
979 * This function was extracted from gpihFindFont with
980 * 0.9.14 to allow for caching the font search results,
981 * which is most helpful for memory device contexts,
982 * where gpihFindFont can be inefficient.
983 *
984 *@@added V0.9.14 (2001-08-03) [umoeller]
985 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed a few weirdos with outline fonts
986 */
987
988BOOL gpihMatchFont(HPS hps, // in: presentation space for output
989 LONG lSize, // in: font point size
990 BOOL fFamily, // in: if TRUE, pszName specifies font family;
991 // if FALSE, pszName specifies font face
992 const char *pcszName, // in: font family or face name (without point size)
993 USHORT usFormat, // in: none, one or several of:
994 // -- FATTR_SEL_ITALIC
995 // -- FATTR_SEL_UNDERSCORE (underline)
996 // -- FATTR_SEL_BOLD
997 // -- FATTR_SEL_STRIKEOUT
998 // -- FATTR_SEL_OUTLINE (hollow)
999 FATTRS *pfa, // out: font attributes if found
1000 PFONTMETRICS pFontMetrics) // out: font metrics of created font (optional)
1001{
1002 // first find out how much memory we need to allocate
1003 // for the FONTMETRICS structures
1004 ULONG ul = 0;
1005 LONG lTemp = 0;
1006 LONG cFonts = GpiQueryFonts(hps,
1007 QF_PUBLIC | QF_PRIVATE,
1008 NULL, // pszFaceName,
1009 &lTemp,
1010 sizeof(FONTMETRICS),
1011 NULL);
1012 PFONTMETRICS pfm = (PFONTMETRICS)malloc(cFonts * sizeof(FONTMETRICS)),
1013 pfm2 = pfm,
1014 pfmFound = NULL;
1015
1016 BOOL fQueriedDevice = FALSE; // V0.9.14 (2001-08-01) [umoeller]
1017 LONG alDevRes[2]; // device resolution
1018
1019 // _Pmpf(("gpihFindFont: enumerating for %s, %d points", pcszName, lSize));
1020
1021 GpiQueryFonts(hps,
1022 QF_PUBLIC | QF_PRIVATE,
1023 NULL, // pszFaceName,
1024 &cFonts,
1025 sizeof(FONTMETRICS), // length of each metrics structure
1026 // -- _not_ total buffer size!
1027 pfm);
1028
1029 // now we have an array of FONTMETRICS
1030 // for EVERY font that is installed on the system...
1031 // these things are completely unsorted, so there's
1032 // nothing we can rely on, we have to check them all.
1033
1034 // fill in some default values for FATTRS,
1035 // in case we don't find something better
1036 // in the loop below; these values will be
1037 // applied if
1038 // a) an outline font has been found;
1039 // b) bitmap fonts have been found, but
1040 // none for the current device resolution
1041 // exists;
1042 // c) no font has been found at all.
1043 // In all cases, GpiCreateLogFont will do
1044 // a "close match" resolution (at the bottom).
1045 pfa->usRecordLength = sizeof(FATTRS);
1046 pfa->fsSelection = usFormat; // changed later if better font is found
1047 pfa->lMatch = 0L; // closest match
1048 strcpy(pfa->szFacename, pcszName);
1049 pfa->idRegistry = 0; // default registry
1050 pfa->usCodePage = 0; // default codepage
1051 // the following two must be zero, or outline fonts
1052 // will not be found; if a bitmap font has been passed
1053 // to us, we'll modify these two fields later
1054 pfa->lMaxBaselineExt = 0; // font size (height)
1055 pfa->lAveCharWidth = 0; // font size (width)
1056 pfa->fsType = 0; // default type
1057 pfa->fsFontUse = FATTR_FONTUSE_NOMIX;
1058
1059 // now go thru the array of FONTMETRICS
1060 // to check if we have a bitmap font
1061 // pszFaceName; the default WPS behavior
1062 // is that bitmap fonts appear to take
1063 // priority over outline fonts of the
1064 // same name, so we check these first
1065 pfm2 = pfm;
1066 for (ul = 0;
1067 ul < cFonts;
1068 ul++)
1069 {
1070 const char *pcszCompare = (fFamily)
1071 ? pfm2->szFamilyname
1072 : pfm2->szFacename;
1073
1074 /* _Pmpf((" Checking font: %s (Fam: %s), %d, %d, %d",
1075 pcszCompare,
1076 pfm2->szFamilyname,
1077 pfm2->sNominalPointSize,
1078 pfm2->lMaxBaselineExt,
1079 pfm2->lAveCharWidth)); */
1080
1081 if (!strcmp(pcszCompare, pcszName))
1082 {
1083 /* _Pmpf((" Found font %s; slope %d, usWeightClass %d",
1084 pfm2->szFacename,
1085 pfm2->sCharSlope,
1086 pfm2->usWeightClass)); */
1087
1088 if ((pfm2->fsDefn & FM_DEFN_OUTLINE) == 0)
1089 {
1090 // image (bitmap) font:
1091 // check point size
1092 if (pfm2->sNominalPointSize == lSize * 10)
1093 {
1094 // OK: check device resolutions, because
1095 // normally, there are always several image
1096 // fonts for different resolutions
1097 // for bitmap fonts, there are normally two versions:
1098 // one for low resolutions, one for high resolutions
1099 if (!fQueriedDevice)
1100 {
1101 DevQueryCaps(GpiQueryDevice(hps),
1102 CAPS_HORIZONTAL_FONT_RES,
1103 2L,
1104 alDevRes);
1105 fQueriedDevice = TRUE;
1106 }
1107
1108 if ( (pfm2->sXDeviceRes == alDevRes[0])
1109 && (pfm2->sYDeviceRes == alDevRes[1])
1110 )
1111 {
1112 // OK: use this for GpiCreateLogFont
1113 pfa->lMaxBaselineExt = pfm2->lMaxBaselineExt;
1114 pfa->lAveCharWidth = pfm2->lAveCharWidth;
1115 // pfa->lMatch = pfm2->lMatch;
1116
1117 pfmFound = pfm2;
1118 break;
1119 }
1120 }
1121 }
1122 else
1123 // outline font:
1124 if (pfmFound == NULL)
1125 {
1126 // no bitmap font found yet:
1127
1128 /*
1129 #define FATTR_SEL_ITALIC 0x0001
1130 #define FATTR_SEL_UNDERSCORE 0x0002
1131 #define FATTR_SEL_OUTLINE 0x0008
1132 #define FATTR_SEL_STRIKEOUT 0x0010
1133 #define FATTR_SEL_BOLD 0x0020
1134 */
1135
1136 if ( (!fFamily) // face mode is OK always
1137 // V0.9.14 (2001-08-03) [umoeller]
1138 || ( ( ( (usFormat & FATTR_SEL_BOLD)
1139 && (pfm2->usWeightClass == 7) // bold
1140 )
1141 || ( (!(usFormat & FATTR_SEL_BOLD))
1142 && (pfm2->usWeightClass == 5) // regular
1143 )
1144 )
1145 && ( ( (usFormat & FATTR_SEL_ITALIC)
1146 && (pfm2->sCharSlope != 0) // italics
1147 )
1148 || ( (!(usFormat & FATTR_SEL_ITALIC))
1149 && (pfm2->sCharSlope == 0) // regular
1150 )
1151 )
1152 )
1153 )
1154 {
1155 // yes, we found a true font for that face:
1156 pfmFound = pfm2;
1157
1158 // use this exact font for GpiCreateLogFont
1159 pfa->lMatch = pfm2->lMatch;
1160
1161 // the following two might have been set
1162 // for a bitmap font above
1163 // V0.9.14 (2001-08-03) [umoeller]
1164 pfa->lMaxBaselineExt = pfm2->lMaxBaselineExt;
1165 pfa->lAveCharWidth = pfm2->lAveCharWidth;
1166
1167 pfa->idRegistry = pfm2->idRegistry;
1168
1169 // override NOMIX // V0.9.14 (2001-08-03) [umoeller]
1170 pfa->fsFontUse = FATTR_FONTUSE_OUTLINE;
1171
1172 // according to GPIREF, we must also specify
1173 // the full face name... geese!
1174 strcpy(pfa->szFacename, pfm2->szFacename);
1175 // unset flag in FATTRS, because this would
1176 // duplicate bold or italic
1177 pfa->fsSelection = 0;
1178
1179 // _Pmpf((" --> using it"));
1180 // but loop on, because we might have a bitmap
1181 // font which should take priority
1182 }
1183 }
1184 }
1185
1186 pfm2++;
1187 }
1188
1189 if (pfmFound)
1190 // FONTMETRICS found:
1191 // copy font metrics?
1192 if (pFontMetrics)
1193 memcpy(pFontMetrics, pfmFound, sizeof(FONTMETRICS));
1194
1195 // free the FONTMETRICS array
1196 free(pfm);
1197
1198 return (pfmFound != NULL);
1199}
1200
1201/*
1202 *@@ gpihSplitPresFont:
1203 * splits a presentation parameter font
1204 * string into the point size and face
1205 * name so that it can be passed to
1206 * gpihFindFont more easily.
1207 *
1208 *@@added V0.9.1 (2000-02-15) [umoeller]
1209 *@@changed V0.9.20 (2002-07-19) [umoeller]: optimized
1210 */
1211
1212BOOL gpihSplitPresFont(PSZ pszFontNameSize, // in: e.g. "12.Courier"
1213 PULONG pulSize, // out: integer point size (e.g. 12);
1214 // ptr must be specified
1215 PSZ *ppszFaceName) // out: ptr into pszFontNameSize
1216 // (e.g. "Courier")
1217{
1218 PCHAR pcDot;
1219 if ( (pszFontNameSize)
1220 && (pcDot = strchr(pszFontNameSize, '.'))
1221 )
1222 {
1223 // _Pmpf(("Found font PP: %s", pszFontFound));
1224 // sscanf(pszFontNameSize, "%lu", pulSize);
1225 *pulSize = atoi(pszFontNameSize); // V0.9.20 (2002-07-19) [umoeller]
1226 *ppszFaceName = pcDot + 1;
1227 return TRUE;
1228 }
1229
1230 return FALSE;
1231}
1232
1233/*
1234 *@@ gpihLockLCIDs:
1235 * requests the mutex for serializing the
1236 * lcids.
1237 *
1238 * With GPI, lcids are a process-wide resource and not
1239 * guaranteed to be unique. In the worst case, while your
1240 * font routines are running, another thread modifies the
1241 * lcids and you get garbage. If your fonts suddenly
1242 * turn to "System Proportional", you know this has
1243 * happened.
1244 *
1245 * As a result, whenever you work on lcids, request this
1246 * mutex during your processing. If you do this consistently
1247 * across all your code, you should be safe.
1248 *
1249 * gpihFindFont uses this mutex. If you call GpiCreateLogFont
1250 * yourself somewhere, you should do this under the protection
1251 * of this function.
1252 *
1253 * Call gpihUnlockLCIDs to unlock.
1254 *
1255 *@@added V0.9.9 (2001-04-01) [umoeller]
1256 */
1257
1258BOOL gpihLockLCIDs(VOID)
1259{
1260 if (!G_hmtxLCIDs)
1261 // first call: create
1262 return !DosCreateMutexSem(NULL,
1263 &G_hmtxLCIDs,
1264 0,
1265 TRUE); // request!
1266
1267 // subsequent calls: request
1268 return !DosRequestMutexSem(G_hmtxLCIDs, SEM_INDEFINITE_WAIT);
1269}
1270
1271/*
1272 *@@ UnlockLCIDs:
1273 * releases the mutex for serializing the
1274 * lcids.
1275 *
1276 *@@added V0.9.9 (2001-04-01) [umoeller]
1277 */
1278
1279VOID gpihUnlockLCIDs(VOID)
1280{
1281 DosReleaseMutexSem(G_hmtxLCIDs);
1282}
1283
1284/*
1285 *@@ gpihQueryNextLCID:
1286 * returns the next available lcid for the given HPS.
1287 * Actually, it's the next available lcid for the
1288 * entire process, since there can be only 255 altogether.
1289 * Gets called by gpihFindFont automatically.
1290 *
1291 * WARNING: This function by itself is not thread-safe.
1292 * See gpihLockLCIDs for how to serialize this.
1293 *
1294 * Code was extensively re-tested, works (V0.9.12 (2001-05-31) [umoeller]).
1295 *
1296 *@@added V0.9.3 (2000-05-06) [umoeller]
1297 *@@changed V0.9.9 (2001-04-01) [umoeller]: removed all those sick sub-allocs
1298 */
1299
1300LONG gpihQueryNextFontID(HPS hps) // in: presentation space for output
1301{
1302 LONG lcidNext = -1;
1303
1304 LONG lCount = GpiQueryNumberSetIds(hps);
1305 // the number of local identifiers
1306 // (lcids) currently in use, and
1307 // therefore the maximum number
1308 // of objects for which information
1309 // can be returned
1310
1311 // _Pmpf((__FUNCTION__ ": Entering"));
1312
1313 if (lCount == 0)
1314 {
1315 // none in use yet:
1316 lcidNext = 15;
1317
1318 // _Pmpf((" no lcids in use"));
1319 }
1320 else
1321 {
1322 // #define GQNCL_BLOCK_SIZE 400*sizeof(LONG)
1323
1324 PLONG alTypes = NULL; // object types
1325 PSTR8 aNames = NULL; // font names
1326 PLONG allcids = NULL; // local identifiers
1327
1328 if ( (alTypes = (PLONG)malloc(lCount * sizeof(LONG)))
1329 && (aNames = (PSTR8)malloc(lCount * sizeof(STR8)))
1330 && (allcids = (PLONG)malloc(lCount * sizeof(LONG)))
1331 )
1332 {
1333 if (GpiQuerySetIds(hps,
1334 lCount,
1335 alTypes,
1336 aNames,
1337 allcids))
1338 {
1339 // FINALLY we have all the lcids in use.
1340 BOOL fContinue = TRUE;
1341 lcidNext = 15;
1342
1343 // _Pmpf((" %d fonts in use, browsing...", lCount));
1344
1345 // now, check if this lcid is in use already:
1346 while (fContinue)
1347 {
1348 BOOL fFound = FALSE;
1349 ULONG ul;
1350 fContinue = FALSE;
1351 for (ul = 0;
1352 ul < lCount;
1353 ul++)
1354 {
1355 if (allcids[ul] == lcidNext)
1356 {
1357 fFound = TRUE;
1358 break;
1359 }
1360 }
1361
1362 if (fFound)
1363 {
1364 // lcid found:
1365 // try next higher one
1366
1367 // _Pmpf((" %d is busy...", lcidNext));
1368
1369 lcidNext++;
1370 fContinue = TRUE;
1371 }
1372 // else
1373 // else: return that one
1374 // _Pmpf((" %d is free", lcidNext));
1375 }
1376 }
1377 }
1378
1379 if (alTypes)
1380 free(alTypes);
1381 if (aNames)
1382 free(aNames);
1383 if (allcids)
1384 free(allcids);
1385 }
1386
1387 // _Pmpf((__FUNCTION__ ": Returning lcid %d", lcidNext));
1388
1389 return lcidNext;
1390}
1391
1392/*
1393 *@@ gpihCreateFont:
1394 *
1395 *@@added V0.9.14 (2001-08-03) [umoeller]
1396 */
1397
1398LONG gpihCreateFont(HPS hps, // in: presentation space for output
1399 FATTRS *pfa)
1400{
1401 LONG lLCIDReturn = 0;
1402
1403 if (gpihLockLCIDs()) // V0.9.9 (2001-04-01) [umoeller]
1404 {
1405 // new logical font ID: last used plus one
1406 lLCIDReturn = gpihQueryNextFontID(hps);
1407
1408 GpiCreateLogFont(hps,
1409 NULL, // don't create "logical font name" (STR8)
1410 lLCIDReturn,
1411 pfa);
1412
1413 gpihUnlockLCIDs();
1414 }
1415
1416 return lLCIDReturn;
1417}
1418
1419/*
1420 *@@ gpihFindFont:
1421 * this returns a new logical font ID (LCID) for the specified
1422 * font by calling gpihMatchFont first and then
1423 * GpiCreateLogFont to create a logical font from the
1424 * data returned.
1425 *
1426 * See gpihMatchFont for additional explanations.
1427 *
1428 * To then use the font whose LCID has been returned by this
1429 * function for text output, call:
1430 + GpiSetCharSet(hps, lLCIDReturned);
1431 *
1432 * <B>Font Point Sizes:</B>
1433 *
1434 * 1) For image (bitmap) fonts, the size is fixed, and
1435 * you can directly draw after the font has been
1436 * selected. GpiSetCharBox has no effect on image
1437 * fonts unless you switch to character mode 2
1438 * (if you care for that: GpiSetCharMode).
1439 *
1440 * 2) For outline fonts however, you need to define a
1441 * character box if you want to output text in a
1442 * size other than the default size. This is almost
1443 * as bad a mess as this function, so gpihSetPointSize
1444 * has been provided for this. See remarks there.
1445 *
1446 * <B>Example:</B>
1447 *
1448 * This example prints text in "24.Courier".
1449 *
1450 + PSZ pszOutput = "Test output";
1451 + FONTMETRICS FontMetrics;
1452 + LONG lLCID = gpihFindFont(hps,
1453 + 24, // point size
1454 + FALSE, // face, not family
1455 + "Courier",
1456 + 0,
1457 + &FontMetrics);
1458 + if (lLCID)
1459 + {
1460 + // no error:
1461 + GpiSetCharSet(hps, lLCID);
1462 + if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
1463 + // outline font found (should be the case):
1464 + gpihSetPointSize(hps, 24);
1465 + }
1466 + GpiCharString(hps, strlen(pszOutput), pszOutput);
1467 *
1468 * <B>Details:</B>
1469 *
1470 * First, GpiQueryFonts is called to enumerate all the fonts on
1471 * the system. We then evaluate the awful FONTMETRICS data
1472 * of those fonts to perform a "real" match.
1473 *
1474 * If that fails, we allow GPI to do a "close" match based
1475 * on the input values. This might result in the system
1476 * default font (System Proportional) to be found, in the
1477 * worst case. But even then, a new LCID is returned.
1478 *
1479 * The "close match" comes in especially when using the
1480 * font attributes (bold, italics) and we are unable to
1481 * find the correct outline font for that. Unfortunately,
1482 * the information in FONTMETRICS.fsSelection is wrong,
1483 * wrong, wrong for the large majority of fonts. (For
1484 * example, I get the "bold" flag set for regular fonts,
1485 * and vice versa.) So we attempt to use the other fields,
1486 * but this doesn't always help. Not even Netscape gets
1487 * this right.
1488 *
1489 * <B>Font faces:</B>
1490 *
1491 * This is terribly complicated as well. You need to
1492 * differentiate between "true" emphasis faces (that
1493 * is, bold and italics are implemented thru separate
1494 * font files) and the ugly GPI simulation, which simply
1495 * makes the characters wider or shears them.
1496 *
1497 * This function even finds true "bold" and "italic" faces
1498 * for outline fonts. To do this, always specify the "family"
1499 * name as pszFaceName (e.g. "Courier" instead of "Courier
1500 * Bold") and set the flags for usFormat (e.g. FATTR_SEL_BOLD).
1501 * Note that this implies that you need call this function
1502 * twice to create two logical fonts for regular and bold faces.
1503 *
1504 * If a "true" emphasis font is not found, the GPI simulation
1505 * is enabled.
1506 *
1507 * <B>Remarks:</B>
1508 *
1509 * 1) This function _always_ creates a new logical font,
1510 * whose ID is returned, even if the specified font
1511 * was not found or a "close match" was performed by
1512 * GPI. As a result, do not call this function twice
1513 * for the same font specification, because there are
1514 * only 255 logical font IDs for each process.
1515 *
1516 * 2) Since this function always creates an LCID,
1517 * you should _always_ free the LCID later.
1518 * This is only valid if the font is no longer selected
1519 * into any presentation space. So use these calls:
1520 + GpiSetCharSet(hps, LCID_DEFAULT);
1521 + GpiDeleteSetId(hps, lLCIDReturnedByThis);
1522 *
1523 * 3) Using this function, bitmap fonts will have priority
1524 * over outline fonts of the same face name. This is
1525 * how the WPS does it too. This is most obvious with
1526 * the "Helv" font, which exists as a bitmap font for
1527 * certain point sizes only.
1528 *
1529 * 4) Since logical font IDs are shared across the
1530 * process, a mutex is requested while the lcids are
1531 * being queried and/or manipulated. In other words,
1532 * this func is now thread-safe (V0.9.9).
1533 *
1534 * This calls gpihQueryNextFontID in turn to find the
1535 * next free lcid. See remarks there.
1536 *
1537 * <B>Font metrics:</B>
1538 *
1539 * The important values in the returned FONTMETRICS are
1540 * like this (according to PMREF):
1541 *
1542 + ÉÍ ________________________________________________
1543 + º
1544 + º lExternalLeading, according to font designer.
1545 + º ________________________________________________ Í»
1546 + ÈÍ º
1547 + # # º
1548 + ## ## º lMaxAscender (of entire;
1549 + ÉÍ _______________ # # # # º font); this can be > capital
1550 + º #### # # # # º letters because miniscules
1551 + º # # # # # º can exceed that.
1552 + º lXHeight # # # # º
1553 + º # # # # º
1554 + º # # # # º
1555 + º _______________#####________#_______#___ baseline Í»
1556 + ÈÍ # º
1557 + # º lMaxDescender
1558 + ______________ ####______________________________ ͌
1559 +
1560 *
1561 * In turn, lMaxBaselineExt is lMaxAscender + lMaxDescender.
1562 *
1563 * Soooo... to find out about the optimal line spacing, GPIREF
1564 * recommends to use lMaxBaselineExt + lExternalLeading.
1565 *
1566 *@@added V0.9.0 [umoeller]
1567 *@@changed V0.9.3 (2000-05-06) [umoeller]: didn't work for more than one font; now using gpihQueryNextFontID
1568 *@@changed V0.9.3 (2000-05-06) [umoeller]: usFormat didn't work; fixed
1569 *@@changed V0.9.4 (2000-08-08) [umoeller]: added fFamily
1570 *@@changed V0.9.9 (2001-04-01) [umoeller]: made this thread-safe, finally
1571 *@@changed V0.9.14 (2001-08-01) [umoeller]: some optimizations
1572 */
1573
1574LONG gpihFindFont(HPS hps, // in: presentation space for output
1575 LONG lSize, // in: font point size
1576 BOOL fFamily, // in: if TRUE, pszName specifies font family;
1577 // if FALSE, pszName specifies font face
1578 const char *pcszName, // in: font family or face name (without point size)
1579 USHORT usFormat, // in: none, one or several of:
1580 // -- FATTR_SEL_ITALIC
1581 // -- FATTR_SEL_UNDERSCORE (underline)
1582 // -- FATTR_SEL_BOLD
1583 // -- FATTR_SEL_STRIKEOUT
1584 // -- FATTR_SEL_OUTLINE (hollow)
1585 PFONTMETRICS pFontMetrics) // out: font metrics of created font (optional)
1586{
1587 FATTRS FontAttrs;
1588
1589 gpihMatchFont(hps,
1590 lSize,
1591 fFamily,
1592 pcszName,
1593 usFormat,
1594 &FontAttrs,
1595 pFontMetrics);
1596
1597 return gpihCreateFont(hps,
1598 &FontAttrs);
1599
1600 // _Pmpf((__FUNCTION__ ": returning lcid %d", lLCIDReturn));
1601}
1602
1603/*
1604 *@@ gpihFindPresFont:
1605 * similar to gpihFindFont, but this one evaluates
1606 * the PP_FONTNAMESIZE presentation parameter of the
1607 * specified window instead. If that one is not set,
1608 * the specified default font is used instead.
1609 *
1610 * Note that as opposed to gpihFindFont, this one
1611 * takes a "8.Helv"-type string as input.
1612 *
1613 * See gpihFindFont for additional remarks, which
1614 * gets called by this function.
1615 *
1616 * Again, if an outline font has been returned, you
1617 * must also set the "character box" for the HPS, or
1618 * your text will always have the same point size.
1619 * Use gpihSetPointSize for that, using the presparam's
1620 * point size, which is returned by this function
1621 * into *plSize, if (plSize != NULL):
1622 +
1623 + FONTMETRICS FontMetrics;
1624 + LONG lPointSize;
1625 + LONG lLCID = gpihFindPresFont(hwnd, hps, "8.Helv",
1626 + &FontMetrics,
1627 + &lPointSize);
1628 + GpiSetCharSet(hps, lLCID);
1629 + if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
1630 + gpihSetPointSize(hps, lPointSize);
1631 *
1632 * If errors occur, e.g. if the font string does not
1633 * conform to the "size.face" format, null is returned.
1634 *
1635 *@@added V0.9.0 [umoeller]
1636 *@@changed V0.9.9 (2001-04-01) [umoeller]: now supporting NULLHANDLE hwnd
1637 */
1638
1639LONG gpihFindPresFont(HWND hwnd, // in: window to search for presparam or NULLHANDLE
1640 BOOL fInherit, // in: search parent windows too?
1641 HPS hps, // in: HPS for font selection
1642 const char *pcszDefaultFont, // in: default font if not found (i.e. "8.Helv")
1643 PFONTMETRICS pFontMetrics, // out: font metrics of created font (optional)
1644 PLONG plSize) // out: presparam's point size (optional)
1645{
1646 CHAR szPPFont[200] = "";
1647 const char *pcszFontFound = 0;
1648 CHAR szFaceName[300] = "";
1649 ULONG ulFontSize = 0;
1650
1651 if ( (hwnd) // V0.9.9 (2001-04-01) [umoeller]
1652 && (WinQueryPresParam(hwnd,
1653 PP_FONTNAMESIZE, // first PP to query
1654 0, // second PP to query
1655 NULL, // out: which one is returned
1656 (ULONG)sizeof(szPPFont), // in: buffer size
1657 (PVOID)&szPPFont, // out: PP value returned
1658 (fInherit)
1659 ? 0
1660 : QPF_NOINHERIT))
1661 )
1662 // PP found:
1663 pcszFontFound = szPPFont;
1664 else
1665 pcszFontFound = pcszDefaultFont;
1666
1667 if (pcszFontFound)
1668 {
1669 const char *pcDot = strchr(pcszFontFound, '.');
1670 if (pcDot)
1671 {
1672 // _Pmpf(("Found font PP: %s", pszFontFound));
1673 sscanf(pcszFontFound, "%lu", &ulFontSize);
1674 if (plSize)
1675 *plSize = ulFontSize;
1676 strcpy(szFaceName, pcDot + 1);
1677 return gpihFindFont(hps,
1678 ulFontSize,
1679 FALSE, // face, not family name
1680 szFaceName,
1681 0,
1682 pFontMetrics);
1683 }
1684 }
1685
1686 return 0;
1687}
1688
1689/*
1690 *@@ gpihSetPointSize:
1691 * this invokes GpiSetCharBox on the given HPS to
1692 * set the correct "character box" with the proper
1693 * parameters.
1694 *
1695 * This is necessary for text output with outline
1696 * fonts. Bitmap fonts have a fixed size, and
1697 * calling this function is not necessary for them.
1698 *
1699 * Unfortunately, IBM has almost not documented
1700 * how to convert nominal point sizes to the
1701 * correct character cell values. This involves
1702 * querying the output device (DevQueryCaps).
1703 *
1704 * I have found one hint for this procedure in GPIREF
1705 * (chapter "Character string primitives", "Using...",
1706 * "Drawing text"), but the example code given
1707 * there is buggy, because it must be "72" instead
1708 * of "720". #%(%!"õ!!.
1709 *
1710 * So here we go. If you want to output text in,
1711 * say, "Courier" and 24 points (nominal point size),
1712 * select the font into your HPS and call
1713 + gpihSetPointSize(hps, 24)
1714 * and you're done. See gpihFindFont for a complete
1715 * example.
1716 *
1717 * Call this function as many times as needed. This
1718 * consumes no resources at all.
1719 *
1720 * This returns the return value of GpiSetCharBox.
1721 *
1722 *@@added V0.9.0 [umoeller]
1723 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed bad rounding errors
1724 */
1725
1726BOOL gpihSetPointSize(HPS hps, // in: presentation space for output
1727 LONG lPointSize) // in: desired nominal point size
1728{
1729 SIZEF box;
1730 HDC hdc = GpiQueryDevice(hps); // get the HDC from the HPS
1731 LONG alDevRes[2];
1732 DevQueryCaps(GpiQueryDevice(hps), // get the HDC from the HPS
1733 CAPS_HORIZONTAL_FONT_RES,
1734 2L,
1735 alDevRes);
1736
1737 // V0.9.14: this code didn't work... it produced rounding
1738 // errors which set the font size different from what
1739 // it should be according to the WPS font palette
1740 /* box.cx = MAKEFIXED((lPointSize * alDevRes[0]) / 72, 0);
1741 box.cy = MAKEFIXED((lPointSize * alDevRes[1]) / 72, 0); */
1742
1743 // V0.9.14 (2001-08-03) [umoeller]: now using this one
1744 // instead
1745 lPointSize *= 65536;
1746 box.cx = (FIXED)(lPointSize / 72 * alDevRes[0]);
1747 box.cy = (FIXED)(lPointSize / 72 * alDevRes[1]);
1748
1749 return GpiSetCharBox(hps, &box);
1750}
1751
1752/*
1753 *@@ gpihQueryLineSpacing:
1754 * this returns the optimal line spacing for text
1755 * output with the current HPS; this is computed
1756 * by evaluating those incredible FONTMETRICS.
1757 *
1758 * This might be helpful if you write text to the
1759 * screen yourself and need the height of a text
1760 * line to advance to the next.
1761 *
1762 *@@changed V0.9.7 (2000-12-20) [umoeller]: removed psz param
1763 */
1764
1765LONG gpihQueryLineSpacing(HPS hps) // in: presentation space for output
1766{
1767 FONTMETRICS fm;
1768
1769 if (GpiQueryFontMetrics(hps, sizeof(FONTMETRICS), &fm))
1770 return ( ( fm.lMaxBaselineExt // max vertical font space
1771 +fm.lExternalLeading) // space advised by font designer
1772 );
1773
1774 return 15;
1775}
1776
1777/*
1778 *@@category: Helpers\PM helpers\GPI helpers\Bitmaps/Icons
1779 */
1780
1781/* ******************************************************************
1782 *
1783 * Bitmap helpers
1784 *
1785 ********************************************************************/
1786
1787/*
1788 *@@ gpihCreateMemPS:
1789 * creates a memory device context and presentation space so
1790 * that they are compatible with the screen device context and
1791 * presentation space. These are stored in *hdcMem and *hpsMem.
1792 *
1793 * This is a one-shot function for the standard code that is
1794 * always needed when working with bitmaps in a memory device
1795 * context.
1796 *
1797 * psizlPage must point to a SIZEL structure containing the
1798 * width and height for the memory PS. Specify the size of
1799 * the future bitmap here. Specify {0, 0} to get a PS with
1800 * the size of the full screen, which consumes quite a bit
1801 * of memory though.
1802 *
1803 * Returns FALSE upon errors. In that case, both hdcMem and
1804 * hpsMem are set to NULLHANDLE.
1805 *
1806 * To cleanup after this function has returned TRUE, use the
1807 * following:
1808 + GpiDestroyPS(hpsMem);
1809 + DevCloseDC(hdcMem);
1810 *
1811 *@@changed V0.9.3 (2000-05-18) [umoeller]: added psiszlPage
1812 */
1813
1814BOOL gpihCreateMemPS(HAB hab, // in: anchor block
1815 PSIZEL psizlPage, // in: width and height for mem PS
1816 HDC *hdcMem, // out: memory DC or NULLHANDLE upon errors
1817 HPS *hpsMem) // out: memory PS or NULLHANDLE upon errors
1818{
1819 BOOL brc = FALSE;
1820 PSZ pszData[4] = { "Display", NULL, NULL, NULL };
1821
1822 // create new memory DC
1823 if ((*hdcMem = DevOpenDC(hab,
1824 OD_MEMORY, // create memory DC
1825 "*", // token: do not take INI info
1826 4, // item count in pszData
1827 (PDEVOPENDATA)pszData,
1828 NULLHANDLE))) // compatible with screen
1829 {
1830 // memory DC created successfully:
1831 // create compatible PS
1832 if ((*hpsMem = GpiCreatePS(hab,
1833 *hdcMem, // HDC to associate HPS with (GPIA_ASSOC);
1834 // mandatory for GPIT_MICRO
1835 psizlPage, // is (0, 0) == screen size
1836 PU_PELS // presentation page units: pixels
1837 | GPIA_ASSOC // associate with hdcMem (req. for GPIT_MICRO)
1838 | GPIT_MICRO))) // micro presentation space
1839 brc = TRUE;
1840 else
1841 {
1842 // error (hpsMem == NULLHANDLE):
1843 // close memory DC again
1844 DevCloseDC(*hdcMem);
1845 *hdcMem = NULLHANDLE;
1846 }
1847 }
1848
1849 return brc;
1850}
1851
1852/*
1853 *@@ gpihCreateBitmap:
1854 * calls gpihCreateBitmap2 with cPlanes and cBitCount == 0
1855 * for compatibility with exports. Widgets might
1856 * have used this func.
1857 *
1858 *@@changed V0.9.0 [umoeller]: function prototype changed to cx and cy
1859 *@@changed V0.9.16 (2001-12-18) [umoeller]: now using optimized gpihCreateBitmap2
1860 */
1861
1862HBITMAP gpihCreateBitmap(HPS hpsMem, // in: memory DC
1863 ULONG cx, // in: width of new bitmap
1864 ULONG cy) // in: height of new bitmap
1865{
1866 return gpihCreateBitmap2(hpsMem,
1867 cx,
1868 cy,
1869 0,
1870 0); // current screen bit count
1871}
1872
1873static const BITMAPINFOHEADER2 G_bih2Template[] =
1874 {
1875 sizeof(BITMAPINFOHEADER2), // ULONG cbFix;
1876 0, // ULONG cx; // tbr
1877 0, // ULONG cy; // tbr
1878 0, // USHORT cPlanes; // tbr
1879 0, // USHORT cBitCount; // tbr
1880 BCA_UNCOMP, // ULONG ulCompression;
1881 0, // ULONG cbImage;
1882 70, // ULONG cxResolution;
1883 70, // ULONG cyResolution;
1884 2, // ULONG cclrUsed;
1885 0, // ULONG cclrImportant;
1886 BRU_METRIC, // USHORT usUnits;
1887 // measure units for cxResolution/cyResolution: pels per meter
1888 0, // USHORT usReserved;
1889 BRA_BOTTOMUP, // USHORT usRecording;
1890 // scan lines are bottom to top (default)
1891
1892 BRH_NOTHALFTONED, // USHORT usRendering;
1893 // other algorithms aren't documented anyway
1894 0, // ULONG cSize1;
1895 // parameter for halftoning (undocumented anyway)
1896 0, // ULONG cSize2;
1897 // parameter for halftoning (undocumented anyway)
1898 BCE_RGB, // ULONG ulColorEncoding;
1899 // only possible value
1900 0 // ULONG ulIdentifier;
1901 // application-specific data
1902 };
1903
1904/*
1905 *@@ gpihCreateBitmap2:
1906 * creates a new bitmap for a given memory PS.
1907 * This bitmap will have the cPlanes and bitcount
1908 * which are found in the memory PS.
1909 * For all the mysterious other values, we use
1910 * fixed default values, this doesn't seem to hurt.
1911 *
1912 * Note that the bitmap is _not_ yet selected into
1913 * the specified memory PS. You must call
1914 + GpiSetBitmap(hpsMem, hbm)
1915 * to do this.
1916 *
1917 * Returns the bitmap handle or NULLHANDLE upon errors.
1918 *
1919 *@@added V0.9.16 (2001-12-18) [umoeller]
1920 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
1921 */
1922
1923HBITMAP gpihCreateBitmap2(HPS hpsMem, // in: memory DC
1924 ULONG cx, // in: width of new bitmap
1925 ULONG cy, // in: height of new bitmap
1926 ULONG cPlanes, // in: color planes (usually 1); if 0, current screen is used
1927 ULONG cBitCount) // in: either 1, 4, or 24; if 0, current screen value
1928{
1929 LONG alData[2];
1930 BITMAPINFOHEADER2 bih2;
1931
1932 // determine the device's plane/bit-count format;
1933 // alData[0] then has cPlanes,
1934 // alData[1] has cBitCount
1935 if (GpiQueryDeviceBitmapFormats(hpsMem, 2, alData))
1936 {
1937 // copy values from global template V1.0.1 (2002-11-30) [umoeller]
1938 memcpy(&bih2, &G_bih2Template, sizeof(bih2));
1939
1940 // fix variable fields V1.0.1 (2002-11-30) [umoeller]
1941 bih2.cx = cx;
1942 bih2.cy = cy;
1943 bih2.cPlanes = (cPlanes) ? cPlanes : alData[0];
1944 bih2.cBitCount = (cBitCount) ? cBitCount : alData[1];
1945 bih2.cbImage = ( ( (bih2.cx
1946 * (1 << bih2.cPlanes)
1947 * (1 << bih2.cBitCount)
1948 ) + 31
1949 ) / 32
1950 ) * bih2.cy;
1951
1952 // create a bit map that is compatible with the display
1953 return GpiCreateBitmap(hpsMem,
1954 &bih2,
1955 0, // do not initialize
1956 NULL, // init data
1957 NULL);
1958 }
1959
1960 return NULLHANDLE;
1961}
1962
1963/*
1964 *@@ gpihQueryBitmapSize:
1965 * returns the width and height of the given bitmap.
1966 *
1967 *@@added V1.0.1 (2002-11-30) [umoeller]
1968 */
1969
1970BOOL gpihQueryBitmapSize(HBITMAP hbm, // in: bitmap handle for query
1971 PSIZEL pszl) // out: size (cx, cy) of bitmap
1972{
1973 BITMAPINFOHEADER2 bmi2;
1974 // query bitmap info
1975 bmi2.cbFix = sizeof(bmi2);
1976 if ( (hbm)
1977 && (GpiQueryBitmapInfoHeader(hbm, &bmi2))
1978 )
1979 {
1980 pszl->cx = bmi2.cx;
1981 pszl->cy = bmi2.cy;
1982 return TRUE;
1983 }
1984
1985 return FALSE;
1986}
1987
1988/*
1989 *@@ gpihCreateHalftonedBitmap:
1990 * this creates a half-toned copy of the
1991 * input bitmap by doing the following:
1992 *
1993 * 1) create a new bitmap with the size of hbmSource;
1994 * 2) copy hbmSource to the new bitmap (using GpiWCBitBlt);
1995 * 3) overpaint every other pixel with lColorGray.
1996 *
1997 * Note that the memory DC is switched to RGB mode, so
1998 * lColorGray better be an RGB color.
1999 *
2000 * Note: hbmSource must _not_ be selected into any device
2001 * context, or otherwise GpiWCBitBlt will fail.
2002 *
2003 * This returns the new bitmap or NULLHANDLE upon errors.
2004 *
2005 *@added V0.9.0
2006 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
2007 */
2008
2009HBITMAP gpihCreateHalftonedBitmap(HAB hab, // in: anchor block
2010 HBITMAP hbmSource, // in: source bitmap
2011 LONG lColorGray) // in: color used for gray
2012{
2013 HBITMAP hbmReturn = NULLHANDLE;
2014
2015 HDC hdcMem;
2016 HPS hpsMem;
2017 SIZEL szlPage;
2018
2019 if (gpihQueryBitmapSize(hbmSource, &szlPage)) // V1.0.1 (2002-11-30) [umoeller]
2020 {
2021 if (gpihCreateMemPS(hab, &szlPage, &hdcMem, &hpsMem))
2022 {
2023 if ((hbmReturn = gpihCreateBitmap(hpsMem,
2024 szlPage.cx,
2025 szlPage.cy)))
2026 {
2027 if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
2028 {
2029 POINTL aptl[4];
2030
2031 // step 1: copy bitmap
2032 memset(aptl, 0, sizeof(POINTL) * 4);
2033 // aptl[0]: target bottom-left, is all 0
2034 // aptl[1]: target top-right (inclusive!)
2035 aptl[1].x = szlPage.cx - 1;
2036 aptl[1].y = szlPage.cy - 1;
2037 // aptl[2]: source bottom-left, is all 0
2038
2039 // aptl[3]: source top-right (exclusive!)
2040 aptl[3].x = szlPage.cx;
2041 aptl[3].y = szlPage.cy;
2042 GpiWCBitBlt(hpsMem, // target HPS (bmp selected)
2043 hbmSource,
2044 4L, // must always be 4
2045 &aptl[0], // points array
2046 ROP_SRCCOPY,
2047 BBO_IGNORE);
2048 // doesn't matter here, because we're not stretching
2049
2050 // step 2: overpaint bitmap
2051 // with half-toned pattern
2052
2053 gpihSwitchToRGB(hpsMem);
2054
2055 GpiMove(hpsMem, &aptl[0]); // still 0, 0
2056 aptl[0].x = szlPage.cx - 1;
2057 aptl[0].y = szlPage.cy - 1;
2058 GpiSetColor(hpsMem, lColorGray);
2059 GpiSetPattern(hpsMem, PATSYM_HALFTONE);
2060 GpiBox(hpsMem,
2061 DRO_FILL, // interior only
2062 &aptl[0],
2063 0, 0); // no corner rounding
2064
2065 // unselect bitmap
2066 GpiSetBitmap(hpsMem, NULLHANDLE);
2067 } // end if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
2068 else
2069 {
2070 // error selecting bitmap:
2071 GpiDeleteBitmap(hbmReturn);
2072 hbmReturn = NULLHANDLE;
2073 }
2074 } // end if (hbmReturn = gpihCreateBitmap...
2075
2076 GpiDestroyPS(hpsMem);
2077 DevCloseDC(hdcMem);
2078 } // end if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
2079 } // end if (hbmSource)
2080
2081 return hbmReturn;
2082}
2083
2084/*
2085typedef struct _BITMAPARRAYFILEHEADER {
2086 USHORT usType; // Type of structure.
2087 ULONG cbSize; // Size of the BITMAPARRAYFILEHEADER structure in bytes.
2088 ULONG offNext; // Offset of the next BITMAPARRAYFILEHEADER structure from the start of the file.
2089 USHORT cxDisplay; // Device width, in pels.
2090 USHORT cyDisplay; // Device height, in pels.
2091 BITMAPFILEHEADER bfh; // Bitmap file header structure.
2092} BITMAPARRAYFILEHEADER;
2093
2094typedef struct _BITMAPARRAYFILEHEADER2 {
2095 USHORT usType; // Type of structure.
2096 ULONG cbSize; // Size of the BITMAPARRAYFILEHEADER2 structure in bytes.
2097 ULONG offNext; // Offset of the next BITMAPARRAYFILEHEADER2 structure from the start of the file.
2098 USHORT cxDisplay; // Device width, in pels.
2099 USHORT cyDisplay; // Device height, in pels.
2100 BITMAPFILEHEADER2 bfh2; // Bitmap file header structure.
2101} BITMAPARRAYFILEHEADER2;
2102
2103These two are binarily the same, except for the file header that is contained.
2104*/
2105
2106/* OLD FORMAT
2107
2108typedef struct _BITMAPFILEHEADER {
2109 USHORT usType; // Type of resource the file contains.
2110 ULONG cbSize; // Size of the BITMAPFILEHEADER structure in bytes.
2111 SHORT xHotspot; // Width of hotspot for icons and pointers.
2112 SHORT yHotspot; // Height of hotspot for icons and pointers.
2113 USHORT offBits; // Offset in bytes.
2114 BITMAPINFOHEADER bmp; // Bitmap information header structure.
2115
2116 typedef struct _BITMAPINFOHEADER {
2117 ULONG cbFix; // Length of structure.
2118 USHORT cx; // Bitmap width in pels.
2119 USHORT cy; // Bitmap height in pels.
2120 USHORT cPlanes; // Number of bit planes.
2121 USHORT cBitCount; // Number of bits per pel within a plane.
2122 } BITMAPINFOHEADER;
2123
2124} BITMAPFILEHEADER;
2125*/
2126
2127/* NEW FORMAT
2128
2129typedef struct _BITMAPFILEHEADER2 {
2130 USHORT usType; // Type of resource the file contains.
2131 ULONG cbSize; // Size of the BITMAPFILEHEADER2 structure in bytes.
2132 SHORT xHotspot; // Width of hotspot for icons and pointers.
2133 SHORT yHotspot; // Height of hotspot for icons and pointers.
2134 USHORT offBits; // Offset in bytes.
2135 BITMAPINFOHEADER2 bmp2; // Bitmap information header structure.
2136
2137 typedef struct _BITMAPINFOHEADER2 {
2138 ULONG cbFix; // Length of structure.
2139 ULONG cx; // Bitmap width in pels.
2140 ULONG cy; // Bitmap height in pels.
2141 USHORT cPlanes; // Number of bit planes.
2142 USHORT cBitCount; // Number of bits per pel within a plane.
2143 ULONG ulCompression; // Compression scheme used to store the bit map.
2144 ULONG cbImage; // Length of bitmap storage data, in bytes.
2145 ULONG cxResolution; // Horizontal component of the resolution of target device.
2146 ULONG cyResolution; // Vertical component of the resolution of target device.
2147 ULONG cclrUsed; // Number of color indexes used.
2148 ULONG cclrImportant; // Minimum number of color indexes for satisfactory appearance of the bit map.
2149 USHORT usUnits; // Units of measure.
2150 USHORT usReserved; // Reserved.
2151 USHORT usRecording; // Recording algorithm.
2152 USHORT usRendering; // Halftoning algorithm.
2153 ULONG cSize1; // Size value 1.
2154 ULONG cSize2; // Size value 2.
2155 ULONG ulColorEncoding; // Color encoding.
2156 ULONG ulIdentifier; // Reserved for application use.
2157 } BITMAPINFOHEADER2;
2158
2159 Because of the unfortunate replacement of USHORTs with ULONGs for
2160 cx and cy in the info header, the cx, cy, and cBitCount data is
2161 NOT the same between old and new formats. Well, IBM, good job.
2162 And ICONEDIT _still_ writes the old format, too.
2163
2164} BITMAPFILEHEADER2;
2165
2166*/
2167
2168/*
2169 *@@ gpihCreateBitmapFromFile:
2170 * creates a new HBITMAP from the given bitmap data,
2171 * which is assumed to be in a standard OS/2 1.3 or
2172 * 2.0 bitmap file format. That is, it should start
2173 * with either a BITMAPFILEHEADER or BITMAPFILEHEADER2
2174 * (if single bitmap) or a BITMAPARRAYFILEHEADER or
2175 * BITMAPARRAYFILEHEADER2 (if bitmap array) and all
2176 * the bitmap bits following. This format is also
2177 * used by bitmap resources.
2178 *
2179 * If the file contains only a single bitmap,
2180 * this bitmap is used.
2181 *
2182 * If it contains a bitmap array, we use the
2183 * "best bitmap" in the array, which is determined
2184 * from the following criteria (in this order):
2185 *
2186 * -- a device-dependent bitmap, if its device
2187 * resolution is not too large and the given
2188 * HPS can display all its colors;
2189 *
2190 * -- a device-dependent bitmap, if its device
2191 * resolution is not too large, even if the
2192 * given HPS cannot display all its colors;
2193 *
2194 * -- a device-independent bitmap, if the given
2195 * HPS can display all its colors;
2196 *
2197 * -- the first device-independent bitmap in
2198 * the file;
2199 *
2200 * -- the first bitmap in the file.
2201 *
2202 * Support for bitmap arrays was added with V0.9.19.
2203 * I'm not quite sure if the above is the same way
2204 * of selecting the "best bitmap" that GpiLoadBitmap
2205 * would do, but without any documentation, who is
2206 * supposed to know. It appears to me however that
2207 * GpiLoadBitmap always does an _exact_ match of
2208 * device resolutions (in contrast to this function).
2209 * In other words, if you're running at 1152x864
2210 * and the device-dependent bitmap is for 1024x768,
2211 * _this_ function would use it, while GpiLoadBitmap
2212 * might load a device-independent VGA bitmap
2213 * instead. (Looks broken, if you ask me.)
2214 *
2215 * Returns:
2216 *
2217 * -- NO_ERROR
2218 *
2219 * -- ERROR_INVALID_DATA: we can't understand this format.
2220 *
2221 *@@added V0.9.20 (2002-07-19) [umoeller]
2222 */
2223
2224APIRET gpihCreateBitmapFromFile(HBITMAP *phbm, // out: bitmap if NO_ERROR
2225 HPS hps, // in: HPS for bmp
2226 PBYTE pData) // in: bitmap data
2227{
2228 APIRET arc = NO_ERROR;
2229
2230 PBITMAPFILEHEADER2 pbfh = (PBITMAPFILEHEADER2)pData;
2231
2232 // check bitmap magic codes
2233 switch (pbfh->usType)
2234 {
2235 case BFT_BMAP: // "BM"
2236 // single bitmap in file (no array):
2237 if (!(*phbm = GpiCreateBitmap(hps,
2238 &pbfh->bmp2,
2239 CBM_INIT,
2240 (PBYTE)pbfh + pbfh->offBits,
2241 (PBITMAPINFO2)&pbfh->bmp2)))
2242 arc = ERROR_INVALID_DATA;
2243 break;
2244
2245 case BFT_BITMAPARRAY: // "BA"
2246 {
2247 // define a handy union for all the above crap
2248 typedef union
2249 {
2250 BITMAPFILEHEADER Old;
2251 BITMAPFILEHEADER2 New;
2252 } BMPUNION, *PBMPUNION;
2253
2254 PBMPUNION
2255 puFirstDI = NULL, // first device-independent bitmap
2256 puBestDI = NULL, // best device-independent bitmap
2257 puFirstDD = NULL, // first device-dependent bitmap
2258 puBestDD = NULL, // best device-dependent bitmap
2259 puFirstAny = NULL, // first bitmap of any type
2260 puUse;
2261
2262 // get device resolution for this HPS
2263 // so we can select the "best bitmap"
2264 #define GET_CAPS_FIRST CAPS_WIDTH
2265 #define GET_CAPS_LAST CAPS_COLOR_BITCOUNT
2266 #define GET_NO_CAPS GET_CAPS_LAST - GET_CAPS_FIRST + 1
2267
2268 LONG alCaps[GET_NO_CAPS];
2269 PBITMAPARRAYFILEHEADER2 pba = (PBITMAPARRAYFILEHEADER2)pData;
2270
2271 DevQueryCaps(GpiQueryDevice(hps),
2272 GET_CAPS_FIRST,
2273 GET_NO_CAPS,
2274 alCaps);
2275
2276 #define BITCOUNT alCaps[CAPS_COLOR_BITCOUNT - GET_CAPS_FIRST]
2277 #define WIDTH alCaps[CAPS_WIDTH - GET_CAPS_FIRST]
2278 #define HEIGHT alCaps[CAPS_HEIGHT - GET_CAPS_FIRST]
2279
2280 // for-all-bitmaps-in-array loop
2281 while (pba)
2282 {
2283 PBMPUNION puThis = (PBMPUNION)&pba->bfh2;
2284
2285 LONG cx = 0,
2286 cy,
2287 cBitCount;
2288
2289 // ignore this if the type isn't "BM"
2290 if (puThis->Old.usType == BFT_BMAP)
2291 {
2292 // fill the three, but watch out, the offsets are
2293 // different between old and new formats
2294 if (puThis->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER))
2295 {
2296 // old format:
2297 cx = puThis->Old.bmp.cx;
2298 cy = puThis->Old.bmp.cy;
2299 cBitCount = puThis->Old.bmp.cBitCount;
2300 }
2301 else if (puThis->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER2))
2302 {
2303 // new format:
2304 cx = puThis->New.bmp2.cx;
2305 cy = puThis->New.bmp2.cy;
2306 cBitCount = puThis->New.bmp2.cBitCount;
2307 }
2308 }
2309
2310 if (cx)
2311 {
2312 // remember the first bitmap from all that we see
2313 if (!puFirstAny)
2314 puFirstAny = puThis;
2315
2316 // check device resolution... device-independent
2317 // one has cxDisplay and cyDisplay set to 0
2318 if ( (!pba->cxDisplay)
2319 && (!pba->cyDisplay)
2320 )
2321 {
2322 // device-independent:
2323
2324 // remember the first device-independent bitmap
2325 if (!puFirstDI)
2326 puFirstDI = puThis;
2327
2328 if (cBitCount <= BITCOUNT)
2329 // we can display all the colors:
2330 puBestDI = puThis;
2331 }
2332 else
2333 {
2334 // device-dependent:
2335 // ignore if device resolution is too large
2336 if ( (pba->cxDisplay <= WIDTH)
2337 && (pba->cyDisplay <= HEIGHT)
2338 )
2339 {
2340 // remember first matching device-dependent bitmap
2341 if (!puFirstDD)
2342 puFirstDD = puThis;
2343
2344 if (cBitCount <= BITCOUNT)
2345 puBestDD = puThis;
2346 }
2347 }
2348 } // end if cx
2349
2350 // go for next bmp in array
2351 if (pba->offNext)
2352 // another one coming up:
2353 // this ofs is from the beginning of the file
2354 pba = (PBITMAPARRAYFILEHEADER2)(pData + pba->offNext);
2355 else
2356 // no more bitmaps:
2357 break;
2358 } // end while (pba)
2359
2360 if ( (puUse = puBestDD)
2361 || (puUse = puFirstDD)
2362 || (puUse = puBestDI)
2363 || (puUse = puFirstDI)
2364 || (puUse = puFirstAny)
2365 )
2366 {
2367 PBITMAPINFOHEADER2 pbih2;
2368 PBYTE pbInitData;
2369
2370 if (puUse->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER))
2371 {
2372 // old format:
2373 pbih2 = (PBITMAPINFOHEADER2)&puUse->Old.bmp;
2374 pbInitData = (PBYTE)pData + puUse->Old.offBits;
2375 }
2376 else
2377 {
2378 // new format:
2379 pbih2 = &puUse->New.bmp2;
2380 pbInitData = (PBYTE)pData + puUse->New.offBits;
2381 }
2382
2383 if (!(*phbm = GpiCreateBitmap(hps,
2384 pbih2,
2385 CBM_INIT,
2386 pbInitData,
2387 (PBITMAPINFO2)pbih2)))
2388 arc = ERROR_INVALID_DATA;
2389 }
2390 else
2391 arc = ERROR_INVALID_DATA;
2392 }
2393 break;
2394 }
2395
2396 return arc;
2397}
2398
2399/*
2400 *@@ gpihLoadBitmap:
2401 * creates a new HBITMAP from the given resource.
2402 *
2403 * This is a replacement for GpiLoadBitmap which handles
2404 * device-dependent bitmaps in bitmap arrays in a
2405 * non-brain-dead way.
2406 *
2407 * Calls gpihCreateBitmapFromFile for handling the resource
2408 * data. See remarks there for how we select a bitmap from
2409 * bitmap arrays, which is _different_ from GpiLoadBitmap.
2410 *
2411 * Returns:
2412 *
2413 * -- NO_ERROR: *phbm has received new HBITMAP,
2414 * to be freed with GpiDeleteBitmap.
2415 *
2416 * -- ERROR_INVALID_DATA: resource exists, but we
2417 * can't understand its format.
2418 *
2419 * plus the error codes from DosGetResource.
2420 *
2421 *@@added V0.9.20 (2002-07-19) [umoeller]
2422 */
2423
2424APIRET gpihLoadBitmap(HBITMAP *phbm, // out: bitmap if NO_ERROR
2425 HPS hps, // in: HPS for bmp
2426 HMODULE hmodResource, // in: module to load bitmap from
2427 ULONG idBitmap) // in: resource ID for bitmap
2428{
2429 APIRET arc;
2430
2431 PBYTE pbData;
2432
2433 if (!(arc = DosGetResource(hmodResource,
2434 RT_BITMAP,
2435 idBitmap,
2436 (PVOID*)&pbData)))
2437 {
2438 arc = gpihCreateBitmapFromFile(phbm,
2439 hps,
2440 pbData);
2441
2442 DosFreeResource(pbData);
2443 }
2444
2445 return arc;
2446}
2447
2448/*
2449 *@@ gpihLoadBitmapFile:
2450 * this loads the specified bitmap file into
2451 * the given HPS. Note that the bitmap is _not_
2452 * yet selected into the HPS.
2453 *
2454 * Calls gpihCreateBitmapFromFile for handling the resource
2455 * data. See remarks there for how we select a bitmap from
2456 * bitmap arrays, which is _different_ from GpiLoadBitmap.
2457 *
2458 * Returns:
2459 *
2460 * -- NO_ERROR: *phbm has received new HBITMAP,
2461 * to be freed with GpiDeleteBitmap.
2462 *
2463 * -- ERROR_INVALID_PARAMETER
2464 *
2465 * -- ERROR_INVALID_DATA: file exists, but we
2466 * can't understand its format.
2467 *
2468 * plus the error codes from doshOpen and DosRead.
2469 *
2470 *@@changed V0.9.4 (2000-08-03) [umoeller]: this didn't return NULLHANDLE on errors
2471 *@@changed V0.9.19 (2002-04-14) [umoeller]: rewritten to support bitmap arrays, prototype changed
2472 *@@changed V0.9.20 (2002-07-19) [umoeller]: extracted bitmap selection into gpihCreateBitmapFromFile
2473 */
2474
2475APIRET gpihLoadBitmapFile(HBITMAP *phbm, // out: bitmap if NO_ERROR
2476 HPS hps, // in: HPS for bmp
2477 PCSZ pcszBmpFile) // in: bitmap filename
2478{
2479 APIRET arc;
2480 PXFILE pFile;
2481 ULONG cbFile = 0;
2482
2483 if (!hps || !pcszBmpFile || !phbm)
2484 return ERROR_INVALID_PARAMETER;
2485
2486 if (!(arc = doshOpen(pcszBmpFile,
2487 XOPEN_READ_EXISTING | XOPEN_BINARY,
2488 &cbFile,
2489 &pFile)))
2490 {
2491 PBYTE pData;
2492 if (!(pData = (PBYTE)malloc(cbFile)))
2493 arc = ERROR_NOT_ENOUGH_MEMORY;
2494 else
2495 {
2496 // read in the ENTIRE file
2497 if (!(arc = DosRead(pFile->hf,
2498 pData,
2499 cbFile,
2500 &cbFile)))
2501 // extracted all the code into this extra func
2502 // V0.9.20 (2002-07-19) [umoeller]
2503 arc = gpihCreateBitmapFromFile(phbm,
2504 hps,
2505 (PBYTE)pData);
2506
2507 free(pData);
2508 }
2509
2510 doshClose(&pFile);
2511 }
2512
2513 return arc;
2514}
2515
2516/*
2517 *@@ gpihStretchBitmap:
2518 * this copies hbmSource to the bitmap selected
2519 * into hpsTarget, which must be a memory PS.
2520 *
2521 * The source size is the whole size of hbmSource,
2522 * the target size is specified in prclTarget
2523 * (which is exclusive, meaning that the top right
2524 * corner of that rectangle lies _outside_ the target).
2525 *
2526 * This uses GpiWCBitBlt to stretch the bitmap.
2527 * hbmSource therefore must _not_ be selected
2528 * into any presentation space, or GpiWCBitBlt will
2529 * fail.
2530 *
2531 * If (fPropotional == TRUE), the target size is
2532 * modified so that the proportions of the bitmap
2533 * are preserved. The bitmap data will then be
2534 * copied to a subrectangle of the target bitmap:
2535 * there will be extra space either to the left
2536 * and right of the bitmap data or to the bottom
2537 * and top.
2538 * The outside areas of the target bitmap are
2539 * not changed then, so you might want to fill
2540 * the bitmap with some color first.
2541 *
2542 * This returns the return value of GpiWCBitBlt,
2543 * which can be:
2544 * -- GPI_OK
2545 * -- GPI_HITS: correlate hits
2546 * -- GPI_ERROR: error occured (probably either hbmSource not free
2547 * or no bitmap selected into hpsTarget)
2548 *
2549 *@added V0.9.0
2550 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
2551 */
2552
2553LONG gpihStretchBitmap(HPS hpsTarget, // in: memory PS to copy bitmap to
2554 HBITMAP hbmSource, // in: bitmap to be copied into hpsTarget (must be free)
2555 PRECTL prclSource, // in: source rectangle -- if NULL, use size of bitmap
2556 PRECTL prclTarget, // in: target rectangle (req.)
2557 BOOL fProportional) // in: preserve proportions when stretching?
2558{
2559 SIZEL szlBmp;
2560 POINTL aptl[4];
2561 BOOL fCalculated = FALSE;
2562
2563 if (!gpihQueryBitmapSize(hbmSource, &szlBmp)) // V1.0.1 (2002-11-30) [umoeller]
2564 return GPI_ERROR;
2565
2566 memset(aptl, 0, sizeof(POINTL) * 4);
2567
2568 // aptl[2]: source bottom-left, is all 0
2569 // aptl[3]: source top-right (exclusive!)
2570 aptl[3].x = szlBmp.cx;
2571 aptl[3].y = szlBmp.cy;
2572
2573 if (fProportional)
2574 {
2575 // proportional mode:
2576
2577 // 1) find out whether cx or cy is too
2578 // large
2579
2580 ULONG ulPropSource = (szlBmp.cx * 1000)
2581 / szlBmp.cy;
2582 // e.g. if the bmp is 200 x 100, we now have 2000
2583 ULONG ulPropTarget = ((prclTarget->xRight - prclTarget->xLeft) * 1000)
2584 / (prclTarget->yTop - prclTarget->yBottom);
2585 // case 1: if prclTarget is 300 x 100, we now have 3000 (> ulPropSource)
2586 // case 2: if prclTarget is 150 x 100, we now have 1500 (< ulPropSource)
2587
2588 // case 1:
2589 if (ulPropTarget > ulPropSource)
2590 {
2591 // prclTarget is too wide (horizontally):
2592 // decrease width, keep height
2593
2594 ULONG cx = (prclTarget->xRight - prclTarget->xLeft);
2595 ULONG cxNew = (cx * ulPropSource) / ulPropTarget;
2596
2597 // aptl[0]: target bottom-left
2598 // move left right (towards center)
2599 aptl[0].x = prclTarget->xLeft + ((cx - cxNew) / 2);
2600 aptl[0].y = prclTarget->yBottom;
2601
2602 // aptl[1]: target top-right (inclusive!)
2603 aptl[1].x = aptl[0].x + cxNew;
2604 aptl[1].y = prclTarget->yTop;
2605
2606 fCalculated = TRUE;
2607 }
2608 else
2609 {
2610 // prclTarget is too high (vertically):
2611 // keep width, decrease height
2612
2613 ULONG cy = (prclTarget->yTop - prclTarget->yBottom);
2614 ULONG cyNew = (cy * ulPropTarget) / ulPropSource;
2615
2616 // aptl[0]: target bottom-left
2617 aptl[0].x = prclTarget->xLeft;
2618 // move bottom up (towards center)
2619 aptl[0].y = prclTarget->yBottom + ((cy - cyNew) / 2);
2620
2621 // aptl[1]: target top-right (inclusive!)
2622 aptl[1].x = prclTarget->xRight;
2623 aptl[1].y = aptl[0].y + cyNew;
2624 // (prclTarget->yTop * ulPropSource) / ulPropTarget;
2625
2626 fCalculated = TRUE;
2627 }
2628 } // end if (pa->ulFlags & ANF_PROPORTIONAL)
2629
2630 if (!fCalculated)
2631 {
2632 // non-proportional mode or equal proportions:
2633 // stretch to whole size of prclTarget
2634
2635 // aptl[0]: target bottom-left
2636 aptl[0].x = prclTarget->xLeft;
2637 aptl[0].y = prclTarget->yBottom;
2638 // aptl[1]: target top-right (inclusive!)
2639 aptl[1].x = prclTarget->xRight;
2640 aptl[1].y = prclTarget->yTop;
2641 }
2642
2643 return GpiWCBitBlt(hpsTarget, // target HPS (bmp selected)
2644 hbmSource,
2645 4L, // must always be 4
2646 &aptl[0], // points array
2647 ROP_SRCCOPY,
2648 BBO_IGNORE);
2649 // ignore eliminated rows or
2650 // columns; useful for color
2651}
2652
2653/*
2654 *@@ gpihDrawPointer:
2655 * replacement for WinDrawPointer that can do clipping.
2656 *
2657 * Normally, to do clipping with WinDrawPointer, one
2658 * would have to alter the clip rectangle for the current
2659 * HPS, which requires creating regions and is thus quite
2660 * expensive.
2661 *
2662 * Instead, this function allows for specifying a clip
2663 * rectangle directly. It blits the icon bitmaps directly
2664 * without calling WinDrawPointer.
2665 * Besides, since it uses GpiWCBitBlt, it should probably
2666 * work with all types of device contexts.
2667 *
2668 * This also replaces gpihIcon2Bitmap, which wasn't quite
2669 * working in the first place and couldn't to clipping
2670 * either.
2671 *
2672 * If you don't need clipping and are drawing to the
2673 * screen only, this function has no advantage over
2674 * WinDrawPointer because it's presumably a bit slower.
2675 *
2676 * Flags presently supported in fl:
2677 *
2678 * -- DP_MINI (not DP_MINIICON, as stated in PMREF):
2679 * use mini-icon.
2680 *
2681 * -- DP_HALFTONED (V0.9.20)
2682 *
2683 * Preconditions:
2684 *
2685 * -- The hps is assumed to be in RGB mode.
2686 *
2687 * Post conditions:
2688 *
2689 * -- This uses GpiSet(Back)Color, so the foreground
2690 * and background colors are undefined after the
2691 * call.
2692 *
2693 *@@added V0.9.19 (2002-06-18) [umoeller]
2694 *@@changed V0.9.20 (2002-07-31) [umoeller]: optimized, saved one GpiQueryBitmapInfoHeader
2695 *@@changed V0.9.20 (2002-08-04) [umoeller]: added DP_HALFTONED
2696 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
2697 */
2698
2699BOOL gpihDrawPointer(HPS hps, // in: target presentation space
2700 LONG x, // in: lower left target position of icon
2701 LONG y, // in: lower left target position of icon
2702 HPOINTER hptr, // in: icon to be drawn
2703 PSIZEL pszlIcon, // in: icon size (req., should be sysvalues SV_CXICON, SV_CYICON always)
2704 PRECTL prclClip, // in: clip rectangle (inclusive!) or NULL
2705 ULONG fl) // in: DP_* flags
2706{
2707 POINTERINFO pi;
2708
2709 if ( (pszlIcon)
2710 && (hptr)
2711 && (WinQueryPointerInfo(hptr, &pi))
2712 )
2713 {
2714 POINTL aptl[4];
2715 HBITMAP hbmThis;
2716 SIZEL szlAndXor, // V1.0.1 (2002-11-30) [umoeller]
2717 szlColor;
2718
2719 // A HPOINTER really consists of two bitmaps,
2720 // one monochrome bitmap that has twice the icon
2721 // height and contains an AND mask in the upper
2722 // half and an XOR mask in the lower half, and
2723 // a (probably color) bitmap with the regular
2724 // icon height.
2725
2726 // Drawing the icon means (1) blitting the AND
2727 // mask with ROP_SRCAND, (2) blitting the color
2728 // bitmap with ROP_SRCPAINT, (3) blitting the
2729 // XOR mask with ROP_SRCINVERT. Hence the slightly
2730 // complicated code that follows.
2731
2732 /*
2733 * 0) preparations
2734 */
2735
2736 // set up a bunch of variables that are used
2737 // by the below calculations. We use cx|yIcon
2738 // to quickly get the system icon dimensions
2739 // and set up the clip offsets here too,
2740 // if a clip rectangle is specified.
2741
2742 LONG cxIcon = pszlIcon->cx,
2743 cyIcon = pszlIcon->cy,
2744 cySrc,
2745 xRight,
2746 yTop,
2747 // clip "rectangle"... this is not really a
2748 // recangle because it specifies _offsets_
2749 // towards the center of the icon to be
2750 // clipped
2751 lClipLeft = 0,
2752 lClipBottom = 0,
2753 lClipRight = 0,
2754 lClipTop = 0;
2755
2756 BOOL fMini;
2757
2758 if (fMini = !!(fl & DP_MINI))
2759 {
2760 cxIcon /= 2;
2761 cyIcon /= 2;
2762 }
2763
2764 // target top right (inclusive)
2765 xRight = x + cxIcon - 1;
2766 yTop = y + cyIcon - 1;
2767
2768 if (prclClip)
2769 {
2770 // we have a clip rectangle:
2771 // set up the clip offsets that are used
2772 // in both the target and source coordinates
2773 // for blitting
2774 if (x < prclClip->xLeft)
2775 lClipLeft = prclClip->xLeft - x;
2776 if (xRight > prclClip->xRight)
2777 lClipRight = xRight - prclClip->xRight;
2778 if (y < prclClip->yBottom)
2779 lClipBottom = prclClip->yBottom - y;
2780 if (yTop > prclClip->yTop)
2781 lClipTop = yTop - prclClip->yTop;
2782 }
2783
2784 // set up target coordinates that are constant
2785 // for all the three blits
2786
2787 // aptl[0]: target bottom-left
2788 aptl[0].x = x + lClipLeft;
2789 aptl[0].y = y + lClipBottom;
2790
2791 // aptl[1]: target top-right (inclusive!)
2792 aptl[1].x = xRight - lClipRight;
2793 aptl[1].y = yTop - lClipTop;
2794
2795 if ( (aptl[0].x < aptl[1].x)
2796 && (aptl[0].y < aptl[1].y)
2797 )
2798 {
2799 LONG lPatternOld = -1; // mark as "not changed" for now
2800 LONG lAndROP = ROP_SRCAND, // 0x0088 = 10001000
2801 lPaintROP = ROP_SRCPAINT, // 0x00EE = 11101110
2802 lInvertROP = ROP_SRCINVERT; // 0x0066 = 01100110
2803
2804 // colors are constant too
2805 GpiSetColor(hps, RGBCOL_WHITE);
2806 GpiSetBackColor(hps, RGBCOL_BLACK);
2807
2808 if (fl & DP_HALFTONED) // V0.9.20 (2002-08-04) [umoeller]
2809 {
2810 lPatternOld = GpiQueryPattern(hps);
2811 GpiSetPattern(hps, PATSYM_HALFTONE);
2812
2813 lAndROP = 0x00A8; // 10101000
2814 lInvertROP = 0x00A6; // 10100110
2815 }
2816
2817 /*
2818 * 1) work on the AND image
2819 * (upper part of the monochrome image)
2820 */
2821
2822 if ( ( (fMini)
2823 && (hbmThis = pi.hbmMiniPointer)
2824 )
2825 || (hbmThis = pi.hbmPointer)
2826 )
2827 {
2828 gpihQueryBitmapSize(hbmThis, &szlAndXor); // V1.0.1 (2002-11-30) [umoeller]
2829
2830 // use only half the bitmap height
2831 cySrc = szlAndXor.cy / 2;
2832
2833 // aptl[2]: source bottom-left
2834 aptl[2].x = 0
2835 + lClipLeft * szlAndXor.cx / cxIcon;
2836 aptl[2].y = cySrc
2837 + lClipBottom * cySrc / cyIcon;
2838
2839 // aptl[3]: source top-right (exclusive!)
2840 aptl[3].x = szlAndXor.cx
2841 - lClipRight * szlAndXor.cx / cxIcon;
2842 aptl[3].y = szlAndXor.cy
2843 - lClipTop * cySrc / cyIcon;
2844
2845 GpiWCBitBlt(hps, // target
2846 hbmThis, // src bmp
2847 4L, // must always be 4
2848 aptl, // point array
2849 lAndROP, // ROP_SRCAND, // source AND target
2850 BBO_IGNORE);
2851 }
2852
2853 /*
2854 * 2) paint the color image; the parts that
2855 * are to be transparent are black
2856 */
2857
2858 if ( ( (fMini)
2859 && (hbmThis = pi.hbmMiniColor)
2860 )
2861 || (hbmThis = pi.hbmColor)
2862 )
2863 {
2864 gpihQueryBitmapSize(hbmThis, &szlColor); // V1.0.1 (2002-11-30) [umoeller]
2865
2866 // aptl[2]: source bottom-left
2867 aptl[2].x = 0
2868 + lClipLeft * szlColor.cx / cxIcon;
2869 aptl[2].y = 0
2870 + lClipBottom * szlColor.cy / cyIcon;
2871
2872 // aptl[3]: source top-right (exclusive!)
2873 aptl[3].x = szlColor.cx
2874 - lClipRight * szlColor.cx / cxIcon;
2875 aptl[3].y = szlColor.cy
2876 - lClipTop * szlColor.cy / cyIcon;
2877
2878 GpiWCBitBlt(hps, // target
2879 hbmThis, // src bmp
2880 4L, // must always be 4
2881 aptl, // point array
2882 lPaintROP, // ROP_SRCPAINT,
2883 BBO_IGNORE);
2884 }
2885
2886 /*
2887 * 3) work on the XOR image:
2888 * (lower part of monochrome bitmap)
2889 */
2890
2891 if ( ( (fMini)
2892 && (hbmThis = pi.hbmMiniPointer)
2893 )
2894 || (hbmThis = pi.hbmPointer)
2895 )
2896 {
2897 // we queried the size of this one above V0.9.20 (2002-07-31) [umoeller]
2898
2899 // use only half the bitmap height
2900 cySrc = szlAndXor.cy / 2;
2901
2902 // aptl[2]: source bottom-left
2903 aptl[2].x = 0
2904 + lClipLeft * szlAndXor.cx / cxIcon;
2905 aptl[2].y = 0
2906 + lClipBottom * cySrc / cyIcon;
2907
2908 // aptl[3]: source top-right (exclusive!)
2909 aptl[3].x = szlAndXor.cx
2910 - lClipRight * szlAndXor.cx / cxIcon;
2911 aptl[3].y = cySrc
2912 - lClipTop * cySrc / cyIcon;
2913
2914 GpiWCBitBlt(hps, // target
2915 hbmThis, // src bmp
2916 4L, // must always be 4
2917 aptl, // point array
2918 lInvertROP, // ROP_SRCINVERT, // source XOR target
2919 BBO_IGNORE);
2920 }
2921
2922 // reset old pattern, if changed
2923 if (lPatternOld != -1)
2924 GpiSetPattern(hps, lPatternOld);
2925
2926 return TRUE;
2927 }
2928 }
2929
2930 return FALSE;
2931}
2932
2933/*
2934 *@@category: Helpers\PM helpers\GPI helpers\XBitmaps
2935 * Extended bitmaps. See gpihCreateXBitmap for an introduction.
2936 */
2937
2938/* ******************************************************************
2939 *
2940 * XBitmap functions
2941 *
2942 ********************************************************************/
2943
2944/*
2945 *@@ gpihCreateXBitmap:
2946 * calls gpihCreateXBitmap2 with cPlanes and cBitCount == 0
2947 * for compatibility with exports. Widgets might
2948 * have used this func.
2949 *
2950 *@@added V0.9.12 (2001-05-20) [umoeller]
2951 *@@changed V0.9.16 (2001-12-18) [umoeller]: now using optimized gpihCreateXBitmap2
2952 */
2953
2954PXBITMAP gpihCreateXBitmap(HAB hab, // in: anchor block
2955 LONG cx, // in: bitmap width
2956 LONG cy) // in: bitmap height
2957{
2958 return gpihCreateXBitmap2(hab,
2959 cx,
2960 cy,
2961 0,
2962 0);
2963}
2964
2965/*
2966 *@@ gpihCreateXBitmap:
2967 * creates an XBitmap, which is returned in an
2968 * XBITMAP structure.
2969 *
2970 * The problem with all the GPI bitmap functions
2971 * is that they are quite complex and it is easy
2972 * to forget one of the "disassociate" and "deselect"
2973 * functions, which then simply leads to enormous
2974 * resource leaks in the application.
2975 *
2976 * This function may relieve this a bit. This
2977 * creates a memory DC, a memory PS, and a bitmap,
2978 * and selects the bitmap into the memory PS.
2979 * You can then use any GPI function on the memory
2980 * PS to draw into the bitmap. Use the fields from
2981 * XBITMAP for that.
2982 *
2983 * The bitmap is created in RGB mode.
2984 *
2985 * Use gpihDestroyXBitmap to destroy the XBitmap
2986 * again.
2987 *
2988 * Example:
2989 *
2990 + PXBITMAP pbmp;
2991 + if (pbmp = gpihCreateXBitmap(hab, 100, 100))
2992 + {
2993 + GpiMove(pbmp->hpsMem, ...);
2994 + GpiBox(pbmp->hpsMem, ...);
2995 +
2996 + WinDrawBitmap(hpsScreen,
2997 + pbmp->hbm, // bitmap handle
2998 + ...);
2999 + gpihDestroyXBitmap(&pbmp);
3000 + }
3001 *
3002 * Without the gpih* functions, the above would expand
3003 * to more than 100 lines.
3004 *
3005 *@@added V0.9.16 (2001-12-18) [umoeller]
3006 */
3007
3008PXBITMAP gpihCreateXBitmap2(HAB hab, // in: anchor block
3009 LONG cx, // in: bitmap width
3010 LONG cy, // in: bitmap height
3011 ULONG cPlanes, // in: color planes (usually 1); if 0, current screen is used
3012 ULONG cBitCount) // in: either 1, 4, or 24; if 0, current screen value
3013{
3014 BOOL fOK = FALSE;
3015 PXBITMAP pbmp;
3016 if (pbmp = (PXBITMAP)malloc(sizeof(XBITMAP)))
3017 {
3018 memset(pbmp, 0, sizeof(XBITMAP));
3019
3020 // create memory PS for bitmap
3021 pbmp->szl.cx = cx;
3022 pbmp->szl.cy = cy;
3023 if (gpihCreateMemPS(hab,
3024 &pbmp->szl,
3025 &pbmp->hdcMem,
3026 &pbmp->hpsMem))
3027 {
3028 if (cBitCount != 1)
3029 // not monochrome bitmap:
3030 gpihSwitchToRGB(pbmp->hpsMem);
3031
3032 if (pbmp->hbm = gpihCreateBitmap2(pbmp->hpsMem,
3033 cx,
3034 cy,
3035 cPlanes,
3036 cBitCount))
3037 {
3038 if (GpiSetBitmap(pbmp->hpsMem,
3039 pbmp->hbm)
3040 != HBM_ERROR)
3041 fOK = TRUE;
3042 }
3043 }
3044
3045 if (!fOK)
3046 gpihDestroyXBitmap(&pbmp);
3047 }
3048
3049 return pbmp;
3050}
3051
3052/*
3053 *@@ gpihDetachBitmap:
3054 * "detaches" the bitmap from the given XBITMAP.
3055 * This will deselect the bitmap from the internal
3056 * memory PS so it can be used with many OS/2 APIs
3057 * that require that the bitmap not be selected
3058 * into any memory PS.
3059 *
3060 * Note that it then becomes the responsibility
3061 * of the caller to explicitly call GpiDeleteBitmap
3062 * because it will not be deleted by gpihDestroyXBitmap.
3063 *
3064 *@@added V0.9.16 (2001-12-18) [umoeller]
3065 */
3066
3067HBITMAP gpihDetachBitmap(PXBITMAP pbmp)
3068{
3069 HBITMAP hbm = pbmp->hbm;
3070 pbmp->hbm = NULLHANDLE;
3071 GpiSetBitmap(pbmp->hpsMem, NULLHANDLE);
3072
3073 return hbm;
3074}
3075
3076/*
3077 *@@ gpihDestroyXBitmap:
3078 * destroys an XBitmap created with gpihCreateXBitmap.
3079 *
3080 * To be on the safe side, this sets the
3081 * given XBITMAP pointer to NULL as well.
3082 *
3083 *@@added V0.9.12 (2001-05-20) [umoeller]
3084 */
3085
3086VOID gpihDestroyXBitmap(PXBITMAP *ppbmp)
3087{
3088 if (ppbmp)
3089 {
3090 PXBITMAP pbmp;
3091 if (pbmp = *ppbmp)
3092 {
3093 if (pbmp->hbm)
3094 {
3095 if (pbmp->hpsMem)
3096 GpiSetBitmap(pbmp->hpsMem, NULLHANDLE);
3097 GpiDeleteBitmap(pbmp->hbm);
3098 }
3099 if (pbmp->hpsMem)
3100 {
3101 GpiAssociate(pbmp->hpsMem, NULLHANDLE);
3102 GpiDestroyPS(pbmp->hpsMem);
3103 }
3104 if (pbmp->hdcMem)
3105 DevCloseDC(pbmp->hdcMem);
3106
3107 free(pbmp);
3108
3109 *ppbmp = NULL;
3110 }
3111 }
3112}
3113
3114/*
3115 *@@ gpihCreateBmpFromPS:
3116 * this creates a new bitmap and copies a screen rectangle
3117 * into it. Consider this a "screen capture" function.
3118 *
3119 * The new bitmap (which is returned) is compatible with the
3120 * device associated with hpsScreen. This function calls
3121 * gpihCreateMemPS and gpihCreateBitmap to have it created.
3122 * The memory PS is only temporary and freed again.
3123 *
3124 * This returns the handle of the new bitmap,
3125 * which can then be used for WinDrawBitmap and such, or
3126 * NULLHANDLE upon errors.
3127 *
3128 *@@changed V0.9.12 (2001-05-20) [umoeller]: fixed excessive mem PS size
3129 *@@changed V0.9.16 (2001-01-04) [umoeller]: now creating XBITMAP
3130 */
3131
3132PXBITMAP gpihCreateBmpFromPS(HAB hab, // in: anchor block
3133 HPS hpsScreen, // in: screen PS to copy from
3134 PRECTL prcl) // in: rectangle to copy
3135{
3136
3137 /* To copy an image from a display screen to a bit map:
3138 1. Associate the memory device context with a presentation space.
3139 2. Create a bit map.
3140 3. Select the bit map into the memory device context by calling GpiSetBitmap.
3141 4. Determine the location (in device coordinates) of the image.
3142 5. Call GpiBitBlt and copy the image to the bit map. */
3143
3144 PXBITMAP pBmp;
3145
3146 if (pBmp = gpihCreateXBitmap(hab,
3147 prcl->xRight - prcl->xLeft,
3148 prcl->yTop - prcl->yBottom))
3149 {
3150 POINTL aptl[3];
3151 // Copy the screen to the bit map.
3152 aptl[0].x = 0; // lower-left corner of destination rectangle
3153 aptl[0].y = 0;
3154 aptl[1].x = prcl->xRight; // upper-right corner for both
3155 aptl[1].y = prcl->yTop;
3156 aptl[2].x = prcl->xLeft; // lower-left corner of source rectangle
3157 aptl[2].y = prcl->yBottom;
3158
3159 if (GPI_ERROR == GpiBitBlt(pBmp->hpsMem,
3160 hpsScreen,
3161 sizeof(aptl) / sizeof(POINTL), // Number of points in aptl
3162 aptl,
3163 ROP_SRCCOPY,
3164 BBO_IGNORE))
3165 {
3166 // error during bitblt:
3167 gpihDestroyXBitmap(&pBmp);
3168 }
3169 }
3170
3171 return pBmp;
3172}
3173
Note: See TracBrowser for help on using the repository browser.