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

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

Misc. changes for V0.9.7.

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