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

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

Initial checkin of helpers code which used to be in WarpIN.

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