home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
vuebmp.zip
/
ViewBMP.c
< prev
next >
Wrap
Text File
|
1994-10-08
|
68KB
|
1,383 lines
/* ********************************************************************** */
/* */
/* Program Name: ViewBMP.exe */
/* Article Title: Color Palette Management with OS/2 */
/* OS/2 Developer Magazine, Issue: November '94, page xxx */
/* Author: John D. Webb CIS: 71075,1117 */
/* */
/* Description: A bitmap viewing program which demonstrates the power */
/* of OS/2's color palette management APIs. */
/* */
/* Program requirements: OS/2 2.x */
/* IBM CSet/2 or CSet++ for OS/2 */
/* OS/2 Developer's Toolkit */
/* Video Driver supporting color palettes */
/* */
/* Note: The program will run on machines without a video */
/* driver which supports color palettes; however, */
/* bitmaps will only be displayed using the default */
/* system color palette. */
/* */
/* ********************************************************************** */
/* ********************************************************************** */
/* */
/* */
/* ViewBMP displays bitmaps, which are loaded from files. */
/* Although I haven't thoroughly tested it with every possible */
/* format, it ought to be able to handle most single-image */
/* OS/2 1.x, OS/2 2.x, and Windows bitmaps. It should handle */
/* most color depths, including 24-bit, and will handle */
/* compressed formats (e.g. Huffman 1-D, 4-bit RLE, 8-bit RLE, */
/* etc). One key feature of ViewBMP is that it can load and */
/* display the bitmaps using either the system default color */
/* palette or using the custom color palette which is stored */
/* in the bitmap. The latter provides the best display results */
/* but requires the use of the OS/2 color palette management */
/* system; note that this option (and the menu item that */
/* selects it) will be disabled on systems that do not support */
/* color palettes. */
/* */
/* The bitmap file to be displayed may be selected not only */
/* from the standard Open File Dialog, but also by entering */
/* the file name on the command line. One benefit of this */
/* feature is that ViewBMP may be associated with .BMP file */
/* extensions, and automatically invoked by opening a .BMP */
/* file. */
/* */
/* It is important to note that ViewBMP was intended as an */
/* educational source code sample first, and as a bitmap */
/* viewing utility second. Priority was given to source code */
/* simplicity and clarity. As such, many features (and most of */
/* the error checking) which would make ViewBMP a robust, */
/* production-level image application have been left out. */
/* Also, for the same reasons, many algorithmic and coding */
/* optimization have been left out. For example, this app */
/* should be multi-threaded, but has been left as single */
/* threaded to dispense with syncronizing logic. This source code */
/* is to be taken as an introduction and guide for color palette */
/* usage, not as an example of commercial grade code. */
/* */
/* The code demonstrates many techniques, including: */
/* */
/* 1) How to read in and decode a single image bitmap file */
/* 2) How to extract color info from a bitmap file */
/* 3) How to create a color palette */
/* 4) How to select a color palette into multiple */
/* Presentation Spaces */
/* 5) How to create a memory bitmap maintaining original */
/* color integrity */
/* 6) How to realize a color palette to maintain proper */
/* bitmap colors when drawing to the screen */
/* 7) How to properly respond to WM_REALIZEPALETTE messages */
/* 8) How to dispose of color palette and bitmap resources */
/* 9) How to reset the system default color palette */
/* */
/* */
/* Please feel free to incorporate the techniques */
/* demonstrated (and portions of the code) in any application */
/* you are writing; however I would ask that you make */
/* *significant* modifications to the code if you are going to */
/* sell it. */
/* */
/* I will try to respond to all comments and questions sent to */
/* my E-mail address below, but I can not guarantee that I */
/* will have timely or complete answers. Thanks for your */
/* interest and your time. I hope you find this sample */
/* application of value. */
/* */
/* John D. Webb */
/* CompuServe: 71075,1117 */
/* Internet: 71075.1117@compuserve.com */
/* */
/* ********************************************************************** */
/* ********************************************************************** */
/* DISCLAIMER OF WARRANTIES ( unfortunate legality ) */
/* ====================================================================== */
/* */
/* This [enclosed] source code (and the application producted from it) */
/* is provided to you solely for the purpose of assisting you in the */
/* development of your applications. The source code is provided */
/* "AS IS", without warranty of any kind. The author shall not be */
/* liable for any damages arising out of your use of the source code, */
/* in whole or in part, or out of the use of the producted application, */
/* even if he has been advised of the possibility of such damages. */
/* */
/* ********************************************************************** */
/* ********************************************************************** */
/* */
/* (c) 1994, John D. Webb. All rights reserved. */
/* */
/* ********************************************************************** */
#define INCL_WIN
#define INCL_GPI
#define INCL_BITMAPFILEFORMAT
#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>
#include "ViewBMP.h"
#include "AboutTxt.h"
/* ------------- Global Definitions ----------------- */
#define UWM_LOAD_BITMAP WM_USER+1
/*
* The above user-defined message is used to initiate a bitmap
* file load. The following parameters are used with the message.
*
* mp1 = (PSZ) file path for bitmap file to load
* mp2 = (BOOL) TRUE if custom bitmap palette is to be used
* FALSE if default system palette is to be used
*/
typedef struct _bmpSpecs {
LONG cx ; // bitmap horz size in pixels
LONG cy ; // bitmap vert size in pixels
LONG colorCount ; // number of colors used by bitmap
HPAL hpal ; // custom palette (if any) for bitmap
} BMPSPECS, *PBMPSPECS ;
/*
* The above structure type is used to keep critical bitmap
* information and specs in a readily available form for the
* application.
*/
/* ------------- Global Variables ----------------- */
HAB hab ;
HMQ hmq ;
HWND hwndClient ;
HWND hwndFrame ;
QMSG qmsg;
HPS hps ; // persistant display PS
HPS hpsMemory ; // persistant memory PS containing bitmap
BOOL bBitmapLoaded ;
BMPSPECS bmpSpecs ;
PSZ szClassName = "ViewBMPClass" ;
PSZ szMainTitle = "ViewBMP" ;
PSZ szErrorTitle = "ViewBMP Error" ;
FILEDLG fdFileDlg ; // variables used for Open File dialog
PSZ apszEATypes[20] = { DRT_BITMAP ,
DRT_UNKNOWN ,
(PSZ) NULL };
/* ---------------- Prototypes ------------------------ */
INT main( INT, PCHAR [] );
MRESULT EXPENTRY MainWindowProc( HWND, USHORT, MPARAM, MPARAM );
HPS CreateMemoryPS( VOID );
PSZ GetBMPFileName( PSZ );
HBITMAP LoadBitmapFile( HPS, PSZ, BOOL, PBMPSPECS );
PBITMAPFILEHEADER2 LoadBMPFileToMem( PSZ );
PRGB2 Convert1xColorTableTo2x( PBITMAPINFO, LONG );
VOID SizeAndPosWinForImage( HWND, LONG, LONG );
VOID SetWindowTitle( HWND, PSZ, PBMPSPECS );
BOOL AreColorPalettesSupported( VOID ) ;
MRESULT EXPENTRY AboutDialogProc( HWND, USHORT, MPARAM, MPARAM );
VOID ShowErrorWindow( PSZ, BOOL );
/* ********************************************************************** */
/* */
/* Main */
/* */
/* Relatively generic PM main() procedure. */
/* Creates standard application window. Registers with task list. */
/* Goes into standard message dispatch loop. */
/* */
/* Two "unique" functions are to check for color palette support */
/* and disable palette menu option is unsupported, and to check the */
/* command line for bitmap filename (and initiate load if found ). */
/* */
/* ********************************************************************** */
INT
main( INT argc, PCHAR argv[] )
{
/* ----- start standard PM initialization mantra... ------ */
if ( (hab = WinInitialize( 0L )) == (HAB) NULL ){
DosBeep( 60, 250 ) ;
DosBeep( 120, 250 ) ;
}
else {
if ( (hmq = WinCreateMsgQueue( hab, 0 )) == (HMQ) NULL ){
DosBeep( 60, 250 ) ;
DosBeep( 120, 250 ) ;
DosBeep( 60, 250 ) ;
}
else {
ULONG fulCreate= FCF_TITLEBAR | FCF_SYSMENU | FCF_SIZEBORDER |
FCF_MENU | FCF_MINMAX | FCF_SHELLPOSITION | FCF_ICON ;
WinSetPointer( HWND_DESKTOP,
WinQuerySysPointer(HWND_DESKTOP,SPTR_WAIT,TRUE));
WinRegisterClass(hab, szClassName, (PFNWP)MainWindowProc, CS_SIZEREDRAW, 0);
/* ------ create the main window ------------ */
hwndFrame = WinCreateStdWindow(HWND_DESKTOP,
0L,
(PULONG)&fulCreate,
szClassName ,
szMainTitle,
0L,
(HMODULE)NULL,
ID_MAIN_WIN,
&hwndClient);
if ( hwndFrame == NULLHANDLE ) {
ShowErrorWindow( "Error creating Main window !", TRUE );
}
else {
PID pid ;
SWCNTRL swCntrl;
HSWITCH hSwitch ;
BOOL bPalettesSupported ;
/* --------- show and activate window -------------- */
WinSetWindowPos( hwndFrame,
HWND_TOP,
0, 0, 0, 0,
SWP_SHOW | SWP_ZORDER | SWP_ACTIVATE );
/* ----------- add program to tasklist --------------- */
WinQueryWindowProcess( hwndFrame, &pid, NULL );
swCntrl.hwnd = hwndFrame ;
swCntrl.hwndIcon = (HWND) NULL ;
swCntrl.hprog = (HPROGRAM) NULL ;
swCntrl.idProcess = pid ;
swCntrl.idSession = (LONG) NULL ;
swCntrl.uchVisibility = SWL_VISIBLE ;
swCntrl.fbJump = SWL_JUMPABLE ;
sprintf( swCntrl.szSwtitle, szMainTitle );
hSwitch = WinCreateSwitchEntry( hab, (PSWCNTRL)&swCntrl);
WinSetPointer(HWND_DESKTOP,
WinQuerySysPointer(HWND_DESKTOP,SPTR_ARROW,TRUE));
/* ---------- determine if color palettes supported ---- */
bPalettesSupported = AreColorPalettesSupported() ;
if ( ! bPalettesSupported ){
/* --- if not, disable palette menu option --- */
WinEnableMenuItem( WinWindowFromID( hwndFrame, FID_MENU ),
MID_LOAD_WITH_PAL,
FALSE );
} // end of if ( ! AreColorPalettesSupported())
/* --- see if we have a bitmap from the command line --- */
if ( argc > 1 ){
/* ----- if so, pass it to the window ------- */
/*
* By posting this message, a load of the bitmap file
* will be initiated with no input necessary from
* the user. If color palettes are supported, then
* they will be automatically used for this bitmap.
*/
WinPostMsg( hwndClient,
UWM_LOAD_BITMAP,
MPFROMP( (PVOID) argv[1] ),
MPFROMLONG( (LONG) bPalettesSupported ));
} // end of if ( argc > 1 )
/* ---------- start the main processing loop ----------- */
while (WinGetMsg(hab, &qmsg,NULLHANDLE,0,0)){
WinDispatchMsg(hab, &qmsg);
}
WinRemoveSwitchEntry( hSwitch );
WinDestroyWindow(hwndFrame);
} /* end of else */
WinSetPointer(HWND_DESKTOP,
WinQuerySysPointer(HWND_DESKTOP,SPTR_ARROW,TRUE));
WinDestroyMsgQueue(hmq);
} /* end of else ( ...WinCreateMsgQueue() */
WinTerminate(hab);
} /* end of else (...WinInitialize(NULL) */
return( 0 );
} /* end of main() */
/* ********************************************************************** */
/* */
/* MainWindowProc */
/* */
/* Processes all the main window messages. */
/* */
/* ********************************************************************** */
MRESULT EXPENTRY
MainWindowProc( HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2 )
{
switch (msg) {
/* ============================================================== */
/* */
/* WM_CREATE */
/* */
/* In processing this message we will create two persistent */
/* Presentation Spaces to be used for the life of the app. */
/* One will be a display PS for drawing to the screen, the */
/* other will be a memory PS which will be used to create and */
/* hold the bitmap when it is loaded. */
/* */
/* The reason to create a persistent memory PS is so that */
/* we can hold the bitmap in it, along with any custom */
/* custom palette that it is using, and then blit from it to */
/* the display PS during WM_PAINT processing. This makes */
/* drawing to the screen very quick. We reuse the same */
/* memory PS for each bitmap loaded. */
/* */
/* The reason to create a persistent display PS, rather */
/* than just using a cached-micro PS returned from */
/* WinBeginPaint(), is that a cached-micro PS is re-initialized */
/* after the painting is complete; this means that it would */
/* lose any color palette setup done, necessitating that */
/* the custom palette be selected and realized into the */
/* cached-micro PS *each* WM_PAINT. This can lead to lots */
/* of overhead. Also having a persistent display PS makes */
/* it convenient to respond to WM_REALIZEPALETTE messages, */
/* without having to create a new PS each time. Note that */
/* it is not a requirement to use a persistent display PS; */
/* but in my opinion, it is a highly recommended. */
/* */
/* The other bit of initialization that we do is to set a */
/* pattern into the display PS. We will use it to fill */
/* the window, and any portions not containing the bitmap */
/* image. This is to help distinguish the edge of the */
/* displayed image (when the image doesn't fill the window). */
/* */
/* ============================================================== */
case WM_CREATE :
{
HDC hdc ;
SIZEL sizel = { 0, 0 };
/* -------- create persistant screen PS ----------- */
hdc = WinOpenWindowDC( hwnd );
hps = GpiCreatePS( hab,
hdc,
&sizel,
GPIT_MICRO | GPIA_ASSOC | PU_PELS );
if ( hps == NULLHANDLE ){
ShowErrorWindow( "Error creating persistant screen PS !!", TRUE );
return( (MRESULT) TRUE ); // cancel window creation
}
/* ------ initialize PS to fill with pattern ------- */
GpiSetPattern( hps, PATSYM_DENSE8 );
/* -------- create persistant memory PS ----------- */
hpsMemory = CreateMemoryPS();
if ( hpsMemory == NULLHANDLE ){
ShowErrorWindow( "Error creating persistant memory PS !!", TRUE );
return( (MRESULT) TRUE ); // cancel window creation
}
} // end of case WM_CREATE
break;
/* ============================================================== */
/* */
/* WM_COMMAND */
/* */
/* This message handles the three menu options: */
/* */
/* Open File with Default Palette - initiates a bitmap file */
/* load that will use the default system color palette. */
/* The colors contained in the bitmap will be mapped */
/* to their closest match in the current physical color */
/* palette. This option doesn't use any color palette */
/* management APIs */
/* */
/* Open File with Custom Palette - initiates a bitmap file */
/* load that will create a palette from the colors */
/* stored in the bitmap, and will use the color */
/* palette management APIs to display using that */
/* palette. In other words, the APIs will be used to */
/* change the physical color palette to match the */
/* colors contained in the bitmap. */
/* */
/* About... - displays an "About box" dialog that describes */
/* the application. */
/* */
/* ============================================================== */
case WM_COMMAND :
switch ( SHORT1FROMMP( mp1 )){
case MID_LOAD_WITHOUT_PAL :
WinPostMsg( hwnd, UWM_LOAD_BITMAP, 0, (MPARAM)FALSE );
break;
case MID_LOAD_WITH_PAL :
WinPostMsg( hwnd, UWM_LOAD_BITMAP, 0, (MPARAM)TRUE );
break;
case MID_ABOUT :
WinDlgBox( HWND_DESKTOP, hwnd,
(PFNWP) AboutDialogProc,
NULLHANDLE, ID_ABOUT_DLG,
(PVOID) NULL );
break;
} // end of switch
break;
/* ============================================================== */
/* */
/* UWM_LOAD_BITMAP */
/* */
/* mp1 = (PSZ) file path for bitmap file to load */
/* mp2 = (BOOL) TRUE if custom bitmap palette is to be used */
/* FALSE if default system palette is to be used */
/* */
/* */
/* This is the user defined message that initiate the load of */
/* a bitmap file and the creation of the bitmap image. In */
/* other words, this is were the real fun happens. */
/* */
/* If a file name is not passed in as a parameter, an Open */
/* File dialog retrieves the name. A call to an app function */
/* LoadBitmapFile() does the actual bitmap creation from the */
/* given file name. It also creates a palette if the user has */
/* specified so. I don't understand why the PM/GPI system */
/* doesn't provide an API that serves this same function; it */
/* is one that I (and others) have needed time and time again. */
/* */
/* Next, any previous bitmaps or palettes are cleaned up. Any */
/* remnants of a previous palette are remove from the hardware */
/* palette by resetting it to the default system palette. */
/* */
/* The newly created palette (if any) is selected into both */
/* the memory PS and the display PS. It is important that */
/* the same palette is selected into both, so that colors are */
/* consistent when blitting from one to the other. It is */
/* also critical that the palette be selected into the memory */
/* PS before the bitmap is set into it (so that colors are not */
/* remapped). */
/* */
/* Next, the palette is realized for the display PS. A call */
/* to WinRealizePalette() is necessary only for *display* */
/* PSs, and is not necessary for other PSs, particularly */
/* memory PSs. The call to WinRealizePalette() must be made */
/* before any drawing to the PS occurs (or the drawing won't */
/* use the selected palette). */
/* */
/* Finally, the window is sized to fit the image, and the */
/* frame title is set to contain the bitmap name and the */
/* bitmap specifications (e.g. size, color depth, etc). */
/* */
/* ============================================================== */
case UWM_LOAD_BITMAP :
{
PSZ pszFileSpec ;
BOOL bUsePaletteManager ;
HBITMAP hbitmap ;
HPOINTER hptrCurrent ;
/* ------- get the passed in parameters --------- */
pszFileSpec = (PSZ) PVOIDFROMMP( mp1 );
bUsePaletteManager = (BOOL) mp2 ;
/* ---- if name not passed in, get it from user ---- */
if ( pszFileSpec == (PSZ)NULL ){
pszFileSpec = GetBMPFileName( bUsePaletteManager ?
"Open Bitmap (with custom palette)":
"Open Bitmap (without custom palette)");
} // end of if (pszFileSpec)
if ( pszFileSpec ){
/* --- save pointer so we can put up wait pointer --- */
/*
* note: we wounldn't have to do this if we were "threading"
*/
hptrCurrent = WinQueryPointer( HWND_DESKTOP );
WinSetPointer( HWND_DESKTOP,
WinQuerySysPointer(HWND_DESKTOP,SPTR_WAIT,TRUE));
/* ----- create a bitmap from the given filename ----- */
hbitmap = LoadBitmapFile( hpsMemory,
pszFileSpec,
bUsePaletteManager,
&bmpSpecs );
/* ---- check if load was successful ----- */
if ( hbitmap ){
HBITMAP hbmOld ;
HPAL hpalOld ;
ULONG ulPhysColorsRemapped ;
/* --- reset (and cleanup) the old palette ---- */
/*
* calling GpiSelectPalette() with a NULLHANDLE
* selects the default system color palette into
* the PS
*/
GpiSelectPalette( hpsMemory, NULLHANDLE );
hpalOld = GpiSelectPalette( hps, NULLHANDLE );
if ( hpalOld && ( hpalOld != (HPAL) PAL_ERROR )){
/* ---- reset the default palette ---- */
/*
* Note that this is a trick. Calling WinRealizePalette()
* three times with a default palette loaded will trigger
* a reset (most of the time) of the hardware palette.
*/
WinRealizePalette( hwnd, hps, &ulPhysColorsRemapped );
WinRealizePalette( hwnd, hps, &ulPhysColorsRemapped );
WinRealizePalette( hwnd, hps, &ulPhysColorsRemapped );
GpiDeletePalette( hpalOld );
}
/* ---- select new palette into memory PS ---- */
GpiSelectPalette( hpsMemory, bmpSpecs.hpal ) ;
/* ------ set the bitmap into the memory PS ---- */
hbmOld = GpiSetBitmap( hpsMemory, hbitmap );
if ( hbmOld != HBM_ERROR ){
bBitmapLoaded = TRUE ;
/* ---- clean up old bitmap, if any ------ */
if ( hbmOld ){
GpiDeleteBitmap( hbmOld );
}
/* -- select new palette into display PS -- */
GpiSelectPalette( hps, bmpSpecs.hpal ) ;
/* --- realize the new palette for display --- */
WinRealizePalette( hwnd, hps, &ulPhysColorsRemapped );
/* -------- adjust window for image ---------- */
SizeAndPosWinForImage( hwndFrame, bmpSpecs.cx, bmpSpecs.cy );
/* --------- set window title ------------- */
SetWindowTitle( hwndFrame, pszFileSpec, &bmpSpecs );
} // end of if ( hbmOld != HBM_ERROR )
else {
bBitmapLoaded = FALSE ;
/* --------- clear window title ------------- */
SetWindowTitle( hwndFrame, (PSZ)NULL, (PBMPSPECS)NULL );
ShowErrorWindow( "Error setting new bitmap in PS !!", TRUE );
GpiDeleteBitmap( hbitmap );
} // end of else ( hbmOld != HBM_ERROR )
} // end of if ( hBitmap )
WinSetPointer( HWND_DESKTOP, hptrCurrent );
} // end of if ( pszFileSpec )
/* ------ make sure we repaint window ------- */
WinInvalidateRect( hwnd, (PRECTL)NULL, FALSE );
} // end of case UWM_LOAD_BITMAP
break;
/* ============================================================== */
/* */
/* WM_REALIZEPALETTE */
/* */
/* This message is sent to a window whenever it is receiving */
/* focus, so that it may realize any custom palette it owns. */
/* It also allows the app to repaint to take advantage of the */
/* new palette. */
/* */
/* This message is also sent whenever another app has */
/* realized it's custom palette, causing changes to be made */
/* in the physical color palette. The message allows a window */
/* to make it's palette know to the system, so that the system */
/* may arbitrate among the palette requests. */
/* */
/* Since this application can optionally use color palettes or */
/* not, it first checks to see if a custom color palette is */
/* being used. If not, then the default window proc is called. */
/* Otherwise, a call is made to WinRealizePalette(). This call */
/* returns the number of physical indices that were remapped. */
/* The basic rule of thumb is that if this returned value is */
/* greater than 0 (i.e. there was remapping), then the window */
/* should be repainted to automatically take best advantage */
/* of the newly remapped colors. */
/* */
/* ============================================================== */
case WM_REALIZEPALETTE :
{
ULONG ulPhysColorsRemapped ;
/* ------ are we currently using a palette -------- */
if ( bmpSpecs.hpal ){
/* --------- if so, then realize it -------------- */
if ( WinRealizePalette( hwnd, hps, &ulPhysColorsRemapped ) > 0 ){
/* ----- trigger a repaint of the window ------- */
WinInvalidateRect( hwnd, (PRECTL)NULL, FALSE );
}
} // end of if ( bUsePaletteManager )
else {
return( WinDefWindowProc(hwnd,msg,mp1,mp2) );
}
} // end of case WM_REALIZEPALETTE
break;
/* ============================================================== */
/* */
/* WM_PAINT */
/* */
/* All the painting of the window is handled during this */
/* message. */
/* */
/* If a bitmap isn't currently loaded, the window is filled */
/* with the pattern set during WM_CREATE. */
/* */
/* If a bitmap is currently loaded, a check is first made to */
/* see if there is any portion of the window that will not */
/* be covered by the bitmap (i.e. the client area is larger */
/* than the bitmap). If so, then this is filled in with the */
/* pattern. The algorithm for this is very simplistic, but */
/* works well if the window is larger in one direction only; */
/* for example, if the user sized the window wider than the */
/* image so that a particularly long title bar would be */
/* entirely displayed. If the window is both wider and taller */
/* than the image, the simplistic algorithm will cause the */
/* bitmap to be repainted in it's entirety. */
/* */
/* To paint the bitmap, the image is blitted directly from */
/* the memory PS to the display PS. The original size of the */
/* bitmap is maintained. The blit neither stretchs nor */
/* compresses. */
/* */
/* It is important to notice that the persistent display PS */
/* is passed into WinBeginPaint() so that a cached-micro PS */
/* is *not* returned or used. */
/* */
/* ============================================================== */
case WM_PAINT:
{
RECTL rectl ;
WinBeginPaint( hwnd, hps, &rectl );
/* ------ check if a bitmap is ready for painting ------- */
if ( bBitmapLoaded ){
POINTL apointl[3] ;
/* --- if window is larger than bitmap, fill in gap ---- */
if ( (rectl.xRight > bmpSpecs.cx) || (rectl.yTop > bmpSpecs.cy)){
RECTL rectlDiff, rectlBMP ;
/*
* The proper way would be to work with height and width
* seperately, or to use regions, but that's
* more effort than is warranted for this simple sample
*/
WinSetRect( hab, &rectlBMP, 0, 0, bmpSpecs.cx, bmpSpecs.cy );
WinSubtractRect( hab, &rectlDiff, &rectl, &rectlBMP );
/* ----- fill in rect with pattern --------- */
WinDrawBorder( hps,
&rectlDiff,
0, 0,
CLR_WHITE, CLR_BLACK,
DB_INTERIOR );
}
/* ----- blit the bitmap into the window --------- */
apointl[0].x = apointl[2].x = rectl.xLeft ;
apointl[0].y = apointl[2].y = rectl.yBottom ;
apointl[1].x = rectl.xRight ;
apointl[1].y = rectl.yTop ;
GpiBitBlt( hps, hpsMemory, 3, apointl, ROP_SRCCOPY, BBO_IGNORE );
}
else {
/* -- no bitmap so just fill in blank w/ pattern -- */
WinDrawBorder( hps,
&rectl,
0, 0,
CLR_WHITE, CLR_BLACK,
DB_INTERIOR );
}
WinEndPaint( hps );
} // end of case WM_PAINT
break;
/* ============================================================== */
/* */
/* WM_CLOSE */
/* */
/* The app is being closed so make sure that all bitmap and */
/* palette resources are properly disposed of. The persistant */
/* PSs are also disposed of. */
/* */
/* It is critical to dispose of any custom color palettes. */
/* Color palettes are considered system global resources, and */
/* as such are not automatically disposed of when the app that */
/* created them ends. If they are not explicitly disposed of, */
/* they will remain in memory until the machine is rebooted. */
/* */
/* ============================================================== */
case WM_CLOSE :
/* ------------ clean up resources -------------- */
if ( hps ){
ULONG ulChanged ; // used by WinRealizePalette()
/* ----- un-select palette from PS ------ */
GpiSelectPalette( hps, NULLHANDLE );
/* ---- reset palette to default ----- */
WinRealizePalette( hwnd, hps, &ulChanged );
WinRealizePalette( hwnd, hps, &ulChanged );
WinRealizePalette( hwnd, hps, &ulChanged );
/* --------- clean up PS/DC ----------- */
/*
* Note that since DC opened with WinOpenWindowDC(), we don't
* need to do a DevCloseDC() as the DC will automatically
* be closed with the window
*/
GpiAssociate( hps, NULLHANDLE );
GpiDestroyPS( hps );
} // end of if ( hps )
if ( hpsMemory ){
HDC hdc ;
/* ----- delete the bitmap --------- */
GpiDeleteBitmap( GpiSetBitmap( hpsMemory, NULLHANDLE ) );
/* ----- un-select palette from PS ------ */
GpiSelectPalette( hpsMemory, NULLHANDLE );
/* ---- do standard cleanup of PS/DC ------ */
hdc = GpiQueryDevice( hpsMemory );
GpiAssociate( hpsMemory, NULLHANDLE );
DevCloseDC( hdc );
GpiDestroyPS( hpsMemory );
}
/* ----- delete the custom palette, if any ------ */
if ( bmpSpecs.hpal ){
GpiDeletePalette( bmpSpecs.hpal );
}
return( WinDefWindowProc(hwnd,msg,mp1,mp2));
default:
return( WinDefWindowProc(hwnd,msg,mp1,mp2));
} // end of switch ()
return( FALSE );
} // end of MainWindowProc
/* ********************************************************************** */
/* */
/* GetBMPFileName */
/* */
/* This function creates a standard Open File dialog to allow the */
/* user to select a bitmap file to display. Only single selection is */
/* allowed. Note that the returned path is maintained, so that the */
/* next file open initiates in the same directory. */
/* */
/* The file filter defaults to "*.BMP". A bitmap EA filter may */
/* also be used. */
/* */
/* ********************************************************************** */
PSZ
GetBMPFileName( PSZ pszDialogTitle )
{
PSZ pszReturn = (PSZ)NULL;
PCHAR pcName ;
/* ------ initialize the file dialog control info ------- */
fdFileDlg.cbSize = sizeof( FILEDLG );
fdFileDlg.fl = FDS_HELPBUTTON | FDS_CENTER | FDS_OPEN_DIALOG | FDS_INCLUDE_EAS;
fdFileDlg.pszTitle = pszDialogTitle ;
fdFileDlg.pfnDlgProc = (PFNWP) NULL ;
fdFileDlg.papszITypeList = (PAPSZ) apszEATypes ;
pcName = strrchr( fdFileDlg.szFullFile, '\\' );
if ( pcName ){
strcpy( ++pcName, "*.BMP" );
}
else {
strcpy( fdFileDlg.szFullFile, "*.BMP" );
}
/* ----- put up the standard Open File dialog -------- */
if ( WinFileDlg( HWND_DESKTOP, hwndClient, &fdFileDlg ) ){
/* -- if dialog not canceled, return the filespec -- */
if ( fdFileDlg.lReturn == DID_OK ){
pszReturn = (PSZ) fdFileDlg.szFullFile ;
} // end of if ( fdFileDlg.lReturn ==...
} // end of if ( WinFileDlg( ...
else {
ShowErrorWindow( "Error creating Open File Dialog window !!", TRUE );
}
return( pszReturn );
} // end of GetBMPFileName()
/* ********************************************************************** */
/* */
/* LoadBitmapFile */
/* */
/* This is the workhorse function. This function takes a given */
/* bitmap file name and "loads" the file, creating a bitmap. This */
/* function handles OS/2 1.x, OS/2 2.x, and Windows 3.x formats, in */
/* any color depth, compressed or uncompressed. Only single image */
/* bitmap files are supported. Multi-image bitmaps or bitmap arrays, */
/* pointers, icons, etc are not supported. I have attempted to */
/* make this function relatively generic so that it might be used */
/* directly in an app other than this one. */
/* */
/* The bitmap is created to be compatible with the HPS passed in. */
/* The HPS is meant to be a reference only, and it's intial state is */
/* stored and then restored so that it is unaffected by this function. */
/* */
/* A parmeter specifies whether the bitmap is to be created so that */
/* it's stored color infomation is preserved and used, or whether it */
/* is created so that it's colors are mapped to the current (default) */
/* system color palette. If the former is specified, then the bitmap */
/* color info is extracted from the bitmap, and a custom palette is */
/* created. This palette is then selected into the reference HPS */
/* *before* the bitmap is actually created. This step is essential, */
/* otherwise upon creation of the bitmap, it's colors will be remapped */
/* to the closest match in the default palette. */
/* */
/* Note that if the bitmap is a 24-bit, true-color format, a custom */
/* palette is *not* created, even if the option is specified. This */
/* is because true-color formats are not supported by palettes. */
/* */
/* If the bitmap is in OS/2 1.x (or Windows 1.x, 2.x) formats */
/* and a color bitmap is to be created, the color table info is */
/* converted from an RGB to an RGB2 structure format. When accessing */
/* fields within the bitmap structure, we must be careful of which */
/* format we are working with, since there are nasty field alignment */
/* problems between 1.x and 2.x formats. Fortunately GpiCreateBitmap() */
/* understands the different formats, so we don't have to convert */
/* all the bitmap data. */
/* */
/* For more information on bitmap and bitmap file formats, see the */
/* online PM Reference in the OS/2 Developers Toolkit. The subject is */
/* discussed under the section 'Related Information'. */
/* */
/* ********************************************************************** */
HBITMAP
LoadBitmapFile( HPS hpsCompat, PSZ pszFileSpec, BOOL bGetPalette, PBMPSPECS pbmpSpecs )
{
PBITMAPFILEHEADER2 pbfh2 ;
PBITMAPINFO2 pbi2 ;
HPAL hpalBitmap ;
LONG lSavePSId ;
HBITMAP hReturn = NULLHANDLE;
/* ----- save the state of the compatible PS ---------- */
/*
* in particular, we want to be sure that we save any
* color palette or color table info, as we may be
* altering it during this function.
*/
lSavePSId = GpiSavePS( hpsCompat );
/* ----- first, load the file into a memory buffer ----- */
pbfh2 = LoadBMPFileToMem( pszFileSpec );
if ( pbfh2 ){
/* ----- make sure we are dealing with a bitmap ---- */
if ( pbfh2->usType == BFT_BMAP ){
PRGB2 pRGB2 ;
BOOL bIs1xFormat ;
BOOL bIs24bitColor ;
LONG lColorCount ;
/* ---- get handle to bitmap info struct ------ */
pbi2 = (PBITMAPINFO2) &pbfh2->bmp2 ;
/* ---- is the bitmap 1.x or 2.x format ------- */
bIs1xFormat = ( pbi2->cbFix == sizeof( BITMAPINFOHEADER ));
/* ----- determine the color count for the bitmap ------- */
if ( bIs1xFormat ){
PBITMAPINFO pbi ;
/* ------ convert structure types -------- */
pbi = (PBITMAPINFO) pbi2 ;
/*
* lColorCount = color planes * ( 2**bitcount)
*/
lColorCount = (LONG)pbi->cPlanes * (LONG)(1<<pbi->cBitCount);
bIs24bitColor = ( pbi->cBitCount == 24 ) ;
} // end of if ( bIs1xFormat )
else {
/* ----- check if cclrUsed field is set -------- */
/*
* cclrUsed can specify that only a subset of the actual
* colors available are used. For example, an 8-bit bitmap
* has 256 colors available, but the bitmap may only use
* 64 of them. If this number is set, it specifies how
* many colors are actually defined in the bitmap color
* table.
*/
if (( pbi2->cbFix > FIELDOFFSET( BITMAPINFOHEADER2, cclrUsed )) &&
( pbi2->cclrUsed > 0 )){
lColorCount = pbi2->cclrUsed ;
}
else {
lColorCount = (LONG)pbi2->cPlanes * (LONG)(1<<pbi2->cBitCount);
}
bIs24bitColor = ( pbi2->cBitCount == 24 ) ;
} // end of else ( bIs1xFormat )
/* ---- if 24-bit color, skip any palette creation ---- */
if ( bGetPalette && bIs24bitColor ){
ShowErrorWindow( "Bitmap is 24-bit, true-color format. Custom palette will not be created !", FALSE );
bGetPalette = FALSE ;
}
/* ------ get color palette from bitmap --------- */
if ( bGetPalette ){
/* -- convert 1x color table (RGB) to 2x format (RGB2) -- */
if ( bIs1xFormat ){
pRGB2 = Convert1xColorTableTo2x( (PBITMAPINFO)pbi2, lColorCount );
} // end of if ( bIs1xFormat )
else {
/* ----- get pointer to bitmap color table (RGB2) ---- */
pRGB2 = ((PRGB2)((PBYTE)pbi2 + pbi2->cbFix)) ;
} // end of else ( bIs1xFormat )
/* --- create a custom color palette from color info --- */
hpalBitmap = GpiCreatePalette( hab,
LCOL_PURECOLOR,
LCOLF_CONSECRGB,
lColorCount,
(PULONG) pRGB2 );
if ( hpalBitmap == NULLHANDLE ){
ShowErrorWindow( "Error creating bitmap palette !!!", TRUE );
} // end of if ( hpalBitmap )
} // end of if ( bGetPalette )
else {
hpalBitmap = NULLHANDLE ; // will reset to default palette
}
/* -- set the palette into PS before bitmap creation -- */
if ( GpiSelectPalette( hpsCompat,
hpalBitmap ) == (HPAL) PAL_ERROR ){
ShowErrorWindow( "Error selecting palette into bitmap PS", TRUE );
}
/* ---- create a bitmap from file info ------- */
hReturn = GpiCreateBitmap( hpsCompat,
(PBITMAPINFOHEADER2)pbi2,
CBM_INIT,
(((PBYTE)pbfh2) + pbfh2->offBits),
pbi2 );
if ( hReturn != GPI_ERROR ){
/* ------- set return/output values ------- */
if ( bIs1xFormat ){
pbmpSpecs->cx = ((PBITMAPINFOHEADER)pbi2)->cx ;
pbmpSpecs->cy = ((PBITMAPINFOHEADER)pbi2)->cy ;
}
else {
pbmpSpecs->cx = pbi2->cx ;
pbmpSpecs->cy = pbi2->cy ;
}
pbmpSpecs->colorCount = lColorCount ;
pbmpSpecs->hpal = hpalBitmap ;
} // end of if ( hReturn != GPI_ERROR)
else {
ShowErrorWindow( "Error creating bitmap from file !!", TRUE );
GpiDeletePalette( hpalBitmap );
} // end of else ( hReturn != GPI_ERROR)
/* ----- if "converted" RGB table, then cleanup ----- */
if ( bIs1xFormat && bGetPalette && pRGB2 ){
free( (PVOID) pRGB2 );
} // end of if ( bIs1xFormat )
} // end of if ( pbfh2->usType ==...
else {
ShowErrorWindow( "The file is not a supported bitmap format! Only single image BMP files are supported.", FALSE );
} // end of else ( pbfh2->usType ==...
/* ------ release the memory file buffer ------- */
free( (PVOID) pbfh2 );
} // end of if ( pbfh2 )
GpiRestorePS( hpsCompat, lSavePSId );
return( hReturn );
} // end of LoadBitmapFile()
/* ********************************************************************** */
/* */
/* CreateMemoryPS */
/* */
/* This function creates and returns a micro Presentation Space */
/* associated with an OD_MEMORY device context. */
/* */
/* ********************************************************************** */
HPS
CreateMemoryPS( VOID )
{
HPS hpsReturn = NULLHANDLE;
SIZEL sizel = { 0, 0 };
HDC hdcMemory ;
hdcMemory = DevOpenDC( hab, OD_MEMORY, "*", 0, (PCHAR*)NULL, NULLHANDLE);
hpsReturn = GpiCreatePS( hab, hdcMemory, &sizel, GPIT_MICRO | GPIA_ASSOC | PU_PELS );
if ( hpsReturn == NULLHANDLE ){
DevCloseDC( hdcMemory );
ShowErrorWindow( "Error creating Memory PS !!", TRUE );
} /* end of else (hpsReturn ... */
return( hpsReturn );
} // end of CreateMemoryPS()
/* ********************************************************************** */
/* */
/* LoadBMPFileToMem */
/* */
/* This function loads the bitmap file data into an allocated */
/* memory buffer. It makes no attempt to interpret the data. */
/* */
/* ********************************************************************** */
PBITMAPFILEHEADER2
LoadBMPFileToMem( PSZ pszFileSpec )
{
PVOID pReturn ;
INT inFile ;
LONG lFileSize ;
/* ----- open the specified bitmap file ------- */
if ( (inFile = _open( pszFileSpec, O_RDONLY | O_BINARY, 0 )) != -1 ){
/* ------- determine the size of the file ------- */
lFileSize = _filelength( inFile ) ;
if ( lFileSize > 0 ){
/* ----- allocate buffer for whole file ------ */
pReturn = malloc( lFileSize ) ;
if ( pReturn ){
/* ----- read the file into the buffer ----- */
if ( lFileSize != _read( inFile, pReturn, lFileSize ) ){
ShowErrorWindow( "Error reading bitmap file !!", FALSE );
free( pReturn );
pReturn = (PVOID)NULL ;
} // end of if ( lFileSize != _read(...
} // end of if ( pReturn )
else{
ShowErrorWindow( "Error allocating bitmap file buffer !!", FALSE );
} // end of else ( pReturn )
} // end of if ( lFileSize > 0 )
else {
ShowErrorWindow( "Error determining size of bitmap file !!", FALSE );
} // end of else ( lFileSize > 0 )
_close( inFile );
} // end of if ((inFile =...
else {
CHAR acBuffer[256] ;
sprintf( acBuffer, "Error opening bitmap file '%.200s' !!", pszFileSpec );
ShowErrorWindow( acBuffer, FALSE );
} // end of else ((inFile =...
return( (PBITMAPFILEHEADER2) pReturn );
} // end of LoadBMPFileToMem()
/* ********************************************************************** */
/* */
/* Convert1xTo2xBitmapInfo */
/* */
/* This converts the color table info associated with a 1.x bitmap */
/* info structure to a 2.x compatible RGB2 array, storing the array */
/* in an allocated buffer. */
/* */
/* ********************************************************************** */
PRGB2
Convert1xColorTableTo2x( PBITMAPINFO pbi, LONG lColorCount )
{
PRGB2 pReturn ;
/* -- allocate memory for color table -- */
pReturn = (PRGB2)malloc( lColorCount * sizeof( RGB2) );
if ( pReturn ){
ULONG i ;
/* ------ move color info from 1.x struct to 2.x struct ------ */
for( i=0; i<lColorCount; i++ ){
pReturn[i].bRed = pbi->argbColor[i].bRed ;
pReturn[i].bGreen = pbi->argbColor[i].bGreen ;
pReturn[i].bBlue = pbi->argbColor[i].bBlue ;
pReturn[i].fcOptions = 0 ;
}
} // end of if ( pReturn )
else {
ShowErrorWindow( "Error allocating memory for bitmap conversion !!", FALSE );
} // end of else ( pReturn )
return ( pReturn );
} // end of Convert1xColorTableTo2x()
/* ********************************************************************** */
/* */
/* SizeAndPosWinForImage */
/* */
/* This function sizes the window to exactly fit the bitmap image */
/* and adjusts it's position, if necessary, so that the title bar */
/* remains visible on the desktop. */
/* */
/* ********************************************************************** */
VOID
SizeAndPosWinForImage( HWND hwndFrame, LONG lImageCX, LONG lImageCY )
{
RECTL rectlSize;
LONG lCX, lCY ;
LONG lScreenCX, lScreenCY ;
LONG lX, lY ;
SWP swp;
/* ---- calc frame size to fit image ----- */
WinSetRect( hab, &rectlSize, 0, 0, lImageCX, lImageCY );
WinCalcFrameRect( hwndFrame, &rectlSize, FALSE );
lCX = rectlSize.xRight - rectlSize.xLeft ;
lCY = rectlSize.yTop - rectlSize.yBottom ;
/* -- determine if window needs to be reposistion to fit screen -- */
lScreenCX = WinQuerySysValue( HWND_DESKTOP, SV_CXSCREEN );
lScreenCY = WinQuerySysValue( HWND_DESKTOP, SV_CYSCREEN );
WinQueryWindowPos( hwndFrame, &swp );
if ( (swp.x+lCX) > lScreenCX ){
lX = lScreenCX - lCX ;
}
else if (( swp.x < 0 ) && ( lCX <= lScreenCX )) {
lX = 0 ;
}
else {
lX = swp.x ;
}
if ( (swp.y+lCY) > lScreenCY ){
lY = lScreenCY - lCY ;
}
else if (( swp.y < 0 ) && ( lCY <= lScreenCY )) {
lY = 0 ;
}
else {
lY = swp.y ;
}
/* ---- resize and position the frame to new dimensions --- */
WinSetWindowPos( hwndFrame,
NULLHANDLE,
lX, lY,
lCX , lCY ,
SWP_MOVE | SWP_SIZE );
} // end of SizeAndPosWinForImage()
/* ********************************************************************** */
/* */
/* SetWindowTitle */
/* */
/* This function sets the title bar text for the application. When */
/* a bitmap is loaded, the title bar contains the name of the bitmap */
/* file, the height, the width, and the color depth of the image, and */
/* whether the image is using a custom (pal)ette or the (def)ault */
/* palette. */
/* */
/* If a NULL is passed in for either the filespec or the bitmap */
/* spec, the title bar is reset to display only the app name. */
/* */
/* The title bar has the format: */
/* */
/* ViewBMP: filename [ width x height x color depth - pal | def ] */
/* */
/* */
/* ********************************************************************** */
VOID
SetWindowTitle( HWND hwndFrame, PSZ pszFileSpec, PBMPSPECS pbmpSpecs )
{
CHAR acNewTitle[ 256 ];
CHAR acDrive[ _MAX_DRIVE ];
CHAR acDir[ _MAX_DIR ];
CHAR acFName[ _MAX_FNAME ];
CHAR acExt[ _MAX_EXT ];
if ( pszFileSpec && pbmpSpecs ){
/* ----- get the bitmap file name -------- */
_splitpath( pszFileSpec, acDrive, acDir, acFName, acExt );
/* ------ construct the title bar string ----- */
sprintf( acNewTitle, " %s : %.164s%.32s [%dx%dx%d%s-%s]",
szMainTitle,
acFName,
acExt,
pbmpSpecs->cx,
pbmpSpecs->cy,
((pbmpSpecs->colorCount != 16777216)?pbmpSpecs->colorCount:16),
((pbmpSpecs->colorCount != 16777216)?"":".7M"),
((pbmpSpecs->hpal)?"pal":"def") );
WinSetWindowText( hwndFrame, acNewTitle );
}
else {
/* ------ reset the title bar to app name only ------- */
WinSetWindowText( hwndFrame, szMainTitle );
}
} //end of SetWindowTitle()
/* ********************************************************************** */
/* */
/* AreColorPalettesSupported */
/* */
/* This function queries the display device to determine if color */
/* palettes are supported. */
/* */
/* ********************************************************************** */
BOOL
AreColorPalettesSupported( VOID )
{
HDC hdcScreen ;
LONG lCapsBitField ;
BOOL bReturn = FALSE ;
/* ----- get DC to check screen palette support ------ */
hdcScreen = WinOpenWindowDC( HWND_DESKTOP );
if ( hdcScreen == NULLHANDLE ){
ShowErrorWindow( "Error opening Screen device context!", TRUE );
} // end of if ( hdcScreen == NULLHANDLE )
else {
if ( ! DevQueryCaps( hdcScreen,
CAPS_ADDITIONAL_GRAPHICS,
1,
&lCapsBitField )){
ShowErrorWindow( "Error reading Screen device capabilities!", TRUE );
} // end of if ( !DevQueryCaps(...
else {
bReturn = ((lCapsBitField & CAPS_PALETTE_MANAGER ) == CAPS_PALETTE_MANAGER );
if ( !bReturn ){
ShowErrorWindow( "The current display configuration does not support Palette Management", FALSE );
}
} // end of else (! DevQueryCaps(...
} // end of else ( hdcScreen == NULLHANDLE )
return( bReturn );
} // end of AreColorPalettesSupported()
/* ********************************************************************** */
/* */
/* AboutDialogProc */
/* */
/* This is the window proc for the "About box". The only unique */
/* processing occurs during WM_INITDLG time, when it loads up the */
/* MLE with info about the app. The info is taken from a buffer */
/* defined in the header file 'AboutTxt.h' */
/* */
/* ********************************************************************** */
MRESULT EXPENTRY
AboutDialogProc( HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2 )
{
switch (msg) {
case WM_INITDLG :
{
IPT iptImport = 0;
LONG lStringSize ;
lStringSize = strlen( acAboutBuffer );
/* --- set the buffer for MLE importing --- */
WinSendDlgItemMsg( hwnd,
DID_ABOUT_MLE,
MLM_SETIMPORTEXPORT,
MPFROMP( (PVOID) acAboutBuffer ),
MPFROMLONG( lStringSize ));
WinSendDlgItemMsg( hwnd,
DID_ABOUT_MLE,
MLM_IMPORT,
MPFROMP( (PVOID) &iptImport ),
MPFROMLONG( lStringSize ));
} // end of case WM_INITDLG
break;
case WM_COMMAND :
switch ( SHORT1FROMMP( mp1 )){
case DID_OK :
WinDismissDlg( hwnd, DID_OK );
break;
} // end of switch
break;
default:
return( WinDefDlgProc(hwnd,msg,mp1,mp2));
} // end of switch ()
return( FALSE );
} // end of AboutDialogProc()
/* ********************************************************************** */
/* */
/* ShowErrorWindow */
/* */
/* This function displays a given string in a Message Box. It can */
/* optionally query and display last PM error that was generated. */
/* */
/* ********************************************************************** */
VOID
ShowErrorWindow( PSZ pszErrorMsg, BOOL bUseLastError )
{
HPOINTER hptrCurrent ;
CHAR acErrorBuffer[256] ;
if ( bUseLastError ) {
ERRORID errorID = WinGetLastError( hab );
sprintf( acErrorBuffer,
"%s \n(code = 0x%lX)",
pszErrorMsg,
(ULONG) errorID );
pszErrorMsg = (PSZ) acErrorBuffer ;
} /* end of if ( bUseLastError ) */
hptrCurrent = WinQueryPointer( HWND_DESKTOP );
WinMessageBox( HWND_DESKTOP,
HWND_DESKTOP,
pszErrorMsg ,
szErrorTitle ,
0,
MB_CUACRITICAL | MB_OK );
WinSetPointer( HWND_DESKTOP, hptrCurrent );
}
/* ********************************************************************** */
/* ********************************************************************** */