home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwphescr.zip / XWPH0208.ZIP / src / helpers / gpih.c < prev    next >
C/C++ Source or Header  |  2002-08-11  |  110KB  |  3,046 lines

  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-2002 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_DOSMODULEMGR
  37. #define INCL_DOSRESOURCES
  38. #define INCL_DOSERRORS
  39.  
  40. #define INCL_WINWINDOWMGR
  41. #define INCL_WINMESSAGEMGR
  42. #define INCL_WINPOINTERS
  43. #define INCL_WINSYS
  44.  
  45. #define INCL_GPIPRIMITIVES
  46. #define INCL_GPIBITMAPS
  47. #define INCL_GPILOGCOLORTABLE
  48. #define INCL_GPILCIDS
  49. #include <os2.h>
  50.  
  51. #include <stdlib.h>
  52. #include <string.h>
  53. #include <stdio.h>
  54. #include <sys/types.h>
  55. #include <sys/stat.h>
  56.  
  57. #include "setup.h"                      // code generation and debugging options
  58.  
  59. #ifdef WINH_STANDARDWRAPPERS
  60. #undef WINH_STANDARDWRAPPERS
  61. #endif
  62. #include "helpers\dosh.h"
  63. #include "helpers\winh.h"
  64. #include "helpers\gpih.h"
  65.  
  66. #pragma hdrstop
  67.  
  68. // array for querying device capabilities (gpihQueryDisplayCaps)
  69. LONG            DisplayCaps[CAPS_DEVICE_POLYSET_POINTS] = {0};
  70. BOOL            G_fCapsQueried = FALSE;
  71.  
  72. /*
  73.  *@@category: Helpers\PM helpers\GPI helpers
  74.  *      See gpih.c.
  75.  */
  76.  
  77. /*
  78.  *@@category: Helpers\PM helpers\GPI helpers\Devices
  79.  */
  80.  
  81. /*
  82.  *@@gloss: GPI_rectangles GPI rectangles
  83.  *      OS/2 PM (and GPI) uses two types of rectangles. This is rarely
  84.  *      mentioned in the documentation, so a word is in order here.
  85.  *
  86.  *      In general, graphics operations involving device coordinates
  87.  *      (such as regions, bit maps and bit blts, and window management)
  88.  *      use inclusive-exclusive rectangles. In other words, with
  89.  *      those rectangles, xRight - xLeft is the same as the width
  90.  *      of the rectangle (and yTop - yBottom = height).
  91.  *
  92.  *      All other graphics operations, such as GPI functions that
  93.  *      define paths, use inclusive-inclusive rectangles.
  94.  *
  95.  *      This can be a problem with mixing Win and Gpi functions. For
  96.  *      example, WinQueryWindowRect returns an inclusive-exclusive
  97.  *      rectangle (tested V0.9.7 (2000-12-20) [umoeller]).
  98.  *
  99.  *      WinFillRect expects an inclusive-exclusive rectangle, so it
  100.  *      will work with a rectangle from WinQueryWindowRect directly.
  101.  *
  102.  *      By contrast, the GpiBox expects an inclusive-inclusive rectangle.
  103.  */
  104.  
  105. /* ******************************************************************
  106.  *
  107.  *   Global variables
  108.  *
  109.  ********************************************************************/
  110.  
  111. static HMTX     G_hmtxLCIDs = NULLHANDLE;
  112.  
  113. /* ******************************************************************
  114.  *
  115.  *   Rectangle helpers
  116.  *
  117.  ********************************************************************/
  118.  
  119. /*
  120.  *@@ gpihIsPointInRect:
  121.  *      like WinPtInRect, but doesn't need a HAB.
  122.  *
  123.  *      NOTE: as opposed to WinPtInRect, prcl is
  124.  *      considered inclusive, that is, TRUE is
  125.  *      returned even if x or y are exactly
  126.  *      the same as prcl->xRight or prcl->yTop.
  127.  *
  128.  *@@added V0.9.9 (2001-02-28) [umoeller]
  129.  */
  130.  
  131. BOOL gpihIsPointInRect(PRECTL prcl,
  132.                        LONG x,
  133.                        LONG y)
  134. {
  135.     if (prcl)
  136.     {
  137.         return (    (x >= prcl->xLeft)
  138.                  && (x <= prcl->xRight)
  139.                  && (y >= prcl->yBottom)
  140.                  && (y <= prcl->yTop)
  141.                );
  142.     }
  143.  
  144.     return FALSE;
  145. }
  146.  
  147. /*
  148.  *@@ gpihInflateRect:
  149.  *      Positive l will make the rectangle larger.
  150.  *      Negative l will make the rectangle smaller.
  151.  *
  152.  *@@added V0.9.9 (2001-02-28) [umoeller]
  153.  */
  154.  
  155. VOID gpihInflateRect(PRECTL prcl,
  156.                      LONG l)
  157. {
  158.     if (prcl && l)
  159.     {
  160.         prcl->xLeft -= l;
  161.         prcl->yBottom -= l;
  162.         prcl->xRight += l;
  163.         prcl->yTop += l;
  164.     }
  165. }
  166.  
  167. /* ******************************************************************
  168.  *
  169.  *   Device helpers
  170.  *
  171.  ********************************************************************/
  172.  
  173. /*
  174.  *@@ gpihQueryDisplayCaps:
  175.  *      this returns certain device capabilities of
  176.  *      the display device. ulIndex must be one of
  177.  *      the indices as described in DevQueryCaps.
  178.  *
  179.  *      This function will load all the device capabilities
  180.  *      only once into a global array and re-use them afterwards.
  181.  *
  182.  *@@changed V0.9.16 (2001-12-18) [umoeller]: fixed multiple loads
  183.  */
  184.  
  185. ULONG gpihQueryDisplayCaps(ULONG ulIndex)
  186. {
  187.     if (!G_fCapsQueried)
  188.     {
  189.         HPS hps = WinGetScreenPS(HWND_DESKTOP);
  190.         HDC hdc = GpiQueryDevice(hps);
  191.         DevQueryCaps(hdc, 0, CAPS_DEVICE_POLYSET_POINTS, &DisplayCaps[0]);
  192.         G_fCapsQueried = TRUE;      // was missing V0.9.16 (2001-12-18) [umoeller]
  193.     }
  194.  
  195.     return DisplayCaps[ulIndex];
  196. }
  197.  
  198. /*
  199.  *@@category: Helpers\PM helpers\GPI helpers\Colors
  200.  */
  201.  
  202. /* ******************************************************************
  203.  *
  204.  *   Color helpers
  205.  *
  206.  ********************************************************************/
  207.  
  208. /*
  209.  * HackColor:
  210.  *
  211.  */
  212.  
  213. static VOID HackColor(PBYTE pb, double dFactor)
  214. {
  215.     ULONG ul = (ULONG)((double)(*pb) * dFactor);
  216.     if (ul > 255)
  217.         *pb = 255;
  218.     else
  219.         *pb = (BYTE)ul;
  220. }
  221.  
  222. /*
  223.  *@@ gpihManipulateRGB:
  224.  *      this changes an RGB color value
  225.  *      by multiplying each color component
  226.  *      (red, green, blue) with dFactor.
  227.  *
  228.  *      Each color component is treated separately,
  229.  *      so if overflows occur (because dFactor
  230.  *      is > 1), this does not affect the other
  231.  *      components.
  232.  *
  233.  *@@changed V0.9.11 (2001-04-25) [umoeller]: changed prototype to use a double now
  234.  */
  235.  
  236. VOID gpihManipulateRGB(PLONG plColor,       // in/out: RGB color
  237.                        double dFactor)      // in: factor (> 1 makes brigher, < 1 makes darker)
  238. {
  239.     PBYTE   pb = (PBYTE)plColor;
  240.  
  241.     // in memory, the bytes are blue, green, red, unused
  242.  
  243.     // blue
  244.     ULONG   ul = (ULONG)(   (double)(*pb) * dFactor
  245.                         );
  246.     if (ul > 255)
  247.         *pb = 255;
  248.     else
  249.         *pb = (BYTE)ul;
  250.  
  251.     // green
  252.     ul = (ULONG)(   (double)(*(++pb)) * dFactor
  253.                 );
  254.     if (ul > 255)
  255.         *pb = 255;
  256.     else
  257.         *pb = (BYTE)ul;
  258.  
  259.     // red
  260.     ul = (ULONG)(   (double)(*(++pb)) * dFactor
  261.                 );
  262.     if (ul > 255)
  263.         *pb = 255;
  264.     else
  265.         *pb = (BYTE)ul;
  266. }
  267.  
  268. #define MEDIATE(a, b) (LONG)(a) + (((LONG)(b) - (LONG)(a)) / 2)
  269.  
  270. /*
  271.  *@@ gpihMediumRGB:
  272.  *      returns the arithmetic medium color between
  273.  *      lcol1 and lcol2.
  274.  *
  275.  *@@added V0.9.19 (2002-05-28) [umoeller]
  276.  */
  277.  
  278. LONG gpihMediumRGB(LONG lcol1, LONG lcol2)
  279. {
  280.     return MAKE_RGB(
  281.                      MEDIATE(GET_RED(lcol1),
  282.                              GET_RED(lcol2)),
  283.                      MEDIATE(GET_GREEN(lcol1),
  284.                              GET_GREEN(lcol2)),
  285.                      MEDIATE(GET_BLUE(lcol1),
  286.                              GET_BLUE(lcol2))
  287.                    );
  288. }
  289.  
  290. /*
  291.  *@@ gpihSwitchToRGB:
  292.  *      this switches the given HPS into RGB mode. You should
  293.  *      always use this if you are operating with RGB colors.
  294.  *
  295.  *      This is just a shortcut to calling
  296.  *
  297.  +          GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
  298.  *
  299.  *@@changed V0.9.7 (2001-01-15) [umoeller]: turned macro into function to reduce fixups
  300.  */
  301.  
  302. BOOL gpihSwitchToRGB(HPS hps)
  303. {
  304.     return GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
  305. }
  306.  
  307. /*
  308.  *@@category: Helpers\PM helpers\GPI helpers\Drawing primitives
  309.  */
  310.  
  311. /* ******************************************************************
  312.  *
  313.  *   Drawing primitives helpers
  314.  *
  315.  ********************************************************************/
  316.  
  317. /*
  318.  *@@ gpihDrawRect:
  319.  *      this draws a simple rectangle with the current
  320.  *      color (use GpiSetColor before calling this function).
  321.  *
  322.  *      The specified rectangle is inclusive, that is, the top
  323.  *      right corner specifies the top right pixel to be drawn
  324.  *      (see @GPI_rectangles).
  325.  *
  326.  *      This sets the current position to the bottom left corner
  327.  *      of prcl.
  328.  *
  329.  *@added V0.9.0
  330.  */
  331.  
  332. VOID gpihDrawRect(HPS hps,      // in: presentation space for output
  333.                   PRECTL prcl)  // in: rectangle to draw (inclusive)
  334. {
  335.     POINTL ptl1;
  336.  
  337.     ptl1.x = prcl->xLeft;
  338.     ptl1.y = prcl->yBottom;
  339.     GpiMove(hps, &ptl1);
  340.     ptl1.y = prcl->yTop - 1;
  341.     GpiLine(hps, &ptl1);
  342.     ptl1.x = prcl->xRight - 1;
  343.     GpiLine(hps, &ptl1);
  344.     ptl1.y = prcl->yBottom;
  345.     GpiLine(hps, &ptl1);
  346.     ptl1.x = prcl->xLeft;
  347.     GpiLine(hps, &ptl1);
  348. }
  349.  
  350. /*
  351.  *@@ gpihBox:
  352.  *      this is a shortcurt to GpiBox, using the specified
  353.  *      rectangle.
  354.  *
  355.  *      As opposed to WinFillRect, this works with memory
  356.  *      (bitmap) PS's also.
  357.  *
  358.  *      The specified rectangle is inclusive, that is, the top
  359.  *      right corner specifies the top right pixel to be drawn.
  360.  *      This is different from WinFillRect (see @GPI_rectangles).
  361.  *
  362.  *      Changes to the HPS:
  363.  *
  364.  *      --  the current position is moved to the lower left
  365.  *          corner of *prcl.
  366.  *
  367.  *@@changed V0.9.0 [umoeller]: renamed from gpihFillRect
  368.  *@@changed V0.9.0 [umoeller]: modified function prototype to support lControl
  369.  *@@changed V0.9.7 (2001-01-17) [umoeller]: removed lColor
  370.  */
  371.  
  372. VOID gpihBox(HPS hps,              // in: presentation space for output
  373.              LONG lControl,        // in: one of DRO_OUTLINE, DRO_FILL, DRO_OUTLINEFILL
  374.              PRECTL prcl)          // in: rectangle to draw (inclusive)
  375. {
  376.     POINTL      ptl;
  377.  
  378.     ptl.x = prcl->xLeft;
  379.     ptl.y = prcl->yBottom;
  380.     GpiMove(hps, &ptl);
  381.     ptl.x = prcl->xRight;
  382.     ptl.y = prcl->yTop;
  383.     GpiBox(hps,
  384.            lControl,    // DRO_*
  385.            &ptl,
  386.            0, 0);       // no corner rounding
  387. }
  388.  
  389. /*
  390.  *@@ gpihMarker:
  391.  *      this draws a quick marker (a filled
  392.  *      rectangle) at the specified position.
  393.  *      The rectangle will be drawn so that
  394.  *      the specified point is in its center.
  395.  *
  396.  *      No PS data is changed.
  397.  *
  398.  *@@changed V0.9.7 (2001-01-17) [umoeller]: removed lColor
  399.  */
  400.  
  401. VOID gpihMarker(HPS hps,
  402.                 LONG x,             // in: x-center of rectangle
  403.                 LONG y,             // in: y-center of rectangle
  404.                 ULONG ulWidth)      // in: rectangle width and height
  405. {
  406.     POINTL  ptlSave;
  407.     RECTL   rclTemp;
  408.     ULONG   ulWidth2 = ulWidth / 2;
  409.     rclTemp.xLeft = x - ulWidth2;
  410.     rclTemp.xRight = x + ulWidth2;
  411.     rclTemp.yBottom = y - ulWidth2;
  412.     rclTemp.yTop    = y + ulWidth2;
  413.  
  414.     GpiQueryCurrentPosition(hps, &ptlSave);
  415.     gpihBox(hps,
  416.             DRO_FILL,
  417.             &rclTemp);
  418.     GpiMove(hps, &ptlSave);
  419. }
  420.  
  421. /*
  422.  *@@ gpihThickBox:
  423.  *      draws a box from the specified rectangle with the
  424.  *      specified width.
  425.  *
  426.  *      The specified rectangle is inclusive, that is, the top
  427.  *      right corner specifies the top right pixel to be drawn.
  428.  *      This is different from WinFillRect
  429.  *      (see @GPI_rectangles).
  430.  *
  431.  *      If usWidth > 1, the additional pixels will be drawn towards
  432.  *      the _center_ of the rectangle. prcl thus always specifies
  433.  *      the bottom left and top right pixels to be drawn.
  434.  *
  435.  *      This is different from using GpiSetLineWidth, with which
  436.  *      I was unable to find out in which direction lines are
  437.  *      extended.
  438.  *
  439.  *      This is similar to gpihDraw3DFrame, except that everything
  440.  *      is painted in the current color.
  441.  *
  442.  *@@added V0.9.7 (2000-12-06) [umoeller]
  443.  */
  444.  
  445. VOID gpihDrawThickFrame(HPS hps,              // in: presentation space for output
  446.                         PRECTL prcl,          // in: rectangle to draw (inclusive)
  447.                         ULONG ulWidth)       // in: line width (>= 1)
  448. {
  449.     ULONG ul = 0;
  450.     for (;
  451.          ul < ulWidth;
  452.          ul++)
  453.     {
  454.         GpiMove(hps, (PPOINTL)prcl);
  455.         GpiBox(hps,
  456.                DRO_OUTLINE,
  457.                (PPOINTL)&(prcl->xRight),
  458.                0,
  459.                0);
  460.  
  461.         // and one more to the outside
  462.         prcl->xLeft++;
  463.         prcl->yBottom++;
  464.         prcl->xRight--;
  465.         prcl->yTop--;
  466.     }
  467. }
  468.  
  469. /*
  470.  *@@ gpihDraw3DFrame:
  471.  *      this draws a rectangle in 3D style with a given line width
  472.  *      and the given colors.
  473.  *
  474.  *      The specified rectangle is inclusive, that is, the top
  475.  *      right corner specifies the top right pixel to be drawn.
  476.  *      This is different from WinFillRect
  477.  *      (see @GPI_rectangles).
  478.  *
  479.  *      If usWidth > 1, the additional pixels will be drawn towards
  480.  *      the _center_ of the rectangle. prcl thus always specifies
  481.  *      the bottom left and top right pixels to be drawn.
  482.  *
  483.  *@@changed V0.9.0 [umoeller]: changed function prototype to have colors specified
  484.  *@@changed V0.9.7 (2000-12-20) [umoeller]: now really using inclusive rectangle...
  485.  */
  486.  
  487. VOID gpihDraw3DFrame(HPS hps,
  488.                      PRECTL prcl,       // in: rectangle (inclusive)
  489.                      USHORT usWidth,    // in: line width (>= 1)
  490.                      LONG lColorLeft,   // in: color to use for left and top; e.g. SYSCLR_BUTTONLIGHT
  491.                      LONG lColorRight)  // in: color to use for right and bottom; e.g. SYSCLR_BUTTONDARK
  492. {
  493.     RECTL rcl2 = *prcl;
  494.     USHORT us;
  495.     POINTL ptl1;
  496.  
  497.     for (us = 0;
  498.          us < usWidth;
  499.          us++)
  500.     {
  501.         GpiSetColor(hps, lColorLeft);
  502.         // draw left line
  503.         ptl1.x = rcl2.xLeft;
  504.         ptl1.y = rcl2.yBottom;
  505.         GpiMove(hps, &ptl1);
  506.         ptl1.y = rcl2.yTop;     // V0.9.7 (2000-12-20) [umoeller]
  507.         GpiLine(hps, &ptl1);
  508.         // go right -> draw top
  509.         ptl1.x = rcl2.xRight;   // V0.9.7 (2000-12-20) [umoeller]
  510.         GpiLine(hps, &ptl1);
  511.         // go down -> draw right
  512.         GpiSetColor(hps, lColorRight);
  513.         ptl1.y = rcl2.yBottom;
  514.         GpiLine(hps, &ptl1);
  515.         // go left -> draw bottom
  516.         ptl1.x = rcl2.xLeft;
  517.         GpiLine(hps, &ptl1);
  518.  
  519.         rcl2.xLeft++;
  520.         rcl2.yBottom++;
  521.         rcl2.xRight--;
  522.         rcl2.yTop--;
  523.     }
  524. }
  525.  
  526. /*
  527.  *@@ gpihCharStringPosAt:
  528.  *      wrapper for GpiCharStringPosAt.
  529.  *      Since that function is limited to 512 characters
  530.  *      (according to GPIREF; on my Warp 4 FP13, I actually
  531.  *      get some 3000 characters... whatever this is),
  532.  *      this splits the string into 512 byte chunks and
  533.  *      calls GpiCharStringPosAt accordingly.
  534.  *
  535.  *@@added V0.9.3 (2000-05-06) [umoeller]
  536.  *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed underline for bitmap fonts, which never worked
  537.  */
  538.  
  539. LONG gpihCharStringPosAt(HPS hps,
  540.                          PPOINTL pptlStart,
  541.                          PRECTL prclRect,
  542.                          ULONG flOptions,
  543.                          LONG lCount,
  544.                          PCH pchString)
  545. {
  546.     LONG    lHits = 0,
  547.             lCountLeft = lCount;
  548.     PCH     pchThis = pchString;
  549.  
  550.     GpiMove(hps, pptlStart);
  551.  
  552.     if (lCount)
  553.     {
  554.         do
  555.         {
  556.             LONG    lCountThis = lCountLeft;
  557.             if (lCountLeft >= 512)
  558.                 lCountThis = 512;
  559.  
  560.             lHits = GpiCharStringPos(hps,
  561.                                      prclRect,
  562.                                      0, // flOptions,
  563.                                      lCountThis,
  564.                                      pchThis,
  565.                                      0);
  566.  
  567.             pchThis += 512;
  568.             lCountLeft -= 512;
  569.         } while (lCountLeft > 0);
  570.     }
  571.  
  572.     // I can't get underscore to work with bitmap fonts,
  573.     // so I'm doing it manually always now
  574.     // V0.9.20 (2002-08-10) [umoeller]
  575.     if (flOptions & CHS_UNDERSCORE)
  576.     {
  577.         POINTL ptl2, ptlSave;
  578.         GpiQueryCurrentPosition(hps, &ptlSave);
  579.         ptl2.x = pptlStart->x;
  580.         ptl2.y = pptlStart->y - 2;
  581.         GpiMove(hps, &ptl2);
  582.         ptl2.x = ptlSave.x;
  583.         GpiLine(hps, &ptl2);
  584.         GpiMove(hps, &ptlSave);
  585.     }
  586.  
  587.     return lHits;
  588. }
  589.  
  590. /*
  591.  *@@ gpihFillBackground:
  592.  *      fills the specified rectangle in the way
  593.  *      that is specified by the given BKGNDINFO
  594.  *      structure. This way one can either use
  595.  *      a solid color, a color fade, a bitmap,
  596.  *      or a combination of those.
  597.  *
  598.  *      See BKGNDINFO for the various parameters.
  599.  *
  600.  *      Since this can potentially be expensive,
  601.  *      it is strongly recommended to use a buffer
  602.  *      bitmap for painting with the size of the
  603.  *      window and bitblt that bitmap into the
  604.  *      window on repaints. This way the background
  605.  *      only has to be recreated on window resize.
  606.  *
  607.  *@@added V0.9.19 (2002-05-07) [umoeller]
  608.  */
  609.  
  610. VOID gpihFillBackground(HPS hps,            // in: PS to paint into
  611.                         PRECTL prcl,        // in: rectangle (inclusive!)
  612.                         PBKGNDINFO pInfo)   // in: background into
  613. {
  614.     LONG    l;
  615.     POINTL  ptl;
  616.  
  617.     switch (pInfo->flPaintMode & PMOD_COLORMASK)
  618.     {
  619.         case PMOD_SOLID:
  620.             // fill with background color
  621.             GpiSetColor(hps,
  622.                         pInfo->lcol1);
  623.             ptl.x = prcl->xLeft;
  624.             ptl.y = prcl->yBottom;
  625.             GpiMove(hps,
  626.                     &ptl);
  627.             ptl.x = prcl->xRight;
  628.             ptl.y = prcl->yTop;
  629.             GpiBox(hps,
  630.                    DRO_FILL,
  631.                    &ptl,
  632.                    0,
  633.                    0);
  634.         break;
  635.  
  636.         case PMOD_TOPBOTTOM:
  637.         {
  638.             LONG lDiffRed   = (LONG)GET_RED(pInfo->lcol2) - (LONG)GET_RED(pInfo->lcol1);
  639.             LONG lDiffGreen = (LONG)GET_GREEN(pInfo->lcol2) - (LONG)GET_GREEN(pInfo->lcol1);
  640.             LONG lDiffBlue  = (LONG)GET_BLUE(pInfo->lcol2) - (LONG)GET_BLUE(pInfo->lcol1);
  641.  
  642.             LONG lMax = prcl->yTop - prcl->yBottom;
  643.  
  644.             // start at top
  645.             ptl.y = prcl->yTop;
  646.  
  647.             for (l = 0;
  648.                  l <= lMax;
  649.                  ++l)
  650.             {
  651.                 // compose RGB color for this line;
  652.                 // lcol1 is top, lcol2 is bottom
  653.                 LONG lRed   =   GET_RED(pInfo->lcol1)
  654.                               + (   lDiffRed
  655.                                   * l
  656.                                   / lMax
  657.                                 );
  658.                 LONG lGreen =   GET_GREEN(pInfo->lcol1)
  659.                               + (   lDiffGreen
  660.                                   * l
  661.                                   / lMax
  662.                                 );
  663.                 LONG lBlue  =   GET_BLUE(pInfo->lcol1)
  664.                               + (   lDiffBlue
  665.                                   * l
  666.                                   / lMax
  667.                                 );
  668.  
  669.                 GpiSetColor(hps, MAKE_RGB(lRed, lGreen, lBlue));
  670.                 ptl.x = prcl->xLeft;
  671.                 GpiMove(hps, &ptl);
  672.                 ptl.x = prcl->xRight;
  673.                 GpiLine(hps, &ptl);
  674.  
  675.                 // next line below
  676.                 --(ptl.y);
  677.             }
  678.         }
  679.         break;
  680.  
  681.         case PMOD_LEFTRIGHT:
  682.         {
  683.             LONG lDiffRed   = (LONG)GET_RED(pInfo->lcol2) - (LONG)GET_RED(pInfo->lcol1);
  684.             LONG lDiffGreen = (LONG)GET_GREEN(pInfo->lcol2) - (LONG)GET_GREEN(pInfo->lcol1);
  685.             LONG lDiffBlue  = (LONG)GET_BLUE(pInfo->lcol2) - (LONG)GET_BLUE(pInfo->lcol1);
  686.  
  687.             LONG lMax = prcl->xRight - prcl->xLeft;
  688.  
  689.             // start at left
  690.             ptl.x = prcl->xLeft;
  691.  
  692.             for (l = 0;
  693.                  l <= lMax;
  694.                  ++l)
  695.             {
  696.                 // compose RGB color for this line;
  697.                 // lcol1 is top, lcol2 is bottom
  698.                 LONG lRed   =   GET_RED(pInfo->lcol1)
  699.                               + (   lDiffRed
  700.                                   * l
  701.                                   / lMax
  702.                                 );
  703.                 LONG lGreen =   GET_GREEN(pInfo->lcol1)
  704.                               + (   lDiffGreen
  705.                                   * l
  706.                                   / lMax
  707.                                 );
  708.                 LONG lBlue  =   GET_BLUE(pInfo->lcol1)
  709.                               + (   lDiffBlue
  710.                                   * l
  711.                                   / lMax
  712.                                 );
  713.  
  714.                 GpiSetColor(hps, MAKE_RGB(lRed, lGreen, lBlue));
  715.                 ptl.y = prcl->yBottom;
  716.                 GpiMove(hps, &ptl);
  717.                 ptl.y = prcl->yTop;
  718.                 GpiLine(hps, &ptl);
  719.  
  720.                 // next line to the right
  721.                 ++(ptl.x);
  722.             }
  723.         }
  724.         break;
  725.     }
  726. }
  727.  
  728. /*
  729.  *@@category: Helpers\PM helpers\GPI helpers\Fonts
  730.  */
  731.  
  732. /* ******************************************************************
  733.  *
  734.  *   Font helpers
  735.  *
  736.  ********************************************************************/
  737.  
  738. /*
  739.  *@@ gpihMatchFont:
  740.  *      attempts to find a font matching the specified
  741.  *      data and fills the specified FATTRS structure
  742.  *      accordingly.
  743.  *
  744.  *      This function performs the insane "11-step process" to
  745.  *      match a font, as described in the GPI reference.
  746.  *
  747.  *      This function can operate in two modes:
  748.  *
  749.  *      -- "Family" mode. In that case, specify the font family name
  750.  *         with pszName and set fFamily to TRUE. This is useful for
  751.  *         WYSIWYG text viewing if you need several font faces for
  752.  *         the same family, such as Courier Bold, Bold Italics, etc.
  753.  *         You can specify those attributes with usFormat then.
  754.  *
  755.  *      -- "Face" mode. In that case, specify the full font face name
  756.  *         with pszName and set fFamily to FALSE. This is useful for
  757.  *         font presentation parameters which use the "WarpSans Bold"
  758.  *         format. In that case, set usFormat to 0.
  759.  *
  760.  *      Returns TRUE if a "true" match was found, FALSE
  761.  *      otherwise. In both cases, *pfa receives data
  762.  *      which will allow GpiCreateLogFont to work; however,
  763.  *      if FALSE is returned, GpiCreateLogFont will most
  764.  *      likely find the default font (System Proportional)
  765.  *      only.
  766.  *
  767.  *      If (pFontMetrics != NULL), *pFontMetrics receives the
  768.  *      FONTMETRICS of the font which was found. If an outline
  769.  *      font has been found (instead of a bitmap font),
  770.  *      FONTMETRICS.fsDefn will have the FM_DEFN_OUTLINE bit set.
  771.  *
  772.  *      This function was extracted from gpihFindFont with
  773.  *      0.9.14 to allow for caching the font search results,
  774.  *      which is most helpful for memory device contexts,
  775.  *      where gpihFindFont can be inefficient.
  776.  *
  777.  *@@added V0.9.14 (2001-08-03) [umoeller]
  778.  *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed a few weirdos with outline fonts
  779.  */
  780.  
  781. BOOL gpihMatchFont(HPS hps,
  782.                    LONG lSize,            // in: font point size
  783.                    BOOL fFamily,          // in: if TRUE, pszName specifies font family;
  784.                                           //     if FALSE, pszName specifies font face
  785.                    const char *pcszName,  // in: font family or face name (without point size)
  786.                    USHORT usFormat,       // in: none, one or several of:
  787.                                           // -- FATTR_SEL_ITALIC
  788.                                           // -- FATTR_SEL_UNDERSCORE (underline)
  789.                                           // -- FATTR_SEL_BOLD
  790.                                           // -- FATTR_SEL_STRIKEOUT
  791.                                           // -- FATTR_SEL_OUTLINE (hollow)
  792.                    FATTRS *pfa,           // out: font attributes if found
  793.                    PFONTMETRICS pFontMetrics) // out: font metrics of created font (optional)
  794. {
  795.     // first find out how much memory we need to allocate
  796.     // for the FONTMETRICS structures
  797.     ULONG   ul = 0;
  798.     LONG    lTemp = 0;
  799.     LONG    cFonts = GpiQueryFonts(hps,
  800.                                    QF_PUBLIC | QF_PRIVATE,
  801.                                    NULL, // pszFaceName,
  802.                                    &lTemp,
  803.                                    sizeof(FONTMETRICS),
  804.                                    NULL);
  805.     PFONTMETRICS    pfm = (PFONTMETRICS)malloc(cFonts * sizeof(FONTMETRICS)),
  806.                     pfm2 = pfm,
  807.                     pfmFound = NULL;
  808.  
  809.     BOOL            fQueriedDevice = FALSE;     // V0.9.14 (2001-08-01) [umoeller]
  810.     LONG            alDevRes[2];            // device resolution
  811.  
  812.     // _Pmpf(("gpihFindFont: enumerating for %s, %d points", pcszName, lSize));
  813.  
  814.     GpiQueryFonts(hps,
  815.                   QF_PUBLIC | QF_PRIVATE,
  816.                   NULL, // pszFaceName,
  817.                   &cFonts,
  818.                   sizeof(FONTMETRICS),      // length of each metrics structure
  819.                                             // -- _not_ total buffer size!
  820.                   pfm);
  821.  
  822.     // now we have an array of FONTMETRICS
  823.     // for EVERY font that is installed on the system...
  824.     // these things are completely unsorted, so there's
  825.     // nothing we can rely on, we have to check them all.
  826.  
  827.     // fill in some default values for FATTRS,
  828.     // in case we don't find something better
  829.     // in the loop below; these values will be
  830.     // applied if
  831.     // a)   an outline font has been found;
  832.     // b)   bitmap fonts have been found, but
  833.     //      none for the current device resolution
  834.     //      exists;
  835.     // c)   no font has been found at all.
  836.     // In all cases, GpiCreateLogFont will do
  837.     // a "close match" resolution (at the bottom).
  838.     pfa->usRecordLength = sizeof(FATTRS);
  839.     pfa->fsSelection = usFormat; // changed later if better font is found
  840.     pfa->lMatch = 0L;             // closest match
  841.     strcpy(pfa->szFacename, pcszName);
  842.     pfa->idRegistry = 0;          // default registry
  843.     pfa->usCodePage = 0;          // default codepage
  844.     // the following two must be zero, or outline fonts
  845.     // will not be found; if a bitmap font has been passed
  846.     // to us, we'll modify these two fields later
  847.     pfa->lMaxBaselineExt = 0;     // font size (height)
  848.     pfa->lAveCharWidth = 0;       // font size (width)
  849.     pfa->fsType = 0;              // default type
  850.     pfa->fsFontUse = FATTR_FONTUSE_NOMIX;
  851.  
  852.     // now go thru the array of FONTMETRICS
  853.     // to check if we have a bitmap font
  854.     // pszFaceName; the default WPS behavior
  855.     // is that bitmap fonts appear to take
  856.     // priority over outline fonts of the
  857.     // same name, so we check these first
  858.     pfm2 = pfm;
  859.     for (ul = 0;
  860.          ul < cFonts;
  861.          ul++)
  862.     {
  863.         const char *pcszCompare = (fFamily)
  864.                                      ? pfm2->szFamilyname
  865.                                      : pfm2->szFacename;
  866.  
  867.         /* _Pmpf(("  Checking font: %s (Fam: %s), %d, %d, %d",
  868.                pcszCompare,
  869.                pfm2->szFamilyname,
  870.                pfm2->sNominalPointSize,
  871.                pfm2->lMaxBaselineExt,
  872.                pfm2->lAveCharWidth)); */
  873.  
  874.         if (!strcmp(pcszCompare, pcszName))
  875.         {
  876.             /* _Pmpf(("  Found font %s; slope %d, usWeightClass %d",
  877.                     pfm2->szFacename,
  878.                     pfm2->sCharSlope,
  879.                     pfm2->usWeightClass)); */
  880.  
  881.             if ((pfm2->fsDefn & FM_DEFN_OUTLINE) == 0)
  882.             {
  883.                 // image (bitmap) font:
  884.                 // check point size
  885.                 if (pfm2->sNominalPointSize == lSize * 10)
  886.                 {
  887.                     // OK: check device resolutions, because
  888.                     // normally, there are always several image
  889.                     // fonts for different resolutions
  890.                     // for bitmap fonts, there are normally two versions:
  891.                     // one for low resolutions, one for high resolutions
  892.                     if (!fQueriedDevice)
  893.                     {
  894.                         DevQueryCaps(GpiQueryDevice(hps),
  895.                                      CAPS_HORIZONTAL_FONT_RES,
  896.                                      2L,
  897.                                      alDevRes);
  898.                         fQueriedDevice = TRUE;
  899.                     }
  900.  
  901.                     if (    (pfm2->sXDeviceRes == alDevRes[0])
  902.                          && (pfm2->sYDeviceRes == alDevRes[1])
  903.                        )
  904.                     {
  905.                         // OK: use this for GpiCreateLogFont
  906.                         pfa->lMaxBaselineExt = pfm2->lMaxBaselineExt;
  907.                         pfa->lAveCharWidth = pfm2->lAveCharWidth;
  908.                         // pfa->lMatch = pfm2->lMatch;
  909.  
  910.                         pfmFound = pfm2;
  911.                         break;
  912.                     }
  913.                 }
  914.             }
  915.             else
  916.                 // outline font:
  917.                 if (pfmFound == NULL)
  918.                 {
  919.                     // no bitmap font found yet:
  920.  
  921.                     /*
  922.                         #define FATTR_SEL_ITALIC               0x0001
  923.                         #define FATTR_SEL_UNDERSCORE           0x0002
  924.                         #define FATTR_SEL_OUTLINE              0x0008
  925.                         #define FATTR_SEL_STRIKEOUT            0x0010
  926.                         #define FATTR_SEL_BOLD                 0x0020
  927.                      */
  928.  
  929.                     if (    (!fFamily)          // face mode is OK always
  930.                                                 // V0.9.14 (2001-08-03) [umoeller]
  931.                          || (    (    (    (usFormat & FATTR_SEL_BOLD)
  932.                                         && (pfm2->usWeightClass == 7) // bold
  933.                                       )
  934.                                    || (    (!(usFormat & FATTR_SEL_BOLD))
  935.                                         && (pfm2->usWeightClass == 5) // regular
  936.                                       )
  937.                                  )
  938.                               && (    (    (usFormat & FATTR_SEL_ITALIC)
  939.                                         && (pfm2->sCharSlope != 0) // italics
  940.                                       )
  941.                                    || (    (!(usFormat & FATTR_SEL_ITALIC))
  942.                                         && (pfm2->sCharSlope == 0) // regular
  943.                                       )
  944.                                  )
  945.                             )
  946.                        )
  947.                     {
  948.                         // yes, we found a true font for that face:
  949.                         pfmFound = pfm2;
  950.  
  951.                         // use this exact font for GpiCreateLogFont
  952.                         pfa->lMatch = pfm2->lMatch;
  953.  
  954.                         // the following two might have been set
  955.                         // for a bitmap font above
  956.                         // V0.9.14 (2001-08-03) [umoeller]
  957.                         pfa->lMaxBaselineExt = pfm2->lMaxBaselineExt;
  958.                         pfa->lAveCharWidth = pfm2->lAveCharWidth;
  959.  
  960.                         pfa->idRegistry = pfm2->idRegistry;
  961.  
  962.                         // override NOMIX // V0.9.14 (2001-08-03) [umoeller]
  963.                         pfa->fsFontUse = FATTR_FONTUSE_OUTLINE;
  964.  
  965.                         // according to GPIREF, we must also specify
  966.                         // the full face name... geese!
  967.                         strcpy(pfa->szFacename, pfm2->szFacename);
  968.                         // unset flag in FATTRS, because this would
  969.                         // duplicate bold or italic
  970.                         pfa->fsSelection = 0;
  971.  
  972.                         // _Pmpf(("    --> using it"));
  973.                         // but loop on, because we might have a bitmap
  974.                         // font which should take priority
  975.                     }
  976.                 }
  977.         }
  978.  
  979.         pfm2++;
  980.     }
  981.  
  982.     if (pfmFound)
  983.         // FONTMETRICS found:
  984.         // copy font metrics?
  985.         if (pFontMetrics)
  986.             memcpy(pFontMetrics, pfmFound, sizeof(FONTMETRICS));
  987.  
  988.     // free the FONTMETRICS array
  989.     free(pfm);
  990.  
  991.     return (pfmFound != NULL);
  992. }
  993.  
  994. /*
  995.  *@@ gpihSplitPresFont:
  996.  *      splits a presentation parameter font
  997.  *      string into the point size and face
  998.  *      name so that it can be passed to
  999.  *      gpihFindFont more easily.
  1000.  *
  1001.  *@@added V0.9.1 (2000-02-15) [umoeller]
  1002.  *@@changed V0.9.20 (2002-07-19) [umoeller]: optimized
  1003.  */
  1004.  
  1005. BOOL gpihSplitPresFont(PSZ pszFontNameSize,  // in: e.g. "12.Courier"
  1006.                        PULONG pulSize,       // out: integer point size (e.g. 12);
  1007.                                              // ptr must be specified
  1008.                        PSZ *ppszFaceName)    // out: ptr into pszFontNameSize
  1009.                                              // (e.g. "Courier")
  1010. {
  1011.     PCHAR   pcDot;
  1012.     if (    (pszFontNameSize)
  1013.          && (pcDot = strchr(pszFontNameSize, '.'))
  1014.        )
  1015.     {
  1016.         // _Pmpf(("Found font PP: %s", pszFontFound));
  1017.         // sscanf(pszFontNameSize, "%lu", pulSize);
  1018.         *pulSize = atoi(pszFontNameSize);       // V0.9.20 (2002-07-19) [umoeller]
  1019.         *ppszFaceName = pcDot + 1;
  1020.         return TRUE;
  1021.     }
  1022.  
  1023.     return FALSE;
  1024. }
  1025.  
  1026. /*
  1027.  *@@ gpihLockLCIDs:
  1028.  *      requests the mutex for serializing the
  1029.  *      lcids.
  1030.  *
  1031.  *      With GPI, lcids are a process-wide resource and not
  1032.  *      guaranteed to be unique. In the worst case, while your
  1033.  *      font routines are running, another thread modifies the
  1034.  *      lcids and you get garbage. If your fonts suddenly
  1035.  *      turn to "System Proportional", you know this has
  1036.  *      happened.
  1037.  *
  1038.  *      As a result, whenever you work on lcids, request this
  1039.  *      mutex during your processing. If you do this consistently
  1040.  *      across all your code, you should be safe.
  1041.  *
  1042.  *      gpihFindFont uses this mutex. If you call GpiCreateLogFont
  1043.  *      yourself somewhere, you should do this under the protection
  1044.  *      of this function.
  1045.  *
  1046.  *      Call gpihUnlockLCIDs to unlock.
  1047.  *
  1048.  *@@added V0.9.9 (2001-04-01) [umoeller]
  1049.  */
  1050.  
  1051. BOOL gpihLockLCIDs(VOID)
  1052. {
  1053.     if (!G_hmtxLCIDs)
  1054.         // first call: create
  1055.         return (!DosCreateMutexSem(NULL,
  1056.                                    &G_hmtxLCIDs,
  1057.                                    0,
  1058.                                    TRUE));     // request!
  1059.  
  1060.     // subsequent calls: request
  1061.     return !DosRequestMutexSem(G_hmtxLCIDs, SEM_INDEFINITE_WAIT);
  1062. }
  1063.  
  1064. /*
  1065.  *@@ UnlockLCIDs:
  1066.  *      releases the mutex for serializing the
  1067.  *      lcids.
  1068.  *
  1069.  *@@added V0.9.9 (2001-04-01) [umoeller]
  1070.  */
  1071.  
  1072. VOID gpihUnlockLCIDs(VOID)
  1073. {
  1074.     DosReleaseMutexSem(G_hmtxLCIDs);
  1075. }
  1076.  
  1077. /*
  1078.  *@@ gpihQueryNextLCID:
  1079.  *      returns the next available lcid for the given HPS.
  1080.  *      Actually, it's the next available lcid for the
  1081.  *      entire process, since there can be only 255 altogether.
  1082.  *      Gets called by gpihFindFont automatically.
  1083.  *
  1084.  *      WARNING: This function by itself is not thread-safe.
  1085.  *      See gpihLockLCIDs for how to serialize this.
  1086.  *
  1087.  *      Code was extensively re-tested, works (V0.9.12 (2001-05-31) [umoeller]).
  1088.  *
  1089.  *@@added V0.9.3 (2000-05-06) [umoeller]
  1090.  *@@changed V0.9.9 (2001-04-01) [umoeller]: removed all those sick sub-allocs
  1091.  */
  1092.  
  1093. LONG gpihQueryNextFontID(HPS hps)
  1094. {
  1095.     LONG    lcidNext = -1;
  1096.  
  1097.     LONG lCount =  GpiQueryNumberSetIds(hps);
  1098.                              //  the number of local identifiers
  1099.                              //  (lcids) currently in use, and
  1100.                              //  therefore the maximum number
  1101.                              //  of objects for which information
  1102.                              //  can be returned
  1103.  
  1104.     // _Pmpf((__FUNCTION__ ": Entering"));
  1105.  
  1106.     if (lCount == 0)
  1107.     {
  1108.         // none in use yet:
  1109.         lcidNext = 15;
  1110.  
  1111.         // _Pmpf(("  no lcids in use"));
  1112.     }
  1113.     else
  1114.     {
  1115.         // #define GQNCL_BLOCK_SIZE 400*sizeof(LONG)
  1116.  
  1117.         PLONG  alTypes = NULL;  // object types
  1118.         PSTR8  aNames = NULL;   // font names
  1119.         PLONG  allcids = NULL;  // local identifiers
  1120.  
  1121.         if (    (alTypes = (PLONG)malloc(lCount * sizeof(LONG)))
  1122.              && (aNames = (PSTR8)malloc(lCount * sizeof(STR8)))
  1123.              && (allcids = (PLONG)malloc(lCount * sizeof(LONG)))
  1124.            )
  1125.         {
  1126.             if (GpiQuerySetIds(hps,
  1127.                                lCount,
  1128.                                alTypes,
  1129.                                aNames,
  1130.                                allcids))
  1131.             {
  1132.                 // FINALLY we have all the lcids in use.
  1133.                 BOOL    fContinue = TRUE;
  1134.                 lcidNext = 15;
  1135.  
  1136.                 // _Pmpf(("  %d fonts in use, browsing...", lCount));
  1137.  
  1138.                 // now, check if this lcid is in use already:
  1139.                 while (fContinue)
  1140.                 {
  1141.                     BOOL fFound = FALSE;
  1142.                     ULONG ul;
  1143.                     fContinue = FALSE;
  1144.                     for (ul = 0;
  1145.                          ul < lCount;
  1146.                          ul++)
  1147.                     {
  1148.                         if (allcids[ul] == lcidNext)
  1149.                         {
  1150.                             fFound = TRUE;
  1151.                             break;
  1152.                         }
  1153.                     }
  1154.  
  1155.                     if (fFound)
  1156.                     {
  1157.                         // lcid found:
  1158.                         // try next higher one
  1159.  
  1160.                         // _Pmpf(("       %d is busy...", lcidNext));
  1161.  
  1162.                         lcidNext++;
  1163.                         fContinue = TRUE;
  1164.                     }
  1165.                     // else
  1166.                         // else: return that one
  1167.                         // _Pmpf(("  %d is free", lcidNext));
  1168.                 }
  1169.             }
  1170.         }
  1171.  
  1172.         if (alTypes)
  1173.             free(alTypes);
  1174.         if (aNames)
  1175.             free(aNames);
  1176.         if (allcids)
  1177.             free(allcids);
  1178.     }
  1179.  
  1180.     // _Pmpf((__FUNCTION__ ": Returning lcid %d", lcidNext));
  1181.  
  1182.     return lcidNext;
  1183. }
  1184.  
  1185. /*
  1186.  *@@ gpihCreateFont:
  1187.  *
  1188.  *@@added V0.9.14 (2001-08-03) [umoeller]
  1189.  */
  1190.  
  1191. LONG gpihCreateFont(HPS hps,
  1192.                     FATTRS *pfa)
  1193. {
  1194.     LONG lLCIDReturn = 0;
  1195.  
  1196.     if (gpihLockLCIDs())        // V0.9.9 (2001-04-01) [umoeller]
  1197.     {
  1198.         // new logical font ID: last used plus one
  1199.         lLCIDReturn = gpihQueryNextFontID(hps);
  1200.  
  1201.         GpiCreateLogFont(hps,
  1202.                          NULL,  // don't create "logical font name" (STR8)
  1203.                          lLCIDReturn,
  1204.                          pfa);
  1205.  
  1206.         gpihUnlockLCIDs();
  1207.     }
  1208.  
  1209.     return lLCIDReturn;
  1210. }
  1211.  
  1212. /*
  1213.  *@@ gpihFindFont:
  1214.  *      this returns a new logical font ID (LCID) for the specified
  1215.  *      font by calling gpihMatchFont first and then
  1216.  *      GpiCreateLogFont to create a logical font from the
  1217.  *      data returned.
  1218.  *
  1219.  *      See gpihMatchFont for additional explanations.
  1220.  *
  1221.  *      To then use the font whose LCID has been returned by this
  1222.  *      function for text output, call:
  1223.  +          GpiSetCharSet(hps, lLCIDReturned);
  1224.  *
  1225.  *      <B>Font Point Sizes:</B>
  1226.  *
  1227.  *      1)  For image (bitmap) fonts, the size is fixed, and
  1228.  *          you can directly draw after the font has been
  1229.  *          selected. GpiSetCharBox has no effect on image
  1230.  *          fonts unless you switch to character mode 2
  1231.  *          (if you care for that: GpiSetCharMode).
  1232.  *
  1233.  *      2)  For outline fonts however, you need to define a
  1234.  *          character box if you want to output text in a
  1235.  *          size other than the default size. This is almost
  1236.  *          as bad a mess as this function, so gpihSetPointSize
  1237.  *          has been provided for this. See remarks there.
  1238.  *
  1239.  *      <B>Example:</B>
  1240.  *
  1241.  *      This example prints text in "24.Courier".
  1242.  *
  1243.  +          PSZ     pszOutput = "Test output";
  1244.  +          FONTMETRICS FontMetrics;
  1245.  +          LONG    lLCID = gpihFindFont(hps,
  1246.  +                                       24,        // point size
  1247.  +                                       FALSE,     // face, not family
  1248.  +                                       "Courier",
  1249.  +                                       0,
  1250.  +                                       &FontMetrics);
  1251.  +          if (lLCID)
  1252.  +          {
  1253.  +              // no error:
  1254.  +              GpiSetCharSet(hps, lLCID);
  1255.  +              if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
  1256.  +                  // outline font found (should be the case):
  1257.  +                  gpihSetPointSize(hps, 24);
  1258.  +          }
  1259.  +          GpiCharString(hps, strlen(pszOutput), pszOutput);
  1260.  *
  1261.  *      <B>Details:</B>
  1262.  *
  1263.  *      First, GpiQueryFonts is called to enumerate all the fonts on
  1264.  *      the system. We then evaluate the awful FONTMETRICS data
  1265.  *      of those fonts to perform a "real" match.
  1266.  *
  1267.  *      If that fails, we allow GPI to do a "close" match based
  1268.  *      on the input values. This might result in the system
  1269.  *      default font (System Proportional) to be found, in the
  1270.  *      worst case. But even then, a new LCID is returned.
  1271.  *
  1272.  *      The "close match" comes in especially when using the
  1273.  *      font attributes (bold, italics) and we are unable to
  1274.  *      find the correct outline font for that. Unfortunately,
  1275.  *      the information in FONTMETRICS.fsSelection is wrong,
  1276.  *      wrong, wrong for the large majority of fonts. (For
  1277.  *      example, I get the "bold" flag set for regular fonts,
  1278.  *      and vice versa.) So we attempt to use the other fields,
  1279.  *      but this doesn't always help. Not even Netscape gets
  1280.  *      this right.
  1281.  *
  1282.  *      <B>Font faces:</B>
  1283.  *
  1284.  *      This is terribly complicated as well. You need to
  1285.  *      differentiate between "true" emphasis faces (that
  1286.  *      is, bold and italics are implemented thru separate
  1287.  *      font files) and the ugly GPI simulation, which simply
  1288.  *      makes the characters wider or shears them.
  1289.  *
  1290.  *      This function even finds true "bold" and "italic" faces
  1291.  *      for outline fonts. To do this, always specify the "family"
  1292.  *      name as pszFaceName (e.g. "Courier" instead of "Courier
  1293.  *      Bold") and set the flags for usFormat (e.g. FATTR_SEL_BOLD).
  1294.  *      Note that this implies that you need call this function
  1295.  *      twice to create two logical fonts for regular and bold faces.
  1296.  *
  1297.  *      If a "true" emphasis font is not found, the GPI simulation
  1298.  *      is enabled.
  1299.  *
  1300.  *      <B>Remarks:</B>
  1301.  *
  1302.  *      1)  This function _always_ creates a new logical font,
  1303.  *          whose ID is returned, even if the specified font
  1304.  *          was not found or a "close match" was performed by
  1305.  *          GPI. As a result, do not call this function twice
  1306.  *          for the same font specification, because there are
  1307.  *          only 255 logical font IDs for each process.
  1308.  *
  1309.  *      2)  Since this function always creates an LCID,
  1310.  *          you should _always_ free the LCID later.
  1311.  *          This is only valid if the font is no longer selected
  1312.  *          into any presentation space. So use these calls:
  1313.  +              GpiSetCharSet(hps, LCID_DEFAULT);
  1314.  +              GpiDeleteSetId(hps, lLCIDReturnedByThis);
  1315.  *
  1316.  *      3)  Using this function, bitmap fonts will have priority
  1317.  *          over outline fonts of the same face name. This is
  1318.  *          how the WPS does it too. This is most obvious with
  1319.  *          the "Helv" font, which exists as a bitmap font for
  1320.  *          certain point sizes only.
  1321.  *
  1322.  *      4)  Since logical font IDs are shared across the
  1323.  *          process, a mutex is requested while the lcids are
  1324.  *          being queried and/or manipulated. In other words,
  1325.  *          this func is now thread-safe (V0.9.9).
  1326.  *
  1327.  *          This calls gpihQueryNextFontID in turn to find the
  1328.  *          next free lcid. See remarks there.
  1329.  *
  1330.  *      <B>Font metrics:</B>
  1331.  *
  1332.  *      The important values in the returned FONTMETRICS are
  1333.  *      like this (according to PMREF):
  1334.  *
  1335.  +   ╔═ ________________________________________________
  1336.  +   ║
  1337.  +   ║ lExternalLeading, according to font designer.
  1338.  +   ║  ________________________________________________  ═╗
  1339.  +   ╚═                                                    ║
  1340.  +                                  #       #              ║
  1341.  +                                  ##     ##              ║ lMaxAscender (of entire;
  1342.  +   ╔═ _______________             # #   # #              ║ font); this can be > capital
  1343.  +   ║                 ####         #  # #  #              ║ letters because miniscules
  1344.  +   ║                #    #        #   #   #              ║ can exceed that.
  1345.  +   ║  lXHeight      #    #        #       #              ║
  1346.  +   ║                #    #        #       #              ║
  1347.  +   ║                #    #        #       #              ║
  1348.  +   ║  _______________#####________#_______#___ baseline ═╗
  1349.  +   ╚═                    #                               ║
  1350.  +                         #                               ║ lMaxDescender
  1351.  +      ______________ ####______________________________ ═╝
  1352.  +
  1353.  *
  1354.  *      In turn, lMaxBaselineExt is lMaxAscender + lMaxDescender.
  1355.  *
  1356.  *      Soooo... to find out about the optimal line spacing, GPIREF
  1357.  *      recommends to use lMaxBaselineExt + lExternalLeading.
  1358.  *
  1359.  *@@added V0.9.0 [umoeller]
  1360.  *@@changed V0.9.3 (2000-05-06) [umoeller]: didn't work for more than one font; now using gpihQueryNextFontID
  1361.  *@@changed V0.9.3 (2000-05-06) [umoeller]: usFormat didn't work; fixed
  1362.  *@@changed V0.9.4 (2000-08-08) [umoeller]: added fFamily
  1363.  *@@changed V0.9.9 (2001-04-01) [umoeller]: made this thread-safe, finally
  1364.  *@@changed V0.9.14 (2001-08-01) [umoeller]: some optimizations
  1365.  */
  1366.  
  1367. LONG gpihFindFont(HPS hps,               // in: HPS for font selection
  1368.                   LONG lSize,            // in: font point size
  1369.                   BOOL fFamily,          // in: if TRUE, pszName specifies font family;
  1370.                                          //     if FALSE, pszName specifies font face
  1371.                   const char *pcszName,  // in: font family or face name (without point size)
  1372.                   USHORT usFormat,       // in: none, one or several of:
  1373.                                          // -- FATTR_SEL_ITALIC
  1374.                                          // -- FATTR_SEL_UNDERSCORE (underline)
  1375.                                          // -- FATTR_SEL_BOLD
  1376.                                          // -- FATTR_SEL_STRIKEOUT
  1377.                                          // -- FATTR_SEL_OUTLINE (hollow)
  1378.                   PFONTMETRICS pFontMetrics) // out: font metrics of created font (optional)
  1379. {
  1380.     FATTRS  FontAttrs;
  1381.  
  1382.     gpihMatchFont(hps,
  1383.                   lSize,
  1384.                   fFamily,
  1385.                   pcszName,
  1386.                   usFormat,
  1387.                   &FontAttrs,
  1388.                   pFontMetrics);
  1389.  
  1390.     return gpihCreateFont(hps,
  1391.                           &FontAttrs);
  1392.  
  1393.     // _Pmpf((__FUNCTION__ ": returning lcid %d", lLCIDReturn));
  1394. }
  1395.  
  1396. /*
  1397.  *@@ gpihFindPresFont:
  1398.  *      similar to gpihFindFont, but this one evaluates
  1399.  *      the PP_FONTNAMESIZE presentation parameter of the
  1400.  *      specified window instead. If that one is not set,
  1401.  *      the specified default font is used instead.
  1402.  *
  1403.  *      Note that as opposed to gpihFindFont, this one
  1404.  *      takes a "8.Helv"-type string as input.
  1405.  *
  1406.  *      See gpihFindFont for additional remarks, which
  1407.  *      gets called by this function.
  1408.  *
  1409.  *      Again, if an outline font has been returned, you
  1410.  *      must also set the "character box" for the HPS, or
  1411.  *      your text will always have the same point size.
  1412.  *      Use gpihSetPointSize for that, using the presparam's
  1413.  *      point size, which is returned by this function
  1414.  *      into *plSize, if (plSize != NULL):
  1415.  +
  1416.  +          FONTMETRICS FontMetrics;
  1417.  +          LONG        lPointSize;
  1418.  +          LONG        lLCID = gpihFindPresFont(hwnd, hps, "8.Helv",
  1419.  +                                               &FontMetrics,
  1420.  +                                               &lPointSize);
  1421.  +          GpiSetCharSet(hps, lLCID);
  1422.  +          if (FontMetrics.fsDefn & FM_DEFN_OUTLINE)
  1423.  +              gpihSetPointSize(hps, lPointSize);
  1424.  *
  1425.  *      If errors occur, e.g. if the font string does not
  1426.  *      conform to the "size.face" format, null is returned.
  1427.  *
  1428.  *@@added V0.9.0 [umoeller]
  1429.  *@@changed V0.9.9 (2001-04-01) [umoeller]: now supporting NULLHANDLE hwnd
  1430.  */
  1431.  
  1432. LONG gpihFindPresFont(HWND hwnd,          // in: window to search for presparam or NULLHANDLE
  1433.                       BOOL fInherit,      // in: search parent windows too?
  1434.                       HPS hps,            // in: HPS for font selection
  1435.                       const char *pcszDefaultFont, // in: default font if not found (i.e. "8.Helv")
  1436.                       PFONTMETRICS pFontMetrics, // out: font metrics of created font (optional)
  1437.                       PLONG plSize)       // out: presparam's point size (optional)
  1438. {
  1439.     CHAR    szPPFont[200] = "";
  1440.     const char *pcszFontFound = 0;
  1441.     CHAR    szFaceName[300] = "";
  1442.     ULONG   ulFontSize = 0;
  1443.  
  1444.     if (    (hwnd)        // V0.9.9 (2001-04-01) [umoeller]
  1445.          && (WinQueryPresParam(hwnd,
  1446.                                PP_FONTNAMESIZE,          // first PP to query
  1447.                                0,                        // second PP to query
  1448.                                NULL,                     // out: which one is returned
  1449.                                (ULONG)sizeof(szPPFont),  // in: buffer size
  1450.                                (PVOID)&szPPFont,         // out: PP value returned
  1451.                                (fInherit)
  1452.                                     ? 0
  1453.                                     : QPF_NOINHERIT))
  1454.        )
  1455.         // PP found:
  1456.         pcszFontFound = szPPFont;
  1457.     else
  1458.         pcszFontFound = pcszDefaultFont;
  1459.  
  1460.     if (pcszFontFound)
  1461.     {
  1462.         const char *pcDot = strchr(pcszFontFound, '.');
  1463.         if (pcDot)
  1464.         {
  1465.             // _Pmpf(("Found font PP: %s", pszFontFound));
  1466.             sscanf(pcszFontFound, "%lu", &ulFontSize);
  1467.             if (plSize)
  1468.                 *plSize = ulFontSize;
  1469.             strcpy(szFaceName, pcDot + 1);
  1470.             return gpihFindFont(hps,
  1471.                                 ulFontSize,
  1472.                                 FALSE,         // face, not family name
  1473.                                 szFaceName,
  1474.                                 0,
  1475.                                 pFontMetrics);
  1476.         }
  1477.     }
  1478.  
  1479.     return 0;
  1480. }
  1481.  
  1482. /*
  1483.  *@@ gpihSetPointSize:
  1484.  *      this invokes GpiSetCharBox on the given HPS to
  1485.  *      set the correct "character box" with the proper
  1486.  *      parameters.
  1487.  *
  1488.  *      This is necessary for text output with outline
  1489.  *      fonts. Bitmap fonts have a fixed size, and
  1490.  *      calling this function is not necessary for them.
  1491.  *
  1492.  *      Unfortunately, IBM has almost not documented
  1493.  *      how to convert nominal point sizes to the
  1494.  *      correct character cell values. This involves
  1495.  *      querying the output device (DevQueryCaps).
  1496.  *
  1497.  *      I have found one hint for this procedure in GPIREF
  1498.  *      (chapter "Character string primitives", "Using...",
  1499.  *      "Drawing text"), but the example code given
  1500.  *      there is buggy, because it must be "72" instead
  1501.  *      of "720". #%(%!"⌡!!.
  1502.  *
  1503.  *      So here we go. If you want to output text in,
  1504.  *      say, "Courier" and 24 points (nominal point size),
  1505.  *      select the font into your HPS and call
  1506.  +          gpihSetPointSize(hps, 24)
  1507.  *      and you're done. See gpihFindFont for a complete
  1508.  *      example.
  1509.  *
  1510.  *      Call this function as many times as needed. This
  1511.  *      consumes no resources at all.
  1512.  *
  1513.  *      This returns the return value of GpiSetCharBox.
  1514.  *
  1515.  *@@added V0.9.0 [umoeller]
  1516.  *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed bad rounding errors
  1517.  */
  1518.  
  1519. BOOL gpihSetPointSize(HPS hps,          // in: presentation space for output
  1520.                       LONG lPointSize)  // in: desired nominal point size
  1521. {
  1522.     SIZEF   box;
  1523.     HDC     hdc = GpiQueryDevice(hps);       // get the HDC from the HPS
  1524.     LONG    alDevRes[2];
  1525.     DevQueryCaps(GpiQueryDevice(hps),       // get the HDC from the HPS
  1526.                  CAPS_HORIZONTAL_FONT_RES,
  1527.                  2L,
  1528.                  alDevRes);
  1529.  
  1530.     // V0.9.14: this code didn't work... it produced rounding
  1531.     // errors which set the font size different from what
  1532.     // it should be according to the WPS font palette
  1533.     /* box.cx = MAKEFIXED((lPointSize * alDevRes[0]) / 72, 0);
  1534.     box.cy = MAKEFIXED((lPointSize * alDevRes[1]) / 72, 0); */
  1535.  
  1536.     // V0.9.14 (2001-08-03) [umoeller]: now using this one
  1537.     // instead
  1538.     lPointSize *= 65536;
  1539.     box.cx = (FIXED)(lPointSize / 72 * alDevRes[0]);
  1540.     box.cy = (FIXED)(lPointSize / 72 * alDevRes[1]);
  1541.  
  1542.     return GpiSetCharBox(hps, &box);
  1543. }
  1544.  
  1545. /*
  1546.  *@@ gpihQueryLineSpacing:
  1547.  *      this returns the optimal line spacing for text
  1548.  *      output with the current HPS; this is computed
  1549.  *      by evaluating those incredible FONTMETRICS.
  1550.  *
  1551.  *      This might be helpful if you write text to the
  1552.  *      screen yourself and need the height of a text
  1553.  *      line to advance to the next.
  1554.  *
  1555.  *@@changed V0.9.7 (2000-12-20) [umoeller]: removed psz param
  1556.  */
  1557.  
  1558. LONG gpihQueryLineSpacing(HPS hps)
  1559. {
  1560.     FONTMETRICS fm;
  1561.  
  1562.     if (GpiQueryFontMetrics(hps, sizeof(FONTMETRICS), &fm))
  1563.         return ( (  fm.lMaxBaselineExt     // max vertical font space
  1564.                    +fm.lExternalLeading)    // space advised by font designer
  1565.                );
  1566.  
  1567.     return 15;
  1568. }
  1569.  
  1570. /*
  1571.  *@@category: Helpers\PM helpers\GPI helpers\Bitmaps/Icons
  1572.  */
  1573.  
  1574. /* ******************************************************************
  1575.  *
  1576.  *   Bitmap helpers
  1577.  *
  1578.  ********************************************************************/
  1579.  
  1580. /*
  1581.  *@@ gpihCreateMemPS:
  1582.  *      creates a memory device context and presentation space so
  1583.  *      that they are compatible with the screen device context and
  1584.  *      presentation space. These are stored in *hdcMem and *hpsMem.
  1585.  *
  1586.  *      This is a one-shot function for the standard code that is
  1587.  *      always needed when working with bitmaps in a memory device
  1588.  *      context.
  1589.  *
  1590.  *      psizlPage must point to a SIZEL structure containing the
  1591.  *      width and height for the memory PS. Specify the size of
  1592.  *      the future bitmap here. Specify {0, 0} to get a PS with
  1593.  *      the size of the full screen, which consumes quite a bit
  1594.  *      of memory though.
  1595.  *
  1596.  *      Returns FALSE upon errors. In that case, both hdcMem and
  1597.  *      hpsMem are set to NULLHANDLE.
  1598.  *
  1599.  *      To cleanup after this function has returned TRUE, use the
  1600.  *      following:
  1601.  +          GpiDestroyPS(hpsMem);
  1602.  +          DevCloseDC(hdcMem);
  1603.  *
  1604.  *@@changed V0.9.3 (2000-05-18) [umoeller]: added psiszlPage
  1605.  */
  1606.  
  1607. BOOL gpihCreateMemPS(HAB hab,       // in: anchor block
  1608.                      PSIZEL psizlPage, // in: width and height for mem PS
  1609.                      HDC *hdcMem,   // out: memory DC or NULLHANDLE upon errors
  1610.                      HPS *hpsMem)   // out: memory PS or NULLHANDLE upon errors
  1611. {
  1612.     BOOL brc = FALSE;
  1613.     PSZ pszData[4] = { "Display", NULL, NULL, NULL };
  1614.  
  1615.     // create new memory DC
  1616.     if ((*hdcMem = DevOpenDC(hab,
  1617.                              OD_MEMORY,      // create memory DC
  1618.                              "*",            // token: do not take INI info
  1619.                              4,              // item count in pszData
  1620.                              (PDEVOPENDATA)pszData,
  1621.                              NULLHANDLE)))    // compatible with screen
  1622.     {
  1623.         // memory DC created successfully:
  1624.         // create compatible PS
  1625.         if ((*hpsMem = GpiCreatePS(hab,
  1626.                                    *hdcMem,      // HDC to associate HPS with (GPIA_ASSOC);
  1627.                                                  // mandatory for GPIT_MICRO
  1628.                                    psizlPage,    // is (0, 0) == screen size
  1629.                                    PU_PELS       // presentation page units: pixels
  1630.                                     | GPIA_ASSOC    // associate with hdcMem (req. for GPIT_MICRO)
  1631.                                     | GPIT_MICRO)))  // micro presentation space
  1632.             brc = TRUE;
  1633.         else
  1634.         {
  1635.             // error (hpsMem == NULLHANDLE):
  1636.             // close memory DC again
  1637.             DevCloseDC(*hdcMem);
  1638.             *hdcMem = NULLHANDLE;
  1639.         }
  1640.     }
  1641.  
  1642.     return brc;
  1643. }
  1644.  
  1645. /*
  1646.  *@@ gpihCreateBitmap:
  1647.  *      calls gpihCreateBitmap2 with cPlanes and cBitCount == 0
  1648.  *      for compatibility with exports. Widgets might
  1649.  *      have used this func.
  1650.  *
  1651.  *@@changed V0.9.0 [umoeller]: function prototype changed to cx and cy
  1652.  *@@changed V0.9.16 (2001-12-18) [umoeller]: now using optimized gpihCreateBitmap2
  1653.  */
  1654.  
  1655. HBITMAP gpihCreateBitmap(HPS hpsMem,        // in: memory DC
  1656.                          ULONG cx,          // in: width of new bitmap
  1657.                          ULONG cy)          // in: height of new bitmap
  1658. {
  1659.     return gpihCreateBitmap2(hpsMem,
  1660.                              cx,
  1661.                              cy,
  1662.                              0,
  1663.                              0);            // current screen bit count
  1664. }
  1665.  
  1666. /*
  1667.  *@@ gpihCreateBitmap2:
  1668.  *      creates a new bitmap for a given memory PS.
  1669.  *      This bitmap will have the cPlanes and bitcount
  1670.  *      which are found in the memory PS.
  1671.  *      For all the mysterious other values, we use
  1672.  *      fixed default values, this doesn't seem to hurt.
  1673.  *
  1674.  *      Note that the bitmap is _not_ yet selected into
  1675.  *      the specified memory PS. You must call
  1676.  +          GpiSetBitmap(hpsMem, hbm)
  1677.  *      to do this.
  1678.  *
  1679.  *      Returns the bitmap handle or NULLHANDLE upon errors.
  1680.  *
  1681.  *@@added V0.9.16 (2001-12-18) [umoeller]
  1682.  */
  1683.  
  1684. HBITMAP gpihCreateBitmap2(HPS hpsMem,        // in: memory DC
  1685.                           ULONG cx,          // in: width of new bitmap
  1686.                           ULONG cy,          // in: height of new bitmap
  1687.                           ULONG cPlanes,     // in: color planes (usually 1); if 0, current screen is used
  1688.                           ULONG cBitCount)   // in: either 1, 4, or 24; if 0, current screen value
  1689. {
  1690.     HBITMAP hbm = NULLHANDLE;
  1691.     LONG alData[2];
  1692.     BITMAPINFOHEADER2 bih2;
  1693.     // PBITMAPINFO2 pbmi = NULL;
  1694.  
  1695.     // determine the device's plane/bit-count format;
  1696.     // alData[0] then has cPlanes,
  1697.     // alData[1] has cBitCount
  1698.     if (GpiQueryDeviceBitmapFormats(hpsMem, 2, alData))
  1699.     {
  1700.         // set up the BITMAPINFOHEADER2 and BITMAPINFO2 structures
  1701.         bih2.cbFix = (ULONG)sizeof(BITMAPINFOHEADER2);
  1702.         bih2.cx = cx;
  1703.         bih2.cy = cy;
  1704.         bih2.cPlanes = (cPlanes) ? cPlanes : alData[0];
  1705.         bih2.cBitCount = (cBitCount) ? cBitCount : alData[1];
  1706.         bih2.ulCompression = BCA_UNCOMP;
  1707.         bih2.cbImage = (    (   (bih2.cx
  1708.                                     * (1 << bih2.cPlanes)
  1709.                                     * (1 << bih2.cBitCount)
  1710.                                 ) + 31
  1711.                             ) / 32
  1712.                        ) * bih2.cy;
  1713.         bih2.cxResolution = 70;
  1714.         bih2.cyResolution = 70;
  1715.         bih2.cclrUsed = 2;
  1716.         bih2.cclrImportant = 0;
  1717.         bih2.usUnits = BRU_METRIC;  // measure units for cxResolution/cyResolution: pels per meter
  1718.         bih2.usReserved = 0;
  1719.         bih2.usRecording = BRA_BOTTOMUP;        // scan lines are bottom to top (default)
  1720.         bih2.usRendering = BRH_NOTHALFTONED;    // other algorithms aren't documented anyway
  1721.         bih2.cSize1 = 0;            // parameter for halftoning (undocumented anyway)
  1722.         bih2.cSize2 = 0;            // parameter for halftoning (undocumented anyway)
  1723.         bih2.ulColorEncoding = BCE_RGB;     // only possible value
  1724.         bih2.ulIdentifier = 0;              // application-specific data
  1725.  
  1726.         // create a bit map that is compatible with the display
  1727.         hbm = GpiCreateBitmap(hpsMem,
  1728.                               &bih2,
  1729.                               0,            // do not initialize
  1730.                               NULL,         // init data
  1731.                               NULL);
  1732.     }
  1733.  
  1734.     return hbm;
  1735. }
  1736.  
  1737. /*
  1738.  *@@ gpihCreateHalftonedBitmap:
  1739.  *      this creates a half-toned copy of the
  1740.  *      input bitmap by doing the following:
  1741.  *
  1742.  *      1)  create a new bitmap with the size of hbmSource;
  1743.  *      2)  copy hbmSource to the new bitmap (using GpiWCBitBlt);
  1744.  *      3)  overpaint every other pixel with lColorGray.
  1745.  *
  1746.  *      Note that the memory DC is switched to RGB mode, so
  1747.  *      lColorGray better be an RGB color.
  1748.  *
  1749.  *      Note: hbmSource must _not_ be selected into any device
  1750.  *      context, or otherwise GpiWCBitBlt will fail.
  1751.  *
  1752.  *      This returns the new bitmap or NULLHANDLE upon errors.
  1753.  *
  1754.  *@added V0.9.0
  1755.  */
  1756.  
  1757. HBITMAP gpihCreateHalftonedBitmap(HAB hab,              // in: anchor block
  1758.                                   HBITMAP hbmSource,    // in: source bitmap
  1759.                                   LONG lColorGray)      // in: color used for gray
  1760. {
  1761.     HBITMAP hbmReturn = NULLHANDLE;
  1762.  
  1763.     HDC     hdcMem;
  1764.     HPS     hpsMem;
  1765.     BITMAPINFOHEADER2 bmi;
  1766.  
  1767.     if (hbmSource)
  1768.     {
  1769.         SIZEL szlPage;
  1770.         // query bitmap info
  1771.         bmi.cbFix = sizeof(bmi);
  1772.         GpiQueryBitmapInfoHeader(hbmSource, &bmi);
  1773.  
  1774.         szlPage.cx = bmi.cx;
  1775.         szlPage.cy = bmi.cy;
  1776.         if (gpihCreateMemPS(hab, &szlPage, &hdcMem, &hpsMem))
  1777.         {
  1778.             if ((hbmReturn = gpihCreateBitmap(hpsMem,
  1779.                                               bmi.cx,
  1780.                                               bmi.cy)))
  1781.             {
  1782.                 if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
  1783.                 {
  1784.                     POINTL  aptl[4];
  1785.  
  1786.                     // step 1: copy bitmap
  1787.                     memset(aptl, 0, sizeof(POINTL) * 4);
  1788.                     // aptl[0]: target bottom-left, is all 0
  1789.                     // aptl[1]: target top-right (inclusive!)
  1790.                     aptl[1].x = bmi.cx - 1;
  1791.                     aptl[1].y = bmi.cy - 1;
  1792.                     // aptl[2]: source bottom-left, is all 0
  1793.  
  1794.                     // aptl[3]: source top-right (exclusive!)
  1795.                     aptl[3].x = bmi.cx;
  1796.                     aptl[3].y = bmi.cy;
  1797.                     GpiWCBitBlt(hpsMem,     // target HPS (bmp selected)
  1798.                                 hbmSource,
  1799.                                 4L,             // must always be 4
  1800.                                 &aptl[0],       // points array
  1801.                                 ROP_SRCCOPY,
  1802.                                 BBO_IGNORE);
  1803.                                         // doesn't matter here, because we're not stretching
  1804.  
  1805.                     // step 2: overpaint bitmap
  1806.                     // with half-toned pattern
  1807.  
  1808.                     gpihSwitchToRGB(hpsMem);
  1809.  
  1810.                     GpiMove(hpsMem, &aptl[0]);  // still 0, 0
  1811.                     aptl[0].x = bmi.cx - 1;
  1812.                     aptl[0].y = bmi.cy - 1;
  1813.                     GpiSetColor(hpsMem, lColorGray);
  1814.                     GpiSetPattern(hpsMem, PATSYM_HALFTONE);
  1815.                     GpiBox(hpsMem,
  1816.                            DRO_FILL, // interior only
  1817.                            &aptl[0],
  1818.                            0, 0);    // no corner rounding
  1819.  
  1820.                     // unselect bitmap
  1821.                     GpiSetBitmap(hpsMem, NULLHANDLE);
  1822.                 } // end if (GpiSetBitmap(hpsMem, hbmReturn) != HBM_ERROR)
  1823.                 else
  1824.                 {
  1825.                     // error selecting bitmap:
  1826.                     GpiDeleteBitmap(hbmReturn);
  1827.                     hbmReturn = NULLHANDLE;
  1828.                 }
  1829.             } // end if (hbmReturn = gpihCreateBitmap...
  1830.  
  1831.             GpiDestroyPS(hpsMem);
  1832.             DevCloseDC(hdcMem);
  1833.         } // end if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
  1834.     } // end if (hbmSource)
  1835.  
  1836.     return hbmReturn;
  1837. }
  1838.  
  1839. /*
  1840. typedef struct _BITMAPARRAYFILEHEADER {
  1841.     USHORT               usType;     //  Type of structure.
  1842.     ULONG                cbSize;     //  Size of the BITMAPARRAYFILEHEADER structure in bytes.
  1843.     ULONG                offNext;    //  Offset of the next BITMAPARRAYFILEHEADER structure from the start of the file.
  1844.     USHORT               cxDisplay;  //  Device width, in pels.
  1845.     USHORT               cyDisplay;  //  Device height, in pels.
  1846.     BITMAPFILEHEADER     bfh;        //  Bitmap file header structure.
  1847. } BITMAPARRAYFILEHEADER;
  1848.  
  1849. typedef struct _BITMAPARRAYFILEHEADER2 {
  1850.     USHORT                usType;     //  Type of structure.
  1851.     ULONG                 cbSize;     //  Size of the BITMAPARRAYFILEHEADER2 structure in bytes.
  1852.     ULONG                 offNext;    //  Offset of the next BITMAPARRAYFILEHEADER2 structure from the start of the file.
  1853.     USHORT                cxDisplay;  //  Device width, in pels.
  1854.     USHORT                cyDisplay;  //  Device height, in pels.
  1855.     BITMAPFILEHEADER2     bfh2;       //  Bitmap file header structure.
  1856. } BITMAPARRAYFILEHEADER2;
  1857.  
  1858. These two are binarily the same, except for the file header that is contained.
  1859. */
  1860.  
  1861. /*      OLD FORMAT
  1862.  
  1863. typedef struct _BITMAPFILEHEADER {
  1864.     USHORT               usType;    //  Type of resource the file contains.
  1865.     ULONG                cbSize;    //  Size of the BITMAPFILEHEADER structure in bytes.
  1866.     SHORT                xHotspot;  //  Width of hotspot for icons and pointers.
  1867.     SHORT                yHotspot;  //  Height of hotspot for icons and pointers.
  1868.     USHORT               offBits;   //  Offset in bytes.
  1869.     BITMAPINFOHEADER     bmp;       //  Bitmap information header structure.
  1870.  
  1871.   typedef struct _BITMAPINFOHEADER {
  1872.     ULONG      cbFix;      //  Length of structure.
  1873.     USHORT     cx;         //  Bitmap width in pels.
  1874.     USHORT     cy;         //  Bitmap height in pels.
  1875.     USHORT     cPlanes;    //  Number of bit planes.
  1876.     USHORT     cBitCount;  //  Number of bits per pel within a plane.
  1877.   } BITMAPINFOHEADER;
  1878.  
  1879. } BITMAPFILEHEADER;
  1880. */
  1881.  
  1882. /*      NEW FORMAT
  1883.  
  1884. typedef struct _BITMAPFILEHEADER2 {
  1885.     USHORT                usType;    //  Type of resource the file contains.
  1886.     ULONG                 cbSize;    //  Size of the BITMAPFILEHEADER2 structure in bytes.
  1887.     SHORT                 xHotspot;  //  Width of hotspot for icons and pointers.
  1888.     SHORT                 yHotspot;  //  Height of hotspot for icons and pointers.
  1889.     USHORT                offBits;   //  Offset in bytes.
  1890.     BITMAPINFOHEADER2     bmp2;      //  Bitmap information header structure.
  1891.  
  1892.     typedef struct _BITMAPINFOHEADER2 {
  1893.       ULONG      cbFix;            //  Length of structure.
  1894.       ULONG      cx;               //  Bitmap width in pels.
  1895.       ULONG      cy;               //  Bitmap height in pels.
  1896.       USHORT     cPlanes;          //  Number of bit planes.
  1897.       USHORT     cBitCount;        //  Number of bits per pel within a plane.
  1898.       ULONG      ulCompression;    //  Compression scheme used to store the bit map.
  1899.       ULONG      cbImage;          //  Length of bitmap storage data, in bytes.
  1900.       ULONG      cxResolution;     //  Horizontal component of the resolution of target device.
  1901.       ULONG      cyResolution;     //  Vertical component of the resolution of target device.
  1902.       ULONG      cclrUsed;         //  Number of color indexes used.
  1903.       ULONG      cclrImportant;    //  Minimum number of color indexes for satisfactory appearance of the bit map.
  1904.       USHORT     usUnits;          //  Units of measure.
  1905.       USHORT     usReserved;       //  Reserved.
  1906.       USHORT     usRecording;      //  Recording algorithm.
  1907.       USHORT     usRendering;      //  Halftoning algorithm.
  1908.       ULONG      cSize1;           //  Size value 1.
  1909.       ULONG      cSize2;           //  Size value 2.
  1910.       ULONG      ulColorEncoding;  //  Color encoding.
  1911.       ULONG      ulIdentifier;     //  Reserved for application use.
  1912.     } BITMAPINFOHEADER2;
  1913.  
  1914.     Because of the unfortunate replacement of USHORTs with ULONGs for
  1915.     cx and cy in the info header, the cx, cy, and cBitCount data is
  1916.     NOT the same between old and new formats. Well, IBM, good job.
  1917.     And ICONEDIT _still_ writes the old format, too.
  1918.  
  1919. } BITMAPFILEHEADER2;
  1920.  
  1921. */
  1922.  
  1923. /*
  1924.  *@@ gpihCreateBitmapFromFile:
  1925.  *      creates a new HBITMAP from the given bitmap data,
  1926.  *      which is assumed to be in a standard OS/2 1.3 or
  1927.  *      2.0 bitmap file format. That is, it should start
  1928.  *      with either a BITMAPFILEHEADER or BITMAPFILEHEADER2
  1929.  *      (if single bitmap) or a BITMAPARRAYFILEHEADER or
  1930.  *      BITMAPARRAYFILEHEADER2 (if bitmap array) and all
  1931.  *      the bitmap bits following. This format is also
  1932.  *      used by bitmap resources.
  1933.  *
  1934.  *      If the file contains only a single bitmap,
  1935.  *      this bitmap is used.
  1936.  *
  1937.  *      If it contains a bitmap array, we use the
  1938.  *      "best bitmap" in the array, which is determined
  1939.  *      from the following criteria (in this order):
  1940.  *
  1941.  *      --  a device-dependent bitmap, if its device
  1942.  *          resolution is not too large and the given
  1943.  *          HPS can display all its colors;
  1944.  *
  1945.  *      --  a device-dependent bitmap, if its device
  1946.  *          resolution is not too large, even if the
  1947.  *          given HPS cannot display all its colors;
  1948.  *
  1949.  *      --  a device-independent bitmap, if the given
  1950.  *          HPS can display all its colors;
  1951.  *
  1952.  *      --  the first device-independent bitmap in
  1953.  *          the file;
  1954.  *
  1955.  *      --  the first bitmap in the file.
  1956.  *
  1957.  *      Support for bitmap arrays was added with V0.9.19.
  1958.  *      I'm not quite sure if the above is the same way
  1959.  *      of selecting the "best bitmap" that GpiLoadBitmap
  1960.  *      would do, but without any documentation, who is
  1961.  *      supposed to know. It appears to me however that
  1962.  *      GpiLoadBitmap always does an _exact_ match of
  1963.  *      device resolutions (in contrast to this function).
  1964.  *      In other words, if you're running at 1152x864
  1965.  *      and the device-dependent bitmap is for 1024x768,
  1966.  *      _this_ function would use it, while GpiLoadBitmap
  1967.  *      might load a device-independent VGA bitmap
  1968.  *      instead. (Looks broken, if you ask me.)
  1969.  *
  1970.  *      Returns:
  1971.  *
  1972.  *      --  NO_ERROR
  1973.  *
  1974.  *      --  ERROR_INVALID_DATA: we can't understand this format.
  1975.  *
  1976.  *@@added V0.9.20 (2002-07-19) [umoeller]
  1977.  */
  1978.  
  1979. APIRET gpihCreateBitmapFromFile(HBITMAP *phbm,        // out: bitmap if NO_ERROR
  1980.                                 HPS hps,              // in: HPS for bmp
  1981.                                 PBYTE pData)          // in: bitmap data
  1982. {
  1983.     APIRET arc = NO_ERROR;
  1984.  
  1985.     PBITMAPFILEHEADER2 pbfh = (PBITMAPFILEHEADER2)pData;
  1986.  
  1987.     // check bitmap magic codes
  1988.     switch (pbfh->usType)
  1989.     {
  1990.         case BFT_BMAP:     // "BM"
  1991.             // single bitmap in file (no array):
  1992.             if (!(*phbm = GpiCreateBitmap(hps,
  1993.                                           &pbfh->bmp2,
  1994.                                           CBM_INIT,
  1995.                                           (PBYTE)pbfh + pbfh->offBits,
  1996.                                           (PBITMAPINFO2)&pbfh->bmp2)))
  1997.                 arc = ERROR_INVALID_DATA;
  1998.         break;
  1999.  
  2000.         case BFT_BITMAPARRAY:   // "BA"
  2001.         {
  2002.             // define a handy union for all the above crap
  2003.             typedef union
  2004.             {
  2005.                 BITMAPFILEHEADER    Old;
  2006.                 BITMAPFILEHEADER2   New;
  2007.             } BMPUNION, *PBMPUNION;
  2008.  
  2009.             PBMPUNION
  2010.                     puFirstDI = NULL,   // first device-independent bitmap
  2011.                     puBestDI = NULL,    // best device-independent bitmap
  2012.                     puFirstDD = NULL,   // first device-dependent bitmap
  2013.                     puBestDD = NULL,    // best device-dependent bitmap
  2014.                     puFirstAny = NULL,  // first bitmap of any type
  2015.                     puUse;
  2016.  
  2017.             // get device resolution for this HPS
  2018.             // so we can select the "best bitmap"
  2019.             #define GET_CAPS_FIRST  CAPS_WIDTH
  2020.             #define GET_CAPS_LAST   CAPS_COLOR_BITCOUNT
  2021.             #define GET_NO_CAPS     GET_CAPS_LAST - GET_CAPS_FIRST + 1
  2022.  
  2023.             LONG alCaps[GET_NO_CAPS];
  2024.             PBITMAPARRAYFILEHEADER2 pba = (PBITMAPARRAYFILEHEADER2)pData;
  2025.  
  2026.             DevQueryCaps(GpiQueryDevice(hps),
  2027.                          GET_CAPS_FIRST,
  2028.                          GET_NO_CAPS,
  2029.                          alCaps);
  2030.  
  2031.             #define BITCOUNT    alCaps[CAPS_COLOR_BITCOUNT - GET_CAPS_FIRST]
  2032.             #define WIDTH       alCaps[CAPS_WIDTH - GET_CAPS_FIRST]
  2033.             #define HEIGHT      alCaps[CAPS_HEIGHT - GET_CAPS_FIRST]
  2034.  
  2035.             // for-all-bitmaps-in-array loop
  2036.             while (pba)
  2037.             {
  2038.                 PBMPUNION puThis = (PBMPUNION)&pba->bfh2;
  2039.  
  2040.                 LONG cx = 0,
  2041.                      cy,
  2042.                      cBitCount;
  2043.  
  2044.                 // ignore this if the type isn't "BM"
  2045.                 if (puThis->Old.usType == BFT_BMAP)
  2046.                 {
  2047.                     // fill the three, but watch out, the offsets are
  2048.                     // different between old and new formats
  2049.                     if (puThis->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER))
  2050.                     {
  2051.                         // old format:
  2052.                         cx = puThis->Old.bmp.cx;
  2053.                         cy = puThis->Old.bmp.cy;
  2054.                         cBitCount = puThis->Old.bmp.cBitCount;
  2055.                     }
  2056.                     else if (puThis->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER2))
  2057.                     {
  2058.                         // new format:
  2059.                         cx = puThis->New.bmp2.cx;
  2060.                         cy = puThis->New.bmp2.cy;
  2061.                         cBitCount = puThis->New.bmp2.cBitCount;
  2062.                     }
  2063.                 }
  2064.  
  2065.                 if (cx)
  2066.                 {
  2067.                     // remember the first bitmap from all that we see
  2068.                     if (!puFirstAny)
  2069.                         puFirstAny = puThis;
  2070.  
  2071.                     // check device resolution... device-independent
  2072.                     // one has cxDisplay and cyDisplay set to 0
  2073.                     if (    (!pba->cxDisplay)
  2074.                          && (!pba->cyDisplay)
  2075.                        )
  2076.                     {
  2077.                         // device-independent:
  2078.  
  2079.                         // remember the first device-independent bitmap
  2080.                         if (!puFirstDI)
  2081.                             puFirstDI = puThis;
  2082.  
  2083.                         if (cBitCount <= BITCOUNT)
  2084.                             // we can display all the colors:
  2085.                             puBestDI = puThis;
  2086.                     }
  2087.                     else
  2088.                     {
  2089.                         // device-dependent:
  2090.                         // ignore if device resolution is too large
  2091.                         if (    (pba->cxDisplay <= WIDTH)
  2092.                              && (pba->cyDisplay <= HEIGHT)
  2093.                            )
  2094.                         {
  2095.                             // remember first matching device-dependent bitmap
  2096.                             if (!puFirstDD)
  2097.                                 puFirstDD = puThis;
  2098.  
  2099.                             if (cBitCount <= BITCOUNT)
  2100.                                 puBestDD = puThis;
  2101.                         }
  2102.                     }
  2103.                 } // end if cx
  2104.  
  2105.                 // go for next bmp in array
  2106.                 if (pba->offNext)
  2107.                     // another one coming up:
  2108.                     // this ofs is from the beginning of the file
  2109.                     pba = (PBITMAPARRAYFILEHEADER2)(pData + pba->offNext);
  2110.                 else
  2111.                     // no more bitmaps:
  2112.                     break;
  2113.             } // end while (pba)
  2114.  
  2115.             if (    (puUse = puBestDD)
  2116.                  || (puUse = puFirstDD)
  2117.                  || (puUse = puBestDI)
  2118.                  || (puUse = puFirstDI)
  2119.                  || (puUse = puFirstAny)
  2120.                )
  2121.             {
  2122.                 PBITMAPINFOHEADER2 pbih2;
  2123.                 PBYTE pbInitData;
  2124.  
  2125.                 if (puUse->Old.bmp.cbFix == sizeof(BITMAPINFOHEADER))
  2126.                 {
  2127.                     // old format:
  2128.                     pbih2 = (PBITMAPINFOHEADER2)&puUse->Old.bmp;
  2129.                     pbInitData = (PBYTE)pData + puUse->Old.offBits;
  2130.                 }
  2131.                 else
  2132.                 {
  2133.                     // new format:
  2134.                     pbih2 = &puUse->New.bmp2;
  2135.                     pbInitData = (PBYTE)pData + puUse->New.offBits;
  2136.                 }
  2137.  
  2138.                 if (!(*phbm = GpiCreateBitmap(hps,
  2139.                                               pbih2,
  2140.                                               CBM_INIT,
  2141.                                               pbInitData,
  2142.                                               (PBITMAPINFO2)pbih2)))
  2143.                     arc = ERROR_INVALID_DATA;
  2144.             }
  2145.             else
  2146.                 arc = ERROR_INVALID_DATA;
  2147.         }
  2148.         break;
  2149.     }
  2150.  
  2151.     return arc;
  2152. }
  2153.  
  2154. /*
  2155.  *@@ gpihLoadBitmap:
  2156.  *      creates a new HBITMAP from the given resource.
  2157.  *
  2158.  *      This is a replacement for GpiLoadBitmap which handles
  2159.  *      device-dependent bitmaps in bitmap arrays in a
  2160.  *      non-brain-dead way.
  2161.  *
  2162.  *      Calls gpihCreateBitmapFromFile for handling the resource
  2163.  *      data. See remarks there for how we select a bitmap from
  2164.  *      bitmap arrays, which is _different_ from GpiLoadBitmap.
  2165.  *
  2166.  *      Returns:
  2167.  *
  2168.  *      --  NO_ERROR: *phbm has received new HBITMAP,
  2169.  *          to be freed with GpiDeleteBitmap.
  2170.  *
  2171.  *      --  ERROR_INVALID_DATA: resource exists, but we
  2172.  *          can't understand its format.
  2173.  *
  2174.  *      plus the error codes from DosGetResource.
  2175.  *
  2176.  *@@added V0.9.20 (2002-07-19) [umoeller]
  2177.  */
  2178.  
  2179. APIRET gpihLoadBitmap(HBITMAP *phbm,        // out: bitmap if NO_ERROR
  2180.                       HPS hps,              // in: HPS for bmp
  2181.                       HMODULE hmodResource, // in: module to load bitmap from
  2182.                       ULONG idBitmap)       // in: resource ID for bitmap
  2183. {
  2184.     APIRET arc;
  2185.  
  2186.     PBYTE pbData;
  2187.  
  2188.     if (!(arc = DosGetResource(hmodResource,
  2189.                                RT_BITMAP,
  2190.                                idBitmap,
  2191.                                (PVOID*)&pbData)))
  2192.     {
  2193.         arc = gpihCreateBitmapFromFile(phbm,
  2194.                                        hps,
  2195.                                        pbData);
  2196.  
  2197.         DosFreeResource(pbData);
  2198.     }
  2199.  
  2200.     return arc;
  2201. }
  2202.  
  2203. /*
  2204.  *@@ gpihLoadBitmapFile:
  2205.  *      this loads the specified bitmap file into
  2206.  *      the given HPS. Note that the bitmap is _not_
  2207.  *      yet selected into the HPS.
  2208.  *
  2209.  *      Calls gpihCreateBitmapFromFile for handling the resource
  2210.  *      data. See remarks there for how we select a bitmap from
  2211.  *      bitmap arrays, which is _different_ from GpiLoadBitmap.
  2212.  *
  2213.  *      Returns:
  2214.  *
  2215.  *      --  NO_ERROR: *phbm has received new HBITMAP,
  2216.  *          to be freed with GpiDeleteBitmap.
  2217.  *
  2218.  *      --  ERROR_INVALID_PARAMETER
  2219.  *
  2220.  *      --  ERROR_INVALID_DATA: file exists, but we
  2221.  *          can't understand its format.
  2222.  *
  2223.  *      plus the error codes from doshOpen and DosRead.
  2224.  *
  2225.  *@@changed V0.9.4 (2000-08-03) [umoeller]: this didn't return NULLHANDLE on errors
  2226.  *@@changed V0.9.19 (2002-04-14) [umoeller]: rewritten to support bitmap arrays, prototype changed
  2227.  *@@changed V0.9.20 (2002-07-19) [umoeller]: extracted bitmap selection into gpihCreateBitmapFromFile
  2228.  */
  2229.  
  2230. APIRET gpihLoadBitmapFile(HBITMAP *phbm,        // out: bitmap if NO_ERROR
  2231.                           HPS hps,              // in: HPS for bmp
  2232.                           PCSZ pcszBmpFile)     // in: bitmap filename
  2233. {
  2234.     APIRET arc;
  2235.     PXFILE pFile;
  2236.     ULONG cbFile = 0;
  2237.  
  2238.     if (!hps || !pcszBmpFile || !phbm)
  2239.         return ERROR_INVALID_PARAMETER;
  2240.  
  2241.     if (!(arc = doshOpen(pcszBmpFile,
  2242.                          XOPEN_READ_EXISTING | XOPEN_BINARY,
  2243.                          &cbFile,
  2244.                          &pFile)))
  2245.     {
  2246.         PBYTE   pData;
  2247.         if (!(pData = (PBYTE)malloc(cbFile)))
  2248.             arc = ERROR_NOT_ENOUGH_MEMORY;
  2249.         else
  2250.         {
  2251.             // read in the ENTIRE file
  2252.             if (!(arc = DosRead(pFile->hf,
  2253.                                 pData,
  2254.                                 cbFile,
  2255.                                 &cbFile)))
  2256.                 // extracted all the code into this extra func
  2257.                 // V0.9.20 (2002-07-19) [umoeller]
  2258.                 arc = gpihCreateBitmapFromFile(phbm,
  2259.                                                hps,
  2260.                                                (PBYTE)pData);
  2261.  
  2262.             free(pData);
  2263.         }
  2264.  
  2265.         doshClose(&pFile);
  2266.     }
  2267.  
  2268.     return arc;
  2269. }
  2270.  
  2271. /*
  2272.  *@@ gpihStretchBitmap:
  2273.  *      this copies hbmSource to the bitmap selected
  2274.  *      into hpsTarget, which must be a memory PS.
  2275.  *
  2276.  *      The source size is the whole size of hbmSource,
  2277.  *      the target size is specified in prclTarget
  2278.  *      (which is exclusive, meaning that the top right
  2279.  *      corner of that rectangle lies _outside_ the target).
  2280.  *
  2281.  *      This uses GpiWCBitBlt to stretch the bitmap.
  2282.  *      hbmSource therefore must _not_ be selected
  2283.  *      into any presentation space, or GpiWCBitBlt will
  2284.  *      fail.
  2285.  *
  2286.  *      If (fPropotional == TRUE), the target size is
  2287.  *      modified so that the proportions of the bitmap
  2288.  *      are preserved. The bitmap data will then be
  2289.  *      copied to a subrectangle of the target bitmap:
  2290.  *      there will be extra space either to the left
  2291.  *      and right of the bitmap data or to the bottom
  2292.  *      and top.
  2293.  *      The outside areas of the target bitmap are
  2294.  *      not changed then, so you might want to fill
  2295.  *      the bitmap with some color first.
  2296.  *
  2297.  *      This returns the return value of GpiWCBitBlt,
  2298.  *      which can be:
  2299.  *      --  GPI_OK
  2300.  *      --  GPI_HITS: correlate hits
  2301.  *      --  GPI_ERROR: error occured (probably either hbmSource not free
  2302.  *                     or no bitmap selected into hpsTarget)
  2303.  *
  2304.  *@added V0.9.0
  2305.  */
  2306.  
  2307. LONG gpihStretchBitmap(HPS hpsTarget,       // in: memory PS to copy bitmap to
  2308.                        HBITMAP hbmSource,   // in: bitmap to be copied into hpsTarget (must be free)
  2309.                        PRECTL prclSource,   // in: source rectangle -- if NULL, use size of bitmap
  2310.                        PRECTL prclTarget,   // in: target rectangle (req.)
  2311.                        BOOL fProportional)  // in: preserve proportions when stretching?
  2312. {
  2313.     BITMAPINFOHEADER2   bih2;
  2314.     POINTL              aptl[4];
  2315.     BOOL                fCalculated = FALSE;
  2316.  
  2317.     memset(aptl, 0, sizeof(POINTL) * 4);
  2318.  
  2319.     bih2.cbFix = sizeof(bih2);
  2320.     GpiQueryBitmapInfoHeader(hbmSource,
  2321.                              &bih2);
  2322.  
  2323.     // aptl[2]: source bottom-left, is all 0
  2324.     // aptl[3]: source top-right (exclusive!)
  2325.     aptl[3].x = bih2.cx;
  2326.     aptl[3].y = bih2.cy;
  2327.  
  2328.     if (fProportional)
  2329.     {
  2330.         // proportional mode:
  2331.  
  2332.         // 1) find out whether cx or cy is too
  2333.         // large
  2334.  
  2335.         ULONG ulPropSource = (bih2.cx * 1000)
  2336.                                     / bih2.cy;
  2337.                 // e.g. if the bmp is 200 x 100, we now have 2000
  2338.         ULONG ulPropTarget = ((prclTarget->xRight - prclTarget->xLeft) * 1000)
  2339.                                     / (prclTarget->yTop - prclTarget->yBottom);
  2340.                 // case 1: if prclTarget is 300 x 100, we now have 3000 (> ulPropSource)
  2341.                 // case 2: if prclTarget is 150 x 100, we now have 1500 (< ulPropSource)
  2342.  
  2343.         // case 1:
  2344.         if (ulPropTarget > ulPropSource)
  2345.         {
  2346.             // prclTarget is too wide (horizontally):
  2347.             // decrease width, keep height
  2348.  
  2349.             ULONG cx = (prclTarget->xRight - prclTarget->xLeft);
  2350.             ULONG cxNew = (cx * ulPropSource) / ulPropTarget;
  2351.  
  2352.             // aptl[0]: target bottom-left
  2353.             // move left right (towards center)
  2354.             aptl[0].x = prclTarget->xLeft + ((cx - cxNew) / 2);
  2355.             aptl[0].y = prclTarget->yBottom;
  2356.  
  2357.             // aptl[1]: target top-right (inclusive!)
  2358.             aptl[1].x = aptl[0].x + cxNew;
  2359.             aptl[1].y = prclTarget->yTop;
  2360.  
  2361.             fCalculated = TRUE;
  2362.         }
  2363.         else
  2364.         {
  2365.             // prclTarget is too high (vertically):
  2366.             // keep width, decrease height
  2367.  
  2368.             ULONG cy = (prclTarget->yTop - prclTarget->yBottom);
  2369.             ULONG cyNew = (cy * ulPropTarget) / ulPropSource;
  2370.  
  2371.             // aptl[0]: target bottom-left
  2372.             aptl[0].x = prclTarget->xLeft;
  2373.             // move bottom up (towards center)
  2374.             aptl[0].y = prclTarget->yBottom + ((cy - cyNew) / 2);
  2375.  
  2376.             // aptl[1]: target top-right (inclusive!)
  2377.             aptl[1].x = prclTarget->xRight;
  2378.             aptl[1].y = aptl[0].y + cyNew;
  2379.                     // (prclTarget->yTop * ulPropSource) / ulPropTarget;
  2380.  
  2381.             fCalculated = TRUE;
  2382.         }
  2383.     } // end if (pa->ulFlags & ANF_PROPORTIONAL)
  2384.  
  2385.     if (!fCalculated)
  2386.     {
  2387.         // non-proportional mode or equal proportions:
  2388.         // stretch to whole size of prclTarget
  2389.  
  2390.         // aptl[0]: target bottom-left
  2391.         aptl[0].x = prclTarget->xLeft;
  2392.         aptl[0].y = prclTarget->yBottom;
  2393.         // aptl[1]: target top-right (inclusive!)
  2394.         aptl[1].x = prclTarget->xRight;
  2395.         aptl[1].y = prclTarget->yTop;
  2396.     }
  2397.  
  2398.     return GpiWCBitBlt(hpsTarget,       // target HPS (bmp selected)
  2399.                        hbmSource,
  2400.                        4L,              // must always be 4
  2401.                        &aptl[0],        // points array
  2402.                        ROP_SRCCOPY,
  2403.                        BBO_IGNORE);
  2404.                                 // ignore eliminated rows or
  2405.                                 // columns; useful for color
  2406. }
  2407.  
  2408. /*
  2409.  * gpihIcon2Bitmap:
  2410.  *      this paints the given icon/pointer into
  2411.  *      a bitmap. Note that if the bitmap is
  2412.  *      larget than the system icon size, only
  2413.  *      the rectangle of the icon will be filled
  2414.  *      with lBkgndColor.
  2415.  *
  2416.  *      Returns FALSE upon errors.
  2417.  *
  2418.  *added V0.9.0 [umoeller]
  2419.  *changed V0.9.16 (2001-10-15) [umoeller]: added pptlLowerLeft
  2420.  *changed V0.9.16 (2001-10-15) [umoeller]: fixed inclusive/exclusive confusion (sigh...)
  2421.  *changed V0.9.19 (2002-06-13) [umoeller]: fixed funny colors when scaling
  2422.  *removed V0.9.19 (2002-06-18) [umoeller]
  2423.  */
  2424.  
  2425. #if 0
  2426.  
  2427. BOOL gpihIcon2Bitmap(HPS hpsMem,         // in: target memory PS with bitmap selected into it
  2428.                      HPOINTER hptr,      // in: source icon
  2429.                      LONG lBkgndColor,   // in: background color for transparent areas
  2430.                      PPOINTL pptlLowerLeft, // in: lower left corner of where to paint (ptr can be NULL)
  2431.                      ULONG ulIconSize)   // in: icon size (should be the value of WinQuerySysValue(HWND_DESKTOP, SV_CXICON))
  2432. {
  2433.     BOOL        brc = FALSE;
  2434.     POINTERINFO pi;
  2435.  
  2436.     // Each icon consists of two (really three)
  2437.     // bitmaps, which are stored in the POINTERINFO
  2438.     // structure:
  2439.     //   pi.hbmColor    is the actual bitmap to be
  2440.     //                  drawn. The parts that are
  2441.     //                  to be transparent or inverted
  2442.     //                  are black in this image.
  2443.     //   pi.hbmPointer  has twice the height of
  2444.     //                  hbmColor. The upper bitmap
  2445.     //                  contains an XOR mask (for
  2446.     //                  inverting parts), the lower
  2447.     //                  bitmap an AND mask (for
  2448.     //                  transparent parts).
  2449.     if (WinQueryPointerInfo(hptr, &pi))
  2450.     {
  2451.         POINTL  ptlLowerLeft = {0, 0};
  2452.         POINTL  aptl[4];
  2453.         memset(aptl, 0, sizeof(POINTL) * 4);
  2454.  
  2455.         if (pptlLowerLeft)
  2456.             // lower left specified: V0.9.16 (2001-10-15) [umoeller]
  2457.             memcpy(&ptlLowerLeft, pptlLowerLeft, sizeof(POINTL));
  2458.  
  2459.         // aptl[0]: target bottom-left, is all 0
  2460.         aptl[0].x = ptlLowerLeft.x;
  2461.         aptl[0].y = ptlLowerLeft.y;
  2462.  
  2463.         // aptl[1]: target top-right (inclusive!)
  2464.         // V0.9.16 (2001-10-15) [umoeller]: fixed rectangle confusion
  2465.         aptl[1].x = ptlLowerLeft.x + ulIconSize - 1;
  2466.         aptl[1].y = ptlLowerLeft.y + ulIconSize - 1;
  2467.  
  2468.         // aptl[2]: source bottom-left, is all 0
  2469.  
  2470.         // aptl[3]: source top-right (exclusive!)
  2471.         // V0.9.16 (2001-10-15) [umoeller]: fixed rectangle confusion
  2472.         aptl[3].x = ulIconSize; //  + 1;
  2473.         aptl[3].y = ulIconSize; //  + 1;
  2474.  
  2475.         GpiSetColor(hpsMem, CLR_WHITE);
  2476.         GpiSetBackColor(hpsMem, CLR_BLACK);
  2477.  
  2478.         // GpiErase(hpsMem);
  2479.  
  2480.         // V0.9.19 (2002-06-13) [umoeller]:
  2481.         // use BBO_IGNORE instead of BBO_OR or we get funny colors
  2482.         // when scaling down
  2483.  
  2484.         // work on the AND image
  2485.         GpiWCBitBlt(hpsMem,     // target
  2486.                     pi.hbmPointer,  // src bmp
  2487.                     4L,         // must always be 4
  2488.                     &aptl[0],   // point array
  2489.                     ROP_SRCAND,   // source AND target
  2490.                     BBO_IGNORE);        // V0.9.19 (2002-06-13) [umoeller]
  2491.  
  2492.         // paint the real image
  2493.         if (pi.hbmColor)
  2494.             GpiWCBitBlt(hpsMem,
  2495.                         pi.hbmColor,
  2496.                         4L,         // must always be 4
  2497.                         &aptl[0],   // point array
  2498.                         ROP_SRCPAINT,    // source OR target
  2499.                         BBO_IGNORE);        // V0.9.19 (2002-06-13) [umoeller]
  2500.  
  2501.         GpiSetColor(hpsMem, lBkgndColor);
  2502.         // work on the XOR image
  2503.         aptl[2].y = ulIconSize;                 // exclusive
  2504.         aptl[3].y = (ulIconSize * 2); //  /* + 1; */       // exclusive
  2505.         // V0.9.16 (2001-10-15) [umoeller]: fixed rectangle confusion
  2506.         GpiWCBitBlt(hpsMem,
  2507.                     pi.hbmPointer,
  2508.                     4L,         // must always be 4
  2509.                     &aptl[0],   // point array
  2510.                     ROP_SRCINVERT,
  2511.                     BBO_IGNORE);        // V0.9.19 (2002-06-13) [umoeller]
  2512.  
  2513.         brc = TRUE;
  2514.     }
  2515.  
  2516.     return brc;
  2517. }
  2518.  
  2519. #endif
  2520.  
  2521. /*
  2522.  *@@ gpihDrawPointer:
  2523.  *      replacement for WinDrawPointer that can do clipping.
  2524.  *
  2525.  *      Normally, to do clipping with WinDrawPointer, one
  2526.  *      would have to alter the clip rectangle for the current
  2527.  *      HPS, which requires creating regions and is thus quite
  2528.  *      expensive.
  2529.  *
  2530.  *      Instead, this function allows for specifying a clip
  2531.  *      rectangle directly. It blits the icon bitmaps directly
  2532.  *      without calling WinDrawPointer.
  2533.  *      Besides, since it uses GpiWCBitBlt, it should probably
  2534.  *      work with all types of device contexts.
  2535.  *
  2536.  *      This also replaces gpihIcon2Bitmap, which wasn't quite
  2537.  *      working in the first place and couldn't to clipping
  2538.  *      either.
  2539.  *
  2540.  *      If you don't need clipping and are drawing to the
  2541.  *      screen only, this function has no advantage over
  2542.  *      WinDrawPointer because it's presumably a bit slower.
  2543.  *
  2544.  *      Flags presently supported in fl:
  2545.  *
  2546.  *      --  DP_MINI (not DP_MINIICON, as stated in PMREF):
  2547.  *          use mini-icon.
  2548.  *
  2549.  *      --  DP_HALFTONED (V0.9.20)
  2550.  *
  2551.  *      Preconditions:
  2552.  *
  2553.  *      --  The hps is assumed to be in RGB mode.
  2554.  *
  2555.  *      Post conditions:
  2556.  *
  2557.  *      --  This uses GpiSet(Back)Color, so the foreground
  2558.  *          and background colors are undefined after the
  2559.  *          call.
  2560.  *
  2561.  *@@added V0.9.19 (2002-06-18) [umoeller]
  2562.  *@@changed V0.9.20 (2002-07-31) [umoeller]: optimized, saved one GpiQueryBitmapInfoHeader
  2563.  *@@changed V0.9.20 (2002-08-04) [umoeller]: added DP_HALFTONED
  2564.  */
  2565.  
  2566. BOOL gpihDrawPointer(HPS hps,           // in: target presentation space
  2567.                      LONG x,            // in: lower left target position of icon
  2568.                      LONG y,            // in: lower left target position of icon
  2569.                      HPOINTER hptr,     // in: icon to be drawn
  2570.                      PSIZEL pszlIcon,   // in: icon size (req., should be sysvalues SV_CXICON, SV_CYICON always)
  2571.                      PRECTL prclClip,   // in: clip rectangle (inclusive!) or NULL
  2572.                      ULONG fl)          // in: DP_* flags
  2573. {
  2574.     POINTERINFO pi;
  2575.  
  2576.     if (    (pszlIcon)
  2577.          && (hptr)
  2578.          && (WinQueryPointerInfo(hptr, &pi))
  2579.        )
  2580.     {
  2581.         POINTL  aptl[4];
  2582.         HBITMAP hbmThis;
  2583.         BITMAPINFOHEADER2 bmiAndXor,
  2584.                           bmiColor;
  2585.  
  2586.         // A HPOINTER really consists of two bitmaps,
  2587.         // one monochrome bitmap that has twice the icon
  2588.         // height and contains an AND mask in the upper
  2589.         // half and an XOR mask in the lower half, and
  2590.         // a (probably color) bitmap with the regular
  2591.         // icon height.
  2592.  
  2593.         // Drawing the icon means (1) blitting the AND
  2594.         // mask with ROP_SRCAND, (2) blitting the color
  2595.         // bitmap with ROP_SRCPAINT, (3) blitting the
  2596.         // XOR mask with ROP_SRCINVERT. Hence the slightly
  2597.         // complicated code that follows.
  2598.  
  2599.         /*
  2600.          * 0)   preparations
  2601.          */
  2602.  
  2603.         // set up a bunch of variables that are used
  2604.         // by the below calculations. We use cx|yIcon
  2605.         // to quickly get the system icon dimensions
  2606.         // and set up the clip offsets here too,
  2607.         // if a clip rectangle is specified.
  2608.  
  2609.         LONG    cxIcon = pszlIcon->cx,
  2610.                 cyIcon = pszlIcon->cy,
  2611.                 cySrc,
  2612.                 xRight,
  2613.                 yTop,
  2614.         // clip "rectangle"... this is not really a
  2615.         // recangle because it specifies _offsets_
  2616.         // towards the center of the icon to be
  2617.         // clipped
  2618.                 lClipLeft = 0,
  2619.                 lClipBottom = 0,
  2620.                 lClipRight = 0,
  2621.                 lClipTop = 0;
  2622.  
  2623.         BOOL    fMini;
  2624.  
  2625.         if (fMini = !!(fl & DP_MINI))
  2626.         {
  2627.             cxIcon /= 2;
  2628.             cyIcon /= 2;
  2629.         }
  2630.  
  2631.         // target top right (inclusive)
  2632.         xRight = x + cxIcon - 1;
  2633.         yTop = y + cyIcon - 1;
  2634.  
  2635.         if (prclClip)
  2636.         {
  2637.             // we have a clip rectangle:
  2638.             // set up the clip offsets that are used
  2639.             // in both the target and source coordinates
  2640.             // for blitting
  2641.             if (x < prclClip->xLeft)
  2642.                 lClipLeft = prclClip->xLeft - x;
  2643.             if (xRight > prclClip->xRight)
  2644.                 lClipRight = xRight - prclClip->xRight;
  2645.             if (y < prclClip->yBottom)
  2646.                 lClipBottom = prclClip->yBottom - y;
  2647.             if (yTop > prclClip->yTop)
  2648.                 lClipTop = yTop - prclClip->yTop;
  2649.         }
  2650.  
  2651.         // set up target coordinates that are constant
  2652.         // for all the three blits
  2653.  
  2654.         // aptl[0]: target bottom-left
  2655.         aptl[0].x = x + lClipLeft;
  2656.         aptl[0].y = y + lClipBottom;
  2657.  
  2658.         // aptl[1]: target top-right (inclusive!)
  2659.         aptl[1].x = xRight - lClipRight;
  2660.         aptl[1].y = yTop - lClipTop;
  2661.  
  2662.         if (    (aptl[0].x < aptl[1].x)
  2663.              && (aptl[0].y < aptl[1].y)
  2664.            )
  2665.         {
  2666.             LONG    lPatternOld = -1;               // mark as "not changed" for now
  2667.             LONG    lAndROP = ROP_SRCAND,           // 0x0088 = 10001000
  2668.                     lPaintROP = ROP_SRCPAINT,       // 0x00EE = 11101110
  2669.                     lInvertROP = ROP_SRCINVERT;     // 0x0066 = 01100110
  2670.  
  2671.             // colors are constant too
  2672.             GpiSetColor(hps, RGBCOL_WHITE);
  2673.             GpiSetBackColor(hps, RGBCOL_BLACK);
  2674.  
  2675.             if (fl & DP_HALFTONED) // V0.9.20 (2002-08-04) [umoeller]
  2676.             {
  2677.                 lPatternOld = GpiQueryPattern(hps);
  2678.                 GpiSetPattern(hps, PATSYM_HALFTONE);
  2679.  
  2680.                 lAndROP     = 0x00A8;               //          10101000
  2681.                 lInvertROP  = 0x00A6;               //          10100110
  2682.             }
  2683.  
  2684.             /*
  2685.              * 1)   work on the AND image
  2686.              *      (upper part of the monochrome image)
  2687.              */
  2688.  
  2689.             if (    (    (fMini)
  2690.                       && (hbmThis = pi.hbmMiniPointer)
  2691.                     )
  2692.                  || (hbmThis = pi.hbmPointer)
  2693.                )
  2694.             {
  2695.                 bmiAndXor.cbFix = sizeof(bmiAndXor);
  2696.                 GpiQueryBitmapInfoHeader(hbmThis, &bmiAndXor);
  2697.  
  2698.                 // use only half the bitmap height
  2699.                 cySrc = bmiAndXor.cy / 2;
  2700.  
  2701.                 // aptl[2]: source bottom-left
  2702.                 aptl[2].x =   0
  2703.                             + lClipLeft   * bmiAndXor.cx / cxIcon;
  2704.                 aptl[2].y =   cySrc
  2705.                             + lClipBottom * cySrc / cyIcon;
  2706.  
  2707.                 // aptl[3]: source top-right (exclusive!)
  2708.                 aptl[3].x =   bmiAndXor.cx
  2709.                             - lClipRight  * bmiAndXor.cx / cxIcon;
  2710.                 aptl[3].y =   bmiAndXor.cy
  2711.                             - lClipTop    * cySrc / cyIcon;
  2712.  
  2713.                 GpiWCBitBlt(hps,        // target
  2714.                             hbmThis,    // src bmp
  2715.                             4L,         // must always be 4
  2716.                             aptl,       // point array
  2717.                             lAndROP, // ROP_SRCAND,   // source AND target
  2718.                             BBO_IGNORE);
  2719.             }
  2720.  
  2721.             /*
  2722.              * 2)   paint the color image; the parts that
  2723.              *      are to be transparent are black
  2724.              */
  2725.  
  2726.             if (    (    (fMini)
  2727.                       && (hbmThis = pi.hbmMiniColor)
  2728.                     )
  2729.                  || (hbmThis = pi.hbmColor)
  2730.                )
  2731.             {
  2732.                 bmiColor.cbFix = sizeof(bmiColor);
  2733.                 GpiQueryBitmapInfoHeader(hbmThis, &bmiColor);
  2734.  
  2735.                 // aptl[2]: source bottom-left
  2736.                 aptl[2].x =   0
  2737.                             + lClipLeft   * bmiColor.cx / cxIcon;
  2738.                 aptl[2].y =   0
  2739.                             + lClipBottom * bmiColor.cy / cyIcon;
  2740.  
  2741.                 // aptl[3]: source top-right (exclusive!)
  2742.                 aptl[3].x =   bmiColor.cx
  2743.                             - lClipRight  * bmiColor.cx / cxIcon;
  2744.                 aptl[3].y =   bmiColor.cy
  2745.                             - lClipTop    * bmiColor.cy / cyIcon;
  2746.  
  2747.                 GpiWCBitBlt(hps,        // target
  2748.                             hbmThis,    // src bmp
  2749.                             4L,         // must always be 4
  2750.                             aptl,       // point array
  2751.                             lPaintROP, // ROP_SRCPAINT,
  2752.                             BBO_IGNORE);
  2753.             }
  2754.  
  2755.             /*
  2756.              *  3)  work on the XOR image:
  2757.              *      (lower part of monochrome bitmap)
  2758.              */
  2759.  
  2760.             if (    (    (fMini)
  2761.                       && (hbmThis = pi.hbmMiniPointer)
  2762.                     )
  2763.                  || (hbmThis = pi.hbmPointer)
  2764.                )
  2765.             {
  2766.                 /*  we queried this one above V0.9.20 (2002-07-31) [umoeller]
  2767.                 bmiAndXor.cbFix = sizeof(bmiAndXor);
  2768.                 GpiQueryBitmapInfoHeader(hbmThis, &bmiAndXor);
  2769.                 */
  2770.  
  2771.                 // use only half the bitmap height
  2772.                 cySrc = bmiAndXor.cy / 2;
  2773.  
  2774.                 // aptl[2]: source bottom-left
  2775.                 aptl[2].x =   0
  2776.                             + lClipLeft   * bmiAndXor.cx / cxIcon;
  2777.                 aptl[2].y =   0
  2778.                             + lClipBottom * cySrc / cyIcon;
  2779.  
  2780.                 // aptl[3]: source top-right (exclusive!)
  2781.                 aptl[3].x =   bmiAndXor.cx
  2782.                             - lClipRight  * bmiAndXor.cx / cxIcon;
  2783.                 aptl[3].y =   cySrc
  2784.                             - lClipTop    * cySrc / cyIcon;
  2785.  
  2786.                 GpiWCBitBlt(hps,        // target
  2787.                             hbmThis,    // src bmp
  2788.                             4L,         // must always be 4
  2789.                             aptl,       // point array
  2790.                             lInvertROP, // ROP_SRCINVERT,   // source XOR target
  2791.                             BBO_IGNORE);
  2792.             }
  2793.  
  2794.             // reset old pattern, if changed
  2795.             if (lPatternOld != -1)
  2796.                 GpiSetPattern(hps, lPatternOld);
  2797.  
  2798.             return TRUE;
  2799.         }
  2800.     }
  2801.  
  2802.     return FALSE;
  2803. }
  2804.  
  2805. /*
  2806.  *@@category: Helpers\PM helpers\GPI helpers\XBitmaps
  2807.  *      Extended bitmaps. See gpihCreateXBitmap for an introduction.
  2808.  */
  2809.  
  2810. /* ******************************************************************
  2811.  *
  2812.  *   XBitmap functions
  2813.  *
  2814.  ********************************************************************/
  2815.  
  2816. /*
  2817.  *@@ gpihCreateXBitmap:
  2818.  *      calls gpihCreateXBitmap2 with cPlanes and cBitCount == 0
  2819.  *      for compatibility with exports. Widgets might
  2820.  *      have used this func.
  2821.  *
  2822.  *@@added V0.9.12 (2001-05-20) [umoeller]
  2823.  *@@changed V0.9.16 (2001-12-18) [umoeller]: now using optimized gpihCreateXBitmap2
  2824.  */
  2825.  
  2826. PXBITMAP gpihCreateXBitmap(HAB hab,         // in: anchor block
  2827.                            LONG cx,         // in: bitmap width
  2828.                            LONG cy)         // in: bitmap height
  2829. {
  2830.     return gpihCreateXBitmap2(hab,
  2831.                               cx,
  2832.                               cy,
  2833.                               0,
  2834.                               0);
  2835. }
  2836.  
  2837. /*
  2838.  *@@ gpihCreateXBitmap:
  2839.  *      creates an XBitmap, which is returned in an
  2840.  *      XBITMAP structure.
  2841.  *
  2842.  *      The problem with all the GPI bitmap functions
  2843.  *      is that they are quite complex and it is easy
  2844.  *      to forget one of the "disassociate" and "deselect"
  2845.  *      functions, which then simply leads to enormous
  2846.  *      resource leaks in the application.
  2847.  *
  2848.  *      This function may relieve this a bit. This
  2849.  *      creates a memory DC, an memory PS, and a bitmap,
  2850.  *      and selects the bitmap into the memory PS.
  2851.  *      You can then use any GPI function on the memory
  2852.  *      PS to draw into the bitmap. Use the fields from
  2853.  *      XBITMAP for that.
  2854.  *
  2855.  *      The bitmap is created in RGB mode.
  2856.  *
  2857.  *      Use gpihDestroyXBitmap to destroy the XBitmap
  2858.  *      again.
  2859.  *
  2860.  *      Example:
  2861.  *
  2862.  +          PXBITMAP pbmp = gpihCreateXBitmap(hab, 100, 100);
  2863.  +          if (pbmp)
  2864.  +          {
  2865.  +              GpiMove(pbmp->hpsMem, ...);
  2866.  +              GpiBox(pbmp->hpsMem, ...);
  2867.  +
  2868.  +              WinDrawBitmap(hpsScreen,
  2869.  +                            pbmp->hbm,       // bitmap handle
  2870.  +                            ...);
  2871.  +              gpihDestroyXBitmap(&pbmp);
  2872.  +          }
  2873.  *
  2874.  *      Without the gpih* functions, the above would expand
  2875.  *      to more than 100 lines.
  2876.  *
  2877.  *@@added V0.9.16 (2001-12-18) [umoeller]
  2878.  */
  2879.  
  2880. PXBITMAP gpihCreateXBitmap2(HAB hab,         // in: anchor block
  2881.                             LONG cx,         // in: bitmap width
  2882.                             LONG cy,         // in: bitmap height
  2883.                             ULONG cPlanes,     // in: color planes (usually 1); if 0, current screen is used
  2884.                             ULONG cBitCount)   // in: either 1, 4, or 24; if 0, current screen value
  2885. {
  2886.     BOOL fOK = FALSE;
  2887.     PXBITMAP pbmp = (PXBITMAP)malloc(sizeof(XBITMAP));
  2888.     if (pbmp)
  2889.     {
  2890.         memset(pbmp, 0, sizeof(XBITMAP));
  2891.  
  2892.         // create memory PS for bitmap
  2893.         pbmp->szl.cx = cx;
  2894.         pbmp->szl.cy = cy;
  2895.         if (gpihCreateMemPS(hab,
  2896.                             &pbmp->szl,
  2897.                             &pbmp->hdcMem,
  2898.                             &pbmp->hpsMem))
  2899.         {
  2900.             if (cBitCount != 1)
  2901.                 // not monochrome bitmap:
  2902.                 gpihSwitchToRGB(pbmp->hpsMem);
  2903.  
  2904.             if (pbmp->hbm = gpihCreateBitmap2(pbmp->hpsMem,
  2905.                                               cx,
  2906.                                               cy,
  2907.                                               cPlanes,
  2908.                                               cBitCount))
  2909.             {
  2910.                 if (GpiSetBitmap(pbmp->hpsMem,
  2911.                                  pbmp->hbm)
  2912.                         != HBM_ERROR)
  2913.                     fOK = TRUE;
  2914.             }
  2915.         }
  2916.  
  2917.         if (!fOK)
  2918.             gpihDestroyXBitmap(&pbmp);
  2919.     }
  2920.  
  2921.     return pbmp;
  2922. }
  2923.  
  2924. /*
  2925.  *@@ gpihDetachBitmap:
  2926.  *      "detaches" the bitmap from the given XBITMAP.
  2927.  *      This will deselect the bitmap from the internal
  2928.  *      memory PS so it can be used with many OS/2 APIs
  2929.  *      that require that the bitmap not be selected
  2930.  *      into any memory PS.
  2931.  *
  2932.  *      Note that it then becomes the responsibility
  2933.  *      of the caller to explicitly call GpiDeleteBitmap
  2934.  *      because it will not be deleted by gpihDestroyXBitmap.
  2935.  *
  2936.  *@@added V0.9.16 (2001-12-18) [umoeller]
  2937.  */
  2938.  
  2939. HBITMAP gpihDetachBitmap(PXBITMAP pbmp)
  2940. {
  2941.     HBITMAP hbm = pbmp->hbm;
  2942.     pbmp->hbm = NULLHANDLE;
  2943.     GpiSetBitmap(pbmp->hpsMem, NULLHANDLE);
  2944.  
  2945.     return hbm;
  2946. }
  2947.  
  2948. /*
  2949.  *@@ gpihDestroyXBitmap:
  2950.  *      destroys an XBitmap created with gpihCreateXBitmap.
  2951.  *
  2952.  *      To be on the safe side, this sets the
  2953.  *      given XBITMAP pointer to NULL as well.
  2954.  *
  2955.  *@@added V0.9.12 (2001-05-20) [umoeller]
  2956.  */
  2957.  
  2958. VOID gpihDestroyXBitmap(PXBITMAP *ppbmp)
  2959. {
  2960.     if (ppbmp)
  2961.     {
  2962.         PXBITMAP pbmp;
  2963.         if (pbmp = *ppbmp)
  2964.         {
  2965.             if (pbmp->hbm)
  2966.             {
  2967.                 if (pbmp->hpsMem)
  2968.                     GpiSetBitmap(pbmp->hpsMem, NULLHANDLE);
  2969.                 GpiDeleteBitmap(pbmp->hbm);
  2970.             }
  2971.             if (pbmp->hpsMem)
  2972.             {
  2973.                 GpiAssociate(pbmp->hpsMem, NULLHANDLE);
  2974.                 GpiDestroyPS(pbmp->hpsMem);
  2975.             }
  2976.             if (pbmp->hdcMem)
  2977.                 DevCloseDC(pbmp->hdcMem);
  2978.  
  2979.             free(pbmp);
  2980.  
  2981.             *ppbmp = NULL;
  2982.         }
  2983.     }
  2984. }
  2985.  
  2986. /*
  2987.  *@@ gpihCreateBmpFromPS:
  2988.  *      this creates a new bitmap and copies a screen rectangle
  2989.  *      into it. Consider this a "screen capture" function.
  2990.  *
  2991.  *      The new bitmap (which is returned) is compatible with the
  2992.  *      device associated with hpsScreen. This function calls
  2993.  *      gpihCreateMemPS and gpihCreateBitmap to have it created.
  2994.  *      The memory PS is only temporary and freed again.
  2995.  *
  2996.  *      This returns the handle of the new bitmap,
  2997.  *      which can then be used for WinDrawBitmap and such, or
  2998.  *      NULLHANDLE upon errors.
  2999.  *
  3000.  *@@changed V0.9.12 (2001-05-20) [umoeller]: fixed excessive mem PS size
  3001.  *@@changed V0.9.16 (2001-01-04) [umoeller]: now creating XBITMAP
  3002.  */
  3003.  
  3004. PXBITMAP gpihCreateBmpFromPS(HAB hab,        // in: anchor block
  3005.                              HPS hpsScreen,  // in: screen PS to copy from
  3006.                              PRECTL prcl)    // in: rectangle to copy
  3007. {
  3008.  
  3009.     /* To copy an image from a display screen to a bit map:
  3010.       1. Associate the memory device context with a presentation space.
  3011.       2. Create a bit map.
  3012.       3. Select the bit map into the memory device context by calling GpiSetBitmap.
  3013.       4. Determine the location (in device coordinates) of the image.
  3014.       5. Call GpiBitBlt and copy the image to the bit map. */
  3015.  
  3016.     PXBITMAP pBmp;
  3017.  
  3018.     if (pBmp = gpihCreateXBitmap(hab,
  3019.                                  prcl->xRight - prcl->xLeft,
  3020.                                  prcl->yTop - prcl->yBottom))
  3021.     {
  3022.         POINTL aptl[3];
  3023.         // Copy the screen to the bit map.
  3024.         aptl[0].x = 0;              // lower-left corner of destination rectangle
  3025.         aptl[0].y = 0;
  3026.         aptl[1].x = prcl->xRight;   // upper-right corner for both
  3027.         aptl[1].y = prcl->yTop;
  3028.         aptl[2].x = prcl->xLeft;    // lower-left corner of source rectangle
  3029.         aptl[2].y = prcl->yBottom;
  3030.  
  3031.         if (GPI_ERROR == GpiBitBlt(pBmp->hpsMem,
  3032.                                    hpsScreen,
  3033.                                    sizeof(aptl) / sizeof(POINTL), // Number of points in aptl
  3034.                                    aptl,
  3035.                                    ROP_SRCCOPY,
  3036.                                    BBO_IGNORE))
  3037.         {
  3038.             // error during bitblt:
  3039.             gpihDestroyXBitmap(&pBmp);
  3040.         }
  3041.     }
  3042.  
  3043.     return pBmp;
  3044. }
  3045.  
  3046.