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

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

Final changes for 0.9.7, i hope...

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