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

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

New build system, multimedia, other misc fixes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 111.6 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->lMaxDescender + pfm->lExternalLeading);
740
741 if (fl & DT_VCENTER)
742 ptlRun.y = (cyRect - cyString) / 2;
743 else
744 ptlRun.y = 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)
792 break;
793
794 if (*pNext == '\r')
795 pNext++;
796 pThis = ++pNext;
797 }
798}
799
800/*
801 *@@ gpihFillBackground:
802 * fills the specified rectangle in the way
803 * that is specified by the given BKGNDINFO
804 * structure. This way one can either use
805 * a solid color, a color fade, a bitmap,
806 * or a combination of those.
807 *
808 * See BKGNDINFO for the various parameters.
809 *
810 * Since this can potentially be expensive,
811 * it is strongly recommended to use a buffer
812 * bitmap for painting with the size of the
813 * window and bitblt that bitmap into the
814 * window on repaints. This way the background
815 * only has to be recreated on window resize.
816 *
817 *@@added V0.9.19 (2002-05-07) [umoeller]
818 */
819
820VOID gpihFillBackground(HPS hps, // in: presentation space for output
821 PRECTL prcl, // in: rectangle (inclusive!)
822 PBKGNDINFO pInfo) // in: background into
823{
824 LONG l;
825 POINTL ptl;
826
827 switch (pInfo->flPaintMode & PMOD_COLORMASK)
828 {
829 case PMOD_SOLID:
830 // fill with background color
831 GpiSetColor(hps,
832 pInfo->lcol1);
833 ptl.x = prcl->xLeft;
834 ptl.y = prcl->yBottom;
835 GpiMove(hps,
836 &ptl);
837 ptl.x = prcl->xRight;
838 ptl.y = prcl->yTop;
839 GpiBox(hps,
840 DRO_FILL,
841 &ptl,
842 0,
843 0);
844 break;
845
846 case PMOD_TOPBOTTOM:
847 {
848 LONG lDiffRed = (LONG)GET_RED(pInfo->lcol2) - (LONG)GET_RED(pInfo->lcol1);
849 LONG lDiffGreen = (LONG)GET_GREEN(pInfo->lcol2) - (LONG)GET_GREEN(pInfo->lcol1);
850 LONG lDiffBlue = (LONG)GET_BLUE(pInfo->lcol2) - (LONG)GET_BLUE(pInfo->lcol1);
851
852 LONG lMax = prcl->yTop - prcl->yBottom;
853
854 // start at top
855 ptl.y = prcl->yTop;
856
857 for (l = 0;
858 l <= lMax;
859 ++l)
860 {
861 // compose RGB color for this line;
862 // lcol1 is top, lcol2 is bottom
863 LONG lRed = GET_RED(pInfo->lcol1)
864 + ( lDiffRed
865 * l
866 / lMax
867 );
868 LONG lGreen = GET_GREEN(pInfo->lcol1)
869 + ( lDiffGreen
870 * l
871 / lMax
872 );
873 LONG lBlue = GET_BLUE(pInfo->lcol1)
874 + ( lDiffBlue
875 * l
876 / lMax
877 );
878
879 GpiSetColor(hps, MAKE_RGB(lRed, lGreen, lBlue));
880 ptl.x = prcl->xLeft;
881 GpiMove(hps, &ptl);
882 ptl.x = prcl->xRight;
883 GpiLine(hps, &ptl);
884
885 // next line below
886 --(ptl.y);
887 }
888 }
889 break;
890
891 case PMOD_LEFTRIGHT:
892 {
893 LONG lDiffRed = (LONG)GET_RED(pInfo->lcol2) - (LONG)GET_RED(pInfo->lcol1);
894 LONG lDiffGreen = (LONG)GET_GREEN(pInfo->lcol2) - (LONG)GET_GREEN(pInfo->lcol1);
895 LONG lDiffBlue = (LONG)GET_BLUE(pInfo->lcol2) - (LONG)GET_BLUE(pInfo->lcol1);
896
897 LONG lMax = prcl->xRight - prcl->xLeft;
898
899 // start at left
900 ptl.x = prcl->xLeft;
901
902 for (l = 0;
903 l <= lMax;
904 ++l)
905 {
906 // compose RGB color for this line;
907 // lcol1 is top, lcol2 is bottom
908 LONG lRed = GET_RED(pInfo->lcol1)
909 + ( lDiffRed
910 * l
911 / lMax
912 );
913 LONG lGreen = GET_GREEN(pInfo->lcol1)
914 + ( lDiffGreen
915 * l
916 / lMax
917 );
918 LONG lBlue = GET_BLUE(pInfo->lcol1)
919 + ( lDiffBlue
920 * l
921 / lMax
922 );
923
924 GpiSetColor(hps, MAKE_RGB(lRed, lGreen, lBlue));
925 ptl.y = prcl->yBottom;
926 GpiMove(hps, &ptl);
927 ptl.y = prcl->yTop;
928 GpiLine(hps, &ptl);
929
930 // next line to the right
931 ++(ptl.x);
932 }
933 }
934 break;
935 }
936}
937
938/*
939 *@@category: Helpers\PM helpers\GPI helpers\Fonts
940 */
941
942/* ******************************************************************
943 *
944 * Font helpers
945 *
946 ********************************************************************/
947
948/*
949 *@@ gpihMatchFont:
950 * attempts to find a font matching the specified
951 * data and fills the specified FATTRS structure
952 * accordingly.
953 *
954 * This function performs the insane "11-step process" to
955 * match a font, as described in the GPI reference.
956 *
957 * This function can operate in two modes:
958 *
959 * -- "Family" mode. In that case, specify the font family name
960 * with pszName and set fFamily to TRUE. This is useful for
961 * WYSIWYG text viewing if you need several font faces for
962 * the same family, such as Courier Bold, Bold Italics, etc.
963 * You can specify those attributes with usFormat then.
964 *
965 * -- "Face" mode. In that case, specify the full font face name
966 * with pszName and set fFamily to FALSE. This is useful for
967 * font presentation parameters which use the "WarpSans Bold"
968 * format. In that case, set usFormat to 0.
969 *
970 * Returns TRUE if a "true" match was found, FALSE
971 * otherwise. In both cases, *pfa receives data
972 * which will allow GpiCreateLogFont to work; however,
973 * if FALSE is returned, GpiCreateLogFont will most
974 * likely find the default font (System Proportional)
975 * only.
976 *
977 * If (pFontMetrics != NULL), *pFontMetrics receives the
978 * FONTMETRICS of the font which was found. If an outline
979 * font has been found (instead of a bitmap font),
980 * FONTMETRICS.fsDefn will have the FM_DEFN_OUTLINE bit set.
981 *
982 * This function was extracted from gpihFindFont with
983 * 0.9.14 to allow for caching the font search results,
984 * which is most helpful for memory device contexts,
985 * where gpihFindFont can be inefficient.
986 *
987 *@@added V0.9.14 (2001-08-03) [umoeller]
988 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed a few weirdos with outline fonts
989 */
990
991BOOL gpihMatchFont(HPS hps, // in: presentation space for output
992 LONG lSize, // in: font point size
993 BOOL fFamily, // in: if TRUE, pszName specifies font family;
994 // if FALSE, pszName specifies font face
995 const char *pcszName, // in: font family or face name (without point size)
996 USHORT usFormat, // in: none, one or several of:
997 // -- FATTR_SEL_ITALIC
998 // -- FATTR_SEL_UNDERSCORE (underline)
999 // -- FATTR_SEL_BOLD
1000 // -- FATTR_SEL_STRIKEOUT
1001 // -- FATTR_SEL_OUTLINE (hollow)
1002 FATTRS *pfa, // out: font attributes if found
1003 PFONTMETRICS pFontMetrics) // out: font metrics of created font (optional)
1004{
1005 // first find out how much memory we need to allocate
1006 // for the FONTMETRICS structures
1007 ULONG ul = 0;
1008 LONG lTemp = 0;
1009 LONG cFonts = GpiQueryFonts(hps,
1010 QF_PUBLIC | QF_PRIVATE,
1011 NULL, // pszFaceName,
1012 &lTemp,
1013 sizeof(FONTMETRICS),
1014 NULL);
1015 PFONTMETRICS pfm = (PFONTMETRICS)malloc(cFonts * sizeof(FONTMETRICS)),
1016 pfm2 = pfm,
1017 pfmFound = NULL;
1018
1019 BOOL fQueriedDevice = FALSE; // V0.9.14 (2001-08-01) [umoeller]
1020 LONG alDevRes[2]; // device resolution
1021
1022 // _Pmpf(("gpihFindFont: enumerating for %s, %d points", pcszName, lSize));
1023
1024 GpiQueryFonts(hps,
1025 QF_PUBLIC | QF_PRIVATE,
1026 NULL, // pszFaceName,
1027 &cFonts,
1028 sizeof(FONTMETRICS), // length of each metrics structure
1029 // -- _not_ total buffer size!
1030 pfm);
1031
1032 // now we have an array of FONTMETRICS
1033 // for EVERY font that is installed on the system...
1034 // these things are completely unsorted, so there's
1035 // nothing we can rely on, we have to check them all.
1036
1037 // fill in some default values for FATTRS,
1038 // in case we don't find something better
1039 // in the loop below; these values will be
1040 // applied if
1041 // a) an outline font has been found;
1042 // b) bitmap fonts have been found, but
1043 // none for the current device resolution
1044 // exists;
1045 // c) no font has been found at all.
1046 // In all cases, GpiCreateLogFont will do
1047 // a "close match" resolution (at the bottom).
1048 pfa->usRecordLength = sizeof(FATTRS);
1049 pfa->fsSelection = usFormat; // changed later if better font is found
1050 pfa->lMatch = 0L; // closest match
1051 strcpy(pfa->szFacename, pcszName);
1052 pfa->idRegistry = 0; // default registry
1053 pfa->usCodePage = 0; // default codepage
1054 // the following two must be zero, or outline fonts
1055 // will not be found; if a bitmap font has been passed
1056 // to us, we'll modify these two fields later
1057 pfa->lMaxBaselineExt = 0; // font size (height)
1058 pfa->lAveCharWidth = 0; // font size (width)
1059 pfa->fsType = 0; // default type
1060 pfa->fsFontUse = FATTR_FONTUSE_NOMIX;
1061
1062 // now go thru the array of FONTMETRICS
1063 // to check if we have a bitmap font
1064 // pszFaceName; the default WPS behavior
1065 // is that bitmap fonts appear to take
1066 // priority over outline fonts of the
1067 // same name, so we check these first
1068 pfm2 = pfm;
1069 for (ul = 0;
1070 ul < cFonts;
1071 ul++)
1072 {
1073 const char *pcszCompare = (fFamily)
1074 ? pfm2->szFamilyname
1075 : pfm2->szFacename;
1076
1077 /* _Pmpf((" Checking font: %s (Fam: %s), %d, %d, %d",
1078 pcszCompare,
1079 pfm2->szFamilyname,
1080 pfm2->sNominalPointSize,
1081 pfm2->lMaxBaselineExt,
1082 pfm2->lAveCharWidth)); */
1083
1084 if (!strcmp(pcszCompare, pcszName))
1085 {
1086 /* _Pmpf((" Found font %s; slope %d, usWeightClass %d",
1087 pfm2->szFacename,
1088 pfm2->sCharSlope,
1089 pfm2->usWeightClass)); */
1090
1091 if ((pfm2->fsDefn & FM_DEFN_OUTLINE) == 0)
1092 {
1093 // image (bitmap) font:
1094 // check point size
1095 if (pfm2->sNominalPointSize == lSize * 10)
1096 {
1097 // OK: check device resolutions, because
1098 // normally, there are always several image
1099 // fonts for different resolutions
1100 // for bitmap fonts, there are normally two versions:
1101 // one for low resolutions, one for high resolutions
1102 if (!fQueriedDevice)
1103 {
1104 DevQueryCaps(GpiQueryDevice(hps),
1105 CAPS_HORIZONTAL_FONT_RES,
1106 2L,
1107 alDevRes);
1108 fQueriedDevice = TRUE;
1109 }
1110
1111 if ( (pfm2->sXDeviceRes == alDevRes[0])
1112 && (pfm2->sYDeviceRes == alDevRes[1])
1113 )
1114 {
1115 // OK: use this for GpiCreateLogFont
1116 pfa->lMaxBaselineExt = pfm2->lMaxBaselineExt;
1117 pfa->lAveCharWidth = pfm2->lAveCharWidth;
1118 // pfa->lMatch = pfm2->lMatch;
1119
1120 pfmFound = pfm2;
1121 break;
1122 }
1123 }
1124 }
1125 else
1126 // outline font:
1127 if (pfmFound == NULL)
1128 {
1129 // no bitmap font found yet:
1130
1131 /*
1132 #define FATTR_SEL_ITALIC 0x0001
1133 #define FATTR_SEL_UNDERSCORE 0x0002
1134 #define FATTR_SEL_OUTLINE 0x0008
1135 #define FATTR_SEL_STRIKEOUT 0x0010
1136 #define FATTR_SEL_BOLD 0x0020
1137 */
1138
1139 if ( (!fFamily) // face mode is OK always
1140 // V0.9.14 (2001-08-03) [umoeller]
1141 || ( ( ( (usFormat & FATTR_SEL_BOLD)
1142 && (pfm2->usWeightClass == 7) // bold
1143 )
1144 || ( (!(usFormat & FATTR_SEL_BOLD))
1145 && (pfm2->usWeightClass == 5) // regular
1146 )
1147 )
1148 && ( ( (usFormat & FATTR_SEL_ITALIC)
1149 && (pfm2->sCharSlope != 0) // italics
1150 )
1151 || ( (!(usFormat & FATTR_SEL_ITALIC))
1152 && (pfm2->sCharSlope == 0) // regular
1153 )
1154 )
1155 )
1156 )
1157 {
1158 // yes, we found a true font for that face:
1159 pfmFound = pfm2;
1160
1161 // use this exact font for GpiCreateLogFont
1162 pfa->lMatch = pfm2->lMatch;
1163
1164 // the following two might have been set
1165 // for a bitmap font above
1166 // V0.9.14 (2001-08-03) [umoeller]
1167 pfa->lMaxBaselineExt = pfm2->lMaxBaselineExt;
1168 pfa->lAveCharWidth = pfm2->lAveCharWidth;
1169
1170 pfa->idRegistry = pfm2->idRegistry;
1171
1172 // override NOMIX // V0.9.14 (2001-08-03) [umoeller]
1173 pfa->fsFontUse = FATTR_FONTUSE_OUTLINE;
1174
1175 // according to GPIREF, we must also specify
1176 // the full face name... geese!
1177 strcpy(pfa->szFacename, pfm2->szFacename);
1178 // unset flag in FATTRS, because this would
1179 // duplicate bold or italic
1180 pfa->fsSelection = 0;
1181
1182 // _Pmpf((" --> using it"));
1183 // but loop on, because we might have a bitmap
1184 // font which should take priority
1185 }
1186 }
1187 }
1188
1189 pfm2++;
1190 }
1191
1192 if (pfmFound)
1193 // FONTMETRICS found:
1194 // copy font metrics?
1195 if (pFontMetrics)
1196 memcpy(pFontMetrics, pfmFound, sizeof(FONTMETRICS));
1197
1198 // free the FONTMETRICS array
1199 free(pfm);
1200
1201 return (pfmFound != NULL);
1202}
1203
1204/*
1205 *@@ gpihSplitPresFont:
1206 * splits a presentation parameter font
1207 * string into the point size and face
1208 * name so that it can be passed to
1209 * gpihFindFont more easily.
1210 *
1211 *@@added V0.9.1 (2000-02-15) [umoeller]
1212 *@@changed V0.9.20 (2002-07-19) [umoeller]: optimized
1213 */
1214
1215BOOL gpihSplitPresFont(PSZ pszFontNameSize, // in: e.g. "12.Courier"
1216 PULONG pulSize, // out: integer point size (e.g. 12);
1217 // ptr must be specified
1218 PSZ *ppszFaceName) // out: ptr into pszFontNameSize
1219 // (e.g. "Courier")
1220{
1221 PCHAR pcDot;
1222 if ( (pszFontNameSize)
1223 && (pcDot = strchr(pszFontNameSize, '.'))
1224 )
1225 {
1226 // _Pmpf(("Found font PP: %s", pszFontFound));
1227 // sscanf(pszFontNameSize, "%lu", pulSize);
1228 *pulSize = atoi(pszFontNameSize); // V0.9.20 (2002-07-19) [umoeller]
1229 *ppszFaceName = pcDot + 1;
1230 return TRUE;
1231 }
1232
1233 return FALSE;
1234}
1235
1236/*
1237 *@@ gpihLockLCIDs:
1238 * requests the mutex for serializing the
1239 * lcids.
1240 *
1241 * With GPI, lcids are a process-wide resource and not
1242 * guaranteed to be unique. In the worst case, while your
1243 * font routines are running, another thread modifies the
1244 * lcids and you get garbage. If your fonts suddenly
1245 * turn to "System Proportional", you know this has
1246 * happened.
1247 *
1248 * As a result, whenever you work on lcids, request this
1249 * mutex during your processing. If you do this consistently
1250 * across all your code, you should be safe.
1251 *
1252 * gpihFindFont uses this mutex. If you call GpiCreateLogFont
1253 * yourself somewhere, you should do this under the protection
1254 * of this function.
1255 *
1256 * Call gpihUnlockLCIDs to unlock.
1257 *
1258 *@@added V0.9.9 (2001-04-01) [umoeller]
1259 */
1260
1261BOOL gpihLockLCIDs(VOID)
1262{
1263 if (!G_hmtxLCIDs)
1264 // first call: create
1265 return !DosCreateMutexSem(NULL,
1266 &G_hmtxLCIDs,
1267 0,
1268 TRUE); // request!
1269
1270 // subsequent calls: request
1271 return !DosRequestMutexSem(G_hmtxLCIDs, SEM_INDEFINITE_WAIT);
1272}
1273
1274/*
1275 *@@ UnlockLCIDs:
1276 * releases the mutex for serializing the
1277 * lcids.
1278 *
1279 *@@added V0.9.9 (2001-04-01) [umoeller]
1280 */
1281
1282VOID gpihUnlockLCIDs(VOID)
1283{
1284 DosReleaseMutexSem(G_hmtxLCIDs);
1285}
1286
1287/*
1288 *@@ gpihQueryNextLCID:
1289 * returns the next available lcid for the given HPS.
1290 * Actually, it's the next available lcid for the
1291 * entire process, since there can be only 255 altogether.
1292 * Gets called by gpihFindFont automatically.
1293 *
1294 * WARNING: This function by itself is not thread-safe.
1295 * See gpihLockLCIDs for how to serialize this.
1296 *
1297 * Code was extensively re-tested, works (V0.9.12 (2001-05-31) [umoeller]).
1298 *
1299 *@@added V0.9.3 (2000-05-06) [umoeller]
1300 *@@changed V0.9.9 (2001-04-01) [umoeller]: removed all those sick sub-allocs
1301 */
1302
1303LONG gpihQueryNextFontID(HPS hps) // in: presentation space for output
1304{
1305 LONG lcidNext = -1;
1306
1307 LONG lCount = GpiQueryNumberSetIds(hps);
1308 // the number of local identifiers
1309 // (lcids) currently in use, and
1310 // therefore the maximum number
1311 // of objects for which information
1312 // can be returned
1313
1314 // _Pmpf((__FUNCTION__ ": Entering"));
1315
1316 if (lCount == 0)
1317 {
1318 // none in use yet:
1319 lcidNext = 15;
1320
1321 // _Pmpf((" no lcids in use"));
1322 }
1323 else
1324 {
1325 // #define GQNCL_BLOCK_SIZE 400*sizeof(LONG)
1326
1327 PLONG alTypes = NULL; // object types
1328 PSTR8 aNames = NULL; // font names
1329 PLONG allcids = NULL; // local identifiers
1330
1331 if ( (alTypes = (PLONG)malloc(lCount * sizeof(LONG)))
1332 && (aNames = (PSTR8)malloc(lCount * sizeof(STR8)))
1333 && (allcids = (PLONG)malloc(lCount * sizeof(LONG)))
1334 )
1335 {
1336 if (GpiQuerySetIds(hps,
1337 lCount,
1338 alTypes,
1339 aNames,
1340 allcids))
1341 {
1342 // FINALLY we have all the lcids in use.
1343 BOOL fContinue = TRUE;
1344 lcidNext = 15;
1345
1346 // _Pmpf((" %d fonts in use, browsing...", lCount));
1347
1348 // now, check if this lcid is in use already:
1349 while (fContinue)
1350 {
1351 BOOL fFound = FALSE;
1352 ULONG ul;
1353 fContinue = FALSE;
1354 for (ul = 0;
1355 ul < lCount;
1356 ul++)
1357 {
1358 if (allcids[ul] == lcidNext)
1359 {
1360 fFound = TRUE;
1361 break;
1362 }
1363 }
1364
1365 if (fFound)
1366 {
1367 // lcid found:
1368 // try next higher one
1369
1370 // _Pmpf((" %d is busy...", lcidNext));
1371
1372 lcidNext++;
1373 fContinue = TRUE;
1374 }
1375 // else
1376 // else: return that one
1377 // _Pmpf((" %d is free", lcidNext));
1378 }
1379 }
1380 }
1381
1382 if (alTypes)
1383 free(alTypes);
1384 if (aNames)
1385 free(aNames);
1386 if (allcids)
1387 free(allcids);
1388 }
1389
1390 // _Pmpf((__FUNCTION__ ": Returning lcid %d", lcidNext));
1391
1392 return lcidNext;
1393}
1394
1395/*
1396 *@@ gpihCreateFont:
1397 *
1398 *@@added V0.9.14 (2001-08-03) [umoeller]
1399 */
1400
1401LONG gpihCreateFont(HPS hps, // in: presentation space for output
1402 FATTRS *pfa)
1403{
1404 LONG lLCIDReturn = 0;
1405
1406 if (gpihLockLCIDs()) // V0.9.9 (2001-04-01) [umoeller]
1407 {
1408 // new logical font ID: last used plus one
1409 lLCIDReturn = gpihQueryNextFontID(hps);
1410
1411 GpiCreateLogFont(hps,
1412 NULL, // don't create "logical font name" (STR8)
1413 lLCIDReturn,
1414 pfa);
1415
1416 gpihUnlockLCIDs();
1417 }
1418
1419 return lLCIDReturn;
1420}
1421
1422/*
1423 *@@ gpihFindFont:
1424 * this returns a new logical font ID (LCID) for the specified
1425 * font by calling gpihMatchFont first and then
1426 * GpiCreateLogFont to create a logical font from the
1427 * data returned.
1428 *
1429 * See gpihMatchFont for additional explanations.
1430 *
1431 * To then use the font whose LCID has been returned by this
1432 * function for text output, call:
1433 + GpiSetCharSet(hps, lLCIDReturned);
1434 *
1435 * <B>Font Point Sizes:</B>
1436 *
1437 * 1) For image (bitmap) fonts, the size is fixed, and
1438 * you can directly draw after the font has been
1439 * selected. GpiSetCharBox has no effect on image
1440 * fonts unless you switch to character mode 2
1441 * (if you care for that: GpiSetCharMode).
1442 *
1443 * 2) For outline fonts however, you need to define a
1444 * character box if you want to output text in a
1445 * size other than the default size. This is almost
1446 * as bad a mess as this function, so gpihSetPointSize
1447 * has been provided for this. See remarks there.
1448 *
1449 * <B>Example:</B>
1450 *
1451 * This example prints text in "24.Courier".
1452 *
1453 + PSZ pszOutput = "Test output";
1454 + FONTMETRICS FontMetrics;
1455 + LONG lLCID = gpihFindFont(hps,
1456 + 24, // point size
1457 + FALSE, // face, not family
1458 + "Courier",
1459 + 0,
1460 + &FontMetrics);
1461 + if (lLCID)
1462 + {
1463 + // no error:
1464 + GpiSetCharSet(hps, lLCID);
1465 + if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
1466 + // outline font found (should be the case):
1467 + gpihSetPointSize(hps, 24);
1468 + }
1469 + GpiCharString(hps, strlen(pszOutput), pszOutput);
1470 *
1471 * <B>Details:</B>
1472 *
1473 * First, GpiQueryFonts is called to enumerate all the fonts on
1474 * the system. We then evaluate the awful FONTMETRICS data
1475 * of those fonts to perform a "real" match.
1476 *
1477 * If that fails, we allow GPI to do a "close" match based
1478 * on the input values. This might result in the system
1479 * default font (System Proportional) to be found, in the
1480 * worst case. But even then, a new LCID is returned.
1481 *
1482 * The "close match" comes in especially when using the
1483 * font attributes (bold, italics) and we are unable to
1484 * find the correct outline font for that. Unfortunately,
1485 * the information in FONTMETRICS.fsSelection is wrong,
1486 * wrong, wrong for the large majority of fonts. (For
1487 * example, I get the "bold" flag set for regular fonts,
1488 * and vice versa.) So we attempt to use the other fields,
1489 * but this doesn't always help. Not even Netscape gets
1490 * this right.
1491 *
1492 * <B>Font faces:</B>
1493 *
1494 * This is terribly complicated as well. You need to
1495 * differentiate between "true" emphasis faces (that
1496 * is, bold and italics are implemented thru separate
1497 * font files) and the ugly GPI simulation, which simply
1498 * makes the characters wider or shears them.
1499 *
1500 * This function even finds true "bold" and "italic" faces
1501 * for outline fonts. To do this, always specify the "family"
1502 * name as pszFaceName (e.g. "Courier" instead of "Courier
1503 * Bold") and set the flags for usFormat (e.g. FATTR_SEL_BOLD).
1504 * Note that this implies that you need call this function
1505 * twice to create two logical fonts for regular and bold faces.
1506 *
1507 * If a "true" emphasis font is not found, the GPI simulation
1508 * is enabled.
1509 *
1510 * <B>Remarks:</B>
1511 *
1512 * 1) This function _always_ creates a new logical font,
1513 * whose ID is returned, even if the specified font
1514 * was not found or a "close match" was performed by
1515 * GPI. As a result, do not call this function twice
1516 * for the same font specification, because there are
1517 * only 255 logical font IDs for each process.
1518 *
1519 * 2) Since this function always creates an LCID,
1520 * you should _always_ free the LCID later.
1521 * This is only valid if the font is no longer selected
1522 * into any presentation space. So use these calls:
1523 + GpiSetCharSet(hps, LCID_DEFAULT);
1524 + GpiDeleteSetId(hps, lLCIDReturnedByThis);
1525 *
1526 * 3) Using this function, bitmap fonts will have priority
1527 * over outline fonts of the same face name. This is
1528 * how the WPS does it too. This is most obvious with
1529 * the "Helv" font, which exists as a bitmap font for
1530 * certain point sizes only.
1531 *
1532 * 4) Since logical font IDs are shared across the
1533 * process, a mutex is requested while the lcids are
1534 * being queried and/or manipulated. In other words,
1535 * this func is now thread-safe (V0.9.9).
1536 *
1537 * This calls gpihQueryNextFontID in turn to find the
1538 * next free lcid. See remarks there.
1539 *
1540 * <B>Font metrics:</B>
1541 *
1542 * The important values in the returned FONTMETRICS are
1543 * like this (according to PMREF):
1544 *
1545 + ÉÍ ________________________________________________
1546 + º
1547 + º lExternalLeading, according to font designer.
1548 + º ________________________________________________ Í»
1549 + ÈÍ º
1550 + # # º
1551 + ## ## º lMaxAscender (of entire;
1552 + ÉÍ _______________ # # # # º font); this can be > capital
1553 + º #### # # # # º letters because miniscules
1554 + º # # # # # º can exceed that.
1555 + º lXHeight # # # # º
1556 + º # # # # º
1557 + º # # # # º
1558 + º _______________#####________#_______#___ baseline Í»
1559 + ÈÍ # º
1560 + # º lMaxDescender
1561 + ______________ ####______________________________ ͌
1562 +
1563 *
1564 * In turn, lMaxBaselineExt is lMaxAscender + lMaxDescender.
1565 *
1566 * Soooo... to find out about the optimal line spacing, GPIREF
1567 * recommends to use lMaxBaselineExt + lExternalLeading.
1568 *
1569 *@@added V0.9.0 [umoeller]
1570 *@@changed V0.9.3 (2000-05-06) [umoeller]: didn't work for more than one font; now using gpihQueryNextFontID
1571 *@@changed V0.9.3 (2000-05-06) [umoeller]: usFormat didn't work; fixed
1572 *@@changed V0.9.4 (2000-08-08) [umoeller]: added fFamily
1573 *@@changed V0.9.9 (2001-04-01) [umoeller]: made this thread-safe, finally
1574 *@@changed V0.9.14 (2001-08-01) [umoeller]: some optimizations
1575 */
1576
1577LONG gpihFindFont(HPS hps, // in: presentation space for output
1578 LONG lSize, // in: font point size
1579 BOOL fFamily, // in: if TRUE, pszName specifies font family;
1580 // if FALSE, pszName specifies font face
1581 const char *pcszName, // in: font family or face name (without point size)
1582 USHORT usFormat, // in: none, one or several of:
1583 // -- FATTR_SEL_ITALIC
1584 // -- FATTR_SEL_UNDERSCORE (underline)
1585 // -- FATTR_SEL_BOLD
1586 // -- FATTR_SEL_STRIKEOUT
1587 // -- FATTR_SEL_OUTLINE (hollow)
1588 PFONTMETRICS pFontMetrics) // out: font metrics of created font (optional)
1589{
1590 FATTRS FontAttrs;
1591
1592 gpihMatchFont(hps,
1593 lSize,
1594 fFamily,
1595 pcszName,
1596 usFormat,
1597 &FontAttrs,
1598 pFontMetrics);
1599
1600 return gpihCreateFont(hps,
1601 &FontAttrs);
1602
1603 // _Pmpf((__FUNCTION__ ": returning lcid %d", lLCIDReturn));
1604}
1605
1606/*
1607 *@@ gpihFindPresFont:
1608 * similar to gpihFindFont, but this one evaluates
1609 * the PP_FONTNAMESIZE presentation parameter of the
1610 * specified window instead. If that one is not set,
1611 * the specified default font is used instead.
1612 *
1613 * Note that as opposed to gpihFindFont, this one
1614 * takes a "8.Helv"-type string as input.
1615 *
1616 * See gpihFindFont for additional remarks, which
1617 * gets called by this function.
1618 *
1619 * Again, if an outline font has been returned, you
1620 * must also set the "character box" for the HPS, or
1621 * your text will always have the same point size.
1622 * Use gpihSetPointSize for that, using the presparam's
1623 * point size, which is returned by this function
1624 * into *plSize, if (plSize != NULL):
1625 +
1626 + FONTMETRICS FontMetrics;
1627 + LONG lPointSize;
1628 + LONG lLCID = gpihFindPresFont(hwnd, hps, "8.Helv",
1629 + &FontMetrics,
1630 + &lPointSize);
1631 + GpiSetCharSet(hps, lLCID);
1632 + if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
1633 + gpihSetPointSize(hps, lPointSize);
1634 *
1635 * If errors occur, e.g. if the font string does not
1636 * conform to the "size.face" format, null is returned.
1637 *
1638 *@@added V0.9.0 [umoeller]
1639 *@@changed V0.9.9 (2001-04-01) [umoeller]: now supporting NULLHANDLE hwnd
1640 */
1641
1642LONG gpihFindPresFont(HWND hwnd, // in: window to search for presparam or NULLHANDLE
1643 BOOL fInherit, // in: search parent windows too?
1644 HPS hps, // in: HPS for font selection
1645 const char *pcszDefaultFont, // in: default font if not found (i.e. "8.Helv")
1646 PFONTMETRICS pFontMetrics, // out: font metrics of created font (optional)
1647 PLONG plSize) // out: presparam's point size (optional)
1648{
1649 CHAR szPPFont[200] = "";
1650 const char *pcszFontFound = 0;
1651 CHAR szFaceName[300] = "";
1652 ULONG ulFontSize = 0;
1653
1654 if ( (hwnd) // V0.9.9 (2001-04-01) [umoeller]
1655 && (WinQueryPresParam(hwnd,
1656 PP_FONTNAMESIZE, // first PP to query
1657 0, // second PP to query
1658 NULL, // out: which one is returned
1659 (ULONG)sizeof(szPPFont), // in: buffer size
1660 (PVOID)&szPPFont, // out: PP value returned
1661 (fInherit)
1662 ? 0
1663 : QPF_NOINHERIT))
1664 )
1665 // PP found:
1666 pcszFontFound = szPPFont;
1667 else
1668 pcszFontFound = pcszDefaultFont;
1669
1670 if (pcszFontFound)
1671 {
1672 const char *pcDot = strchr(pcszFontFound, '.');
1673 if (pcDot)
1674 {
1675 // _Pmpf(("Found font PP: %s", pszFontFound));
1676 sscanf(pcszFontFound, "%lu", &ulFontSize);
1677 if (plSize)
1678 *plSize = ulFontSize;
1679 strcpy(szFaceName, pcDot + 1);
1680 return gpihFindFont(hps,
1681 ulFontSize,
1682 FALSE, // face, not family name
1683 szFaceName,
1684 0,
1685 pFontMetrics);
1686 }
1687 }
1688
1689 return 0;
1690}
1691
1692/*
1693 *@@ gpihSetPointSize:
1694 * this invokes GpiSetCharBox on the given HPS to
1695 * set the correct "character box" with the proper
1696 * parameters.
1697 *
1698 * This is necessary for text output with outline
1699 * fonts. Bitmap fonts have a fixed size, and
1700 * calling this function is not necessary for them.
1701 *
1702 * Unfortunately, IBM has almost not documented
1703 * how to convert nominal point sizes to the
1704 * correct character cell values. This involves
1705 * querying the output device (DevQueryCaps).
1706 *
1707 * I have found one hint for this procedure in GPIREF
1708 * (chapter "Character string primitives", "Using...",
1709 * "Drawing text"), but the example code given
1710 * there is buggy, because it must be "72" instead
1711 * of "720". #%(%!"õ!!.
1712 *
1713 * So here we go. If you want to output text in,
1714 * say, "Courier" and 24 points (nominal point size),
1715 * select the font into your HPS and call
1716 + gpihSetPointSize(hps, 24)
1717 * and you're done. See gpihFindFont for a complete
1718 * example.
1719 *
1720 * Call this function as many times as needed. This
1721 * consumes no resources at all.
1722 *
1723 * This returns the return value of GpiSetCharBox.
1724 *
1725 *@@added V0.9.0 [umoeller]
1726 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed bad rounding errors
1727 */
1728
1729BOOL gpihSetPointSize(HPS hps, // in: presentation space for output
1730 LONG lPointSize) // in: desired nominal point size
1731{
1732 SIZEF box;
1733 HDC hdc = GpiQueryDevice(hps); // get the HDC from the HPS
1734 LONG alDevRes[2];
1735 DevQueryCaps(GpiQueryDevice(hps), // get the HDC from the HPS
1736 CAPS_HORIZONTAL_FONT_RES,
1737 2L,
1738 alDevRes);
1739
1740 // V0.9.14: this code didn't work... it produced rounding
1741 // errors which set the font size different from what
1742 // it should be according to the WPS font palette
1743 /* box.cx = MAKEFIXED((lPointSize * alDevRes[0]) / 72, 0);
1744 box.cy = MAKEFIXED((lPointSize * alDevRes[1]) / 72, 0); */
1745
1746 // V0.9.14 (2001-08-03) [umoeller]: now using this one
1747 // instead
1748 lPointSize *= 65536;
1749 box.cx = (FIXED)(lPointSize / 72 * alDevRes[0]);
1750 box.cy = (FIXED)(lPointSize / 72 * alDevRes[1]);
1751
1752 return GpiSetCharBox(hps, &box);
1753}
1754
1755/*
1756 *@@ gpihQueryLineSpacing:
1757 * this returns the optimal line spacing for text
1758 * output with the current HPS; this is computed
1759 * by evaluating those incredible FONTMETRICS.
1760 *
1761 * This might be helpful if you write text to the
1762 * screen yourself and need the height of a text
1763 * line to advance to the next.
1764 *
1765 *@@changed V0.9.7 (2000-12-20) [umoeller]: removed psz param
1766 */
1767
1768LONG gpihQueryLineSpacing(HPS hps) // in: presentation space for output
1769{
1770 FONTMETRICS fm;
1771
1772 if (GpiQueryFontMetrics(hps, sizeof(FONTMETRICS), &fm))
1773 return ( ( fm.lMaxBaselineExt // max vertical font space
1774 +fm.lExternalLeading) // space advised by font designer
1775 );
1776
1777 return 15;
1778}
1779
1780/*
1781 *@@category: Helpers\PM helpers\GPI helpers\Bitmaps/Icons
1782 */
1783
1784/* ******************************************************************
1785 *
1786 * Bitmap helpers
1787 *
1788 ********************************************************************/
1789
1790/*
1791 *@@ gpihCreateMemPS:
1792 * creates a memory device context and presentation space so
1793 * that they are compatible with the screen device context and
1794 * presentation space. These are stored in *hdcMem and *hpsMem.
1795 *
1796 * This is a one-shot function for the standard code that is
1797 * always needed when working with bitmaps in a memory device
1798 * context.
1799 *
1800 * psizlPage must point to a SIZEL structure containing the
1801 * width and height for the memory PS. Specify the size of
1802 * the future bitmap here. Specify {0, 0} to get a PS with
1803 * the size of the full screen, which consumes quite a bit
1804 * of memory though.
1805 *
1806 * Returns FALSE upon errors. In that case, both hdcMem and
1807 * hpsMem are set to NULLHANDLE.
1808 *
1809 * To cleanup after this function has returned TRUE, use the
1810 * following:
1811 + GpiDestroyPS(hpsMem);
1812 + DevCloseDC(hdcMem);
1813 *
1814 *@@changed V0.9.3 (2000-05-18) [umoeller]: added psiszlPage
1815 */
1816
1817BOOL gpihCreateMemPS(HAB hab, // in: anchor block
1818 PSIZEL psizlPage, // in: width and height for mem PS
1819 HDC *hdcMem, // out: memory DC or NULLHANDLE upon errors
1820 HPS *hpsMem) // out: memory PS or NULLHANDLE upon errors
1821{
1822 BOOL brc = FALSE;
1823 PSZ pszData[4] = { "Display", NULL, NULL, NULL };
1824
1825 // create new memory DC
1826 if ((*hdcMem = DevOpenDC(hab,
1827 OD_MEMORY, // create memory DC
1828 "*", // token: do not take INI info
1829 4, // item count in pszData
1830 (PDEVOPENDATA)pszData,
1831 NULLHANDLE))) // compatible with screen
1832 {
1833 // memory DC created successfully:
1834 // create compatible PS
1835 if ((*hpsMem = GpiCreatePS(hab,
1836 *hdcMem, // HDC to associate HPS with (GPIA_ASSOC);
1837 // mandatory for GPIT_MICRO
1838 psizlPage, // is (0, 0) == screen size
1839 PU_PELS // presentation page units: pixels
1840 | GPIA_ASSOC // associate with hdcMem (req. for GPIT_MICRO)
1841 | GPIT_MICRO))) // micro presentation space
1842 brc = TRUE;
1843 else
1844 {
1845 // error (hpsMem == NULLHANDLE):
1846 // close memory DC again
1847 DevCloseDC(*hdcMem);
1848 *hdcMem = NULLHANDLE;
1849 }
1850 }
1851
1852 return brc;
1853}
1854
1855/*
1856 *@@ gpihCreateBitmap:
1857 * calls gpihCreateBitmap2 with cPlanes and cBitCount == 0
1858 * for compatibility with exports. Widgets might
1859 * have used this func.
1860 *
1861 *@@changed V0.9.0 [umoeller]: function prototype changed to cx and cy
1862 *@@changed V0.9.16 (2001-12-18) [umoeller]: now using optimized gpihCreateBitmap2
1863 */
1864
1865HBITMAP gpihCreateBitmap(HPS hpsMem, // in: memory DC
1866 ULONG cx, // in: width of new bitmap
1867 ULONG cy) // in: height of new bitmap
1868{
1869 return gpihCreateBitmap2(hpsMem,
1870 cx,
1871 cy,
1872 0,
1873 0); // current screen bit count
1874}
1875
1876static const BITMAPINFOHEADER2 G_bih2Template[] =
1877 {
1878 sizeof(BITMAPINFOHEADER2), // ULONG cbFix;
1879 0, // ULONG cx; // tbr
1880 0, // ULONG cy; // tbr
1881 0, // USHORT cPlanes; // tbr
1882 0, // USHORT cBitCount; // tbr
1883 BCA_UNCOMP, // ULONG ulCompression;
1884 0, // ULONG cbImage;
1885 70, // ULONG cxResolution;
1886 70, // ULONG cyResolution;
1887 2, // ULONG cclrUsed;
1888 0, // ULONG cclrImportant;
1889 BRU_METRIC, // USHORT usUnits;
1890 // measure units for cxResolution/cyResolution: pels per meter
1891 0, // USHORT usReserved;
1892 BRA_BOTTOMUP, // USHORT usRecording;
1893 // scan lines are bottom to top (default)
1894
1895 BRH_NOTHALFTONED, // USHORT usRendering;
1896 // other algorithms aren't documented anyway
1897 0, // ULONG cSize1;
1898 // parameter for halftoning (undocumented anyway)
1899 0, // ULONG cSize2;
1900 // parameter for halftoning (undocumented anyway)
1901 BCE_RGB, // ULONG ulColorEncoding;
1902 // only possible value
1903 0 // ULONG ulIdentifier;
1904 // application-specific data
1905 };
1906
1907/*
1908 *@@ gpihCreateBitmap2:
1909 * creates a new bitmap for a given memory PS.
1910 * This bitmap will have the cPlanes and bitcount
1911 * which are found in the memory PS.
1912 * For all the mysterious other values, we use
1913 * fixed default values, this doesn't seem to hurt.
1914 *
1915 * Note that the bitmap is _not_ yet selected into
1916 * the specified memory PS. You must call
1917 + GpiSetBitmap(hpsMem, hbm)
1918 * to do this.
1919 *
1920 * Returns the bitmap handle or NULLHANDLE upon errors.
1921 *
1922 *@@added V0.9.16 (2001-12-18) [umoeller]
1923 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
1924 */
1925
1926HBITMAP gpihCreateBitmap2(HPS hpsMem, // in: memory DC
1927 ULONG cx, // in: width of new bitmap
1928 ULONG cy, // in: height of new bitmap
1929 ULONG cPlanes, // in: color planes (usually 1); if 0, current screen is used
1930 ULONG cBitCount) // in: either 1, 4, or 24; if 0, current screen value
1931{
1932 LONG alData[2];
1933 BITMAPINFOHEADER2 bih2;
1934
1935 // determine the device's plane/bit-count format;
1936 // alData[0] then has cPlanes,
1937 // alData[1] has cBitCount
1938 if (GpiQueryDeviceBitmapFormats(hpsMem, 2, alData))
1939 {
1940 // copy values from global template V1.0.1 (2002-11-30) [umoeller]
1941 memcpy(&bih2, &G_bih2Template, sizeof(bih2));
1942
1943 // fix variable fields V1.0.1 (2002-11-30) [umoeller]
1944 bih2.cx = cx;
1945 bih2.cy = cy;
1946 bih2.cPlanes = (cPlanes) ? cPlanes : alData[0];
1947 bih2.cBitCount = (cBitCount) ? cBitCount : alData[1];
1948 bih2.cbImage = ( ( (bih2.cx
1949 * (1 << bih2.cPlanes)
1950 * (1 << bih2.cBitCount)
1951 ) + 31
1952 ) / 32
1953 ) * bih2.cy;
1954
1955 // create a bit map that is compatible with the display
1956 return GpiCreateBitmap(hpsMem,
1957 &bih2,
1958 0, // do not initialize
1959 NULL, // init data
1960 NULL);
1961 }
1962
1963 return NULLHANDLE;
1964}
1965
1966/*
1967 *@@ gpihQueryBitmapSize:
1968 * returns the width and height of the given bitmap.
1969 *
1970 *@@added V1.0.1 (2002-11-30) [umoeller]
1971 */
1972
1973BOOL gpihQueryBitmapSize(HBITMAP hbm, // in: bitmap handle for query
1974 PSIZEL pszl) // out: size (cx, cy) of bitmap
1975{
1976 BITMAPINFOHEADER2 bmi2;
1977 // query bitmap info
1978 bmi2.cbFix = sizeof(bmi2);
1979 if ( (hbm)
1980 && (GpiQueryBitmapInfoHeader(hbm, &bmi2))
1981 )
1982 {
1983 pszl->cx = bmi2.cx;
1984 pszl->cy = bmi2.cy;
1985 return TRUE;
1986 }
1987
1988 return FALSE;
1989}
1990
1991/*
1992 *@@ gpihCreateHalftonedBitmap:
1993 * this creates a half-toned copy of the
1994 * input bitmap by doing the following:
1995 *
1996 * 1) create a new bitmap with the size of hbmSource;
1997 * 2) copy hbmSource to the new bitmap (using GpiWCBitBlt);
1998 * 3) overpaint every other pixel with lColorGray.
1999 *
2000 * Note that the memory DC is switched to RGB mode, so
2001 * lColorGray better be an RGB color.
2002 *
2003 * Note: hbmSource must _not_ be selected into any device
2004 * context, or otherwise GpiWCBitBlt will fail.
2005 *
2006 * This returns the new bitmap or NULLHANDLE upon errors.
2007 *
2008 *@added V0.9.0
2009 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
2010 */
2011
2012HBITMAP gpihCreateHalftonedBitmap(HAB hab, // in: anchor block
2013 HBITMAP hbmSource, // in: source bitmap
2014 LONG lColorGray) // in: color used for gray
2015{
2016 HBITMAP hbmReturn = NULLHANDLE;
2017
2018 HDC hdcMem;
2019 HPS hpsMem;
2020 SIZEL szlPage;
2021
2022 if (gpihQueryBitmapSize(hbmSource, &szlPage)) // V1.0.1 (2002-11-30) [umoeller]
2023 {
2024 if (gpihCreateMemPS(hab, &szlPage, &hdcMem, &hpsMem))
2025 {
2026 if ((hbmReturn = gpihCreateBitmap(hpsMem,
2027 szlPage.cx,
2028 szlPage.cy)))
2029 {
2030 if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
2031 {
2032 POINTL aptl[4];
2033
2034 // step 1: copy bitmap
2035 memset(aptl, 0, sizeof(POINTL) * 4);
2036 // aptl[0]: target bottom-left, is all 0
2037 // aptl[1]: target top-right (inclusive!)
2038 aptl[1].x = szlPage.cx - 1;
2039 aptl[1].y = szlPage.cy - 1;
2040 // aptl[2]: source bottom-left, is all 0
2041
2042 // aptl[3]: source top-right (exclusive!)
2043 aptl[3].x = szlPage.cx;
2044 aptl[3].y = szlPage.cy;
2045 GpiWCBitBlt(hpsMem, // target HPS (bmp selected)
2046 hbmSource,
2047 4L, // must always be 4
2048 &aptl[0], // points array
2049 ROP_SRCCOPY,
2050 BBO_IGNORE);
2051 // doesn't matter here, because we're not stretching
2052
2053 // step 2: overpaint bitmap
2054 // with half-toned pattern
2055
2056 gpihSwitchToRGB(hpsMem);
2057
2058 GpiMove(hpsMem, &aptl[0]); // still 0, 0
2059 aptl[0].x = szlPage.cx - 1;
2060 aptl[0].y = szlPage.cy - 1;
2061 GpiSetColor(hpsMem, lColorGray);
2062 GpiSetPattern(hpsMem, PATSYM_HALFTONE);
2063 GpiBox(hpsMem,
2064 DRO_FILL, // interior only
2065 &aptl[0],
2066 0, 0); // no corner rounding
2067
2068 // unselect bitmap
2069 GpiSetBitmap(hpsMem, NULLHANDLE);
2070 } // end if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
2071 else
2072 {
2073 // error selecting bitmap:
2074 GpiDeleteBitmap(hbmReturn);
2075 hbmReturn = NULLHANDLE;
2076 }
2077 } // end if (hbmReturn = gpihCreateBitmap...
2078
2079 GpiDestroyPS(hpsMem);
2080 DevCloseDC(hdcMem);
2081 } // end if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
2082 } // end if (hbmSource)
2083
2084 return hbmReturn;
2085}
2086
2087/*
2088typedef struct _BITMAPARRAYFILEHEADER {
2089 USHORT usType; // Type of structure.
2090 ULONG cbSize; // Size of the BITMAPARRAYFILEHEADER structure in bytes.
2091 ULONG offNext; // Offset of the next BITMAPARRAYFILEHEADER structure from the start of the file.
2092 USHORT cxDisplay; // Device width, in pels.
2093 USHORT cyDisplay; // Device height, in pels.
2094 BITMAPFILEHEADER bfh; // Bitmap file header structure.
2095} BITMAPARRAYFILEHEADER;
2096
2097typedef struct _BITMAPARRAYFILEHEADER2 {
2098 USHORT usType; // Type of structure.
2099 ULONG cbSize; // Size of the BITMAPARRAYFILEHEADER2 structure in bytes.
2100 ULONG offNext; // Offset of the next BITMAPARRAYFILEHEADER2 structure from the start of the file.
2101 USHORT cxDisplay; // Device width, in pels.
2102 USHORT cyDisplay; // Device height, in pels.
2103 BITMAPFILEHEADER2 bfh2; // Bitmap file header structure.
2104} BITMAPARRAYFILEHEADER2;
2105
2106These two are binarily the same, except for the file header that is contained.
2107*/
2108
2109/* OLD FORMAT
2110
2111typedef struct _BITMAPFILEHEADER {
2112 USHORT usType; // Type of resource the file contains.
2113 ULONG cbSize; // Size of the BITMAPFILEHEADER structure in bytes.
2114 SHORT xHotspot; // Width of hotspot for icons and pointers.
2115 SHORT yHotspot; // Height of hotspot for icons and pointers.
2116 USHORT offBits; // Offset in bytes.
2117 BITMAPINFOHEADER bmp; // Bitmap information header structure.
2118
2119 typedef struct _BITMAPINFOHEADER {
2120 ULONG cbFix; // Length of structure.
2121 USHORT cx; // Bitmap width in pels.
2122 USHORT cy; // Bitmap height in pels.
2123 USHORT cPlanes; // Number of bit planes.
2124 USHORT cBitCount; // Number of bits per pel within a plane.
2125 } BITMAPINFOHEADER;
2126
2127} BITMAPFILEHEADER;
2128*/
2129
2130/* NEW FORMAT
2131
2132typedef struct _BITMAPFILEHEADER2 {
2133 USHORT usType; // Type of resource the file contains.
2134 ULONG cbSize; // Size of the BITMAPFILEHEADER2 structure in bytes.
2135 SHORT xHotspot; // Width of hotspot for icons and pointers.
2136 SHORT yHotspot; // Height of hotspot for icons and pointers.
2137 USHORT offBits; // Offset in bytes.
2138 BITMAPINFOHEADER2 bmp2; // Bitmap information header structure.
2139
2140 typedef struct _BITMAPINFOHEADER2 {
2141 ULONG cbFix; // Length of structure.
2142 ULONG cx; // Bitmap width in pels.
2143 ULONG cy; // Bitmap height in pels.
2144 USHORT cPlanes; // Number of bit planes.
2145 USHORT cBitCount; // Number of bits per pel within a plane.
2146 ULONG ulCompression; // Compression scheme used to store the bit map.
2147 ULONG cbImage; // Length of bitmap storage data, in bytes.
2148 ULONG cxResolution; // Horizontal component of the resolution of target device.
2149 ULONG cyResolution; // Vertical component of the resolution of target device.
2150 ULONG cclrUsed; // Number of color indexes used.
2151 ULONG cclrImportant; // Minimum number of color indexes for satisfactory appearance of the bit map.
2152 USHORT usUnits; // Units of measure.
2153 USHORT usReserved; // Reserved.
2154 USHORT usRecording; // Recording algorithm.
2155 USHORT usRendering; // Halftoning algorithm.
2156 ULONG cSize1; // Size value 1.
2157 ULONG cSize2; // Size value 2.
2158 ULONG ulColorEncoding; // Color encoding.
2159 ULONG ulIdentifier; // Reserved for application use.
2160 } BITMAPINFOHEADER2;
2161
2162 Because of the unfortunate replacement of USHORTs with ULONGs for
2163 cx and cy in the info header, the cx, cy, and cBitCount data is
2164 NOT the same between old and new formats. Well, IBM, good job.
2165 And ICONEDIT _still_ writes the old format, too.
2166
2167} BITMAPFILEHEADER2;
2168
2169*/
2170
2171/*
2172 *@@ gpihCreateBitmapFromFile:
2173 * creates a new HBITMAP from the given bitmap data,
2174 * which is assumed to be in a standard OS/2 1.3 or
2175 * 2.0 bitmap file format. That is, it should start
2176 * with either a BITMAPFILEHEADER or BITMAPFILEHEADER2
2177 * (if single bitmap) or a BITMAPARRAYFILEHEADER or
2178 * BITMAPARRAYFILEHEADER2 (if bitmap array) and all
2179 * the bitmap bits following. This format is also
2180 * used by bitmap resources.
2181 *
2182 * If the file contains only a single bitmap,
2183 * this bitmap is used.
2184 *
2185 * If it contains a bitmap array, we use the
2186 * "best bitmap" in the array, which is determined
2187 * from the following criteria (in this order):
2188 *
2189 * -- a device-dependent bitmap, if its device
2190 * resolution is not too large and the given
2191 * HPS can display all its colors;
2192 *
2193 * -- a device-dependent bitmap, if its device
2194 * resolution is not too large, even if the
2195 * given HPS cannot display all its colors;
2196 *
2197 * -- a device-independent bitmap, if the given
2198 * HPS can display all its colors;
2199 *
2200 * -- the first device-independent bitmap in
2201 * the file;
2202 *
2203 * -- the first bitmap in the file.
2204 *
2205 * Support for bitmap arrays was added with V0.9.19.
2206 * I'm not quite sure if the above is the same way
2207 * of selecting the "best bitmap" that GpiLoadBitmap
2208 * would do, but without any documentation, who is
2209 * supposed to know. It appears to me however that
2210 * GpiLoadBitmap always does an _exact_ match of
2211 * device resolutions (in contrast to this function).
2212 * In other words, if you're running at 1152x864
2213 * and the device-dependent bitmap is for 1024x768,
2214 * _this_ function would use it, while GpiLoadBitmap
2215 * might load a device-independent VGA bitmap
2216 * instead. (Looks broken, if you ask me.)
2217 *
2218 * Returns:
2219 *
2220 * -- NO_ERROR
2221 *
2222 * -- ERROR_INVALID_DATA: we can't understand this format.
2223 *
2224 *@@added V0.9.20 (2002-07-19) [umoeller]
2225 */
2226
2227APIRET gpihCreateBitmapFromFile(HBITMAP *phbm, // out: bitmap if NO_ERROR
2228 HPS hps, // in: HPS for bmp
2229 PBYTE pData) // in: bitmap data
2230{
2231 APIRET arc = NO_ERROR;
2232
2233 PBITMAPFILEHEADER2 pbfh = (PBITMAPFILEHEADER2)pData;
2234
2235 // check bitmap magic codes
2236 switch (pbfh->usType)
2237 {
2238 case BFT_BMAP: // "BM"
2239 // single bitmap in file (no array):
2240 if (!(*phbm = GpiCreateBitmap(hps,
2241 &pbfh->bmp2,
2242 CBM_INIT,
2243 (PBYTE)pbfh + pbfh->offBits,
2244 (PBITMAPINFO2)&pbfh->bmp2)))
2245 arc = ERROR_INVALID_DATA;
2246 break;
2247
2248 case BFT_BITMAPARRAY: // "BA"
2249 {
2250 // define a handy union for all the above crap
2251 typedef union
2252 {
2253 BITMAPFILEHEADER Old;
2254 BITMAPFILEHEADER2 New;
2255 } BMPUNION, *PBMPUNION;
2256
2257 PBMPUNION
2258 puFirstDI = NULL, // first device-independent bitmap
2259 puBestDI = NULL, // best device-independent bitmap
2260 puFirstDD = NULL, // first device-dependent bitmap
2261 puBestDD = NULL, // best device-dependent bitmap
2262 puFirstAny = NULL, // first bitmap of any type
2263 puUse;
2264
2265 // get device resolution for this HPS
2266 // so we can select the "best bitmap"
2267 #define GET_CAPS_FIRST CAPS_WIDTH
2268 #define GET_CAPS_LAST CAPS_COLOR_BITCOUNT
2269 #define GET_NO_CAPS GET_CAPS_LAST - GET_CAPS_FIRST + 1
2270
2271 LONG alCaps[GET_NO_CAPS];
2272 PBITMAPARRAYFILEHEADER2 pba = (PBITMAPARRAYFILEHEADER2)pData;
2273
2274 DevQueryCaps(GpiQueryDevice(hps),
2275 GET_CAPS_FIRST,
2276 GET_NO_CAPS,
2277 alCaps);
2278
2279 #define BITCOUNT alCaps[CAPS_COLOR_BITCOUNT - GET_CAPS_FIRST]
2280 #define WIDTH alCaps[CAPS_WIDTH - GET_CAPS_FIRST]
2281 #define HEIGHT alCaps[CAPS_HEIGHT - GET_CAPS_FIRST]
2282
2283 // for-all-bitmaps-in-array loop
2284 while (pba)
2285 {
2286 PBMPUNION puThis = (PBMPUNION)&pba->bfh2;
2287
2288 LONG cx = 0,
2289 cy,
2290 cBitCount;
2291
2292 // ignore this if the type isn't "BM"
2293 if (puThis->Old.usType == BFT_BMAP)
2294 {
2295 // fill the three, but watch out, the offsets are
2296 // different between old and new formats
2297 if (puThis->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER))
2298 {
2299 // old format:
2300 cx = puThis->Old.bmp.cx;
2301 cy = puThis->Old.bmp.cy;
2302 cBitCount = puThis->Old.bmp.cBitCount;
2303 }
2304 else if (puThis->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER2))
2305 {
2306 // new format:
2307 cx = puThis->New.bmp2.cx;
2308 cy = puThis->New.bmp2.cy;
2309 cBitCount = puThis->New.bmp2.cBitCount;
2310 }
2311 }
2312
2313 if (cx)
2314 {
2315 // remember the first bitmap from all that we see
2316 if (!puFirstAny)
2317 puFirstAny = puThis;
2318
2319 // check device resolution... device-independent
2320 // one has cxDisplay and cyDisplay set to 0
2321 if ( (!pba->cxDisplay)
2322 && (!pba->cyDisplay)
2323 )
2324 {
2325 // device-independent:
2326
2327 // remember the first device-independent bitmap
2328 if (!puFirstDI)
2329 puFirstDI = puThis;
2330
2331 if (cBitCount <= BITCOUNT)
2332 // we can display all the colors:
2333 puBestDI = puThis;
2334 }
2335 else
2336 {
2337 // device-dependent:
2338 // ignore if device resolution is too large
2339 if ( (pba->cxDisplay <= WIDTH)
2340 && (pba->cyDisplay <= HEIGHT)
2341 )
2342 {
2343 // remember first matching device-dependent bitmap
2344 if (!puFirstDD)
2345 puFirstDD = puThis;
2346
2347 if (cBitCount <= BITCOUNT)
2348 puBestDD = puThis;
2349 }
2350 }
2351 } // end if cx
2352
2353 // go for next bmp in array
2354 if (pba->offNext)
2355 // another one coming up:
2356 // this ofs is from the beginning of the file
2357 pba = (PBITMAPARRAYFILEHEADER2)(pData + pba->offNext);
2358 else
2359 // no more bitmaps:
2360 break;
2361 } // end while (pba)
2362
2363 if ( (puUse = puBestDD)
2364 || (puUse = puFirstDD)
2365 || (puUse = puBestDI)
2366 || (puUse = puFirstDI)
2367 || (puUse = puFirstAny)
2368 )
2369 {
2370 PBITMAPINFOHEADER2 pbih2;
2371 PBYTE pbInitData;
2372
2373 if (puUse->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER))
2374 {
2375 // old format:
2376 pbih2 = (PBITMAPINFOHEADER2)&puUse->Old.bmp;
2377 pbInitData = (PBYTE)pData + puUse->Old.offBits;
2378 }
2379 else
2380 {
2381 // new format:
2382 pbih2 = &puUse->New.bmp2;
2383 pbInitData = (PBYTE)pData + puUse->New.offBits;
2384 }
2385
2386 if (!(*phbm = GpiCreateBitmap(hps,
2387 pbih2,
2388 CBM_INIT,
2389 pbInitData,
2390 (PBITMAPINFO2)pbih2)))
2391 arc = ERROR_INVALID_DATA;
2392 }
2393 else
2394 arc = ERROR_INVALID_DATA;
2395 }
2396 break;
2397 }
2398
2399 return arc;
2400}
2401
2402/*
2403 *@@ gpihLoadBitmap:
2404 * creates a new HBITMAP from the given resource.
2405 *
2406 * This is a replacement for GpiLoadBitmap which handles
2407 * device-dependent bitmaps in bitmap arrays in a
2408 * non-brain-dead way.
2409 *
2410 * Calls gpihCreateBitmapFromFile for handling the resource
2411 * data. See remarks there for how we select a bitmap from
2412 * bitmap arrays, which is _different_ from GpiLoadBitmap.
2413 *
2414 * Returns:
2415 *
2416 * -- NO_ERROR: *phbm has received new HBITMAP,
2417 * to be freed with GpiDeleteBitmap.
2418 *
2419 * -- ERROR_INVALID_DATA: resource exists, but we
2420 * can't understand its format.
2421 *
2422 * plus the error codes from DosGetResource.
2423 *
2424 *@@added V0.9.20 (2002-07-19) [umoeller]
2425 */
2426
2427APIRET gpihLoadBitmap(HBITMAP *phbm, // out: bitmap if NO_ERROR
2428 HPS hps, // in: HPS for bmp
2429 HMODULE hmodResource, // in: module to load bitmap from
2430 ULONG idBitmap) // in: resource ID for bitmap
2431{
2432 APIRET arc;
2433
2434 PBYTE pbData;
2435
2436 if (!(arc = DosGetResource(hmodResource,
2437 RT_BITMAP,
2438 idBitmap,
2439 (PVOID*)&pbData)))
2440 {
2441 arc = gpihCreateBitmapFromFile(phbm,
2442 hps,
2443 pbData);
2444
2445 DosFreeResource(pbData);
2446 }
2447
2448 return arc;
2449}
2450
2451/*
2452 *@@ gpihLoadBitmapFile:
2453 * this loads the specified bitmap file into
2454 * the given HPS. Note that the bitmap is _not_
2455 * yet selected into the HPS.
2456 *
2457 * Calls gpihCreateBitmapFromFile for handling the resource
2458 * data. See remarks there for how we select a bitmap from
2459 * bitmap arrays, which is _different_ from GpiLoadBitmap.
2460 *
2461 * Returns:
2462 *
2463 * -- NO_ERROR: *phbm has received new HBITMAP,
2464 * to be freed with GpiDeleteBitmap.
2465 *
2466 * -- ERROR_INVALID_PARAMETER
2467 *
2468 * -- ERROR_INVALID_DATA: file exists, but we
2469 * can't understand its format.
2470 *
2471 * plus the error codes from doshOpen and DosRead.
2472 *
2473 *@@changed V0.9.4 (2000-08-03) [umoeller]: this didn't return NULLHANDLE on errors
2474 *@@changed V0.9.19 (2002-04-14) [umoeller]: rewritten to support bitmap arrays, prototype changed
2475 *@@changed V0.9.20 (2002-07-19) [umoeller]: extracted bitmap selection into gpihCreateBitmapFromFile
2476 */
2477
2478APIRET gpihLoadBitmapFile(HBITMAP *phbm, // out: bitmap if NO_ERROR
2479 HPS hps, // in: HPS for bmp
2480 PCSZ pcszBmpFile) // in: bitmap filename
2481{
2482 APIRET arc;
2483 PXFILE pFile;
2484 ULONG cbFile = 0;
2485
2486 if (!hps || !pcszBmpFile || !phbm)
2487 return ERROR_INVALID_PARAMETER;
2488
2489 if (!(arc = doshOpen(pcszBmpFile,
2490 XOPEN_READ_EXISTING | XOPEN_BINARY,
2491 &cbFile,
2492 &pFile)))
2493 {
2494 PBYTE pData;
2495 if (!(pData = (PBYTE)malloc(cbFile)))
2496 arc = ERROR_NOT_ENOUGH_MEMORY;
2497 else
2498 {
2499 // read in the ENTIRE file
2500 if (!(arc = DosRead(pFile->hf,
2501 pData,
2502 cbFile,
2503 &cbFile)))
2504 // extracted all the code into this extra func
2505 // V0.9.20 (2002-07-19) [umoeller]
2506 arc = gpihCreateBitmapFromFile(phbm,
2507 hps,
2508 (PBYTE)pData);
2509
2510 free(pData);
2511 }
2512
2513 doshClose(&pFile);
2514 }
2515
2516 return arc;
2517}
2518
2519/*
2520 *@@ gpihStretchBitmap:
2521 * this copies hbmSource to the bitmap selected
2522 * into hpsTarget, which must be a memory PS.
2523 *
2524 * The source size is the whole size of hbmSource,
2525 * the target size is specified in prclTarget
2526 * (which is exclusive, meaning that the top right
2527 * corner of that rectangle lies _outside_ the target).
2528 *
2529 * This uses GpiWCBitBlt to stretch the bitmap.
2530 * hbmSource therefore must _not_ be selected
2531 * into any presentation space, or GpiWCBitBlt will
2532 * fail.
2533 *
2534 * If (fPropotional == TRUE), the target size is
2535 * modified so that the proportions of the bitmap
2536 * are preserved. The bitmap data will then be
2537 * copied to a subrectangle of the target bitmap:
2538 * there will be extra space either to the left
2539 * and right of the bitmap data or to the bottom
2540 * and top.
2541 * The outside areas of the target bitmap are
2542 * not changed then, so you might want to fill
2543 * the bitmap with some color first.
2544 *
2545 * This returns the return value of GpiWCBitBlt,
2546 * which can be:
2547 * -- GPI_OK
2548 * -- GPI_HITS: correlate hits
2549 * -- GPI_ERROR: error occured (probably either hbmSource not free
2550 * or no bitmap selected into hpsTarget)
2551 *
2552 *@added V0.9.0
2553 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
2554 */
2555
2556LONG gpihStretchBitmap(HPS hpsTarget, // in: memory PS to copy bitmap to
2557 HBITMAP hbmSource, // in: bitmap to be copied into hpsTarget (must be free)
2558 PRECTL prclSource, // in: source rectangle -- if NULL, use size of source bmp
2559 PRECTL prclTarget, // in: target rectangle (required)
2560 BOOL fProportional) // in: preserve proportions when stretching?
2561{
2562 SIZEL szlBmp;
2563 POINTL aptl[4];
2564 BOOL fCalculated = FALSE;
2565
2566 if (!gpihQueryBitmapSize(hbmSource, &szlBmp)) // V1.0.1 (2002-11-30) [umoeller]
2567 return GPI_ERROR;
2568
2569 memset(aptl, 0, sizeof(POINTL) * 4);
2570
2571 // aptl[2]: source bottom-left, is all 0
2572 // aptl[3]: source top-right (exclusive!)
2573 aptl[3].x = szlBmp.cx;
2574 aptl[3].y = szlBmp.cy;
2575
2576 if (fProportional)
2577 {
2578 // proportional mode:
2579
2580 // 1) find out whether cx or cy is too
2581 // large
2582
2583 ULONG ulPropSource = (szlBmp.cx * 1000)
2584 / szlBmp.cy;
2585 // e.g. if the bmp is 200 x 100, we now have 2000
2586 ULONG ulPropTarget = ((prclTarget->xRight - prclTarget->xLeft) * 1000)
2587 / (prclTarget->yTop - prclTarget->yBottom);
2588 // case 1: if prclTarget is 300 x 100, we now have 3000 (> ulPropSource)
2589 // case 2: if prclTarget is 150 x 100, we now have 1500 (< ulPropSource)
2590
2591 // case 1:
2592 if (ulPropTarget > ulPropSource)
2593 {
2594 // prclTarget is too wide (horizontally):
2595 // decrease width, keep height
2596
2597 ULONG cx = (prclTarget->xRight - prclTarget->xLeft);
2598 ULONG cxNew = (cx * ulPropSource) / ulPropTarget;
2599
2600 // aptl[0]: target bottom-left
2601 // move left right (towards center)
2602 aptl[0].x = prclTarget->xLeft + ((cx - cxNew) / 2);
2603 aptl[0].y = prclTarget->yBottom;
2604
2605 // aptl[1]: target top-right (inclusive!)
2606 aptl[1].x = aptl[0].x + cxNew;
2607 aptl[1].y = prclTarget->yTop;
2608
2609 fCalculated = TRUE;
2610 }
2611 else
2612 {
2613 // prclTarget is too high (vertically):
2614 // keep width, decrease height
2615
2616 ULONG cy = (prclTarget->yTop - prclTarget->yBottom);
2617 ULONG cyNew = (cy * ulPropTarget) / ulPropSource;
2618
2619 // aptl[0]: target bottom-left
2620 aptl[0].x = prclTarget->xLeft;
2621 // move bottom up (towards center)
2622 aptl[0].y = prclTarget->yBottom + ((cy - cyNew) / 2);
2623
2624 // aptl[1]: target top-right (inclusive!)
2625 aptl[1].x = prclTarget->xRight;
2626 aptl[1].y = aptl[0].y + cyNew;
2627 // (prclTarget->yTop * ulPropSource) / ulPropTarget;
2628
2629 fCalculated = TRUE;
2630 }
2631 } // end if (pa->ulFlags & ANF_PROPORTIONAL)
2632
2633 if (!fCalculated)
2634 {
2635 // non-proportional mode or equal proportions:
2636 // stretch to whole size of prclTarget
2637
2638 // aptl[0]: target bottom-left
2639 aptl[0].x = prclTarget->xLeft;
2640 aptl[0].y = prclTarget->yBottom;
2641 // aptl[1]: target top-right (inclusive!)
2642 aptl[1].x = prclTarget->xRight;
2643 aptl[1].y = prclTarget->yTop;
2644 }
2645
2646 return GpiWCBitBlt(hpsTarget, // target HPS (bmp selected)
2647 hbmSource,
2648 4L, // must always be 4
2649 &aptl[0], // points array
2650 ROP_SRCCOPY,
2651 BBO_IGNORE);
2652 // ignore eliminated rows or
2653 // columns; useful for color
2654}
2655
2656/*
2657 *@@ gpihDrawPointer:
2658 * replacement for WinDrawPointer that can do clipping.
2659 *
2660 * Normally, to do clipping with WinDrawPointer, one
2661 * would have to alter the clip rectangle for the current
2662 * HPS, which requires creating regions and is thus quite
2663 * expensive.
2664 *
2665 * Instead, this function allows for specifying a clip
2666 * rectangle directly. It blits the icon bitmaps directly
2667 * without calling WinDrawPointer.
2668 * Besides, since it uses GpiWCBitBlt, it should probably
2669 * work with all types of device contexts.
2670 *
2671 * This also replaces gpihIcon2Bitmap, which wasn't quite
2672 * working in the first place and couldn't to clipping
2673 * either.
2674 *
2675 * If you don't need clipping and are drawing to the
2676 * screen only, this function has no advantage over
2677 * WinDrawPointer because it's presumably a bit slower.
2678 *
2679 * Flags presently supported in fl:
2680 *
2681 * -- DP_MINI (not DP_MINIICON, as stated in PMREF):
2682 * use mini-icon.
2683 *
2684 * -- DP_HALFTONED (V0.9.20)
2685 *
2686 * Preconditions:
2687 *
2688 * -- The hps is assumed to be in RGB mode.
2689 *
2690 * Post conditions:
2691 *
2692 * -- This uses GpiSet(Back)Color, so the foreground
2693 * and background colors are undefined after the
2694 * call.
2695 *
2696 *@@added V0.9.19 (2002-06-18) [umoeller]
2697 *@@changed V0.9.20 (2002-07-31) [umoeller]: optimized, saved one GpiQueryBitmapInfoHeader
2698 *@@changed V0.9.20 (2002-08-04) [umoeller]: added DP_HALFTONED
2699 *@@changed V1.0.1 (2002-11-30) [umoeller]: optimized
2700 */
2701
2702BOOL gpihDrawPointer(HPS hps, // in: target presentation space
2703 LONG x, // in: lower left target position of icon
2704 LONG y, // in: lower left target position of icon
2705 HPOINTER hptr, // in: icon to be drawn
2706 PSIZEL pszlIcon, // in: icon size (req., should be sysvalues SV_CXICON, SV_CYICON always)
2707 PRECTL prclClip, // in: clip rectangle (inclusive!) or NULL
2708 ULONG fl) // in: DP_* flags
2709{
2710 POINTERINFO pi;
2711
2712 if ( (pszlIcon)
2713 && (hptr)
2714 && (WinQueryPointerInfo(hptr, &pi))
2715 )
2716 {
2717 POINTL aptl[4];
2718 HBITMAP hbmThis;
2719 SIZEL szlAndXor, // V1.0.1 (2002-11-30) [umoeller]
2720 szlColor;
2721
2722 // A HPOINTER really consists of two bitmaps,
2723 // one monochrome bitmap that has twice the icon
2724 // height and contains an AND mask in the upper
2725 // half and an XOR mask in the lower half, and
2726 // a (probably color) bitmap with the regular
2727 // icon height.
2728
2729 // Drawing the icon means (1) blitting the AND
2730 // mask with ROP_SRCAND, (2) blitting the color
2731 // bitmap with ROP_SRCPAINT, (3) blitting the
2732 // XOR mask with ROP_SRCINVERT. Hence the slightly
2733 // complicated code that follows.
2734
2735 /*
2736 * 0) preparations
2737 */
2738
2739 // set up a bunch of variables that are used
2740 // by the below calculations. We use cx|yIcon
2741 // to quickly get the system icon dimensions
2742 // and set up the clip offsets here too,
2743 // if a clip rectangle is specified.
2744
2745 LONG cxIcon = pszlIcon->cx,
2746 cyIcon = pszlIcon->cy,
2747 cySrc,
2748 xRight,
2749 yTop,
2750 // clip "rectangle"... this is not really a
2751 // recangle because it specifies _offsets_
2752 // towards the center of the icon to be
2753 // clipped
2754 lClipLeft = 0,
2755 lClipBottom = 0,
2756 lClipRight = 0,
2757 lClipTop = 0;
2758
2759 BOOL fMini;
2760
2761 if (fMini = !!(fl & DP_MINI))
2762 {
2763 cxIcon /= 2;
2764 cyIcon /= 2;
2765 }
2766
2767 // target top right (inclusive)
2768 xRight = x + cxIcon - 1;
2769 yTop = y + cyIcon - 1;
2770
2771 if (prclClip)
2772 {
2773 // we have a clip rectangle:
2774 // set up the clip offsets that are used
2775 // in both the target and source coordinates
2776 // for blitting
2777 if (x < prclClip->xLeft)
2778 lClipLeft = prclClip->xLeft - x;
2779 if (xRight > prclClip->xRight)
2780 lClipRight = xRight - prclClip->xRight;
2781 if (y < prclClip->yBottom)
2782 lClipBottom = prclClip->yBottom - y;
2783 if (yTop > prclClip->yTop)
2784 lClipTop = yTop - prclClip->yTop;
2785 }
2786
2787 // set up target coordinates that are constant
2788 // for all the three blits
2789
2790 // aptl[0]: target bottom-left
2791 aptl[0].x = x + lClipLeft;
2792 aptl[0].y = y + lClipBottom;
2793
2794 // aptl[1]: target top-right (inclusive!)
2795 aptl[1].x = xRight - lClipRight;
2796 aptl[1].y = yTop - lClipTop;
2797
2798 if ( (aptl[0].x < aptl[1].x)
2799 && (aptl[0].y < aptl[1].y)
2800 )
2801 {
2802 LONG lPatternOld = -1; // mark as "not changed" for now
2803 LONG lAndROP = ROP_SRCAND, // 0x0088 = 10001000
2804 lPaintROP = ROP_SRCPAINT, // 0x00EE = 11101110
2805 lInvertROP = ROP_SRCINVERT; // 0x0066 = 01100110
2806
2807 // colors are constant too
2808 GpiSetColor(hps, RGBCOL_WHITE);
2809 GpiSetBackColor(hps, RGBCOL_BLACK);
2810
2811 if (fl & DP_HALFTONED) // V0.9.20 (2002-08-04) [umoeller]
2812 {
2813 lPatternOld = GpiQueryPattern(hps);
2814 GpiSetPattern(hps, PATSYM_HALFTONE);
2815
2816 lAndROP = 0x00A8; // 10101000
2817 lInvertROP = 0x00A6; // 10100110
2818 }
2819
2820 /*
2821 * 1) work on the AND image
2822 * (upper part of the monochrome image)
2823 */
2824
2825 if ( ( (fMini)
2826 && (hbmThis = pi.hbmMiniPointer)
2827 )
2828 || (hbmThis = pi.hbmPointer)
2829 )
2830 {
2831 gpihQueryBitmapSize(hbmThis, &szlAndXor); // V1.0.1 (2002-11-30) [umoeller]
2832
2833 // use only half the bitmap height
2834 cySrc = szlAndXor.cy / 2;
2835
2836 // aptl[2]: source bottom-left
2837 aptl[2].x = 0
2838 + lClipLeft * szlAndXor.cx / cxIcon;
2839 aptl[2].y = cySrc
2840 + lClipBottom * cySrc / cyIcon;
2841
2842 // aptl[3]: source top-right (exclusive!)
2843 aptl[3].x = szlAndXor.cx
2844 - lClipRight * szlAndXor.cx / cxIcon;
2845 aptl[3].y = szlAndXor.cy
2846 - lClipTop * cySrc / cyIcon;
2847
2848 GpiWCBitBlt(hps, // target
2849 hbmThis, // src bmp
2850 4L, // must always be 4
2851 aptl, // point array
2852 lAndROP, // ROP_SRCAND, // source AND target
2853 BBO_IGNORE);
2854 }
2855
2856 /*
2857 * 2) paint the color image; the parts that
2858 * are to be transparent are black
2859 */
2860
2861 if ( ( (fMini)
2862 && (hbmThis = pi.hbmMiniColor)
2863 )
2864 || (hbmThis = pi.hbmColor)
2865 )
2866 {
2867 gpihQueryBitmapSize(hbmThis, &szlColor); // V1.0.1 (2002-11-30) [umoeller]
2868
2869 // aptl[2]: source bottom-left
2870 aptl[2].x = 0
2871 + lClipLeft * szlColor.cx / cxIcon;
2872 aptl[2].y = 0
2873 + lClipBottom * szlColor.cy / cyIcon;
2874
2875 // aptl[3]: source top-right (exclusive!)
2876 aptl[3].x = szlColor.cx
2877 - lClipRight * szlColor.cx / cxIcon;
2878 aptl[3].y = szlColor.cy
2879 - lClipTop * szlColor.cy / cyIcon;
2880
2881 GpiWCBitBlt(hps, // target
2882 hbmThis, // src bmp
2883 4L, // must always be 4
2884 aptl, // point array
2885 lPaintROP, // ROP_SRCPAINT,
2886 BBO_IGNORE);
2887 }
2888
2889 /*
2890 * 3) work on the XOR image:
2891 * (lower part of monochrome bitmap)
2892 */
2893
2894 if ( ( (fMini)
2895 && (hbmThis = pi.hbmMiniPointer)
2896 )
2897 || (hbmThis = pi.hbmPointer)
2898 )
2899 {
2900 // we queried the size of this one above V0.9.20 (2002-07-31) [umoeller]
2901
2902 // use only half the bitmap height
2903 cySrc = szlAndXor.cy / 2;
2904
2905 // aptl[2]: source bottom-left
2906 aptl[2].x = 0
2907 + lClipLeft * szlAndXor.cx / cxIcon;
2908 aptl[2].y = 0
2909 + lClipBottom * cySrc / cyIcon;
2910
2911 // aptl[3]: source top-right (exclusive!)
2912 aptl[3].x = szlAndXor.cx
2913 - lClipRight * szlAndXor.cx / cxIcon;
2914 aptl[3].y = cySrc
2915 - lClipTop * cySrc / cyIcon;
2916
2917 GpiWCBitBlt(hps, // target
2918 hbmThis, // src bmp
2919 4L, // must always be 4
2920 aptl, // point array
2921 lInvertROP, // ROP_SRCINVERT, // source XOR target
2922 BBO_IGNORE);
2923 }
2924
2925 // reset old pattern, if changed
2926 if (lPatternOld != -1)
2927 GpiSetPattern(hps, lPatternOld);
2928
2929 return TRUE;
2930 }
2931 }
2932
2933 return FALSE;
2934}
2935
2936/*
2937 *@@category: Helpers\PM helpers\GPI helpers\XBitmaps
2938 * Extended bitmaps. See gpihCreateXBitmap for an introduction.
2939 */
2940
2941/* ******************************************************************
2942 *
2943 * XBitmap functions
2944 *
2945 ********************************************************************/
2946
2947/*
2948 *@@ gpihCreateXBitmap:
2949 * calls gpihCreateXBitmap2 with cPlanes and cBitCount == 0
2950 * for compatibility with exports. Widgets might
2951 * have used this func.
2952 *
2953 *@@added V0.9.12 (2001-05-20) [umoeller]
2954 *@@changed V0.9.16 (2001-12-18) [umoeller]: now using optimized gpihCreateXBitmap2
2955 */
2956
2957PXBITMAP gpihCreateXBitmap(HAB hab, // in: anchor block
2958 LONG cx, // in: bitmap width
2959 LONG cy) // in: bitmap height
2960{
2961 return gpihCreateXBitmap2(hab,
2962 cx,
2963 cy,
2964 0,
2965 0);
2966}
2967
2968/*
2969 *@@ gpihCreateXBitmap:
2970 * creates an XBitmap, which is returned in an
2971 * XBITMAP structure.
2972 *
2973 * The problem with all the GPI bitmap functions
2974 * is that they are quite complex and it is easy
2975 * to forget one of the "disassociate" and "deselect"
2976 * functions, which then simply leads to enormous
2977 * resource leaks in the application.
2978 *
2979 * This function may relieve this a bit. This
2980 * creates a memory DC, a memory PS, and a bitmap,
2981 * and selects the bitmap into the memory PS.
2982 * You can then use any GPI function on the memory
2983 * PS to draw into the bitmap. Use the fields from
2984 * XBITMAP for that.
2985 *
2986 * The bitmap is created in RGB mode.
2987 *
2988 * Use gpihDestroyXBitmap to destroy the XBitmap
2989 * again.
2990 *
2991 * Example:
2992 *
2993 + PXBITMAP pbmp;
2994 + if (pbmp = gpihCreateXBitmap(hab, 100, 100))
2995 + {
2996 + GpiMove(pbmp->hpsMem, ...);
2997 + GpiBox(pbmp->hpsMem, ...);
2998 +
2999 + WinDrawBitmap(hpsScreen,
3000 + pbmp->hbm, // bitmap handle
3001 + ...);
3002 + gpihDestroyXBitmap(&pbmp);
3003 + }
3004 *
3005 * Without the gpih* functions, the above would expand
3006 * to more than 100 lines.
3007 *
3008 *@@added V0.9.16 (2001-12-18) [umoeller]
3009 */
3010
3011PXBITMAP gpihCreateXBitmap2(HAB hab, // in: anchor block
3012 LONG cx, // in: bitmap width
3013 LONG cy, // in: bitmap height
3014 ULONG cPlanes, // in: color planes (usually 1); if 0, current screen is used
3015 ULONG cBitCount) // in: either 1, 4, or 24; if 0, current screen value
3016{
3017 BOOL fOK = FALSE;
3018 PXBITMAP pbmp;
3019 if (pbmp = (PXBITMAP)malloc(sizeof(XBITMAP)))
3020 {
3021 memset(pbmp, 0, sizeof(XBITMAP));
3022
3023 // create memory PS for bitmap
3024 pbmp->szl.cx = cx;
3025 pbmp->szl.cy = cy;
3026 if (gpihCreateMemPS(hab,
3027 &pbmp->szl,
3028 &pbmp->hdcMem,
3029 &pbmp->hpsMem))
3030 {
3031 if (cBitCount != 1)
3032 // not monochrome bitmap:
3033 gpihSwitchToRGB(pbmp->hpsMem);
3034
3035 if (pbmp->hbm = gpihCreateBitmap2(pbmp->hpsMem,
3036 cx,
3037 cy,
3038 cPlanes,
3039 cBitCount))
3040 {
3041 if (GpiSetBitmap(pbmp->hpsMem,
3042 pbmp->hbm)
3043 != HBM_ERROR)
3044 fOK = TRUE;
3045 }
3046 }
3047
3048 if (!fOK)
3049 gpihDestroyXBitmap(&pbmp);
3050 }
3051
3052 return pbmp;
3053}
3054
3055/*
3056 *@@ gpihDetachBitmap:
3057 * "detaches" the bitmap from the given XBITMAP.
3058 * This will deselect the bitmap from the internal
3059 * memory PS so it can be used with many OS/2 APIs
3060 * that require that the bitmap not be selected
3061 * into any memory PS.
3062 *
3063 * Note that it then becomes the responsibility
3064 * of the caller to explicitly call GpiDeleteBitmap
3065 * because it will not be deleted by gpihDestroyXBitmap.
3066 *
3067 *@@added V0.9.16 (2001-12-18) [umoeller]
3068 */
3069
3070HBITMAP gpihDetachBitmap(PXBITMAP pbmp)
3071{
3072 HBITMAP hbm = pbmp->hbm;
3073 pbmp->hbm = NULLHANDLE;
3074 GpiSetBitmap(pbmp->hpsMem, NULLHANDLE);
3075
3076 return hbm;
3077}
3078
3079/*
3080 *@@ gpihDestroyXBitmap:
3081 * destroys an XBitmap created with gpihCreateXBitmap.
3082 *
3083 * To be on the safe side, this sets the
3084 * given XBITMAP pointer to NULL as well.
3085 *
3086 *@@added V0.9.12 (2001-05-20) [umoeller]
3087 */
3088
3089VOID gpihDestroyXBitmap(PXBITMAP *ppbmp)
3090{
3091 if (ppbmp)
3092 {
3093 PXBITMAP pbmp;
3094 if (pbmp = *ppbmp)
3095 {
3096 if (pbmp->hbm)
3097 {
3098 if (pbmp->hpsMem)
3099 GpiSetBitmap(pbmp->hpsMem, NULLHANDLE);
3100 GpiDeleteBitmap(pbmp->hbm);
3101 }
3102 if (pbmp->hpsMem)
3103 {
3104 GpiAssociate(pbmp->hpsMem, NULLHANDLE);
3105 GpiDestroyPS(pbmp->hpsMem);
3106 }
3107 if (pbmp->hdcMem)
3108 DevCloseDC(pbmp->hdcMem);
3109
3110 free(pbmp);
3111
3112 *ppbmp = NULL;
3113 }
3114 }
3115}
3116
3117/*
3118 *@@ gpihCreateBmpFromPS:
3119 * this creates a new bitmap and copies a screen rectangle
3120 * into it. Consider this a "screen capture" function.
3121 *
3122 * The new bitmap (which is returned) is compatible with the
3123 * device associated with hpsScreen. This function calls
3124 * gpihCreateMemPS and gpihCreateBitmap to have it created.
3125 * The memory PS is only temporary and freed again.
3126 *
3127 * This returns the handle of the new bitmap,
3128 * which can then be used for WinDrawBitmap and such, or
3129 * NULLHANDLE upon errors.
3130 *
3131 *@@changed V0.9.12 (2001-05-20) [umoeller]: fixed excessive mem PS size
3132 *@@changed V0.9.16 (2001-01-04) [umoeller]: now creating XBITMAP
3133 */
3134
3135PXBITMAP gpihCreateBmpFromPS(HAB hab, // in: anchor block
3136 HPS hpsScreen, // in: screen PS to copy from
3137 PRECTL prcl) // in: rectangle to copy
3138{
3139
3140 /* To copy an image from a display screen to a bit map:
3141 1. Associate the memory device context with a presentation space.
3142 2. Create a bit map.
3143 3. Select the bit map into the memory device context by calling GpiSetBitmap.
3144 4. Determine the location (in device coordinates) of the image.
3145 5. Call GpiBitBlt and copy the image to the bit map. */
3146
3147 PXBITMAP pBmp;
3148
3149 if (pBmp = gpihCreateXBitmap(hab,
3150 prcl->xRight - prcl->xLeft,
3151 prcl->yTop - prcl->yBottom))
3152 {
3153 POINTL aptl[3];
3154 // Copy the screen to the bit map.
3155 aptl[0].x = 0; // lower-left corner of destination rectangle
3156 aptl[0].y = 0;
3157 aptl[1].x = prcl->xRight; // upper-right corner for both
3158 aptl[1].y = prcl->yTop;
3159 aptl[2].x = prcl->xLeft; // lower-left corner of source rectangle
3160 aptl[2].y = prcl->yBottom;
3161
3162 if (GPI_ERROR == GpiBitBlt(pBmp->hpsMem,
3163 hpsScreen,
3164 sizeof(aptl) / sizeof(POINTL), // Number of points in aptl
3165 aptl,
3166 ROP_SRCCOPY,
3167 BBO_IGNORE))
3168 {
3169 // error during bitblt:
3170 gpihDestroyXBitmap(&pBmp);
3171 }
3172 }
3173
3174 return pBmp;
3175}
3176
Note: See TracBrowser for help on using the repository browser.