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

Last change on this file since 57 was 56, checked in by umoeller, 24 years ago

misc changes

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