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

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

Misc changes.

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