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

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

Misc. updates.

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