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

Last change on this file since 52 was 46, checked in by umoeller, 24 years ago

Mistc. updates for WarpIN, new features.

  • 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,
1383 hpsScreen,
1384 sizeof(aptl) / sizeof(POINTL), // Number of points in aptl
1385 aptl,
1386 ROP_SRCCOPY,
1387 BBO_IGNORE)
1388 == GPI_ERROR)
1389 {
1390 // error during bitblt:
1391 GpiDeleteBitmap(hbm);
1392 hbm = NULLHANDLE; // for return code
1393 }
1394 } else {
1395 // error selecting bitmap for hpsMem:
1396 GpiDeleteBitmap(hbm);
1397 hbm = NULLHANDLE; // for return code
1398 }
1399 }
1400
1401 GpiDestroyPS(hpsMem);
1402 DevCloseDC(hdcMem);
1403 } // end if (hdcMem = DevOpenDC())
1404
1405 return (hbm);
1406}
1407
1408/*
1409 *@@ gpihCreateHalftonedBitmap:
1410 * this creates a half-toned copy of the
1411 * input bitmap by doing the following:
1412 *
1413 * 1) create a new bitmap with the size of hbmSource;
1414 * 2) copy hbmSource to the new bitmap (using GpiWCBitBlt);
1415 * 3) overpaint every other pixel with lColorGray.
1416 *
1417 * Note that the memory DC is switched to RGB mode, so
1418 * lColorGray better be an RGB color.
1419 *
1420 * Note: hbmSource must _not_ be selected into any device
1421 * context, or otherwise GpiWCBitBlt will fail.
1422 *
1423 * This returns the new bitmap or NULLHANDLE upon errors.
1424 *
1425 *@added V0.9.0
1426 */
1427
1428HBITMAP gpihCreateHalftonedBitmap(HAB hab, // in: anchor block
1429 HBITMAP hbmSource, // in: source bitmap
1430 LONG lColorGray) // in: color used for gray
1431{
1432 HBITMAP hbmReturn = NULLHANDLE;
1433
1434 HDC hdcMem;
1435 HPS hpsMem;
1436 BITMAPINFOHEADER2 bmi;
1437 // RECTL rclSource;
1438
1439 if (hbmSource)
1440 {
1441 SIZEL szlPage;
1442 // query bitmap info
1443 bmi.cbFix = sizeof(bmi);
1444 GpiQueryBitmapInfoHeader(hbmSource, &bmi);
1445
1446 szlPage.cx = bmi.cx;
1447 szlPage.cy = bmi.cy;
1448 if (gpihCreateMemPS(hab, &szlPage, &hdcMem, &hpsMem))
1449 {
1450 if ((hbmReturn = gpihCreateBitmap(hpsMem,
1451 bmi.cx,
1452 bmi.cy)))
1453 {
1454 if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
1455 {
1456 POINTL aptl[4];
1457
1458 // step 1: copy bitmap
1459 memset(aptl, 0, sizeof(POINTL) * 4);
1460 // aptl[0]: target bottom-left, is all 0
1461 // aptl[1]: target top-right (inclusive!)
1462 aptl[1].x = bmi.cx - 1;
1463 aptl[1].y = bmi.cy - 1;
1464 // aptl[2]: source bottom-left, is all 0
1465
1466 // aptl[3]: source top-right (exclusive!)
1467 aptl[3].x = bmi.cx;
1468 aptl[3].y = bmi.cy;
1469 GpiWCBitBlt(hpsMem, // target HPS (bmp selected)
1470 hbmSource,
1471 4L, // must always be 4
1472 &aptl[0], // points array
1473 ROP_SRCCOPY,
1474 BBO_IGNORE);
1475 // doesn't matter here, because we're not stretching
1476
1477 // step 2: overpaint bitmap
1478 // with half-toned pattern
1479
1480 gpihSwitchToRGB(hpsMem);
1481
1482 GpiMove(hpsMem, &aptl[0]); // still 0, 0
1483 aptl[0].x = bmi.cx - 1;
1484 aptl[0].y = bmi.cy - 1;
1485 GpiSetColor(hpsMem, lColorGray);
1486 GpiSetPattern(hpsMem, PATSYM_HALFTONE);
1487 GpiBox(hpsMem,
1488 DRO_FILL, // interior only
1489 &aptl[0],
1490 0, 0); // no corner rounding
1491
1492 // unselect bitmap
1493 GpiSetBitmap(hpsMem, NULLHANDLE);
1494 } // end if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
1495 else
1496 {
1497 // error selecting bitmap:
1498 GpiDeleteBitmap(hbmReturn);
1499 hbmReturn = NULLHANDLE;
1500 }
1501 } // end if (hbmReturn = gpihCreateBitmap...
1502
1503 GpiDestroyPS(hpsMem);
1504 DevCloseDC(hdcMem);
1505 } // end if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
1506 } // end if (hbmSource)
1507
1508 return (hbmReturn);
1509}
1510
1511/*
1512 *@@ gpihLoadBitmapFile:
1513 * this loads the specified bitmap file into
1514 * the given HPS. Note that the bitmap is _not_
1515 * yet selected into the HPS.
1516 *
1517 * This function can currently only handle OS/2 1.3
1518 * bitmaps.
1519 *
1520 * Returns the new bitmap handle or NULL upon errors
1521 * (e.g. if an OS/2 2.0 bitmap was accessed).
1522 *
1523 * In the latter case, *pulError is set to one of
1524 * the following:
1525 * -- -1: file not found
1526 * -- -2: malloc failed
1527 * -- -3: the bitmap data could not be read (fopen failed)
1528 * -- -4: file format not recognized (maybe OS/2 2.0 bitmap)
1529 * -- -5: GpiCreateBitmap error (maybe file corrupt)
1530 *
1531 *@@changed V0.9.4 (2000-08-03) [umoeller]: this didn't return NULLHANDLE on errors
1532 */
1533
1534HBITMAP gpihLoadBitmapFile(HPS hps, // in: HPS for bmp
1535 PSZ pszBmpFile, // in: bitmap filename
1536 PULONG pulError) // out: error code if FALSE is returned
1537{
1538 HBITMAP hbm = NULLHANDLE;
1539 PBITMAPFILEHEADER2 pbfh;
1540
1541 struct stat st;
1542 PBYTE pBmpData;
1543 FILE *BmpFile;
1544
1545 if (stat(pszBmpFile, &st) == 0)
1546 {
1547
1548 if ((pBmpData = (PBYTE)malloc(st.st_size)))
1549 {
1550 // open bmp file
1551 if ((BmpFile = fopen(pszBmpFile, "rb")))
1552 {
1553 // read bmp data
1554 fread(pBmpData, 1, st.st_size, BmpFile);
1555 fclose(BmpFile);
1556
1557 // check bitmap magic codes
1558 if (pBmpData[0] == 'B' && pBmpData[1] == 'M')
1559 {
1560 pbfh = (PBITMAPFILEHEADER2)pBmpData;
1561 hbm = GpiCreateBitmap(hps,
1562 &pbfh->bmp2,
1563 CBM_INIT,
1564 (pBmpData + pbfh->offBits),
1565 (PBITMAPINFO2)&pbfh->bmp2);
1566
1567 if (hbm == NULLHANDLE)
1568 {
1569 if (pulError)
1570 *pulError = -5;
1571 }
1572 }
1573 else if (pulError)
1574 *pulError = -4;
1575
1576 }
1577 else if (pulError)
1578 *pulError = -3;
1579
1580 free(pBmpData);
1581 }
1582 else if (pulError)
1583 *pulError = -2;
1584 }
1585 else if (pulError)
1586 *pulError = -1;
1587
1588 return (hbm);
1589}
1590
1591/*
1592 *@@ gpihStretchBitmap:
1593 * this copies hbmSource to the bitmap selected
1594 * into hpsTarget, which must be a memory PS.
1595 *
1596 * The source size is the whole size of hbmSource,
1597 * the target size is specified in prclTarget
1598 * (which is exclusive, meaning that the top right
1599 * corner of that rectangle lies _outside_ the target).
1600 *
1601 * This uses GpiWCBitBlt to stretch the bitmap.
1602 * hbmSource therefore must _not_ be selected
1603 * into any presentation space, or GpiWCBitBlt will
1604 * fail.
1605 *
1606 * If (fPropotional == TRUE), the target size is
1607 * modified so that the proportions of the bitmap
1608 * are preserved. The bitmap data will then be
1609 * copied to a subrectangle of the target bitmap:
1610 * there will be extra space either to the left
1611 * and right of the bitmap data or to the bottom
1612 * and top.
1613 * The outside areas of the target bitmap are
1614 * not changed then, so you might want to fill
1615 * the bitmap with some color first.
1616 *
1617 * This returns the return value of GpiWCBitBlt,
1618 * which can be:
1619 * -- GPI_OK
1620 * -- GPI_HITS: correlate hits
1621 * -- GPI_ERROR: error occured (probably either hbmSource not free
1622 * or no bitmap selected into hpsTarget)
1623 *
1624 *@added V0.9.0
1625 */
1626
1627LONG gpihStretchBitmap(HPS hpsTarget, // in: memory PS to copy bitmap to
1628 HBITMAP hbmSource, // in: bitmap to be copied into hpsTarget (must be free)
1629 PRECTL prclSource, // in: source rectangle -- if NULL, use size of bitmap
1630 PRECTL prclTarget, // in: target rectangle (req.)
1631 BOOL fProportional) // in: preserve proportions when stretching?
1632{
1633 LONG lHits = 0;
1634 BITMAPINFOHEADER2 bih2;
1635 POINTL aptl[4];
1636 BOOL fCalculated = FALSE;
1637
1638 memset(aptl, 0, sizeof(POINTL) * 4);
1639
1640 bih2.cbFix = sizeof(bih2);
1641 GpiQueryBitmapInfoHeader(hbmSource,
1642 &bih2);
1643
1644 // aptl[2]: source bottom-left, is all 0
1645 // aptl[3]: source top-right (exclusive!)
1646 aptl[3].x = bih2.cx;
1647 aptl[3].y = bih2.cy;
1648
1649 if (fProportional)
1650 {
1651 // proportional mode:
1652
1653 // 1) find out whether cx or cy is too
1654 // large
1655
1656 ULONG ulPropSource = (bih2.cx * 1000)
1657 / bih2.cy;
1658 // e.g. if the bmp is 200 x 100, we now have 2000
1659 ULONG ulPropTarget = ((prclTarget->xRight - prclTarget->xLeft) * 1000)
1660 / (prclTarget->yTop - prclTarget->yBottom);
1661 // case 1: if prclTarget is 300 x 100, we now have 3000 (> ulPropSource)
1662 // case 2: if prclTarget is 150 x 100, we now have 1500 (< ulPropSource)
1663
1664 // case 1:
1665 if (ulPropTarget > ulPropSource)
1666 {
1667 // prclTarget is too wide (horizontally):
1668 // decrease width, keep height
1669
1670 ULONG cx = (prclTarget->xRight - prclTarget->xLeft);
1671 ULONG cxNew = (cx * ulPropSource) / ulPropTarget;
1672
1673 // aptl[0]: target bottom-left
1674 // move left right (towards center)
1675 aptl[0].x = prclTarget->xLeft + ((cx - cxNew) / 2);
1676 aptl[0].y = prclTarget->yBottom;
1677
1678 // aptl[1]: target top-right (inclusive!)
1679 aptl[1].x = aptl[0].x + cxNew;
1680 aptl[1].y = prclTarget->yTop;
1681
1682 fCalculated = TRUE;
1683 }
1684 else
1685 {
1686 // prclTarget is too high (vertically):
1687 // keep width, decrease height
1688
1689 ULONG cy = (prclTarget->yTop - prclTarget->yBottom);
1690 ULONG cyNew = (cy * ulPropTarget) / ulPropSource;
1691
1692 // aptl[0]: target bottom-left
1693 aptl[0].x = prclTarget->xLeft;
1694 // move bottom up (towards center)
1695 aptl[0].y = prclTarget->yBottom + ((cy - cyNew) / 2);
1696
1697 // aptl[1]: target top-right (inclusive!)
1698 aptl[1].x = prclTarget->xRight;
1699 aptl[1].y = aptl[0].y + cyNew;
1700 // (prclTarget->yTop * ulPropSource) / ulPropTarget;
1701
1702 fCalculated = TRUE;
1703 }
1704 } // end if (pa->ulFlags & ANF_PROPORTIONAL)
1705
1706 if (!fCalculated)
1707 {
1708 // non-proportional mode or equal proportions:
1709 // stretch to whole size of prclTarget
1710
1711 // aptl[0]: target bottom-left
1712 aptl[0].x = prclTarget->xLeft;
1713 aptl[0].y = prclTarget->yBottom;
1714 // aptl[1]: target top-right (inclusive!)
1715 aptl[1].x = prclTarget->xRight;
1716 aptl[1].y = prclTarget->yTop;
1717 }
1718
1719 lHits = GpiWCBitBlt(hpsTarget, // target HPS (bmp selected)
1720 hbmSource,
1721 4L, // must always be 4
1722 &aptl[0], // points array
1723 ROP_SRCCOPY,
1724 BBO_IGNORE);
1725 // ignore eliminated rows or
1726 // columns; useful for color
1727
1728 return (lHits);
1729}
1730
1731/*
1732 *@@ gpihIcon2Bitmap:
1733 * this paints the given icon/pointer into
1734 * a bitmap.
1735 *
1736 * Returns FALSE upon errors.
1737 *
1738 *@@added V0.9.0 [umoeller]
1739 */
1740
1741BOOL gpihIcon2Bitmap(HPS hpsMem, // in: target memory PS with bitmap selected into it
1742 HPOINTER hptr, // in: source icon
1743 LONG lBkgndColor, // in: background color for transparent areas
1744 ULONG ulIconSize) // in: icon size (should be the value of WinQuerySysValue(HWND_DESKTOP, SV_CXICON))
1745{
1746 BOOL brc = FALSE;
1747 POINTERINFO pi;
1748
1749 // Each icon consists of two (really three)
1750 // bitmaps, which are stored in the POINTERINFO
1751 // structure:
1752 // pi.hbmColor is the actual bitmap to be
1753 // drawn. The parts that are
1754 // to be transparent or inverted
1755 // are black in this image.
1756 // pi.hbmPointer has twice the height of
1757 // hbmColor. The upper bitmap
1758 // contains an XOR mask (for
1759 // inverting parts), the lower
1760 // bitmap an AND mask (for
1761 // transparent parts).
1762 if (WinQueryPointerInfo(hptr, &pi))
1763 {
1764 POINTL aptl[4];
1765 memset(aptl, 0, sizeof(POINTL) * 4);
1766
1767 // aptl[0]: target bottom-left, is all 0
1768
1769 // aptl[1]: target top-right (inclusive!)
1770 aptl[1].x = ulIconSize;
1771 aptl[1].y = ulIconSize;
1772
1773 // aptl[2]: source bottom-left, is all 0
1774
1775 // aptl[3]: source top-right (exclusive!)
1776 aptl[3].x = ulIconSize + 1;
1777 aptl[3].y = ulIconSize + 1;
1778
1779 GpiSetColor(hpsMem, CLR_WHITE);
1780 GpiSetBackColor(hpsMem, CLR_BLACK);
1781
1782 // GpiErase(hpsMem);
1783
1784 // work on the AND image
1785 GpiWCBitBlt(hpsMem,
1786 pi.hbmPointer,
1787 4L, // must always be 4
1788 &aptl[0], // point array
1789 ROP_SRCAND, // source AND target
1790 BBO_OR);
1791
1792 // paint the real image
1793 if (pi.hbmColor)
1794 GpiWCBitBlt(hpsMem,
1795 pi.hbmColor,
1796 4L, // must always be 4
1797 &aptl[0], // point array
1798 ROP_SRCPAINT, // source OR target
1799 BBO_OR);
1800
1801 GpiSetColor(hpsMem, lBkgndColor);
1802 // work on the XOR image
1803 aptl[2].y = ulIconSize;
1804 aptl[3].y = (ulIconSize * 2) + 1;
1805 GpiWCBitBlt(hpsMem,
1806 pi.hbmPointer,
1807 4L, // must always be 4
1808 &aptl[0], // point array
1809 ROP_SRCINVERT,
1810 BBO_OR);
1811
1812 brc = TRUE;
1813 }
1814
1815 return (brc);
1816}
1817
Note: See TracBrowser for help on using the repository browser.