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

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

Fixes for V0.9.7.

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