source: branches/branch-1-0/src/helpers/gpih.c@ 368

Last change on this file since 368 was 264, checked in by pr, 21 years ago

Fix spelling errors.

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