home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / vuebmp.zip / ViewBMP.c < prev    next >
Text File  |  1994-10-08  |  68KB  |  1,383 lines

  1. /* ********************************************************************** */
  2. /*                                                                        */
  3. /*  Program Name: ViewBMP.exe                                             */
  4. /*  Article Title: Color Palette Management with OS/2                     */
  5. /*  OS/2 Developer Magazine, Issue: November '94, page  xxx               */
  6. /*  Author:  John D. Webb      CIS: 71075,1117                            */
  7. /*                                                                        */
  8. /*  Description: A bitmap viewing program which demonstrates the power    */
  9. /*    of OS/2's color palette management APIs.                            */
  10. /*                                                                        */
  11. /*  Program requirements:  OS/2 2.x                                       */
  12. /*                         IBM CSet/2 or CSet++ for OS/2                  */
  13. /*                         OS/2 Developer's Toolkit                       */
  14. /*                         Video Driver supporting color palettes         */
  15. /*                                                                        */
  16. /*         Note: The program will run on machines without a video         */
  17. /*               driver which supports color palettes; however,           */
  18. /*               bitmaps will only be displayed using the default         */
  19. /*               system color palette.                                    */
  20. /*                                                                        */
  21. /* ********************************************************************** */
  22. /* ********************************************************************** */
  23. /*                                                                        */
  24. /*                                                                        */
  25. /*         ViewBMP displays bitmaps, which are loaded from files.         */
  26. /*      Although I haven't thoroughly tested it with every possible       */
  27. /*      format, it ought to be able to handle most single-image           */
  28. /*      OS/2 1.x, OS/2 2.x, and Windows bitmaps.  It should handle        */
  29. /*      most color depths, including 24-bit, and will handle              */
  30. /*      compressed formats (e.g. Huffman 1-D, 4-bit RLE, 8-bit RLE,       */
  31. /*      etc). One key feature of ViewBMP is that it can load and          */
  32. /*      display the bitmaps using either the system default color         */
  33. /*      palette or using the custom color palette which is stored         */
  34. /*      in the bitmap. The latter provides the best display results       */
  35. /*      but requires the use of the OS/2 color palette management         */
  36. /*      system; note that this option (and the menu item that             */
  37. /*      selects it) will be disabled on systems that do not support       */
  38. /*      color palettes.                                                   */
  39. /*                                                                        */
  40. /*         The bitmap file to be displayed may be selected not only       */
  41. /*      from the standard Open File Dialog, but also by entering          */
  42. /*      the file name on the command line. One benefit of this            */
  43. /*      feature is that ViewBMP may be associated with .BMP file          */
  44. /*      extensions, and automatically invoked by opening a .BMP           */
  45. /*      file.                                                             */
  46. /*                                                                        */
  47. /*         It is important to note that ViewBMP was intended as an        */
  48. /*      educational source code sample first, and as a bitmap             */
  49. /*      viewing utility second.  Priority was given to source code        */
  50. /*      simplicity and clarity. As such, many features (and most of       */
  51. /*      the error checking) which would make ViewBMP a robust,            */
  52. /*      production-level image application have been left out.            */
  53. /*      Also, for the same reasons, many algorithmic and coding           */
  54. /*      optimization have been left out. For example, this app            */
  55. /*      should be multi-threaded, but has been left as single             */
  56. /*      threaded to dispense with syncronizing logic. This source code    */
  57. /*      is to be taken as an introduction and guide for color palette     */
  58. /*      usage, not as an example of commercial grade code.                */
  59. /*                                                                        */
  60. /*         The code demonstrates many techniques, including:              */
  61. /*                                                                        */
  62. /*        1) How to read in and decode a single image bitmap file         */
  63. /*        2) How to extract color info from a bitmap file                 */
  64. /*        3) How to create a color palette                                */
  65. /*        4) How to select a color palette into multiple                  */
  66. /*           Presentation Spaces                                          */
  67. /*        5) How to create a memory bitmap maintaining original           */
  68. /*           color integrity                                              */
  69. /*        6) How to realize a color palette to maintain proper            */
  70. /*           bitmap colors when drawing to the screen                     */
  71. /*        7) How to properly respond to WM_REALIZEPALETTE messages        */
  72. /*        8) How to dispose of color palette and bitmap resources         */
  73. /*        9) How to reset the system default color palette                */
  74. /*                                                                        */
  75. /*                                                                        */
  76. /*        Please feel free to incorporate the techniques                  */
  77. /*      demonstrated (and portions of the code) in any application        */
  78. /*      you are writing; however I would ask that you make                */
  79. /*      *significant* modifications to the code if you are going to       */
  80. /*      sell it.                                                          */
  81. /*                                                                        */
  82. /*      I will try to respond to all comments and questions sent to       */
  83. /*      my E-mail address below, but I can not guarantee that I           */
  84. /*      will have timely or complete answers. Thanks for your             */
  85. /*      interest and your time. I hope you find this sample               */
  86. /*      application of value.                                             */
  87. /*                                                                        */
  88. /*      John D. Webb                                                      */
  89. /*      CompuServe: 71075,1117                                            */
  90. /*      Internet: 71075.1117@compuserve.com                               */
  91. /*                                                                        */
  92. /* ********************************************************************** */
  93. /* ********************************************************************** */
  94. /*       DISCLAIMER OF WARRANTIES ( unfortunate legality )                */
  95. /* ====================================================================== */
  96. /*                                                                        */
  97. /*   This [enclosed] source code (and the application producted from it)  */
  98. /*   is provided to you solely for the purpose of assisting you in the    */
  99. /*   development of your applications.  The source code is provided       */
  100. /*   "AS IS", without warranty of any kind.  The author shall not be      */
  101. /*   liable for any damages arising out of your use of the source code,   */
  102. /*   in whole or in part, or out of the use of the producted application, */
  103. /*   even if he has been advised of the possibility of such damages.      */
  104. /*                                                                        */
  105. /* ********************************************************************** */
  106. /* ********************************************************************** */
  107. /*                                                                        */
  108. /*  (c) 1994, John D. Webb. All rights reserved.                          */
  109. /*                                                                        */
  110. /* ********************************************************************** */
  111.  
  112.  
  113. #define INCL_WIN
  114. #define INCL_GPI
  115. #define INCL_BITMAPFILEFORMAT
  116. #include <os2.h>
  117.  
  118. #include <stdio.h>
  119. #include <stdlib.h>
  120. #include <string.h>
  121. #include <io.h>
  122. #include <fcntl.h>
  123. #include <sys\stat.h>
  124. #include "ViewBMP.h"
  125. #include "AboutTxt.h"
  126.  
  127.  
  128.         /* ------------- Global Definitions ----------------- */
  129.  
  130. #define  UWM_LOAD_BITMAP   WM_USER+1
  131.         /*
  132.          * The above user-defined message is used to initiate a bitmap
  133.          * file load. The following parameters are used with the message.
  134.          *
  135.          *    mp1 = (PSZ)  file path for bitmap file to load
  136.          *    mp2 = (BOOL) TRUE if custom bitmap palette is to be used
  137.          *                 FALSE if default system palette is to be used
  138.          */
  139.  
  140. typedef  struct  _bmpSpecs {
  141.   LONG    cx ;            // bitmap horz size in pixels
  142.   LONG    cy ;            // bitmap vert size in pixels
  143.   LONG    colorCount ;    // number of colors used by bitmap
  144.   HPAL    hpal ;          // custom palette (if any) for bitmap
  145. } BMPSPECS, *PBMPSPECS ;
  146.          /*
  147.           * The above structure type is used to keep critical bitmap
  148.           * information and specs in a readily available form for the
  149.           * application.
  150.           */
  151.  
  152.  
  153.         /* ------------- Global Variables ----------------- */
  154.  
  155. HAB     hab ;
  156. HMQ     hmq ;
  157. HWND    hwndClient ;
  158. HWND    hwndFrame ;
  159. QMSG    qmsg;
  160.  
  161. HPS     hps ;                // persistant display PS
  162. HPS     hpsMemory ;          // persistant memory PS containing bitmap
  163.  
  164. BOOL      bBitmapLoaded ;
  165. BMPSPECS  bmpSpecs ;
  166.  
  167. PSZ    szClassName  = "ViewBMPClass" ;
  168. PSZ    szMainTitle  = "ViewBMP" ;
  169. PSZ    szErrorTitle = "ViewBMP Error" ;
  170.  
  171. FILEDLG  fdFileDlg ;         // variables used for Open File dialog
  172. PSZ    apszEATypes[20] = { DRT_BITMAP   ,
  173.                            DRT_UNKNOWN  ,
  174.                            (PSZ) NULL };
  175.  
  176.  
  177.         /* ----------------  Prototypes  ------------------------ */
  178. INT                 main( INT, PCHAR [] );
  179. MRESULT EXPENTRY    MainWindowProc( HWND, USHORT, MPARAM, MPARAM );
  180. HPS                 CreateMemoryPS( VOID );
  181. PSZ                 GetBMPFileName( PSZ );
  182. HBITMAP             LoadBitmapFile( HPS, PSZ, BOOL, PBMPSPECS );
  183. PBITMAPFILEHEADER2  LoadBMPFileToMem( PSZ );
  184. PRGB2               Convert1xColorTableTo2x( PBITMAPINFO, LONG );
  185. VOID                SizeAndPosWinForImage( HWND, LONG, LONG );
  186. VOID                SetWindowTitle( HWND, PSZ, PBMPSPECS );
  187. BOOL                AreColorPalettesSupported( VOID ) ;
  188. MRESULT EXPENTRY    AboutDialogProc( HWND, USHORT, MPARAM, MPARAM );
  189. VOID                ShowErrorWindow( PSZ, BOOL );
  190.  
  191.  
  192.  
  193. /* ********************************************************************** */
  194. /*                                                                        */
  195. /*   Main                                                                 */
  196. /*                                                                        */
  197. /*      Relatively generic PM main() procedure.                           */
  198. /*      Creates standard application window. Registers with task list.    */
  199. /*      Goes into standard message dispatch loop.                         */
  200. /*                                                                        */
  201. /*      Two "unique" functions are to check for color palette support     */
  202. /*      and disable palette menu option is unsupported, and to check the  */
  203. /*      command line for bitmap filename (and initiate load if found ).   */
  204. /*                                                                        */
  205. /* ********************************************************************** */
  206.  
  207. INT
  208. main( INT argc, PCHAR  argv[] )
  209. {
  210.  
  211.       /* ----- start standard PM initialization mantra... ------ */
  212.   if ( (hab = WinInitialize( 0L )) == (HAB) NULL ){
  213.     DosBeep( 60, 250 ) ;
  214.     DosBeep( 120, 250 ) ;
  215.   }
  216.   else {
  217.      if ( (hmq = WinCreateMsgQueue( hab, 0 )) == (HMQ) NULL ){
  218.         DosBeep( 60, 250 ) ;
  219.         DosBeep( 120, 250 ) ;
  220.         DosBeep( 60, 250 ) ;
  221.      }
  222.      else {
  223.  
  224.        ULONG fulCreate= FCF_TITLEBAR | FCF_SYSMENU | FCF_SIZEBORDER |
  225.                        FCF_MENU | FCF_MINMAX | FCF_SHELLPOSITION | FCF_ICON  ;
  226.  
  227.         WinSetPointer( HWND_DESKTOP,
  228.                        WinQuerySysPointer(HWND_DESKTOP,SPTR_WAIT,TRUE));
  229.  
  230.         WinRegisterClass(hab, szClassName, (PFNWP)MainWindowProc, CS_SIZEREDRAW, 0);
  231.  
  232.              /* ------ create the main window  ------------ */
  233.         hwndFrame = WinCreateStdWindow(HWND_DESKTOP,
  234.                                        0L,
  235.                                        (PULONG)&fulCreate,
  236.                                        szClassName ,
  237.                                        szMainTitle,
  238.                                        0L,
  239.                                        (HMODULE)NULL,
  240.                                        ID_MAIN_WIN,
  241.                                        &hwndClient);
  242.         if ( hwndFrame == NULLHANDLE ) {
  243.            ShowErrorWindow( "Error creating Main window !", TRUE );
  244.         }
  245.         else {
  246.            PID     pid ;
  247.            SWCNTRL swCntrl;
  248.            HSWITCH hSwitch ;
  249.            BOOL    bPalettesSupported ;
  250.  
  251.              /* ---------  show and activate window  -------------- */
  252.            WinSetWindowPos( hwndFrame,
  253.                             HWND_TOP,
  254.                             0, 0, 0, 0,
  255.                             SWP_SHOW | SWP_ZORDER | SWP_ACTIVATE );
  256.  
  257.             /* ----------- add program to tasklist  --------------- */
  258.            WinQueryWindowProcess( hwndFrame, &pid, NULL );
  259.            swCntrl.hwnd = hwndFrame ;
  260.            swCntrl.hwndIcon = (HWND) NULL ;
  261.            swCntrl.hprog = (HPROGRAM) NULL ;
  262.            swCntrl.idProcess = pid ;
  263.            swCntrl.idSession = (LONG) NULL ;
  264.            swCntrl.uchVisibility = SWL_VISIBLE ;
  265.            swCntrl.fbJump = SWL_JUMPABLE ;
  266.            sprintf( swCntrl.szSwtitle, szMainTitle );
  267.            hSwitch = WinCreateSwitchEntry( hab, (PSWCNTRL)&swCntrl);
  268.  
  269.  
  270.            WinSetPointer(HWND_DESKTOP,
  271.                          WinQuerySysPointer(HWND_DESKTOP,SPTR_ARROW,TRUE));
  272.  
  273.               /* ---------- determine if color palettes supported ---- */
  274.            bPalettesSupported = AreColorPalettesSupported() ;
  275.            if ( ! bPalettesSupported ){
  276.                  /* --- if not, disable palette menu option  --- */
  277.               WinEnableMenuItem( WinWindowFromID( hwndFrame, FID_MENU ),
  278.                                  MID_LOAD_WITH_PAL,
  279.                                  FALSE );
  280.            } // end of if ( ! AreColorPalettesSupported())
  281.  
  282.               /* --- see if we have a bitmap from the command line --- */
  283.            if ( argc > 1 ){
  284.               /* ----- if so, pass it to the window ------- */
  285.               /*
  286.                *  By posting this message, a load of the bitmap file
  287.                *  will be initiated with no input necessary from
  288.                *  the user. If color palettes are supported, then
  289.                *  they will be automatically used for this bitmap.
  290.                */
  291.               WinPostMsg( hwndClient,
  292.                           UWM_LOAD_BITMAP,
  293.                           MPFROMP( (PVOID) argv[1] ),
  294.                           MPFROMLONG( (LONG) bPalettesSupported ));
  295.            } // end of if ( argc > 1 )
  296.  
  297.               /* ---------- start the main processing loop ----------- */
  298.            while (WinGetMsg(hab, &qmsg,NULLHANDLE,0,0)){
  299.                WinDispatchMsg(hab, &qmsg);
  300.            }
  301.  
  302.            WinRemoveSwitchEntry( hSwitch );
  303.            WinDestroyWindow(hwndFrame);
  304.         }  /* end of else */
  305.  
  306.         WinSetPointer(HWND_DESKTOP,
  307.                       WinQuerySysPointer(HWND_DESKTOP,SPTR_ARROW,TRUE));
  308.         WinDestroyMsgQueue(hmq);
  309.      }  /* end of else ( ...WinCreateMsgQueue() */
  310.  
  311.    WinTerminate(hab);
  312.   }  /* end of else (...WinInitialize(NULL) */
  313.  
  314.   return( 0 );
  315. }  /*  end of main() */
  316.  
  317. /* ********************************************************************** */
  318. /*                                                                        */
  319. /*   MainWindowProc                                                       */
  320. /*                                                                        */
  321. /*       Processes all the main window messages.                          */
  322. /*                                                                        */
  323. /* ********************************************************************** */
  324.  
  325. MRESULT EXPENTRY
  326. MainWindowProc( HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2 )
  327. {
  328.  
  329.   switch (msg) {
  330.  
  331.  
  332.     /* ============================================================== */
  333.     /*                                                                */
  334.     /*   WM_CREATE                                                    */
  335.     /*                                                                */
  336.     /*   In processing this message we will create two persistent     */
  337.     /*   Presentation Spaces to be used for the life of the app.      */
  338.     /*   One will be a display PS for drawing to the screen, the      */
  339.     /*   other will be a memory PS which will be used to create and   */
  340.     /*   hold the bitmap when it is loaded.                           */
  341.     /*                                                                */
  342.     /*   The reason to create a persistent memory PS is so that       */
  343.     /*   we can hold the bitmap in it, along with any custom          */
  344.     /*   custom palette that it is using, and then blit from it to    */
  345.     /*   the display PS during WM_PAINT processing. This makes        */
  346.     /*   drawing to the screen very quick. We reuse the same          */
  347.     /*   memory PS for each bitmap loaded.                            */
  348.     /*                                                                */
  349.     /*   The reason to create a persistent display PS, rather         */
  350.     /*   than just using a cached-micro PS returned from              */
  351.     /*   WinBeginPaint(), is that a cached-micro PS is re-initialized */
  352.     /*   after the painting is complete; this means that it would     */
  353.     /*   lose any color palette setup done, necessitating that        */
  354.     /*   the custom palette be selected and realized into the         */
  355.     /*   cached-micro PS *each* WM_PAINT. This can lead to lots       */
  356.     /*   of overhead. Also having a persistent display PS makes       */
  357.     /*   it convenient to respond to WM_REALIZEPALETTE messages,      */
  358.     /*   without having to create a new PS each time. Note that       */
  359.     /*   it is not a requirement to use a persistent display PS;      */
  360.     /*   but in my opinion, it is a highly recommended.               */
  361.     /*                                                                */
  362.     /*   The other bit of initialization that we do is to set a       */
  363.     /*   pattern into the display PS. We will use it to fill          */
  364.     /*   the window, and any portions not containing the bitmap       */
  365.     /*   image. This is to help distinguish the edge of the           */
  366.     /*   displayed image (when the image doesn't fill the window).    */
  367.     /*                                                                */
  368.     /* ============================================================== */
  369.     case WM_CREATE :
  370.         {
  371.           HDC     hdc ;
  372.           SIZEL   sizel = { 0, 0 };
  373.  
  374.            /* -------- create persistant screen PS  ----------- */
  375.           hdc = WinOpenWindowDC( hwnd );
  376.           hps = GpiCreatePS( hab,
  377.                              hdc,
  378.                              &sizel,
  379.                              GPIT_MICRO | GPIA_ASSOC | PU_PELS );
  380.           if ( hps == NULLHANDLE ){
  381.             ShowErrorWindow( "Error creating persistant screen PS !!", TRUE );
  382.             return( (MRESULT) TRUE );   // cancel window creation
  383.           }
  384.  
  385.            /* ------ initialize PS to fill with pattern ------- */
  386.           GpiSetPattern( hps, PATSYM_DENSE8 );
  387.  
  388.            /* -------- create persistant memory PS  ----------- */
  389.           hpsMemory = CreateMemoryPS();
  390.           if ( hpsMemory == NULLHANDLE ){
  391.             ShowErrorWindow( "Error creating persistant memory PS !!", TRUE );
  392.             return( (MRESULT) TRUE );   // cancel window creation
  393.           }
  394.         } // end of case WM_CREATE
  395.         break;
  396.  
  397.     /* ============================================================== */
  398.     /*                                                                */
  399.     /*  WM_COMMAND                                                    */
  400.     /*                                                                */
  401.     /*    This message handles the three menu options:                */
  402.     /*                                                                */
  403.     /*    Open File with Default Palette - initiates a bitmap file    */
  404.     /*          load that will use the default system color palette.  */
  405.     /*          The colors contained in the bitmap will be mapped     */
  406.     /*          to their closest match in the current physical color  */
  407.     /*          palette. This option doesn't use any color palette    */
  408.     /*          management APIs                                       */
  409.     /*                                                                */
  410.     /*    Open File with Custom Palette - initiates a bitmap file     */
  411.     /*          load that will create a palette from the colors       */
  412.     /*          stored in the bitmap, and will use the color          */
  413.     /*          palette management APIs to display using that         */
  414.     /*          palette. In other words, the APIs will be used to     */
  415.     /*          change the physical color palette to match the        */
  416.     /*          colors contained in the bitmap.                       */
  417.     /*                                                                */
  418.     /*    About... - displays an "About box" dialog that describes    */
  419.     /*          the application.                                      */
  420.     /*                                                                */
  421.     /* ============================================================== */
  422.     case WM_COMMAND :
  423.         switch ( SHORT1FROMMP( mp1 )){
  424.           case  MID_LOAD_WITHOUT_PAL  :
  425.               WinPostMsg( hwnd, UWM_LOAD_BITMAP, 0, (MPARAM)FALSE );
  426.               break;
  427.           case  MID_LOAD_WITH_PAL  :
  428.               WinPostMsg( hwnd, UWM_LOAD_BITMAP, 0, (MPARAM)TRUE );
  429.               break;
  430.           case  MID_ABOUT :
  431.               WinDlgBox( HWND_DESKTOP, hwnd,
  432.                          (PFNWP) AboutDialogProc,
  433.                          NULLHANDLE, ID_ABOUT_DLG,
  434.                          (PVOID) NULL );
  435.               break;
  436.          } // end of switch
  437.         break;
  438.  
  439.  
  440.     /* ============================================================== */
  441.     /*                                                                */
  442.     /*   UWM_LOAD_BITMAP                                              */
  443.     /*                                                                */
  444.     /*     mp1 = (PSZ)  file path for bitmap file to load             */
  445.     /*     mp2 = (BOOL) TRUE if custom bitmap palette is to be used   */
  446.     /*                  FALSE if default system palette is to be used */
  447.     /*                                                                */
  448.     /*                                                                */
  449.     /*   This is the user defined message that initiate the load of   */
  450.     /*   a bitmap file and the creation of the bitmap image. In       */
  451.     /*   other words, this is were the real fun happens.              */
  452.     /*                                                                */
  453.     /*   If a file name is not passed in as a parameter, an Open      */
  454.     /*   File dialog retrieves the name. A call to an app function    */
  455.     /*   LoadBitmapFile() does the actual bitmap creation from the    */
  456.     /*   given file name. It also creates a palette if the user has   */
  457.     /*   specified so. I don't understand why the PM/GPI system       */
  458.     /*   doesn't provide an API that serves this same function; it    */
  459.     /*   is one that I (and others) have needed time and time again.  */
  460.     /*                                                                */
  461.     /*   Next, any previous bitmaps or palettes are cleaned up. Any   */
  462.     /*   remnants of a previous palette are remove from the hardware  */
  463.     /*   palette by resetting it to the default system palette.       */
  464.     /*                                                                */
  465.     /*   The newly created palette (if any) is selected into both     */
  466.     /*   the memory PS and the display PS. It is important that       */
  467.     /*   the same palette is selected into both, so that colors are   */
  468.     /*   consistent when blitting from one to the other. It is        */
  469.     /*   also critical that the palette be selected into the memory   */
  470.     /*   PS before the bitmap is set into it (so that colors are not  */
  471.     /*   remapped).                                                   */
  472.     /*                                                                */
  473.     /*   Next, the palette is realized for the display PS. A call     */
  474.     /*   to WinRealizePalette() is necessary only for *display*       */
  475.     /*   PSs, and is not necessary for other PSs, particularly        */
  476.     /*   memory PSs. The call to WinRealizePalette() must be made     */
  477.     /*   before any drawing to the PS occurs (or the drawing won't    */
  478.     /*   use the selected palette).                                   */
  479.     /*                                                                */
  480.     /*   Finally, the window is sized to fit the image, and the       */
  481.     /*   frame title is set to contain the bitmap name and the        */
  482.     /*   bitmap specifications (e.g. size, color depth, etc).         */
  483.     /*                                                                */
  484.     /* ============================================================== */
  485.     case UWM_LOAD_BITMAP :
  486.         {
  487.           PSZ   pszFileSpec ;
  488.           BOOL  bUsePaletteManager ;
  489.           HBITMAP hbitmap ;
  490.           HPOINTER  hptrCurrent ;
  491.  
  492.  
  493.              /* ------- get the passed in parameters --------- */
  494.           pszFileSpec = (PSZ) PVOIDFROMMP( mp1 );
  495.           bUsePaletteManager = (BOOL) mp2 ;
  496.  
  497.              /* ---- if name not passed in, get it from user ---- */
  498.           if ( pszFileSpec == (PSZ)NULL ){
  499.              pszFileSpec = GetBMPFileName( bUsePaletteManager ?
  500.                                            "Open Bitmap (with custom palette)":
  501.                                            "Open Bitmap (without custom palette)");
  502.           } // end of if (pszFileSpec)
  503.  
  504.           if ( pszFileSpec ){
  505.                 /* --- save pointer so we can put up wait pointer  --- */
  506.                 /*
  507.                  * note: we wounldn't have to do this if we were "threading"
  508.                  */
  509.              hptrCurrent = WinQueryPointer( HWND_DESKTOP );
  510.              WinSetPointer( HWND_DESKTOP,
  511.                             WinQuerySysPointer(HWND_DESKTOP,SPTR_WAIT,TRUE));
  512.  
  513.                 /* ----- create a bitmap from the given filename ----- */
  514.              hbitmap = LoadBitmapFile( hpsMemory,
  515.                                        pszFileSpec,
  516.                                        bUsePaletteManager,
  517.                                        &bmpSpecs );
  518.  
  519.                  /* ---- check if load was successful ----- */
  520.              if ( hbitmap ){
  521.                HBITMAP   hbmOld ;
  522.                HPAL      hpalOld ;
  523.                ULONG     ulPhysColorsRemapped ;
  524.  
  525.                    /* --- reset (and cleanup) the old palette ---- */
  526.                    /*
  527.                     * calling GpiSelectPalette() with a NULLHANDLE
  528.                     * selects the default system color palette into
  529.                     * the PS
  530.                     */
  531.                GpiSelectPalette( hpsMemory, NULLHANDLE );
  532.                hpalOld = GpiSelectPalette( hps, NULLHANDLE );
  533.                if ( hpalOld && ( hpalOld != (HPAL) PAL_ERROR )){
  534.                      /* ---- reset the default palette ---- */
  535.                      /*
  536.                      * Note that this is a trick. Calling WinRealizePalette()
  537.                      * three times with a default palette loaded will trigger
  538.                      * a reset (most of the time) of the hardware palette.
  539.                      */
  540.                   WinRealizePalette( hwnd, hps, &ulPhysColorsRemapped );
  541.                   WinRealizePalette( hwnd, hps, &ulPhysColorsRemapped );
  542.                   WinRealizePalette( hwnd, hps, &ulPhysColorsRemapped );
  543.                   GpiDeletePalette( hpalOld );
  544.                }
  545.  
  546.                  /* ---- select new palette into memory PS ---- */
  547.                GpiSelectPalette( hpsMemory, bmpSpecs.hpal ) ;
  548.  
  549.                  /* ------ set the bitmap into the memory PS ---- */
  550.                hbmOld = GpiSetBitmap( hpsMemory, hbitmap );
  551.                if ( hbmOld  != HBM_ERROR ){
  552.                   bBitmapLoaded = TRUE ;
  553.  
  554.                      /* ---- clean up old bitmap, if any  ------ */
  555.                   if ( hbmOld ){
  556.                      GpiDeleteBitmap( hbmOld );
  557.                   }
  558.                      /* -- select new palette into display PS -- */
  559.                   GpiSelectPalette( hps, bmpSpecs.hpal ) ;
  560.  
  561.                      /* --- realize the new palette for display --- */
  562.                   WinRealizePalette( hwnd, hps, &ulPhysColorsRemapped );
  563.  
  564.                      /* -------- adjust window for image  ---------- */
  565.                   SizeAndPosWinForImage( hwndFrame, bmpSpecs.cx, bmpSpecs.cy );
  566.  
  567.                      /* --------- set window title ------------- */
  568.                   SetWindowTitle( hwndFrame, pszFileSpec, &bmpSpecs );
  569.  
  570.                }  // end of if ( hbmOld != HBM_ERROR )
  571.                else {
  572.                   bBitmapLoaded = FALSE ;
  573.                      /* --------- clear window title ------------- */
  574.                   SetWindowTitle( hwndFrame, (PSZ)NULL, (PBMPSPECS)NULL );
  575.                   ShowErrorWindow( "Error setting new bitmap in PS !!", TRUE );
  576.                   GpiDeleteBitmap( hbitmap );
  577.                }  // end of else ( hbmOld != HBM_ERROR )
  578.              }  // end of if ( hBitmap )
  579.  
  580.              WinSetPointer( HWND_DESKTOP, hptrCurrent );
  581.           } // end of if ( pszFileSpec )
  582.              /* ------ make sure we repaint window  ------- */
  583.           WinInvalidateRect( hwnd, (PRECTL)NULL, FALSE );
  584.         } // end of case UWM_LOAD_BITMAP
  585.         break;
  586.  
  587.  
  588.     /* ============================================================== */
  589.     /*                                                                */
  590.     /*  WM_REALIZEPALETTE                                             */
  591.     /*                                                                */
  592.     /*  This message is sent to a window whenever it is receiving     */
  593.     /*  focus, so that it may realize any custom palette it owns.     */
  594.     /*  It also allows the app to repaint to take advantage of the    */
  595.     /*  new palette.                                                  */
  596.     /*                                                                */
  597.     /*  This message is also sent whenever another app has            */
  598.     /*  realized it's custom palette, causing changes to be made      */
  599.     /*  in the physical color palette. The message allows a window    */
  600.     /*  to make it's palette know to the system, so that the system   */
  601.     /*  may arbitrate among the palette requests.                     */
  602.     /*                                                                */
  603.     /*  Since this application can optionally use color palettes or   */
  604.     /*  not, it first checks to see if a custom color palette is      */
  605.     /*  being used. If not, then the default window proc is called.   */
  606.     /*  Otherwise, a call is made to WinRealizePalette(). This call   */
  607.     /*  returns the number of physical indices that were remapped.    */
  608.     /*  The basic rule of thumb is that if this returned value is     */
  609.     /*  greater than 0 (i.e. there was remapping), then the window    */
  610.     /*  should be repainted to automatically take best advantage      */
  611.     /*  of the newly remapped colors.                                 */
  612.     /*                                                                */
  613.     /* ============================================================== */
  614.     case WM_REALIZEPALETTE :
  615.         {
  616.           ULONG  ulPhysColorsRemapped ;
  617.  
  618.              /* ------ are we currently using a palette  -------- */
  619.           if ( bmpSpecs.hpal  ){
  620.                /* --------- if so, then realize it -------------- */
  621.             if ( WinRealizePalette( hwnd, hps, &ulPhysColorsRemapped ) > 0 ){
  622.                  /* ----- trigger a repaint of the window ------- */
  623.               WinInvalidateRect( hwnd, (PRECTL)NULL, FALSE );
  624.             }
  625.           } // end of if ( bUsePaletteManager )
  626.           else {
  627.             return( WinDefWindowProc(hwnd,msg,mp1,mp2) );
  628.           }
  629.         } // end of case WM_REALIZEPALETTE
  630.         break;
  631.  
  632.     /* ============================================================== */
  633.     /*                                                                */
  634.     /*   WM_PAINT                                                     */
  635.     /*                                                                */
  636.     /*   All the painting of the window is handled during this        */
  637.     /*   message.                                                     */
  638.     /*                                                                */
  639.     /*   If a bitmap isn't currently loaded, the window is filled     */
  640.     /*   with the pattern set during WM_CREATE.                       */
  641.     /*                                                                */
  642.     /*   If a bitmap is currently loaded, a check is first made to    */
  643.     /*   see if there is any portion of the window that will not      */
  644.     /*   be covered by the bitmap (i.e. the client area is larger     */
  645.     /*   than the bitmap). If so, then this is filled in with the     */
  646.     /*   pattern. The algorithm for this is very simplistic, but      */
  647.     /*   works well if the window is larger in one direction only;    */
  648.     /*   for example, if the user sized the window wider than the     */
  649.     /*   image so that a particularly long title bar would be         */
  650.     /*   entirely displayed. If the window is both wider and taller   */
  651.     /*   than the image, the simplistic algorithm will cause the      */
  652.     /*   bitmap to be repainted in it's entirety.                     */
  653.     /*                                                                */
  654.     /*   To paint the bitmap, the image is blitted directly from      */
  655.     /*   the memory PS to the display PS. The original size of the    */
  656.     /*   bitmap is maintained. The blit neither stretchs nor          */
  657.     /*   compresses.                                                  */
  658.     /*                                                                */
  659.     /*   It is important to notice that the persistent display PS     */
  660.     /*   is passed into WinBeginPaint() so that a cached-micro PS     */
  661.     /*   is *not* returned or used.                                   */
  662.     /*                                                                */
  663.     /* ============================================================== */
  664.     case WM_PAINT:
  665.        {
  666.          RECTL rectl ;
  667.  
  668.          WinBeginPaint( hwnd, hps, &rectl );
  669.             /* ------ check if a bitmap is ready for painting  ------- */
  670.          if ( bBitmapLoaded ){
  671.             POINTL   apointl[3] ;
  672.  
  673.               /* --- if window is larger than bitmap, fill in gap ---- */
  674.             if ( (rectl.xRight > bmpSpecs.cx) || (rectl.yTop > bmpSpecs.cy)){
  675.                RECTL  rectlDiff, rectlBMP ;
  676.                  /*
  677.                   * The proper way would be to work with height and width
  678.                   * seperately, or to use regions, but that's
  679.                   * more effort than is warranted for this simple sample
  680.                   */
  681.                WinSetRect( hab, &rectlBMP, 0, 0, bmpSpecs.cx, bmpSpecs.cy );
  682.                WinSubtractRect( hab, &rectlDiff, &rectl, &rectlBMP );
  683.                   /* ----- fill in rect with pattern  --------- */
  684.                WinDrawBorder( hps,
  685.                               &rectlDiff,
  686.                               0, 0,
  687.                               CLR_WHITE, CLR_BLACK,
  688.                               DB_INTERIOR );
  689.             }
  690.  
  691.               /* ----- blit the bitmap into the window --------- */
  692.             apointl[0].x = apointl[2].x = rectl.xLeft ;
  693.             apointl[0].y = apointl[2].y = rectl.yBottom ;
  694.             apointl[1].x = rectl.xRight ;
  695.             apointl[1].y = rectl.yTop ;
  696.             GpiBitBlt( hps, hpsMemory, 3, apointl, ROP_SRCCOPY, BBO_IGNORE );
  697.  
  698.          }
  699.          else {
  700.                /* -- no bitmap so just fill in blank w/ pattern -- */
  701.             WinDrawBorder( hps,
  702.                            &rectl,
  703.                            0, 0,
  704.                            CLR_WHITE, CLR_BLACK,
  705.                            DB_INTERIOR );
  706.          }
  707.          WinEndPaint( hps );
  708.       } // end of case WM_PAINT
  709.       break;
  710.  
  711.     /* ============================================================== */
  712.     /*                                                                */
  713.     /*  WM_CLOSE                                                      */
  714.     /*                                                                */
  715.     /*  The app is being closed so make sure that all bitmap and      */
  716.     /*  palette resources are properly disposed of. The persistant    */
  717.     /*  PSs are also disposed of.                                     */
  718.     /*                                                                */
  719.     /*  It is critical to dispose of any custom color palettes.       */
  720.     /*  Color palettes are considered system global resources, and    */
  721.     /*  as such are not automatically disposed of when the app that   */
  722.     /*  created them ends. If they are not explicitly disposed of,    */
  723.     /*  they will remain in memory until the machine is rebooted.     */
  724.     /*                                                                */
  725.     /* ============================================================== */
  726.     case WM_CLOSE :
  727.          /* ------------  clean up resources  -------------- */
  728.       if ( hps ){
  729.         ULONG  ulChanged ;  // used by WinRealizePalette()
  730.  
  731.             /* ----- un-select palette from PS  ------ */
  732.         GpiSelectPalette( hps, NULLHANDLE );
  733.  
  734.             /* ---- reset palette to default ----- */
  735.         WinRealizePalette( hwnd, hps, &ulChanged );
  736.         WinRealizePalette( hwnd, hps, &ulChanged );
  737.         WinRealizePalette( hwnd, hps, &ulChanged );
  738.  
  739.             /* --------- clean up PS/DC  ----------- */
  740.             /*
  741.              * Note that since DC opened with WinOpenWindowDC(), we don't
  742.              * need to do a DevCloseDC() as the DC will automatically
  743.              * be closed with the window
  744.              */
  745.         GpiAssociate( hps, NULLHANDLE );
  746.         GpiDestroyPS( hps );
  747.       } // end of if ( hps )
  748.  
  749.       if ( hpsMemory ){
  750.         HDC  hdc ;
  751.  
  752.             /* ----- delete the bitmap  --------- */
  753.         GpiDeleteBitmap( GpiSetBitmap( hpsMemory, NULLHANDLE ) );
  754.  
  755.             /* ----- un-select palette from PS  ------ */
  756.         GpiSelectPalette( hpsMemory, NULLHANDLE );
  757.  
  758.             /* ---- do standard cleanup of PS/DC  ------ */
  759.         hdc = GpiQueryDevice( hpsMemory );
  760.         GpiAssociate( hpsMemory, NULLHANDLE );
  761.         DevCloseDC( hdc );
  762.         GpiDestroyPS( hpsMemory );
  763.       }
  764.  
  765.           /* ----- delete the custom palette, if any  ------ */
  766.       if ( bmpSpecs.hpal ){
  767.          GpiDeletePalette( bmpSpecs.hpal );
  768.       }
  769.       return( WinDefWindowProc(hwnd,msg,mp1,mp2));
  770.  
  771.     default:
  772.       return( WinDefWindowProc(hwnd,msg,mp1,mp2));
  773.  
  774.   } //  end of switch () 
  775.   return( FALSE );
  776.  
  777. } //  end of MainWindowProc 
  778.  
  779. /* ********************************************************************** */
  780. /*                                                                        */
  781. /*   GetBMPFileName                                                       */
  782. /*                                                                        */
  783. /*     This function creates a standard Open File dialog to allow the     */
  784. /*   user to select a bitmap file to display. Only single selection is    */
  785. /*   allowed. Note that the returned path is maintained, so that the      */
  786. /*   next file open initiates in the same directory.                      */
  787. /*                                                                        */
  788. /*      The file filter defaults to "*.BMP". A bitmap EA filter may       */
  789. /*   also be used.                                                        */
  790. /*                                                                        */
  791. /* ********************************************************************** */
  792. PSZ
  793. GetBMPFileName( PSZ  pszDialogTitle )
  794. {
  795.    PSZ   pszReturn = (PSZ)NULL;
  796.    PCHAR pcName ;
  797.  
  798.       /* ------ initialize the file dialog control info  ------- */
  799.    fdFileDlg.cbSize = sizeof( FILEDLG );
  800.    fdFileDlg.fl = FDS_HELPBUTTON | FDS_CENTER | FDS_OPEN_DIALOG | FDS_INCLUDE_EAS;
  801.    fdFileDlg.pszTitle = pszDialogTitle ;
  802.    fdFileDlg.pfnDlgProc = (PFNWP) NULL ;
  803.    fdFileDlg.papszITypeList = (PAPSZ) apszEATypes ;
  804.    pcName = strrchr( fdFileDlg.szFullFile, '\\' );
  805.    if ( pcName ){
  806.       strcpy( ++pcName, "*.BMP" );
  807.    }
  808.    else {
  809.       strcpy( fdFileDlg.szFullFile, "*.BMP" );
  810.    }
  811.  
  812.  
  813.       /* ----- put up the standard Open File dialog -------- */
  814.    if ( WinFileDlg( HWND_DESKTOP, hwndClient, &fdFileDlg ) ){
  815.           /* -- if dialog not canceled, return the filespec  -- */
  816.       if ( fdFileDlg.lReturn == DID_OK ){
  817.          pszReturn = (PSZ) fdFileDlg.szFullFile ;
  818.       } // end of if ( fdFileDlg.lReturn ==...
  819.    } // end of if ( WinFileDlg( ...
  820.    else {
  821.       ShowErrorWindow( "Error creating Open File Dialog window !!", TRUE );
  822.    }
  823.  
  824.    return( pszReturn );
  825.  
  826. } // end of GetBMPFileName()
  827.  
  828. /* ********************************************************************** */
  829. /*                                                                        */
  830. /*   LoadBitmapFile                                                       */
  831. /*                                                                        */
  832. /*      This is the workhorse function. This function takes a given       */
  833. /*   bitmap file name and "loads" the file, creating a bitmap. This       */
  834. /*   function handles OS/2 1.x, OS/2 2.x, and Windows 3.x formats, in     */
  835. /*   any color depth, compressed or uncompressed. Only single image       */
  836. /*   bitmap files are supported. Multi-image bitmaps or bitmap arrays,    */
  837. /*   pointers, icons, etc are not supported. I have attempted to          */
  838. /*   make this function relatively generic so that it might be used       */
  839. /*   directly in an app other than this one.                              */
  840. /*                                                                        */
  841. /*      The bitmap is created to be compatible with the HPS passed in.    */
  842. /*   The HPS is meant to be a reference only, and it's intial state is    */
  843. /*   stored and then restored so that it is unaffected by this function.  */
  844. /*                                                                        */
  845. /*      A parmeter specifies whether the bitmap is to be created so that  */
  846. /*   it's stored color infomation is preserved and used, or whether it    */
  847. /*   is created so that it's colors are mapped to the current (default)   */
  848. /*   system color palette. If the former is specified, then the bitmap    */
  849. /*   color info is extracted from the bitmap, and a custom palette is     */
  850. /*   created. This palette is then selected into the reference HPS        */
  851. /*   *before* the bitmap is actually created. This step is essential,     */
  852. /*   otherwise upon creation of the bitmap, it's colors will be remapped  */
  853. /*   to the closest match in the default palette.                         */
  854. /*                                                                        */
  855. /*     Note that if the bitmap is a 24-bit, true-color format, a custom   */
  856. /*   palette is *not* created, even if the option is specified. This      */
  857. /*   is because true-color formats are not supported by palettes.         */
  858. /*                                                                        */
  859. /*      If the bitmap is in OS/2 1.x (or Windows 1.x, 2.x) formats        */
  860. /*   and a color bitmap is to be created, the color table info is         */
  861. /*   converted from an RGB to an RGB2 structure format. When accessing    */
  862. /*   fields within the bitmap structure, we must be careful of which      */
  863. /*   format we are working with, since there are nasty field alignment    */
  864. /*   problems between 1.x and 2.x formats. Fortunately GpiCreateBitmap()  */
  865. /*   understands the different formats, so we don't have to convert       */
  866. /*   all the bitmap data.                                                 */
  867. /*                                                                        */
  868. /*      For more information on bitmap and bitmap file formats, see the   */
  869. /*   online PM Reference in the OS/2 Developers Toolkit. The subject is   */
  870. /*   discussed under the section 'Related Information'.                   */
  871. /*                                                                        */
  872. /* ********************************************************************** */
  873. HBITMAP
  874. LoadBitmapFile( HPS hpsCompat, PSZ pszFileSpec, BOOL bGetPalette, PBMPSPECS pbmpSpecs )
  875. {
  876.   PBITMAPFILEHEADER2  pbfh2 ;
  877.   PBITMAPINFO2        pbi2 ;
  878.   HPAL                hpalBitmap ;
  879.   LONG                lSavePSId ;
  880.   HBITMAP             hReturn = NULLHANDLE;
  881.  
  882.  
  883.  
  884.      /* ----- save the state of the compatible PS  ---------- */
  885.      /*
  886.       *  in particular, we want to be sure that we save any
  887.       *  color palette or color table info, as we may be
  888.       *  altering it during this function.
  889.       */
  890.   lSavePSId = GpiSavePS( hpsCompat );
  891.  
  892.      /* ----- first, load the file into a memory buffer ----- */
  893.   pbfh2 = LoadBMPFileToMem( pszFileSpec );
  894.   if ( pbfh2 ){
  895.  
  896.          /* ----- make sure we are dealing with a bitmap ---- */
  897.      if ( pbfh2->usType == BFT_BMAP ){
  898.         PRGB2  pRGB2 ;
  899.         BOOL   bIs1xFormat ;
  900.         BOOL   bIs24bitColor ;
  901.         LONG   lColorCount ;
  902.  
  903.            /* ---- get handle to bitmap info struct ------ */
  904.         pbi2 = (PBITMAPINFO2) &pbfh2->bmp2 ;
  905.  
  906.            /* ---- is the bitmap 1.x or 2.x format ------- */
  907.         bIs1xFormat = ( pbi2->cbFix == sizeof( BITMAPINFOHEADER ));
  908.  
  909.            /* ----- determine the color count for the bitmap ------- */
  910.         if ( bIs1xFormat ){
  911.             PBITMAPINFO   pbi ;
  912.  
  913.                /* ------ convert structure types  -------- */
  914.             pbi = (PBITMAPINFO) pbi2 ;
  915.                /*
  916.                 *  lColorCount = color planes * ( 2**bitcount)
  917.                 */
  918.             lColorCount = (LONG)pbi->cPlanes * (LONG)(1<<pbi->cBitCount);
  919.             bIs24bitColor = ( pbi->cBitCount == 24 ) ;
  920.         } // end of if ( bIs1xFormat )
  921.         else {
  922.                /* ----- check if cclrUsed field is set -------- */
  923.                /*
  924.                 *  cclrUsed can specify that only a subset of the actual
  925.                 *  colors available are used. For example, an 8-bit bitmap
  926.                 *  has 256 colors available, but the bitmap may only use
  927.                 *  64 of them. If this number is set, it specifies how
  928.                 *  many colors are actually defined in the bitmap color
  929.                 *  table.
  930.                 */
  931.             if (( pbi2->cbFix > FIELDOFFSET( BITMAPINFOHEADER2, cclrUsed )) &&
  932.                 ( pbi2->cclrUsed > 0 )){
  933.               lColorCount = pbi2->cclrUsed ;
  934.             }
  935.             else {
  936.                lColorCount = (LONG)pbi2->cPlanes * (LONG)(1<<pbi2->cBitCount);
  937.             }
  938.             bIs24bitColor = ( pbi2->cBitCount == 24 ) ;
  939.         } // end of else ( bIs1xFormat )
  940.  
  941.            /* ---- if 24-bit color, skip any palette creation ---- */
  942.         if ( bGetPalette && bIs24bitColor ){
  943.           ShowErrorWindow( "Bitmap is 24-bit, true-color format. Custom palette will not be created !", FALSE );
  944.           bGetPalette = FALSE ;
  945.         }
  946.  
  947.            /* ------  get color palette from bitmap  --------- */
  948.         if ( bGetPalette ){
  949.  
  950.               /* -- convert 1x color table (RGB) to 2x format (RGB2) -- */
  951.            if ( bIs1xFormat ){
  952.               pRGB2 = Convert1xColorTableTo2x( (PBITMAPINFO)pbi2, lColorCount );
  953.            }  // end of if ( bIs1xFormat )
  954.            else {
  955.                 /* ----- get pointer to bitmap color table (RGB2) ---- */
  956.               pRGB2 = ((PRGB2)((PBYTE)pbi2 + pbi2->cbFix)) ;
  957.            }  // end of else ( bIs1xFormat )
  958.  
  959.  
  960.               /* --- create a custom color palette from color info --- */
  961.           hpalBitmap = GpiCreatePalette( hab,
  962.                                          LCOL_PURECOLOR,
  963.                                          LCOLF_CONSECRGB,
  964.                                          lColorCount,
  965.                                          (PULONG) pRGB2 );
  966.           if ( hpalBitmap == NULLHANDLE ){
  967.              ShowErrorWindow( "Error creating bitmap palette !!!", TRUE );
  968.           } // end of if ( hpalBitmap )
  969.         } // end of if ( bGetPalette )
  970.         else {
  971.            hpalBitmap = NULLHANDLE ;  // will reset to default palette
  972.         }
  973.  
  974.                /* -- set the palette into PS before bitmap creation -- */
  975.         if ( GpiSelectPalette( hpsCompat,
  976.                                hpalBitmap ) == (HPAL) PAL_ERROR ){
  977.           ShowErrorWindow( "Error selecting palette into bitmap PS", TRUE );
  978.         }
  979.  
  980.            /* ---- create a bitmap from file info  ------- */
  981.         hReturn = GpiCreateBitmap( hpsCompat,
  982.                                    (PBITMAPINFOHEADER2)pbi2,
  983.                                    CBM_INIT,
  984.                                    (((PBYTE)pbfh2) + pbfh2->offBits),
  985.                                    pbi2 );
  986.  
  987.         if ( hReturn != GPI_ERROR ){
  988.                /* -------  set return/output values  ------- */
  989.             if ( bIs1xFormat ){
  990.               pbmpSpecs->cx = ((PBITMAPINFOHEADER)pbi2)->cx ;
  991.               pbmpSpecs->cy = ((PBITMAPINFOHEADER)pbi2)->cy ;
  992.             }
  993.             else {
  994.               pbmpSpecs->cx = pbi2->cx ;
  995.               pbmpSpecs->cy = pbi2->cy ;
  996.             }
  997.             pbmpSpecs->colorCount = lColorCount ;
  998.             pbmpSpecs->hpal = hpalBitmap  ;
  999.  
  1000.         } // end of if ( hReturn != GPI_ERROR)
  1001.         else {
  1002.            ShowErrorWindow( "Error creating bitmap from file !!", TRUE );
  1003.            GpiDeletePalette( hpalBitmap );
  1004.         } // end of else ( hReturn != GPI_ERROR)
  1005.  
  1006.  
  1007.            /* ----- if "converted" RGB table, then cleanup ----- */
  1008.         if ( bIs1xFormat && bGetPalette && pRGB2 ){
  1009.            free( (PVOID) pRGB2 );
  1010.         }  // end of if ( bIs1xFormat )
  1011.  
  1012.      } // end of if ( pbfh2->usType ==...
  1013.      else {
  1014.         ShowErrorWindow( "The file is not a supported bitmap format! Only single image BMP files are supported.", FALSE );
  1015.      } // end of else ( pbfh2->usType ==...
  1016.  
  1017.         /* ------ release the memory file buffer  ------- */
  1018.      free( (PVOID) pbfh2 );
  1019.   }  // end of if ( pbfh2 )
  1020.  
  1021.   GpiRestorePS( hpsCompat, lSavePSId );
  1022.  
  1023.   return( hReturn );
  1024. }  // end of LoadBitmapFile()
  1025.  
  1026. /* ********************************************************************** */
  1027. /*                                                                        */
  1028. /*   CreateMemoryPS                                                       */
  1029. /*                                                                        */
  1030. /*      This function creates and returns a micro Presentation Space      */
  1031. /*   associated with an OD_MEMORY device context.                         */
  1032. /*                                                                        */
  1033. /* ********************************************************************** */
  1034. HPS
  1035. CreateMemoryPS( VOID )
  1036. {
  1037.   HPS          hpsReturn = NULLHANDLE;
  1038.   SIZEL        sizel = { 0, 0 };
  1039.   HDC          hdcMemory ;
  1040.  
  1041.  
  1042.   hdcMemory = DevOpenDC( hab, OD_MEMORY, "*", 0, (PCHAR*)NULL, NULLHANDLE);
  1043.   hpsReturn = GpiCreatePS( hab, hdcMemory, &sizel, GPIT_MICRO | GPIA_ASSOC | PU_PELS );
  1044.   if ( hpsReturn == NULLHANDLE ){
  1045.       DevCloseDC( hdcMemory );
  1046.       ShowErrorWindow( "Error creating Memory PS !!", TRUE );
  1047.    }  /* end of else (hpsReturn ... */
  1048.  
  1049.    return( hpsReturn );
  1050.  
  1051. } // end of CreateMemoryPS()
  1052.  
  1053. /* ********************************************************************** */
  1054. /*                                                                        */
  1055. /*   LoadBMPFileToMem                                                     */
  1056. /*                                                                        */
  1057. /*      This function loads the bitmap file data into an allocated        */
  1058. /*   memory buffer. It makes no attempt to interpret the data.            */
  1059. /*                                                                        */
  1060. /* ********************************************************************** */
  1061. PBITMAPFILEHEADER2
  1062. LoadBMPFileToMem( PSZ pszFileSpec )
  1063. {
  1064.   PVOID  pReturn ;
  1065.   INT    inFile ;
  1066.   LONG   lFileSize ;
  1067.  
  1068.        /* ----- open the specified bitmap file ------- */
  1069.   if ( (inFile = _open( pszFileSpec, O_RDONLY | O_BINARY, 0 )) != -1 ){
  1070.        /* ------- determine the size of the file ------- */
  1071.     lFileSize = _filelength( inFile ) ;
  1072.     if ( lFileSize > 0 ){
  1073.          /* ----- allocate buffer for whole file  ------ */
  1074.        pReturn = malloc( lFileSize ) ;
  1075.        if ( pReturn ){
  1076.             /* ----- read the file into the buffer ----- */
  1077.          if ( lFileSize != _read( inFile, pReturn, lFileSize ) ){
  1078.             ShowErrorWindow( "Error reading bitmap file !!", FALSE );
  1079.             free( pReturn );
  1080.             pReturn = (PVOID)NULL ;
  1081.          } // end of if ( lFileSize != _read(...
  1082.        }  // end of if ( pReturn )
  1083.        else{
  1084.           ShowErrorWindow( "Error allocating bitmap file buffer !!", FALSE );
  1085.        }  // end of else ( pReturn )
  1086.     } // end of if ( lFileSize > 0 )
  1087.     else {
  1088.       ShowErrorWindow( "Error determining size of bitmap file !!", FALSE );
  1089.     } // end of else ( lFileSize > 0 )
  1090.  
  1091.     _close( inFile );
  1092.   } // end of if ((inFile =...
  1093.   else {
  1094.     CHAR   acBuffer[256] ;
  1095.  
  1096.     sprintf( acBuffer, "Error opening bitmap file '%.200s' !!", pszFileSpec );
  1097.     ShowErrorWindow( acBuffer, FALSE );
  1098.   } // end of else ((inFile =...
  1099.  
  1100.   return( (PBITMAPFILEHEADER2) pReturn );
  1101.  
  1102. } // end of LoadBMPFileToMem()
  1103. /* ********************************************************************** */
  1104. /*                                                                        */
  1105. /*   Convert1xTo2xBitmapInfo                                              */
  1106. /*                                                                        */
  1107. /*      This converts the color table info associated with a 1.x bitmap   */
  1108. /*   info structure to a 2.x compatible RGB2 array, storing the array     */
  1109. /*   in an allocated buffer.                                              */
  1110. /*                                                                        */
  1111. /* ********************************************************************** */
  1112. PRGB2
  1113. Convert1xColorTableTo2x( PBITMAPINFO pbi, LONG lColorCount )
  1114. {
  1115.   PRGB2  pReturn ;
  1116.  
  1117.  
  1118.         /* -- allocate memory for color table -- */
  1119.   pReturn = (PRGB2)malloc( lColorCount * sizeof( RGB2) );
  1120.  
  1121.   if ( pReturn ){
  1122.     ULONG  i ;
  1123.         /* ------ move color info from 1.x struct to 2.x struct  ------ */
  1124.     for( i=0; i<lColorCount; i++ ){
  1125.        pReturn[i].bRed = pbi->argbColor[i].bRed ;
  1126.        pReturn[i].bGreen = pbi->argbColor[i].bGreen ;
  1127.        pReturn[i].bBlue = pbi->argbColor[i].bBlue ;
  1128.        pReturn[i].fcOptions = 0 ;
  1129.     }
  1130.  
  1131.   } // end of if ( pReturn )
  1132.   else {
  1133.     ShowErrorWindow( "Error allocating memory for bitmap conversion !!", FALSE );
  1134.   } // end of else ( pReturn )
  1135.  
  1136.   return ( pReturn );
  1137. } // end of Convert1xColorTableTo2x()
  1138.  
  1139. /* ********************************************************************** */
  1140. /*                                                                        */
  1141. /*   SizeAndPosWinForImage                                                */
  1142. /*                                                                        */
  1143. /*      This function sizes the window to exactly fit the bitmap image    */
  1144. /*   and adjusts it's position, if necessary, so that the title bar       */
  1145. /*   remains visible on the desktop.                                      */
  1146. /*                                                                        */
  1147. /* ********************************************************************** */
  1148. VOID
  1149. SizeAndPosWinForImage( HWND  hwndFrame, LONG lImageCX, LONG lImageCY )
  1150. {
  1151.   RECTL  rectlSize;
  1152.   LONG   lCX, lCY ;
  1153.   LONG   lScreenCX, lScreenCY ;
  1154.   LONG   lX, lY ;
  1155.   SWP    swp;
  1156.  
  1157.       /* ---- calc frame size to fit image ----- */
  1158.    WinSetRect( hab, &rectlSize, 0, 0, lImageCX, lImageCY );
  1159.    WinCalcFrameRect( hwndFrame, &rectlSize, FALSE );
  1160.    lCX = rectlSize.xRight - rectlSize.xLeft ;
  1161.    lCY = rectlSize.yTop - rectlSize.yBottom ;
  1162.  
  1163.       /* --  determine if window needs to be reposistion to fit screen -- */
  1164.    lScreenCX = WinQuerySysValue( HWND_DESKTOP, SV_CXSCREEN );
  1165.    lScreenCY = WinQuerySysValue( HWND_DESKTOP, SV_CYSCREEN );
  1166.    WinQueryWindowPos( hwndFrame, &swp );
  1167.    if ( (swp.x+lCX) > lScreenCX ){
  1168.      lX = lScreenCX - lCX ;
  1169.    }
  1170.    else if (( swp.x < 0 ) && ( lCX <= lScreenCX )) {
  1171.      lX = 0 ;
  1172.    }
  1173.    else {
  1174.      lX = swp.x ;
  1175.    }
  1176.  
  1177.    if ( (swp.y+lCY) > lScreenCY ){
  1178.      lY = lScreenCY - lCY ;
  1179.    }
  1180.    else if (( swp.y < 0 ) && ( lCY <= lScreenCY )) {
  1181.      lY = 0 ;
  1182.    }
  1183.    else {
  1184.      lY = swp.y ;
  1185.    }
  1186.  
  1187.       /* ---- resize and position the frame to new dimensions --- */
  1188.    WinSetWindowPos( hwndFrame,
  1189.                     NULLHANDLE,
  1190.                     lX, lY,
  1191.                     lCX , lCY ,
  1192.                     SWP_MOVE | SWP_SIZE );
  1193.  
  1194. } // end of SizeAndPosWinForImage()
  1195.  
  1196. /* ********************************************************************** */
  1197. /*                                                                        */
  1198. /*   SetWindowTitle                                                       */
  1199. /*                                                                        */
  1200. /*      This function sets the title bar text for the application. When   */
  1201. /*   a bitmap is loaded, the title bar contains the name of the bitmap    */
  1202. /*   file, the height, the width, and the color depth of the image, and   */
  1203. /*   whether the image is using a custom (pal)ette or the (def)ault       */
  1204. /*   palette.                                                             */
  1205. /*                                                                        */
  1206. /*      If a NULL is passed in for either the filespec or the bitmap      */
  1207. /*   spec, the title bar is reset to display only the app name.           */
  1208. /*                                                                        */
  1209. /*   The title bar has the format:                                        */
  1210. /*                                                                        */
  1211. /*      ViewBMP: filename [ width x height x color depth - pal | def ]    */
  1212. /*                                                                        */
  1213. /*                                                                        */
  1214. /* ********************************************************************** */
  1215. VOID
  1216. SetWindowTitle( HWND hwndFrame, PSZ pszFileSpec, PBMPSPECS pbmpSpecs )
  1217. {
  1218.   CHAR  acNewTitle[ 256 ];
  1219.   CHAR  acDrive[ _MAX_DRIVE ];
  1220.   CHAR  acDir[ _MAX_DIR ];
  1221.   CHAR  acFName[ _MAX_FNAME ];
  1222.   CHAR  acExt[ _MAX_EXT ];
  1223.  
  1224.   if ( pszFileSpec && pbmpSpecs ){
  1225.  
  1226.          /* ----- get the bitmap file name  -------- */
  1227.      _splitpath( pszFileSpec, acDrive, acDir, acFName, acExt );
  1228.          /* ------ construct the title bar string ----- */
  1229.      sprintf( acNewTitle, " %s : %.164s%.32s [%dx%dx%d%s-%s]",
  1230.               szMainTitle,
  1231.               acFName,
  1232.               acExt,
  1233.               pbmpSpecs->cx,
  1234.               pbmpSpecs->cy,
  1235.               ((pbmpSpecs->colorCount != 16777216)?pbmpSpecs->colorCount:16),
  1236.               ((pbmpSpecs->colorCount != 16777216)?"":".7M"),
  1237.               ((pbmpSpecs->hpal)?"pal":"def") );
  1238.     WinSetWindowText( hwndFrame, acNewTitle );
  1239.   }
  1240.   else {
  1241.        /* ------ reset the title bar to app name only ------- */
  1242.     WinSetWindowText( hwndFrame, szMainTitle );
  1243.   }
  1244.  
  1245.  
  1246. }  //end of SetWindowTitle()
  1247.  
  1248. /* ********************************************************************** */
  1249. /*                                                                        */
  1250. /*   AreColorPalettesSupported                                            */
  1251. /*                                                                        */
  1252. /*      This function queries the display device to determine if color    */
  1253. /*   palettes are supported.                                              */
  1254. /*                                                                        */
  1255. /* ********************************************************************** */
  1256. BOOL
  1257. AreColorPalettesSupported( VOID )
  1258. {
  1259.   HDC   hdcScreen ;
  1260.   LONG  lCapsBitField ;
  1261.   BOOL  bReturn = FALSE ;
  1262.  
  1263.       /* ----- get DC to check screen palette support  ------ */
  1264.   hdcScreen = WinOpenWindowDC( HWND_DESKTOP );
  1265.   if ( hdcScreen == NULLHANDLE ){
  1266.     ShowErrorWindow( "Error opening Screen device context!", TRUE );
  1267.   } // end of if ( hdcScreen == NULLHANDLE )
  1268.   else {
  1269.     if ( ! DevQueryCaps( hdcScreen,
  1270.                          CAPS_ADDITIONAL_GRAPHICS,
  1271.                          1,
  1272.                          &lCapsBitField )){
  1273.         ShowErrorWindow( "Error reading Screen device capabilities!", TRUE );
  1274.     } // end of if ( !DevQueryCaps(...
  1275.     else {
  1276.  
  1277.       bReturn = ((lCapsBitField & CAPS_PALETTE_MANAGER ) == CAPS_PALETTE_MANAGER );
  1278.       if ( !bReturn ){
  1279.         ShowErrorWindow( "The current display configuration does not support Palette Management", FALSE );
  1280.       }
  1281.  
  1282.     } // end of else (! DevQueryCaps(...
  1283.   } // end of else ( hdcScreen == NULLHANDLE )
  1284.  
  1285.   return( bReturn );
  1286. } // end of AreColorPalettesSupported()
  1287.  
  1288. /* ********************************************************************** */
  1289. /*                                                                        */
  1290. /*   AboutDialogProc                                                      */
  1291. /*                                                                        */
  1292. /*       This is the window proc for the "About box". The only unique     */
  1293. /*   processing occurs during WM_INITDLG time, when it loads up the       */
  1294. /*   MLE with info about the app. The info is taken from a buffer         */
  1295. /*   defined in the header file 'AboutTxt.h'                              */
  1296. /*                                                                        */
  1297. /* ********************************************************************** */
  1298. MRESULT EXPENTRY
  1299. AboutDialogProc( HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2 )
  1300. {
  1301.  
  1302.   switch (msg) {
  1303.  
  1304.     case WM_INITDLG :
  1305.         {
  1306.           IPT    iptImport = 0;
  1307.           LONG   lStringSize ;
  1308.  
  1309.           lStringSize = strlen( acAboutBuffer );
  1310.  
  1311.                /* --- set the buffer for MLE importing  --- */
  1312.           WinSendDlgItemMsg( hwnd,
  1313.                              DID_ABOUT_MLE,
  1314.                              MLM_SETIMPORTEXPORT,
  1315.                              MPFROMP( (PVOID) acAboutBuffer ),
  1316.                              MPFROMLONG( lStringSize ));
  1317.  
  1318.  
  1319.            WinSendDlgItemMsg( hwnd,
  1320.                               DID_ABOUT_MLE,
  1321.                               MLM_IMPORT,
  1322.                               MPFROMP( (PVOID) &iptImport ),
  1323.                               MPFROMLONG( lStringSize ));
  1324.  
  1325.         } // end of case WM_INITDLG
  1326.         break;
  1327.  
  1328.     case WM_COMMAND :
  1329.         switch ( SHORT1FROMMP( mp1 )){
  1330.           case  DID_OK  :
  1331.               WinDismissDlg( hwnd, DID_OK );
  1332.               break;
  1333.          } // end of switch
  1334.         break;
  1335.  
  1336.     default:
  1337.       return( WinDefDlgProc(hwnd,msg,mp1,mp2));
  1338.  
  1339.   } //  end of switch ()
  1340.   return( FALSE );
  1341.  
  1342. }  // end of AboutDialogProc()
  1343.  
  1344. /* ********************************************************************** */
  1345. /*                                                                        */
  1346. /*   ShowErrorWindow                                                      */
  1347. /*                                                                        */
  1348. /*      This function displays a given string in a Message Box. It can    */
  1349. /*   optionally query and display last PM error that was generated.       */
  1350. /*                                                                        */
  1351. /* ********************************************************************** */
  1352. VOID
  1353. ShowErrorWindow( PSZ  pszErrorMsg, BOOL bUseLastError )
  1354. {
  1355.   HPOINTER  hptrCurrent ;
  1356.   CHAR      acErrorBuffer[256] ;
  1357.  
  1358.   if ( bUseLastError ) {
  1359.       ERRORID   errorID = WinGetLastError( hab );
  1360.  
  1361.       sprintf( acErrorBuffer,
  1362.                "%s \n(code = 0x%lX)",
  1363.                pszErrorMsg,
  1364.                (ULONG) errorID );
  1365.       pszErrorMsg = (PSZ) acErrorBuffer ;
  1366.   }  /* end of if ( bUseLastError ) */
  1367.  
  1368.   hptrCurrent = WinQueryPointer( HWND_DESKTOP );
  1369.  
  1370.   WinMessageBox( HWND_DESKTOP,
  1371.                  HWND_DESKTOP,
  1372.                  pszErrorMsg ,
  1373.                  szErrorTitle ,
  1374.                  0,
  1375.                  MB_CUACRITICAL | MB_OK );
  1376.  
  1377.    WinSetPointer( HWND_DESKTOP, hptrCurrent );
  1378.  
  1379.  
  1380. }
  1381. /* ********************************************************************** */
  1382. /* ********************************************************************** */
  1383.