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

Last change on this file since 19 was 18, checked in by umoeller, 25 years ago

Tons of updates.

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