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

Last change on this file since 40 was 38, checked in by umoeller, 25 years ago

Updates to XML.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 66.1 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_DOSERRORS
36
37#define INCL_WINWINDOWMGR
38#define INCL_WINPOINTERS
39#define INCL_WINSYS
40
41#define INCL_GPIPRIMITIVES
42#define INCL_GPIBITMAPS
43#define INCL_GPILOGCOLORTABLE
44#define INCL_GPILCIDS
45#include <os2.h>
46
47#include <stdlib.h>
48#include <string.h>
49#include <stdio.h>
50#include <sys/types.h>
51#include <sys/stat.h>
52
53#include "setup.h" // code generation and debugging options
54
55#include "helpers\winh.h"
56#include "helpers\gpih.h"
57
58#pragma hdrstop
59
60// array for querying device capabilities (gpihQueryDisplayCaps)
61LONG DisplayCaps[CAPS_DEVICE_POLYSET_POINTS] = {0};
62BOOL fCapsQueried = FALSE;
63
64/*
65 *@@category: Helpers\PM helpers\GPI helpers
66 * See gpih.c.
67 */
68
69/*
70 *@@category: Helpers\PM helpers\GPI helpers\Devices
71 */
72
73/*
74 *@@gloss: GPI_rectangles GPI rectangles
75 * OS/2 PM (and GPI) uses two types of rectangles. This is rarely
76 * mentioned in the documentation, so a word is in order here.
77 *
78 * In general, graphics operations
79 * involving device coordinates (such as regions, bit maps and
80 * bit blts, and window management) use inclusive-exclusive
81 * rectangles. All other graphics operations, such as GPI
82 * functions that define paths, use inclusive-inclusive rectangles.
83 *
84 * This can be a problem with mixing Win and Gpi functions. For
85 * example, WinQueryWindowRect returns an inclusive-exclusive
86 * rectangle (so that the xRight value is the same as the window
87 * width -- tested V0.9.7 (2000-12-20) [umoeller]).
88 *
89 * WinFillRect expects an inclusive-exclusive rectangle, so it
90 * will work with a rectangle from WinQueryWindowRect directly.
91 *
92 * By contrast, the GpiBox expects an inclusive-inclusive rectangle.
93 */
94
95/* ******************************************************************
96 *
97 * Device helpers
98 *
99 ********************************************************************/
100
101/*
102 *@@ gpihQueryDisplayCaps:
103 * this returns certain device capabilities of
104 * the Display device. ulIndex must be one of
105 * the indices as described in DevQueryCaps.
106 *
107 * This function will load all the device capabilities
108 * only once into a global array and re-use them afterwards.
109 */
110
111ULONG gpihQueryDisplayCaps(ULONG ulIndex)
112{
113 if (!fCapsQueried)
114 {
115 HPS hps = WinGetScreenPS(HWND_DESKTOP);
116 HDC hdc = GpiQueryDevice(hps);
117 DevQueryCaps(hdc, 0, CAPS_DEVICE_POLYSET_POINTS, &DisplayCaps[0]);
118 }
119
120 return (DisplayCaps[ulIndex]);
121}
122
123/*
124 *@@category: Helpers\PM helpers\GPI helpers\Colors
125 */
126
127/* ******************************************************************
128 *
129 * Color helpers
130 *
131 ********************************************************************/
132
133/*
134 *@@ gpihManipulateRGB:
135 * this changes an RGB color value
136 * by multiplying each color component
137 * (red, green, blue) with bMultiplier
138 * and dividing it by bDivisor afterwards.
139 *
140 * Each color component is treated separately,
141 * so if overflows occur (because bMultiplier
142 * is > 1), this does not affect the other
143 * components.
144 *
145 * Example: if you pass a brigt red
146 * (0xFF0000) to this func, this will
147 * be 0x7F0000 if bMultiplier and
148 * bDivisor are 1 and 2.
149 */
150
151VOID gpihManipulateRGB(PLONG plColor, // in/out: RGB color
152 BYTE bMultiplier, // in: multiplier
153 BYTE bDivisor) // in: divisor
154{
155 PBYTE pb = (PBYTE)plColor;
156 // in memory, the bytes are blue, green, red, unused
157 *pb++ = (BYTE)((LONG)(*pb) * bMultiplier / bDivisor); // blue
158 *pb++ = (BYTE)((LONG)(*pb) * bMultiplier / bDivisor); // green
159 *pb++ = (BYTE)((LONG)(*pb) * bMultiplier / bDivisor); // red
160}
161
162/*
163 *@@ gpihSwitchToRGB:
164 * this switches the given HPS into RGB mode. You should
165 * always use this if you are operating with RGB colors.
166 *
167 * This is just a shortcut to calling
168 *
169 + GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
170 *
171 *@@changed V0.9.7 (2001-01-15) [umoeller]: turned macro into function
172 */
173
174BOOL gpihSwitchToRGB(HPS hps)
175{
176 return (GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL));
177}
178
179/*
180 *@@category: Helpers\PM helpers\GPI helpers\Drawing primitives
181 */
182
183/* ******************************************************************
184 *
185 * Drawing primitives helpers
186 *
187 ********************************************************************/
188
189/*
190 *@@ gpihDrawRect:
191 * this draws a simple rectangle with the current
192 * color (use GpiSetColor before calling this function).
193 *
194 * The specified rectangle is inclusive, that is, the top
195 * right corner specifies the top right pixel to be drawn
196 * (see @GPI_rectangles).
197 *
198 * This sets the current position to the bottom left corner
199 * of prcl.
200 *
201 *@added V0.9.0
202 */
203
204VOID gpihDrawRect(HPS hps, // in: presentation space for output
205 PRECTL prcl) // in: rectangle to draw (inclusive)
206{
207 POINTL ptl1;
208
209 ptl1.x = prcl->xLeft;
210 ptl1.y = prcl->yBottom;
211 GpiMove(hps, &ptl1);
212 ptl1.y = prcl->yTop-1;
213 GpiLine(hps, &ptl1);
214 ptl1.x = prcl->xRight-1;
215 GpiLine(hps, &ptl1);
216 ptl1.y = prcl->yBottom;
217 GpiLine(hps, &ptl1);
218 ptl1.x = prcl->xLeft;
219 GpiLine(hps, &ptl1);
220}
221
222/*
223 *@@ gpihBox:
224 * this is a shortcurt to GpiBox, using the specified
225 * rectangle.
226 *
227 * As opposed to WinFillRect, this works with memory
228 * (bitmap) PS's also.
229 *
230 * The specified rectangle is inclusive, that is, the top
231 * right corner specifies the top right pixel to be drawn.
232 * This is different from WinFillRect
233 * (see @GPI_rectangles).
234 *
235 * If (lColor != -1), the HPS's current foreground color
236 * is changed to that color.
237 *
238 * Changes to the HPS:
239 *
240 * -- the current position is moved to the lower left
241 * corner of *prcl.
242 *
243 *@@changed V0.9.0 [umoeller]: renamed from gpihFillRect
244 *@@changed V0.9.0 [umoeller]: modified function prototype to support lControl
245 *@@changed V0.9.7 (2001-01-17) [umoeller]: removed lColor
246 */
247
248VOID gpihBox(HPS hps, // in: presentation space for output
249 LONG lControl, // in: one of DRO_OUTLINE, DRO_FILL, DRO_OUTLINEFILL
250 PRECTL prcl) // in: rectangle to draw (exclusive)
251{
252 POINTL ptl;
253
254 ptl.x = prcl->xLeft;
255 ptl.y = prcl->yBottom;
256 GpiMove(hps, &ptl);
257 ptl.x = prcl->xRight;
258 ptl.y = prcl->yTop;
259 GpiBox(hps,
260 lControl, // DRO_*
261 &ptl,
262 0, 0); // no corner rounding
263}
264
265/*
266 *@@ gpihMarker:
267 * this draws a quick marker (a filled
268 * rectangle) at the specified position.
269 * The rectangle will be drawn so that
270 * the specified point is in its center.
271 *
272 * No PS data is changed.
273 *
274 *@@changed V0.9.7 (2001-01-17) [umoeller]: removed lColor
275 */
276
277VOID gpihMarker(HPS hps,
278 LONG x, // in: x-center of rectangle
279 LONG y, // in: y-center of rectangle
280 ULONG ulWidth) // in: rectangle width and height
281{
282 POINTL ptlSave;
283 RECTL rclTemp;
284 ULONG ulWidth2 = ulWidth / 2;
285 rclTemp.xLeft = x - ulWidth2;
286 rclTemp.xRight = x + ulWidth2;
287 rclTemp.yBottom = y - ulWidth2;
288 rclTemp.yTop = y + ulWidth2;
289
290 GpiQueryCurrentPosition(hps, &ptlSave);
291 gpihBox(hps,
292 DRO_FILL,
293 &rclTemp);
294 GpiMove(hps, &ptlSave);
295}
296
297/*
298 *@@ gpihThickBox:
299 * draws a box from the specified rectangle with the
300 * specified width.
301 *
302 * The specified rectangle is inclusive, that is, the top
303 * right corner specifies the top right pixel to be drawn.
304 * This is different from WinFillRect
305 * (see @GPI_rectangles).
306 *
307 * If usWidth > 1, the additional pixels will be drawn towards
308 * the _center_ of the rectangle. prcl thus always specifies
309 * the bottom left and top right pixels to be drawn.
310 *
311 * This is different from using GpiSetLineWidth, with which
312 * I was unable to find out in which direction lines are
313 * extended.
314 *
315 * This is similar to gpihDraw3DFrame, except that everything
316 * is painted in the current color.
317 *
318 *@@added V0.9.7 (2000-12-06) [umoeller]
319 */
320
321VOID gpihDrawThickFrame(HPS hps, // in: presentation space for output
322 PRECTL prcl, // in: rectangle to draw (inclusive)
323 ULONG ulWidth) // in: line width (>= 1)
324{
325 ULONG ul = 0;
326 for (;
327 ul < ulWidth;
328 ul++)
329 {
330 GpiMove(hps, (PPOINTL)prcl);
331 GpiBox(hps,
332 DRO_OUTLINE,
333 (PPOINTL)&(prcl->xRight),
334 0,
335 0);
336
337 // and one more to the outside
338 prcl->xLeft++;
339 prcl->yBottom++;
340 prcl->xRight--;
341 prcl->yTop--;
342 }
343}
344
345/*
346 *@@ gpihDraw3DFrame:
347 * this draws a rectangle in 3D style with a given line width
348 * and the given colors.
349 *
350 * The specified rectangle is inclusive, that is, the top
351 * right corner specifies the top right pixel to be drawn.
352 * This is different from WinFillRect
353 * (see @GPI_rectangles).
354 *
355 * If usWidth > 1, the additional pixels will be drawn towards
356 * the _center_ of the rectangle. prcl thus always specifies
357 * the bottom left and top right pixels to be drawn.
358 *
359 *@@changed V0.9.0 [umoeller]: changed function prototype to have colors specified
360 *@@changed V0.9.7 (2000-12-20) [umoeller]: now really using inclusive rectangle...
361 */
362
363VOID gpihDraw3DFrame(HPS hps,
364 PRECTL prcl, // in: rectangle (inclusive)
365 USHORT usWidth, // in: line width (>= 1)
366 LONG lColorLeft, // in: color to use for left and top; e.g. SYSCLR_BUTTONLIGHT
367 LONG lColorRight) // in: color to use for right and bottom; e.g. SYSCLR_BUTTONDARK
368{
369 RECTL rcl2 = *prcl;
370 USHORT us;
371 POINTL ptl1;
372
373 for (us = 0;
374 us < usWidth;
375 us++)
376 {
377 GpiSetColor(hps, lColorLeft);
378 // draw left line
379 ptl1.x = rcl2.xLeft;
380 ptl1.y = rcl2.yBottom;
381 GpiMove(hps, &ptl1);
382 ptl1.y = rcl2.yTop; // V0.9.7 (2000-12-20) [umoeller]
383 GpiLine(hps, &ptl1);
384 // go right -> draw top
385 ptl1.x = rcl2.xRight; // V0.9.7 (2000-12-20) [umoeller]
386 GpiLine(hps, &ptl1);
387 // go down -> draw right
388 GpiSetColor(hps, lColorRight);
389 ptl1.y = rcl2.yBottom;
390 GpiLine(hps, &ptl1);
391 // go left -> draw bottom
392 ptl1.x = rcl2.xLeft;
393 GpiLine(hps, &ptl1);
394
395 rcl2.xLeft++;
396 rcl2.yBottom++;
397 rcl2.xRight--;
398 rcl2.yTop--;
399 }
400}
401
402/*
403 *@@ gpihCharStringPosAt:
404 * wrapper for GpiCharStringPosAt.
405 * Since that function is limited to 512 characters
406 * (according to GPIREF; on my Warp 4 FP13, I actually
407 * get some 3000 characters... whatever this is),
408 * this splits the string into 512 byte chunks and
409 * calls GpiCharStringPosAt accordingly.
410 *
411 *@@added V0.9.3 (2000-05-06) [umoeller]
412 */
413
414LONG gpihCharStringPosAt(HPS hps,
415 PPOINTL pptlStart,
416 PRECTL prclRect,
417 ULONG flOptions,
418 LONG lCount,
419 PCH pchString)
420{
421 LONG lHits = 0,
422 lCountLeft = lCount;
423 PCH pchThis = pchString;
424
425 GpiMove(hps, pptlStart);
426
427 if (lCount)
428 {
429 do
430 {
431 LONG lCountThis = lCountLeft;
432 if (lCountLeft >= 512)
433 lCountThis = 512;
434
435 lHits = GpiCharStringPos(hps,
436 prclRect,
437 flOptions,
438 lCountThis,
439 pchThis,
440 0);
441
442 pchThis += 512;
443 lCountLeft -= 512;
444 } while (lCountLeft > 0);
445 }
446
447 return (lHits);
448}
449
450/*
451 *@@category: Helpers\PM helpers\GPI helpers\Fonts
452 */
453
454/* ******************************************************************
455 *
456 * Font helpers
457 *
458 ********************************************************************/
459
460/*
461 *@@ gpihSplitPresFont:
462 * splits a presentation parameter font
463 * string into the point size and face
464 * name so that it can be passed to
465 * gpihFindFont more easily.
466 *
467 *@@added V0.9.1 (2000-02-15) [umoeller]
468 */
469
470BOOL gpihSplitPresFont(PSZ pszFontNameSize, // in: e.g. "12.Courier"
471 PULONG pulSize, // out: integer point size (e.g. 12);
472 // ptr must be specified
473 PSZ *ppszFaceName) // out: ptr into pszFontNameSize
474 // (e.g. "Courier")
475{
476 BOOL brc = FALSE;
477
478 if (pszFontNameSize)
479 {
480 PCHAR pcDot = strchr(pszFontNameSize, '.');
481 if (pcDot)
482 {
483 // _Pmpf(("Found font PP: %s", pszFontFound));
484 sscanf(pszFontNameSize, "%lu", pulSize);
485 *ppszFaceName = pcDot + 1;
486 brc = TRUE;
487 }
488 }
489
490 return (brc);
491}
492
493/*
494 *@@ gpihQueryNextLCID:
495 * returns the next available lcid for the given HPS.
496 * Actually, it's the next available lcid for the
497 * entire process, since there can be only 255 altogether.
498 * Gets called by gpihFindFont automatically.
499 *
500 * This is possibly the sickest code I've ever written.
501 *
502 * Warning: This function is _not_ thread-safe.
503 * We use GpiQueryNumberSetIds to find out the no. of
504 * LCID's in use by the process. In the worst case,
505 * between that call and the call to GpiCreateLogFont,
506 * another font has been created, and you'll get garbage.
507 *
508 *@@added V0.9.3 (2000-05-06) [umoeller]
509 */
510
511LONG gpihQueryNextFontID(HPS hps)
512{
513 LONG lcidNext = -1;
514
515 LONG lCount = GpiQueryNumberSetIds(hps);
516 // the number of local identifiers
517 // (lcids) currently in use, and
518 // therefore the maximum number
519 // of objects for which information
520 // can be returned
521 if (lCount == 0)
522 // none in use yet:
523 lcidNext = 1;
524 else
525 {
526 #define GQNCL_BLOCK_SIZE 400*sizeof(LONG)
527 PLONG pBase;
528
529 APIRET arc;
530
531 // _Pmpf(("gpihQueryNextFontID: calling DosAllocMem"));
532
533 arc = DosAllocMem((PPVOID)(&pBase),
534 GQNCL_BLOCK_SIZE,
535 // space is needed for an array of lCount longs.
536 PAG_READ |
537 PAG_WRITE);
538 if (arc == NO_ERROR)
539 {
540 arc = DosSubSetMem(pBase,
541 DOSSUB_INIT | DOSSUB_SPARSE_OBJ,
542 GQNCL_BLOCK_SIZE);
543 if (arc == NO_ERROR)
544 {
545 PLONG alTypes; // object types
546 PSTR8 aNames; // font names
547 PLONG allcids; // local identifiers
548
549 arc = DosSubAllocMem((PVOID)pBase,
550 (PPVOID)(&aNames),
551 (ULONG)(lCount*(ULONG)sizeof(STR8)));
552 // space is needed for an array of
553 // lCount longs
554 if (arc == NO_ERROR)
555 {
556 arc = DosSubAllocMem((PVOID)pBase,
557 (PPVOID)(&allcids),
558 (ULONG)lCount*sizeof(LONG));
559 // space is needed for an array of
560 // lCount longs.
561 if (arc == NO_ERROR)
562 {
563 arc = DosSubAllocMem((PVOID)pBase,
564 (PPVOID)(&alTypes),
565 (ULONG)lCount*sizeof(LONG));
566 // space is needed for an array of
567 // lCount longs.
568 if (arc == NO_ERROR)
569 {
570 if (GpiQuerySetIds(hps,
571 lCount,
572 alTypes,
573 aNames,
574 allcids))
575 {
576 // FINALLY we have all the lcids in use.
577 BOOL fContinue = TRUE;
578 lcidNext = 1;
579
580 // now, check if this lcid is in use already:
581 while (fContinue)
582 {
583 BOOL fFound = FALSE;
584 ULONG ul;
585 fContinue = FALSE;
586 for (ul = 0;
587 ul < lCount;
588 ul++)
589 {
590 if (allcids[ul] == lcidNext)
591 {
592 fFound = TRUE;
593 break;
594 }
595 }
596
597 if (fFound)
598 {
599 // lcid found:
600 // try next higher one
601 lcidNext++;
602 fContinue = TRUE;
603 }
604 // else: return that one
605 }
606 }
607 }
608 }
609 }
610 }
611
612 arc = DosFreeMem(pBase);
613 }
614 }
615
616 return (lcidNext);
617}
618
619/*
620 *@@ gpihFindFont:
621 * this returns a new logical font ID (LCID) for the specified
622 * font by calling GpiCreateLogFont.
623 * This function performs the insane "11-step process" to
624 * match a font, as described in the GPI reference.
625 *
626 * This function can operate in two modes:
627 *
628 * -- "Family" mode. In that case, specify the font family name
629 * with pszName and set fFamily to TRUE. This is useful for
630 * WYSIWYG text viewing if you need several font faces for
631 * the same family, such as Courier Bold, Bold Italics, etc.
632 * You can specify those attributes with usFormat then.
633 *
634 * -- "Face" mode. In that case, specify the full font face name
635 * with pszName and set fFamily to FALSE. This is useful for
636 * font presentation parameters which use the "WarpSans Bold"
637 * format. In that case, set usFormat to 0.
638 *
639 * After the font has been created, if (pFontMetrics != NULL),
640 * *pFontMetrics receives the FONTMETRICS of the font which
641 * has been created. If an outline font has been created
642 * (instead of a bitmap font), FONTMETRICS.fsDefn will have
643 * the FM_DEFN_OUTLINE bit set.
644 *
645 * To then use the font whose LCID has been returned by this
646 * function for text output, call:
647 + GpiSetCharSet(hps, lLCIDReturned);
648 *
649 * <B>Font Point Sizes:</B>
650 *
651 * 1) For image (bitmap) fonts, the size is fixed, and
652 * you can directly draw after the font has been
653 * selected. GpiSetCharBox has no effect on image
654 * fonts unless you switch to character mode 2
655 * (if you care for that: GpiSetCharMode).
656 *
657 * 2) For outline fonts however, you need to define a
658 * character box if you want to output text in a
659 * size other than the default size. This is almost
660 * as bad a mess as this function, so gpihSetPointSize
661 * has been provided for this. See remarks there.
662 *
663 * <B>Example:</B>
664 *
665 * This example prints text in "24.Courier".
666 *
667 + PSZ pszOutput = "Test output";
668 + FONTMETRICS FontMetrics;
669 + LONG lLCID = gpihFindFont(hps,
670 + 24, // point size
671 + FALSE, // face, not family
672 + "Courier",
673 + 0,
674 + &FontMetrics);
675 + if (lLCID)
676 + {
677 + // no error:
678 + GpiSetCharSet(hps, lLCID);
679 + if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
680 + // outline font found (should be the case):
681 + gpihSetPointSize(hps, 24);
682 + }
683 + GpiCharString(hps, strlen(pszOutput), pszOutput);
684 *
685 * <B>Details:</B>
686 *
687 * First, GpiQueryFonts is called to enumerate all the fonts on
688 * the system. We then evaluate the awful FONTMETRICS data
689 * of those fonts to perform a "real" match.
690 *
691 * If that fails, we allow GPI to do a "close" match based
692 * on the input values. This might result in the system
693 * default font (System Proportional) to be found, in the
694 * worst case. But even then, a new LCID is returned.
695 *
696 * The "close match" comes in especially when using the
697 * font attributes (bold, italics) and we are unable to
698 * find the correct outline font for that. Unfortunately,
699 * the information in FONTMETRICS.fsSelection is wrong,
700 * wrong, wrong for the large majority of fonts. (For
701 * example, I get the "bold" flag set for regular fonts,
702 * and vice versa.) So we attempt to use the other fields,
703 * but this doesn't always help. Not even Netscape gets
704 * this right.
705 *
706 * <B>Font faces:</B>
707 *
708 * This is terribly complicated as well. You need to
709 * differentiate between "true" emphasis faces (that
710 * is, bold and italics are implemented thru separate
711 * font files) and the ugly GPI simulation, which simply
712 * makes the characters wider or shears them.
713 *
714 * This function even finds true "bold" and "italic" faces
715 * for outline fonts. To do this, always specify the "family"
716 * name as pszFaceName (e.g. "Courier" instead of "Courier
717 * Bold") and set the flags for usFormat (e.g. FATTR_SEL_BOLD).
718 * Note that this implies that you need call this function
719 * twice to create two logical fonts for regular and bold faces.
720 *
721 * If a "true" emphasis font is not found, the GPI simulation
722 * is enabled.
723 *
724 * <B>Remarks:</B>
725 *
726 * 1) This function _always_ creates a new logical font,
727 * whose ID is returned, even if the specified font
728 * was not found or a "close match" was performed by
729 * GPI. As a result, do not call this function twice
730 * for the same font specification, because there are
731 * only 255 logical font IDs for each process.
732 *
733 * 2) Since this function always creates an LCID,
734 * you should _always_ free the LCID later.
735 * This is only valid if the font is no longer selected
736 * into any presentation space. So use these calls:
737 + GpiSetCharSet(hps, LCID_DEFAULT);
738 + GpiDeleteSetId(hps, lLCIDReturnedByThis);
739 *
740 * 3) Using this function, bitmap fonts will have priority
741 * over outline fonts of the same face name. This is
742 * how the WPS does it too. This is most obvious with
743 * the "Helv" font, which exists as a bitmap font for
744 * certain point sizes only.
745 *
746 * 4) <B>Warning: This function is _not_ thread-safe.</B>
747 * Since logical font IDs are shared across the
748 * process, this function should _not_ be called
749 * from several threads at the same time.
750 *
751 * This calls gpihQueryNextFontID in turn to find the
752 * next free lcid. See remarks there.
753 * If your fonts suddenly change to "System Proportional"
754 * in your application, you probably have a serialization
755 * problem.
756 *
757 * To make this thread-safe, create your own wrapper
758 * function which calls this function while a mutex
759 * semaphore is held by the wrapper. Then use only
760 * the wrapper in your code.
761 *
762 * <B>Font metrics:</B>
763 *
764 * The important values in the returned FONTMETRICS are
765 * like this (according to PMREF):
766 *
767 + ÉÍ ________________________________________________
768 + º
769 + º lExternalLeading, according to font designer.
770 + º ________________________________________________ Í»
771 + ÈÍ º
772 + # # º
773 + ## ## º lMaxAscender (of entire;
774 + ÉÍ _______________ # # # # º font); this can be > capital
775 + º #### # # # # º letters because miniscules
776 + º # # # # # º can exceed that.
777 + º lXHeight # # # # º
778 + º # # # # º
779 + º # # # # º
780 + º _______________#####________#_______#___ baseline Í»
781 + ÈÍ # º
782 + # º lMaxDescender
783 + ______________ ####______________________________ ͌
784 +
785 *
786 * In turn, lMaxBaselineExt is lMaxAscender + lMaxDescender.
787 *
788 * Soooo... to find out about the optimal line spacing, GPIREF
789 * recommends to use lMaxBaselineExt + lExternalLeading.
790 *
791 *@@added V0.9.0 [umoeller]
792 *@@changed V0.9.3 (2000-05-06) [umoeller]: didn't work for more than one font; now using gpihQueryNextFontID
793 *@@changed V0.9.3 (2000-05-06) [umoeller]: usFormat didn't work; fixed
794 *@@changed V0.9.4 (2000-08-08) [umoeller]: added fFamily
795 */
796
797LONG gpihFindFont(HPS hps, // in: HPS for font selection
798 LONG lSize, // in: font point size
799 BOOL fFamily, // in: if TRUE, pszName specifies font family;
800 // if FALSE, pszName specifies font face
801 PSZ pszName, // in: font family or face name (without point size)
802 USHORT usFormat, // in: none, one or several of:
803 // -- FATTR_SEL_ITALIC
804 // -- FATTR_SEL_UNDERSCORE (underline)
805 // -- FATTR_SEL_BOLD
806 // -- FATTR_SEL_STRIKEOUT
807 // -- FATTR_SEL_OUTLINE (hollow)
808 PFONTMETRICS pFontMetrics) // out: font metrics of created font (optional)
809{
810 LONG lLCIDReturn = 0;
811 ULONG ul = 0;
812 FATTRS FontAttrs;
813
814 // first find out how much memory we need to allocate
815 // for the FONTMETRICS structures
816 LONG lTemp = 0;
817 LONG cFonts = GpiQueryFonts(hps,
818 QF_PUBLIC | QF_PRIVATE,
819 NULL, // pszFaceName,
820 &lTemp,
821 sizeof(FONTMETRICS),
822 NULL);
823 PFONTMETRICS pfm = (PFONTMETRICS)malloc(cFonts * sizeof(FONTMETRICS)),
824 pfm2 = pfm,
825 pfmFound = NULL;
826
827 // _Pmpf(("gpihFindFont: enumerating for %s, %d points", pszFaceName, lSize));
828
829 GpiQueryFonts(hps,
830 QF_PUBLIC | QF_PRIVATE,
831 NULL, // pszFaceName,
832 &cFonts,
833 sizeof(FONTMETRICS), // length of each metrics structure
834 // -- _not_ total buffer size!
835 pfm);
836 // now we have an array of FONTMETRICS
837 // for EVERY font that is installed on the system...
838 // these things are completely unsorted, so there's
839 // nothing we can rely on, we have to check them all.
840
841 // fill in some default values for FATTRS,
842 // in case we don't find something better
843 // in the loop below; these values will be
844 // applied if
845 // a) an outline font has been found;
846 // b) bitmap fonts have been found, but
847 // none for the current device resolution
848 // exists;
849 // c) no font has been found at all.
850 // In all cases, GpiCreateLogFont will do
851 // a "close match" resolution (at the bottom).
852 FontAttrs.usRecordLength = sizeof(FATTRS);
853 FontAttrs.fsSelection = usFormat; // changed later if better font is found
854 FontAttrs.lMatch = 0L; // closest match
855 strcpy(FontAttrs.szFacename, pszName);
856 FontAttrs.idRegistry = 0; // default registry
857 FontAttrs.usCodePage = 0; // default codepage
858 // the following two must be zero, or outline fonts
859 // will not be found; if a bitmap font has been passed
860 // to us, we'll modify these two fields later
861 FontAttrs.lMaxBaselineExt = 0; // font size (height)
862 FontAttrs.lAveCharWidth = 0; // font size (width)
863 FontAttrs.fsType = 0; // default type
864 FontAttrs.fsFontUse = FATTR_FONTUSE_NOMIX;
865
866 // now go thru the array of FONTMETRICS
867 // to check if we have a bitmap font
868 // pszFaceName; the default WPS behavior
869 // is that bitmap fonts appear to take
870 // priority over outline fonts of the
871 // same name, so we check these first
872 pfm2 = pfm;
873 for (ul = 0;
874 ul < cFonts;
875 ul++)
876 {
877 /* _Pmpf((" Checking font: %s (Fam: %s), %d, %d, %d",
878 pszFaceName,
879 pfm2->szFamilyname,
880 pfm2->sNominalPointSize,
881 pfm2->lMaxBaselineExt,
882 pfm2->lAveCharWidth)); */
883
884 PSZ pszCompare = (fFamily)
885 ? pfm2->szFamilyname
886 : pfm2->szFacename;
887
888 if (strcmp(pszCompare, pszName) == 0)
889 {
890 /* _Pmpf((" Found font %s; slope %d, usWeightClass %d",
891 pfm2->szFacename,
892 pfm2->sCharSlope,
893 pfm2->usWeightClass)); */
894
895 if ((pfm2->fsDefn & FM_DEFN_OUTLINE) == 0)
896 {
897 // image (bitmap) font:
898 // check point size
899 if (pfm2->sNominalPointSize == lSize * 10)
900 {
901 // OK: check device resolutions, because
902 // normally, there are always several image
903 // fonts for different resolutions
904 // for bitmap fonts, there are always two versions:
905 // one for low resolutions, one for high resolutions
906 LONG alDevRes[2];
907 DevQueryCaps(GpiQueryDevice(hps),
908 CAPS_HORIZONTAL_FONT_RES,
909 2L,
910 alDevRes);
911 if ( (pfm2->sXDeviceRes == alDevRes[0])
912 && (pfm2->sYDeviceRes == alDevRes[1])
913 )
914 {
915 // OK: use this for GpiCreateLogFont
916 FontAttrs.lMaxBaselineExt = pfm2->lMaxBaselineExt;
917 FontAttrs.lAveCharWidth = pfm2->lAveCharWidth;
918 FontAttrs.lMatch = pfm2->lMatch;
919
920 pfmFound = pfm2;
921 break;
922 }
923 }
924 }
925 else
926 // outline font:
927 if (pfmFound == NULL)
928 {
929 /*
930 #define FATTR_SEL_ITALIC 0x0001
931 #define FATTR_SEL_UNDERSCORE 0x0002
932 #define FATTR_SEL_OUTLINE 0x0008
933 #define FATTR_SEL_STRIKEOUT 0x0010
934 #define FATTR_SEL_BOLD 0x0020
935 */
936 // no bitmap font found yet:
937 if ( ( ( (usFormat & FATTR_SEL_BOLD)
938 && (pfm2->usWeightClass == 7) // bold
939 )
940 || ( ((usFormat & FATTR_SEL_BOLD) == 0)
941 && (pfm2->usWeightClass == 5) // regular
942 )
943 )
944 && ( ( (usFormat & FATTR_SEL_ITALIC)
945 && (pfm2->sCharSlope != 0) // italics
946 )
947 || ( ((usFormat & FATTR_SEL_ITALIC) == 0)
948 && (pfm2->sCharSlope == 0) // regular
949 )
950 )
951 )
952 {
953 // yes, we found a true font for that face:
954 pfmFound = pfm2;
955 // use this exact font for GpiCreateLogFont
956 FontAttrs.lMatch = pfm2->lMatch;
957 // according to GPIREF, we must also specify
958 // the full face name... Jesus!
959 strcpy(FontAttrs.szFacename, pfm2->szFacename);
960 // unset flag in FATTRS, because this would
961 // duplicate bold or italic
962 FontAttrs.fsSelection = 0;
963
964 // _Pmpf((" --> using it"));
965 // but loop on, because we might have a bitmap
966 // font which should take priority
967 }
968 }
969 }
970
971 pfm2++;
972 }
973
974 if (pfmFound)
975 // FONTMETRICS found:
976 // copy font metrics?
977 if (pFontMetrics)
978 memcpy(pFontMetrics, pfmFound, sizeof(FONTMETRICS));
979
980 // free the FONTMETRICS array
981 free(pfm);
982
983 // new logical font ID: last used plus one
984 lLCIDReturn = gpihQueryNextFontID(hps);
985
986 GpiCreateLogFont(hps,
987 NULL, // don't create "logical font name" (STR8)
988 lLCIDReturn,
989 &FontAttrs);
990
991 return (lLCIDReturn);
992}
993
994/*
995 *@@ gpihFindPresFont:
996 * similar to gpihFindFont, but this one evaluates
997 * the PP_FONTNAMESIZE presentation parameter of the
998 * specified window instead. If that one is not set,
999 * the specified default font is used instead.
1000 *
1001 * Note that as opposed to gpihFindFont, this one
1002 * takes a "8.Helv"-type string as input.
1003 *
1004 * See gpihFindFont for additional remarks, which
1005 * gets called by this function.
1006 *
1007 * Again, if an outline font has been returned, you
1008 * must also set the "character box" for the HPS, or
1009 * your text will always have the same point size.
1010 * Use gpihSetPointSize for that, using the presparam's
1011 * point size, which is returned by this function
1012 * into *plSize, if (plSize != NULL):
1013 +
1014 + FONTMETRICS FontMetrics;
1015 + LONG lPointSize;
1016 + LONG lLCID = gpihFindPresFont(hwnd, hps, "8.Helv",
1017 + &FontMetrics,
1018 + &lPointSize);
1019 + GpiSetCharSet(hps, lLCID);
1020 + if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
1021 + gpihSetPointSize(hps, lPointSize);
1022 *
1023 * If errors occur, e.g. if the font string does not
1024 * conform to the "size.face" format, null is returned.
1025 *
1026 *@@added V0.9.0 [umoeller]
1027 */
1028
1029LONG gpihFindPresFont(HWND hwnd, // in: window to search for presparam
1030 BOOL fInherit, // in: search parent windows too?
1031 HPS hps, // in: HPS for font selection
1032 PSZ pszDefaultFont, // in: default font if not found (i.e. "8.Helv")
1033 PFONTMETRICS pFontMetrics, // out: font metrics of created font (optional)
1034 PLONG plSize) // out: presparam's point size (optional)
1035{
1036 CHAR szPPFont[200] = "";
1037 PSZ pszFontFound = 0;
1038 CHAR szFaceName[300] = "";
1039 ULONG ulFontSize = 0;
1040
1041 if (WinQueryPresParam(hwnd,
1042 PP_FONTNAMESIZE, // first PP to query
1043 0, // second PP to query
1044 NULL, // out: which one is returned
1045 (ULONG)sizeof(szPPFont), // in: buffer size
1046 (PVOID)&szPPFont, // out: PP value returned
1047 (fInherit)
1048 ? 0
1049 : QPF_NOINHERIT))
1050 // PP found:
1051 pszFontFound = szPPFont;
1052 else
1053 pszFontFound = pszDefaultFont;
1054
1055 if (pszFontFound)
1056 {
1057 PCHAR pcDot = strchr(pszFontFound, '.');
1058 if (pcDot)
1059 {
1060 // _Pmpf(("Found font PP: %s", pszFontFound));
1061 sscanf(pszFontFound, "%lu", &ulFontSize);
1062 if (plSize)
1063 *plSize = ulFontSize;
1064 strcpy(szFaceName, pcDot + 1);
1065 return (gpihFindFont(hps,
1066 ulFontSize,
1067 FALSE, // face, not family name
1068 szFaceName,
1069 0,
1070 pFontMetrics));
1071 }
1072 }
1073 return (0);
1074}
1075
1076/*
1077 *@@ gpihSetPointSize:
1078 * this invokes GpiSetCharBox on the given HPS to
1079 * set the correct "character box" with the proper
1080 * parameters.
1081 *
1082 * This is necessary for text output with outline
1083 * fonts. Bitmap fonts have a fixed size, and
1084 * calling this function is not necessary for them.
1085 *
1086 * Unfortunately, IBM has almost not documented
1087 * how to convert nominal point sizes to the
1088 * correct character cell values. This involves
1089 * querying the output device (DevQueryCaps).
1090 *
1091 * I have found one hint for this procedure in GPIREF
1092 * (chapter "Character string primitives", "Using...",
1093 * "Drawing text"), but the example code given
1094 * there is buggy, because it must be "72" instead
1095 * of "720". #%(%!"õ!!.
1096 *
1097 * So here we go. If you want to output text in,
1098 * say, "Courier" and 24 points (nominal point size),
1099 * select the font into your HPS and call
1100 + gpihSetPointSize(hps, 24)
1101 * and you're done. See gpihFindFont for a complete
1102 * example.
1103 *
1104 * Call this function as many times as needed. This
1105 * consumes no resources at all.
1106 *
1107 * This returns the return value of GpiSetCharBox.
1108 *
1109 *@@added V0.9.0 [umoeller]
1110 */
1111
1112BOOL gpihSetPointSize(HPS hps, // in: presentation space for output
1113 LONG lPointSize) // in: desired nominal point size
1114{
1115 SIZEF box;
1116 LONG alDevRes[2];
1117 DevQueryCaps(GpiQueryDevice(hps), // get the HDC from the HPS
1118 CAPS_HORIZONTAL_FONT_RES,
1119 2L,
1120 alDevRes);
1121 box.cx = MAKEFIXED((lPointSize * alDevRes[0]) / 72, 0);
1122 box.cy = MAKEFIXED((lPointSize * alDevRes[1]) / 72, 0);
1123 return (GpiSetCharBox(hps, &box));
1124}
1125
1126/*
1127 *@@ gpihQueryLineSpacing:
1128 * this returns the optimal line spacing for text
1129 * output with the current HPS; this is computed
1130 * by evaluating those incredible FONTMETRICS.
1131 *
1132 * This might be helpful if you write text to the
1133 * screen yourself and need the height of a text
1134 * line to advance to the next.
1135 *
1136 *@@changed V0.9.7 (2000-12-20) [umoeller]: removed psz param
1137 */
1138
1139LONG gpihQueryLineSpacing(HPS hps)
1140{
1141 FONTMETRICS fm;
1142
1143 if (GpiQueryFontMetrics(hps, sizeof(FONTMETRICS), &fm))
1144 return ( ( fm.lMaxBaselineExt // max vertical font space
1145 +fm.lExternalLeading) // space advised by font designer
1146 );
1147 else
1148 return (15);
1149}
1150
1151/*
1152 *@@category: Helpers\PM helpers\GPI helpers\Bitmaps/Icons
1153 */
1154
1155/* ******************************************************************
1156 *
1157 * Bitmap helpers
1158 *
1159 ********************************************************************/
1160
1161/*
1162 *@@ gpihCreateMemPS:
1163 * creates a memory device context and presentation space so
1164 * that they are compatible with the screen device context and
1165 * presentation space. These are stored in *hdcMem and *hpsMem.
1166 *
1167 * This is a one-shot function for the standard code that is
1168 * always needed when working with bitmaps in a memory device
1169 * context.
1170 *
1171 * psizlPage must point to a SIZEL structure containing the
1172 * width and height for the memory PS. Specify the size of
1173 * the future bitmap here. Specify {0, 0} to get a PS with
1174 * the size of the full screen, which consumes quite a bit
1175 * of memory though.
1176 *
1177 * Returns FALSE upon errors. In that case, both hdcMem and
1178 * hpsMem are set to NULLHANDLE.
1179 *
1180 * To cleanup after this function has returned TRUE, use the
1181 * following:
1182 + GpiDestroyPS(hpsMem);
1183 + DevCloseDC(hdcMem);
1184 *
1185 *@@changed V0.9.3 (2000-05-18) [umoeller]: added psiszlPage
1186 */
1187
1188BOOL gpihCreateMemPS(HAB hab, // in: anchor block
1189 PSIZEL psizlPage, // in: width and height for mem PS
1190 HDC *hdcMem, // out: memory DC or NULLHANDLE upon errors
1191 HPS *hpsMem) // out: memory PS or NULLHANDLE upon errors
1192{
1193 BOOL brc = FALSE;
1194 PSZ pszData[4] = { "Display", NULL, NULL, NULL };
1195
1196 // create new memory DC
1197 if ((*hdcMem = DevOpenDC(hab,
1198 OD_MEMORY, // create memory DC
1199 "*", // token: do not take INI info
1200 4, // item count in pszData
1201 (PDEVOPENDATA)pszData,
1202 NULLHANDLE))) // compatible with screen
1203 {
1204 // memory DC created successfully:
1205 // create compatible PS
1206 if ((*hpsMem = GpiCreatePS(hab,
1207 *hdcMem, // HDC to associate HPS with (GPIA_ASSOC);
1208 // mandatory for GPIT_MICRO
1209 psizlPage, // is (0, 0) == screen size
1210 PU_PELS // presentation page units: pixels
1211 | GPIA_ASSOC // associate with hdcMem (req. for GPIT_MICRO)
1212 | GPIT_MICRO))) // micro presentation space
1213 brc = TRUE;
1214 else
1215 {
1216 // error (hpsMem == NULLHANDLE):
1217 // close memory DC again
1218 DevCloseDC(*hdcMem);
1219 *hdcMem = NULLHANDLE;
1220 }
1221 }
1222
1223 return (brc);
1224}
1225
1226/*
1227 *@@ gpihCreateBitmap:
1228 * creates a new bitmap for a given memory PS.
1229 * This bitmap will have the cPlanes and bitcount
1230 * which are found in the memory PS.
1231 * For all the mysterious other values, we use
1232 * fixed default values, this doesn't seem to hurt.
1233 *
1234 * Note that the bitmap is _not_ yet selected into
1235 * the specified memory PS. You must call
1236 + GpiSetBitmap(hpsMem, hbm)
1237 * to do this.
1238 *
1239 * Returns the bitmap handle or NULLHANDLE upon errors.
1240 *
1241 *@@changed V0.9.0 [umoeller]: function prototype changed to cx and cy
1242 */
1243
1244HBITMAP gpihCreateBitmap(HPS hpsMem, // in: memory DC
1245 ULONG cx, // in: width of new bitmap
1246 ULONG cy) // in: height of new bitmap
1247{
1248 HBITMAP hbm = NULLHANDLE;
1249 LONG alData[2];
1250 BITMAPINFOHEADER2 bih2;
1251 PBITMAPINFO2 pbmi = NULL;
1252
1253 // determine the device's plane/bit-count format;
1254 // alData[0] then has cPlanes,
1255 // alData[1] has cBitCount
1256 if (GpiQueryDeviceBitmapFormats(hpsMem, 2, alData))
1257 {
1258 // set up the BITMAPINFOHEADER2 and BITMAPINFO2 structures
1259 bih2.cbFix = (ULONG)sizeof(BITMAPINFOHEADER2);
1260 bih2.cx = cx; // (prcl->xRight - prcl->xLeft); changed V0.9.0
1261 bih2.cy = cy; // (prcl->yTop - prcl->yBottom); changed V0.9.0
1262 bih2.cPlanes = alData[0];
1263 bih2.cBitCount = alData[1];
1264 bih2.ulCompression = BCA_UNCOMP;
1265 bih2.cbImage = ( ( (bih2.cx
1266 * (1 << bih2.cPlanes)
1267 * (1 << bih2.cBitCount)
1268 ) + 31
1269 ) / 32
1270 ) * bih2.cy;
1271 bih2.cxResolution = 70;
1272 bih2.cyResolution = 70;
1273 bih2.cclrUsed = 2;
1274 bih2.cclrImportant = 0;
1275 bih2.usUnits = BRU_METRIC; // measure units for cxResolution/cyResolution: pels per meter
1276 bih2.usReserved = 0;
1277 bih2.usRecording = BRA_BOTTOMUP; // scan lines are bottom to top (default)
1278 bih2.usRendering = BRH_NOTHALFTONED; // other algorithms aren't documented anyways
1279 bih2.cSize1 = 0; // parameter for halftoning (undocumented anyways)
1280 bih2.cSize2 = 0; // parameter for halftoning (undocumented anyways)
1281 bih2.ulColorEncoding = BCE_RGB; // only possible value
1282 bih2.ulIdentifier = 0; // application-specific data
1283
1284 // allocate memory for info header
1285 if (DosAllocMem((PPVOID)&pbmi,
1286 sizeof(BITMAPINFO2) +
1287 (sizeof(RGB2)
1288 * (1 << bih2.cPlanes)
1289 * (1 << bih2.cBitCount)
1290 ),
1291 PAG_COMMIT | PAG_READ | PAG_WRITE)
1292 == NO_ERROR)
1293 {
1294 pbmi->cbFix = bih2.cbFix;
1295 pbmi->cx = bih2.cx;
1296 pbmi->cy = bih2.cy;
1297 pbmi->cPlanes = bih2.cPlanes;
1298 pbmi->cBitCount = bih2.cBitCount;
1299 pbmi->ulCompression = BCA_UNCOMP;
1300 pbmi->cbImage = ((bih2.cx+31)/32) * bih2.cy;
1301 pbmi->cxResolution = 70;
1302 pbmi->cyResolution = 70;
1303 pbmi->cclrUsed = 2;
1304 pbmi->cclrImportant = 0;
1305 pbmi->usUnits = BRU_METRIC;
1306 pbmi->usReserved = 0;
1307 pbmi->usRecording = BRA_BOTTOMUP;
1308 pbmi->usRendering = BRH_NOTHALFTONED;
1309 pbmi->cSize1 = 0;
1310 pbmi->cSize2 = 0;
1311 pbmi->ulColorEncoding = BCE_RGB;
1312 pbmi->ulIdentifier = 0;
1313
1314 // create a bit map that is compatible with the display
1315 hbm = GpiCreateBitmap(hpsMem,
1316 &bih2,
1317 FALSE,
1318 NULL,
1319 pbmi);
1320
1321 // free the memory we allocated previously; GPI has
1322 // allocated all the resources it needs itself, so
1323 // we can release this
1324 DosFreeMem(pbmi);
1325 }
1326 }
1327
1328 return (hbm);
1329}
1330
1331/*
1332 *@@ gpihCreateBmpFromPS:
1333 * this creates a new bitmap and copies a screen rectangle
1334 * into it. Consider this a "screen capture" function.
1335 *
1336 * The new bitmap (which is returned) is compatible with the
1337 * device associated with hpsScreen. This function calls
1338 * gpihCreateMemPS and gpihCreateBitmap to have it created.
1339 * The memory PS is only temporary and freed again.
1340 *
1341 * This returns the handle of the new bitmap,
1342 * which can then be used for WinDrawBitmap and such, or
1343 * NULLHANDLE upon errors.
1344 */
1345
1346HBITMAP gpihCreateBmpFromPS(HAB hab, // in: anchor block
1347 HPS hpsScreen, // in: screen PS to copy from
1348 PRECTL prcl) // in: rectangle to copy
1349{
1350
1351 /* To copy an image from a display screen to a bit map:
1352 1. Associate the memory device context with a presentation space.
1353 2. Create a bit map.
1354 3. Select the bit map into the memory device context by calling GpiSetBitmap.
1355 4. Determine the location (in device coordinates) of the image.
1356 5. Call GpiBitBlt and copy the image to the bit map. */
1357
1358 HDC hdcMem;
1359 HPS hpsMem;
1360 HBITMAP hbm = NULLHANDLE;
1361 POINTL aptl[3];
1362
1363 SIZEL szlPage = {0, 0};
1364 if (gpihCreateMemPS(hab, &szlPage, &hdcMem, &hpsMem))
1365 {
1366 if ((hbm = gpihCreateBitmap(hpsMem,
1367 prcl->xRight - prcl->xLeft,
1368 prcl->yTop - prcl->yBottom)))
1369 {
1370 // Associate the bit map and the memory presentation space.
1371 if (GpiSetBitmap(hpsMem, hbm)
1372 != HBM_ERROR)
1373 {
1374 // Copy the screen to the bit map.
1375 aptl[0].x = 0; // lower-left corner of destination rectangle
1376 aptl[0].y = 0;
1377 aptl[1].x = prcl->xRight; // upper-right corner for both
1378 aptl[1].y = prcl->yTop;
1379 aptl[2].x = prcl->xLeft; // lower-left corner of source rectangle
1380 aptl[2].y = prcl->yBottom;
1381
1382 if (GpiBitBlt(hpsMem, hpsScreen,
1383 sizeof(aptl) / sizeof(POINTL), // Number of points in aptl
1384 aptl,
1385 ROP_SRCCOPY,
1386 BBO_IGNORE)
1387 == GPI_ERROR)
1388 {
1389 // error during bitblt:
1390 GpiDeleteBitmap(hbm);
1391 hbm = NULLHANDLE; // for return code
1392 }
1393 } else {
1394 // error selecting bitmap for hpsMem:
1395 GpiDeleteBitmap(hbm);
1396 hbm = NULLHANDLE; // for return code
1397 }
1398 }
1399
1400 GpiDestroyPS(hpsMem);
1401 DevCloseDC(hdcMem);
1402 } // end if (hdcMem = DevOpenDC())
1403
1404 return (hbm);
1405}
1406
1407/*
1408 *@@ gpihCreateHalftonedBitmap:
1409 * this creates a half-toned copy of the
1410 * input bitmap by doing the following:
1411 *
1412 * 1) create a new bitmap with the size of hbmSource;
1413 * 2) copy hbmSource to the new bitmap (using GpiWCBitBlt);
1414 * 3) overpaint every other pixel with lColorGray.
1415 *
1416 * Note that the memory DC is switched to RGB mode, so
1417 * lColorGray better be an RGB color.
1418 *
1419 * Note: hbmSource must _not_ be selected into any device
1420 * context, or otherwise GpiWCBitBlt will fail.
1421 *
1422 * This returns the new bitmap or NULLHANDLE upon errors.
1423 *
1424 *@added V0.9.0
1425 */
1426
1427HBITMAP gpihCreateHalftonedBitmap(HAB hab, // in: anchor block
1428 HBITMAP hbmSource, // in: source bitmap
1429 LONG lColorGray) // in: color used for gray
1430{
1431 HBITMAP hbmReturn = NULLHANDLE;
1432
1433 HDC hdcMem;
1434 HPS hpsMem;
1435 BITMAPINFOHEADER2 bmi;
1436 // RECTL rclSource;
1437
1438 if (hbmSource)
1439 {
1440 SIZEL szlPage;
1441 // query bitmap info
1442 bmi.cbFix = sizeof(bmi);
1443 GpiQueryBitmapInfoHeader(hbmSource, &bmi);
1444
1445 szlPage.cx = bmi.cx;
1446 szlPage.cy = bmi.cy;
1447 if (gpihCreateMemPS(hab, &szlPage, &hdcMem, &hpsMem))
1448 {
1449 if ((hbmReturn = gpihCreateBitmap(hpsMem,
1450 bmi.cx,
1451 bmi.cy)))
1452 {
1453 if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
1454 {
1455 POINTL aptl[4];
1456
1457 // step 1: copy bitmap
1458 memset(aptl, 0, sizeof(POINTL) * 4);
1459 // aptl[0]: target bottom-left, is all 0
1460 // aptl[1]: target top-right (inclusive!)
1461 aptl[1].x = bmi.cx - 1;
1462 aptl[1].y = bmi.cy - 1;
1463 // aptl[2]: source bottom-left, is all 0
1464
1465 // aptl[3]: source top-right (exclusive!)
1466 aptl[3].x = bmi.cx;
1467 aptl[3].y = bmi.cy;
1468 GpiWCBitBlt(hpsMem, // target HPS (bmp selected)
1469 hbmSource,
1470 4L, // must always be 4
1471 &aptl[0], // points array
1472 ROP_SRCCOPY,
1473 BBO_IGNORE);
1474 // doesn't matter here, because we're not stretching
1475
1476 // step 2: overpaint bitmap
1477 // with half-toned pattern
1478
1479 gpihSwitchToRGB(hpsMem);
1480
1481 GpiMove(hpsMem, &aptl[0]); // still 0, 0
1482 aptl[0].x = bmi.cx - 1;
1483 aptl[0].y = bmi.cy - 1;
1484 GpiSetColor(hpsMem, lColorGray);
1485 GpiSetPattern(hpsMem, PATSYM_HALFTONE);
1486 GpiBox(hpsMem,
1487 DRO_FILL, // interior only
1488 &aptl[0],
1489 0, 0); // no corner rounding
1490
1491 // unselect bitmap
1492 GpiSetBitmap(hpsMem, NULLHANDLE);
1493 } // end if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
1494 else
1495 {
1496 // error selecting bitmap:
1497 GpiDeleteBitmap(hbmReturn);
1498 hbmReturn = NULLHANDLE;
1499 }
1500 } // end if (hbmReturn = gpihCreateBitmap...
1501
1502 GpiDestroyPS(hpsMem);
1503 DevCloseDC(hdcMem);
1504 } // end if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
1505 } // end if (hbmSource)
1506
1507 return (hbmReturn);
1508}
1509
1510/*
1511 *@@ gpihLoadBitmapFile:
1512 * this loads the specified bitmap file into
1513 * the given HPS. Note that the bitmap is _not_
1514 * yet selected into the HPS.
1515 *
1516 * This function can currently only handle OS/2 1.3
1517 * bitmaps.
1518 *
1519 * Returns the new bitmap handle or NULL upon errors
1520 * (e.g. if an OS/2 2.0 bitmap was accessed).
1521 *
1522 * In the latter case, *pulError is set to one of
1523 * the following:
1524 * -- -1: file not found
1525 * -- -2: malloc failed
1526 * -- -3: the bitmap data could not be read (fopen failed)
1527 * -- -4: file format not recognized (maybe OS/2 2.0 bitmap)
1528 * -- -5: GpiCreateBitmap error (maybe file corrupt)
1529 *
1530 *@@changed V0.9.4 (2000-08-03) [umoeller]: this didn't return NULLHANDLE on errors
1531 */
1532
1533HBITMAP gpihLoadBitmapFile(HPS hps, // in: HPS for bmp
1534 PSZ pszBmpFile, // in: bitmap filename
1535 PULONG pulError) // out: error code if FALSE is returned
1536{
1537 HBITMAP hbm = NULLHANDLE;
1538 PBITMAPFILEHEADER2 pbfh;
1539
1540 struct stat st;
1541 PBYTE pBmpData;
1542 FILE *BmpFile;
1543
1544 if (stat(pszBmpFile, &st) == 0)
1545 {
1546
1547 if ((pBmpData = (PBYTE)malloc(st.st_size)))
1548 {
1549 // open bmp file
1550 if ((BmpFile = fopen(pszBmpFile, "rb")))
1551 {
1552 // read bmp data
1553 fread(pBmpData, 1, st.st_size, BmpFile);
1554 fclose(BmpFile);
1555
1556 // check bitmap magic codes
1557 if (pBmpData[0] == 'B' && pBmpData[1] == 'M')
1558 {
1559 pbfh = (PBITMAPFILEHEADER2)pBmpData;
1560 hbm = GpiCreateBitmap(hps,
1561 &pbfh->bmp2,
1562 CBM_INIT,
1563 (pBmpData + pbfh->offBits),
1564 (PBITMAPINFO2)&pbfh->bmp2);
1565
1566 if (hbm == NULLHANDLE)
1567 {
1568 if (pulError)
1569 *pulError = -5;
1570 }
1571 }
1572 else if (pulError)
1573 *pulError = -4;
1574
1575 }
1576 else if (pulError)
1577 *pulError = -3;
1578
1579 free(pBmpData);
1580 }
1581 else if (pulError)
1582 *pulError = -2;
1583 }
1584 else if (pulError)
1585 *pulError = -1;
1586
1587 return (hbm);
1588}
1589
1590/*
1591 *@@ gpihStretchBitmap:
1592 * this copies hbmSource to the bitmap selected
1593 * into hpsTarget, which must be a memory PS.
1594 *
1595 * The source size is the whole size of hbmSource,
1596 * the target size is specified in prclTarget
1597 * (which is exclusive, meaning that the top right
1598 * corner of that rectangle lies _outside_ the target).
1599 *
1600 * This uses GpiWCBitBlt to stretch the bitmap.
1601 * hbmSource therefore must _not_ be selected
1602 * into any presentation space, or GpiWCBitBlt will
1603 * fail.
1604 *
1605 * If (fPropotional == TRUE), the target size is
1606 * modified so that the proportions of the bitmap
1607 * are preserved. The bitmap data will then be
1608 * copied to a subrectangle of the target bitmap:
1609 * there will be extra space either to the left
1610 * and right of the bitmap data or to the bottom
1611 * and top.
1612 * The outside areas of the target bitmap are
1613 * not changed then, so you might want to fill
1614 * the bitmap with some color first.
1615 *
1616 * This returns the return value of GpiWCBitBlt,
1617 * which can be:
1618 * -- GPI_OK
1619 * -- GPI_HITS: correlate hits
1620 * -- GPI_ERROR: error occured (probably either hbmSource not free
1621 * or no bitmap selected into hpsTarget)
1622 *
1623 *@added V0.9.0
1624 */
1625
1626LONG gpihStretchBitmap(HPS hpsTarget, // in: memory PS to copy bitmap to
1627 HBITMAP hbmSource, // in: bitmap to be copied into hpsTarget (must be free)
1628 PRECTL prclSource, // in: source rectangle -- if NULL, use size of bitmap
1629 PRECTL prclTarget, // in: target rectangle (req.)
1630 BOOL fProportional) // in: preserve proportions when stretching?
1631{
1632 LONG lHits = 0;
1633 BITMAPINFOHEADER2 bih2;
1634 POINTL aptl[4];
1635 BOOL fCalculated = FALSE;
1636
1637 memset(aptl, 0, sizeof(POINTL) * 4);
1638
1639 bih2.cbFix = sizeof(bih2);
1640 GpiQueryBitmapInfoHeader(hbmSource,
1641 &bih2);
1642
1643 // aptl[2]: source bottom-left, is all 0
1644 // aptl[3]: source top-right (exclusive!)
1645 aptl[3].x = bih2.cx;
1646 aptl[3].y = bih2.cy;
1647
1648 if (fProportional)
1649 {
1650 // proportional mode:
1651
1652 // 1) find out whether cx or cy is too
1653 // large
1654
1655 ULONG ulPropSource = (bih2.cx * 1000)
1656 / bih2.cy;
1657 // e.g. if the bmp is 200 x 100, we now have 2000
1658 ULONG ulPropTarget = ((prclTarget->xRight - prclTarget->xLeft) * 1000)
1659 / (prclTarget->yTop - prclTarget->yBottom);
1660 // case 1: if prclTarget is 300 x 100, we now have 3000 (> ulPropSource)
1661 // case 2: if prclTarget is 150 x 100, we now have 1500 (< ulPropSource)
1662
1663 // case 1:
1664 if (ulPropTarget > ulPropSource)
1665 {
1666 // prclTarget is too wide (horizontally):
1667 // decrease width, keep height
1668
1669 ULONG cx = (prclTarget->xRight - prclTarget->xLeft);
1670 ULONG cxNew = (cx * ulPropSource) / ulPropTarget;
1671
1672 // aptl[0]: target bottom-left
1673 // move left right (towards center)
1674 aptl[0].x = prclTarget->xLeft + ((cx - cxNew) / 2);
1675 aptl[0].y = prclTarget->yBottom;
1676
1677 // aptl[1]: target top-right (inclusive!)
1678 aptl[1].x = aptl[0].x + cxNew;
1679 aptl[1].y = prclTarget->yTop;
1680
1681 fCalculated = TRUE;
1682 }
1683 else
1684 {
1685 // prclTarget is too high (vertically):
1686 // keep width, decrease height
1687
1688 ULONG cy = (prclTarget->yTop - prclTarget->yBottom);
1689 ULONG cyNew = (cy * ulPropTarget) / ulPropSource;
1690
1691 // aptl[0]: target bottom-left
1692 aptl[0].x = prclTarget->xLeft;
1693 // move bottom up (towards center)
1694 aptl[0].y = prclTarget->yBottom + ((cy - cyNew) / 2);
1695
1696 // aptl[1]: target top-right (inclusive!)
1697 aptl[1].x = prclTarget->xRight;
1698 aptl[1].y = aptl[0].y + cyNew;
1699 // (prclTarget->yTop * ulPropSource) / ulPropTarget;
1700
1701 fCalculated = TRUE;
1702 }
1703 } // end if (pa->ulFlags & ANF_PROPORTIONAL)
1704
1705 if (!fCalculated)
1706 {
1707 // non-proportional mode or equal proportions:
1708 // stretch to whole size of prclTarget
1709
1710 // aptl[0]: target bottom-left
1711 aptl[0].x = prclTarget->xLeft;
1712 aptl[0].y = prclTarget->yBottom;
1713 // aptl[1]: target top-right (inclusive!)
1714 aptl[1].x = prclTarget->xRight;
1715 aptl[1].y = prclTarget->yTop;
1716 }
1717
1718 lHits = GpiWCBitBlt(hpsTarget, // target HPS (bmp selected)
1719 hbmSource,
1720 4L, // must always be 4
1721 &aptl[0], // points array
1722 ROP_SRCCOPY,
1723 BBO_IGNORE);
1724 // ignore eliminated rows or
1725 // columns; useful for color
1726
1727 return (lHits);
1728}
1729
1730/*
1731 *@@ gpihIcon2Bitmap:
1732 * this paints the given icon/pointer into
1733 * a bitmap.
1734 *
1735 * Returns FALSE upon errors.
1736 *
1737 *@@added V0.9.0 [umoeller]
1738 */
1739
1740BOOL gpihIcon2Bitmap(HPS hpsMem, // in: target memory PS with bitmap selected into it
1741 HPOINTER hptr, // in: source icon
1742 LONG lBkgndColor, // in: background color for transparent areas
1743 ULONG ulIconSize) // in: icon size (should be the value of WinQuerySysValue(HWND_DESKTOP, SV_CXICON))
1744{
1745 BOOL brc = FALSE;
1746 POINTERINFO pi;
1747
1748 // Each icon consists of two (really three)
1749 // bitmaps, which are stored in the POINTERINFO
1750 // structure:
1751 // pi.hbmColor is the actual bitmap to be
1752 // drawn. The parts that are
1753 // to be transparent or inverted
1754 // are black in this image.
1755 // pi.hbmPointer has twice the height of
1756 // hbmColor. The upper bitmap
1757 // contains an XOR mask (for
1758 // inverting parts), the lower
1759 // bitmap an AND mask (for
1760 // transparent parts).
1761 if (WinQueryPointerInfo(hptr, &pi))
1762 {
1763 POINTL aptl[4];
1764 memset(aptl, 0, sizeof(POINTL) * 4);
1765
1766 // aptl[0]: target bottom-left, is all 0
1767
1768 // aptl[1]: target top-right (inclusive!)
1769 aptl[1].x = ulIconSize;
1770 aptl[1].y = ulIconSize;
1771
1772 // aptl[2]: source bottom-left, is all 0
1773
1774 // aptl[3]: source top-right (exclusive!)
1775 aptl[3].x = ulIconSize + 1;
1776 aptl[3].y = ulIconSize + 1;
1777
1778 GpiSetColor(hpsMem, CLR_WHITE);
1779 GpiSetBackColor(hpsMem, CLR_BLACK);
1780
1781 // GpiErase(hpsMem);
1782
1783 // work on the AND image
1784 GpiWCBitBlt(hpsMem,
1785 pi.hbmPointer,
1786 4L, // must always be 4
1787 &aptl[0], // point array
1788 ROP_SRCAND, // source AND target
1789 BBO_OR);
1790
1791 // paint the real image
1792 if (pi.hbmColor)
1793 GpiWCBitBlt(hpsMem,
1794 pi.hbmColor,
1795 4L, // must always be 4
1796 &aptl[0], // point array
1797 ROP_SRCPAINT, // source OR target
1798 BBO_OR);
1799
1800 GpiSetColor(hpsMem, lBkgndColor);
1801 // work on the XOR image
1802 aptl[2].y = ulIconSize;
1803 aptl[3].y = (ulIconSize * 2) + 1;
1804 GpiWCBitBlt(hpsMem,
1805 pi.hbmPointer,
1806 4L, // must always be 4
1807 &aptl[0], // point array
1808 ROP_SRCINVERT,
1809 BBO_OR);
1810
1811 brc = TRUE;
1812 }
1813
1814 return (brc);
1815}
1816
Note: See TracBrowser for help on using the repository browser.