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

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

Major updates; timers, LVM, miscellaneous.

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