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

Last change on this file since 54 was 53, checked in by umoeller, 24 years ago

Dialog mgr.

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