/******************   "IMSHOW.C" Source Code File  *************************/
/*                                                                         */
/* PROGRAM-NAME: IMSHOW                                                    */
/* -------------                                                           */
/*                                                                         */
/*                                                                         */
/* DEVELOPED BY:                                                           */
/* -------------                                                           */
/*  Martin Erzberger, 1989/93                                              */
/*                                                                         */
/*                                                                         */
/* PURPOSE OF THE APPLICATION:                                             */
/* ---------------------------                                             */
/*  - Display of pictures in the formats:                                  */
/*     im ("a general purpose image file format")                          */
/*     bmp (OS/2 and Windows 3 Bitmaps)                                    */
/*     GIF (Graphics Interchange Format)                                   */
/*                                                                         */
/*                                                                         */
/* VERSION: 2.02, 1/93                                                     */
/* --------                                                                */
/*                                                                         */
/*                                                                         */
/* PROGRAM HISTORY:                                                        */
/* ----------------                                                        */
/*  - 1.00, 12/89: First edition.                                          */
/*  - 1.10,  1/90: Added conversion routines,                              */
/*                 Supports new im library (0.93 beta), e.g.               */
/*                  - Better error management,                             */
/*                  - Can read compressed pictures,                        */
/*                  - More efficient storage of IMMAP pictures,            */
/*                 Userinterface now in english,                           */
/*                 Userinterface now SAA compliant.                        */
/*  - 1.20,  7/91: 'Realize' function of OS/2 1.2 supported,               */
/*                 Can use long filenames,                                 */
/*                 Can now also read Windows 3 bitmaps,                    */
/*                 Better error management in the conversion routines,     */
/*                 BMP2IM recognizes greyscale pictures.                   */
/*  - 2.00,  11/92: Version for OS/2 2.0,                                  */
/*                 im library now statically linked (it isn't reentrant    */
/*                  anyway),                                               */
/*                 Clipboard support gone,                                 */
/*                 Supports the palette manager instead of OS/2 1.2's      */
/*                   "realize" function,                                   */
/*                 Can now also read GIF pictures,                         */
/*  - 2.01,  11/92: Some bug fixes:                                        */
/*                 'ESC=Cancel' didn't work with GIF's,                    */
/*                 Better work around for 100% pictures,                   */
/*                 GIF loader a bit faster.                                */
/*  - 2.02,   1/93: Bug fix: Now works also on TSENG video cards,          */
/*                 '100% work around' isn't necessary anymore,             */
/*                 Comments translated to english.                         */
/*                                                                         */
/*                                                                         */
/*  SOURCEFILES:                                                           */
/*  ------------                                                           */
/*                                                                         */
/*                                                                         */
/*    IMSHOW.MAK     - For NMAKE utility                                   */
/*    IMSHOW.DEP     - For NMAKE utility (both generated by Workframe/2)   */
/*    IMSHOW.C       - Main source code                                    */
/*    IMSHOW.H       - Header file                                         */
/*    IMSHOW.ICO     - Icon                                                */
/*    IMSHOW.RC      - Ressources (menus, strings)                         */
/*    BMLIB.C        - Procedures for bitmap handling                      */
/*    BMLIB.H        - Header                                              */
/*    GIFLIB.C       - Procedures for GIF file handling                    */
/*    GIFLIB.C       - Header                                              */
/*    FILEDLG.C      - Procedures fr the "File Open" dialog               */
/*    IM.H           - Header for the im library                           */
/*                                                                         */
/*                                                                         */
/*                                                                         */
/*  USED LIBRARIES:                                                        */
/*  ---------------                                                        */
/*                                                                         */
/*    IMLIB.LIB      - im library                                          */
/*                                                                         */
/*  USED SOFTWARE:                                                         */
/*  --------------                                                         */
/*                                                                         */
/*    'im' Library                                                         */
/*    IBM Operating System/2, Version 2.0                                  */
/*    IBM C Set/2 Compiler                                                 */
/*    IBM Toolkit for OS/2 2.0                                             */
/*                                                                         */
/*  USED LITERATURE:                                                       */
/*  ----------------                                                       */
/*    IBM Operating System/2 1.2 Programming Tools and Information         */
/*     IBM Corporation, 1988,                                              */
/*                                                                         */
/*    Kerninghan, Brian W., Ritchie, Dennis M.:                            */
/*     "The C Programming Language", Second Edition,                       */
/*     Prentice Hall, 1988, 1978, ISBN: 0-13-110362-8                      */
/*                                                                         */
/*    Petzold Charles: "Programming the OS/2 Presentation Manager",        */
/*     Microsoft Press, 1989, ISBN: 1-55615-170-5                          */
/*                                                                         */
/*    Schildt Herbert: "OS/2 Programming: An Introduction",                */
/*     McGraw-Hill, 1988, ISBN: 0-07-881427-8                              */
/*                                                                         */
/*                                                                         */
/***************************************************************************/
#define INCL_WIN                       /* Window manager headerfiles       */
#define INCL_GPILOGCOLORTABLE          /* To use the palette manager       */
#define INCL_GPIBITMAPS                /* Header for bitmaps               */
#define INCL_GPIPRIMITIVES             /* Box, Line etc.                   */
#define INCL_GPICONTROL                /* For GpiResetPS                   */
#define INCL_BITMAPFILEFORMAT          /* Header for bitmapfiles           */
#define INCL_ERRORS                    /* Error definitions                */
#define INCL_DOSSEMAPHORES             /* Header for semaphore handling    */

#include <os2.h>                       /* Main header file                 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>                   

#include "imshow.h"                    /* General identifiers              */
#include "bmlib.h"                     /* Bitmap subroutines               */
#include "giflib.h"                    /* GIF subroutines                  */
#include "im.h"                        /* 'im' library header              */

/*#define DEBUG_NOPALETTE*/         /* To test a no palette manager system */


/***************************************************************************/
/* Variables to control Presentation Manager.                              */
/***************************************************************************/

HAB          hab;                       /* PM anchor block handler         */
HMQ          hmq;                       /* Message queue handler           */
HWND         hwndClient;                /* Client area window handler      */
HWND         hwndFrame;                 /* Frame window handler            */
HWND         hwndHscroll, hwndVscroll;  /* Scrollbar handlers              */
HWND         hwndDlg;                   /* Dialogbox handler               */
HPOINTER     hptrArrow;                 /* Arrow mousepointer              */
HPOINTER     hptrWait;                  /* Clock mousepointer              */
HDC          hdc = NULLHANDLE;          /* Screen devicecontext handler    */
HDC          hdcMem = NULLHANDLE;       /* Memory devicecontext handler    */
HPS          hps = NULLHANDLE;          /* Presentationspace handler       */
HPS          hpsMem = NULLHANDLE;       /* Memory pres. space handler      */
HBITMAP      hbm = NULLHANDLE;          /* Bitmap handler                  */
PLONG        plColTable;                /* Pointer to colormap             */
PBITMAPINFO2 pbmiBitmap;                /* Pointer to bitmapinfo           */
HPAL         hpal = NULLHANDLE;         /* Palette handler                 */
ULONG    flCreate = FCF_TITLEBAR   |    /* Window control flags            */
                    FCF_SYSMENU    |
                    FCF_MENU       |
                    FCF_MINMAX     |
                    FCF_SIZEBORDER |
                    FCF_ACCELTABLE |
                    FCF_ICON       |
                    FCF_TASKLIST   |
                    FCF_SHELLPOSITION |
                    FCF_VERTSCROLL |
                    FCF_HORZSCROLL;
CHAR     titleBar[80] = "imShow 2.02";  /* Titlebar text                   */


/***************************************************************************/
/* Other control variables.                                                */
/***************************************************************************/

BOOL   fCmdLine = FALSE;                /* Command line argument there?    */
BOOL   fFill = TRUE;                    /* Background must be erased?      */
BOOL   bRealizeSupported;               /* Realizing supported?            */
BOOL   fRealized = FALSE;               /* Palette realized?               */
HEV    semInputReady;                   /* Loading thread can start        */
HEV    semPictureReady;                 /* Picture is ready to display     */
HEV    semHalt;                         /* Loading should stop             */
TID    tidLoad;                         /* Thread-id of the loading thread */
USHORT usMenuChecked = IDM_100;         /* The checked menu                */
LONG   lScaleFactor = 100;              /* Scale factor of the picture     */
ULONG  ulSemHelper;                     /* Dummy, used with semaphores     */


/***************************************************************************/
/* Variables of the application                                            */
/***************************************************************************/

USHORT   usType;                        /* Picture type                    */
USHORT   usStorage;                     /* Storage method                  */
USHORT   usSamples;                     /* Samples per pixel               */
USHORT   usDepth;                       /* Bitdepth per pixel              */
USHORT   usColors;                      /* Number of picture colors        */
ULONG    ulBytesPerLine;                /* Bitmap bytes per line           */
CHAR     szFileSource[80];              /* Picture filename                */
HFILE    hImage;                        /* Handle to GIF and BMP file      */
PBYTE    pbBitmapData = NULL;           /* Buffer for imagedata            */
IMAGE   *imBild;                        /* Handle to IM file               */
IMBYTE  *imbRowBuff = NULL;             /* Pointer to IM rowbuffer         */

ULONG  ulDisplayColors;                 /* Number of screen colors         */

ULONG  ulSizeX, ulSizeY;                /* Size of the picture             */
LONG   cxClient, cyClient;              /* Size of the client area         */
LONG   cxEdge, cyEdge;                  /* First visible picture dot       */
LONG   cxScaled, cyScaled;              /* Size of the scaled picture      */
LONG   cxCut, cyCut;                    /* Size of visible part            */
BOOL   fPicReadOK = FALSE;              /* Picture was read in OK?         */


/***************************************************************************/
/* Prototypes of functions.                                                */
/***************************************************************************/

INT              main (int argc, char *argv[]);
MRESULT EXPENTRY WinProc (HWND, USHORT, MPARAM, MPARAM);
VOID             Init (HWND);
VOID             Paint (HWND);
VOID             SetScrollBars (LONG);
BOOL             MakePalette (PBITMAPINFO2, PLONG *);
MRESULT EXPENTRY AboutDlgProc (HWND, USHORT, MPARAM, MPARAM);
MRESULT EXPENTRY CancelDlgProc (HWND, USHORT, MPARAM, MPARAM);
MRESULT EXPENTRY PropDlgProc (HWND, USHORT, MPARAM, MPARAM);
VOID             CentreDlgBox(HWND, HWND);
VOID             Help (HWND);
BOOL             OpenFile (HAB, HDC, HWND, HBITMAP *);
VOID             ReadFile (VOID* nichts);
VOID             ShowMsg (LONG, LONG, ULONG);
VOID             ImErrorMsg (int);
USHORT           GetFileType (CHAR *, HFILE *, IMAGE **);

extern BOOL GetFileName (CHAR *, HWND, HWND, BOOL);


/*************************  Start of main()  *******************************/
/*                                                                         */
/* main() must do three things:                                            */
/*  -Initialize PM (create the window, start threads etc.).                */
/*  -Dispatch the messages to the correct window.                          */
/*  -Release the ressources and close the application.                     */
/***************************************************************************/

INT main(int argc, char *argv[])
{
  QMSG qmsg;                            /* Message from message queue      */

 /*************************************************************************/
 /* Initialize the application and PM:                                    */
 /*************************************************************************/
  if (argc == 2) {                      /* Command line argument here?     */
    strcpy (szFileSource, argv[1]);     /* yup, copy it into szFileSource, */
    fCmdLine = TRUE;                    /* and set flag.                   */
  }

  hab = WinInitialize(0);               /* Init PM                         */

  hmq = WinCreateMsgQueue(hab, 0);      /* Create message queue            */

  WinRegisterClass(                     /* Register window class           */
                   hab,                 /* Anchor block handle             */
                   "IMSHOWWindow",      /* Classname                       */
                   (PFNWP)WinProc,      /* Address of window procedure     */
                   0,                   /* No Flags                        */
                   0                    /* No window words                 */
                  );

  hwndFrame = WinCreateStdWindow(       /* Create window                   */
               HWND_DESKTOP,            /* Desktop is parent               */
               WS_VISIBLE,              /* Window is visible               */
               (PULONG)&flCreate,       /* Frame control flags             */
               "IMSHOWWindow",          /* Classname                       */
               "",                      /* Set title later                 */
               0L,                      /* No window style                 */
               0,                       /* Ressources in EXE file          */
               ID_RESOURCE,             /* Resource ID                     */
               (PHWND)&hwndClient       /* client window handle            */
              );

  DosCreateEventSem (0, &semInputReady, 0, 0);   /* Init the semaphores    */
  DosCreateEventSem (0, &semPictureReady, 0, 0);
  DosCreateEventSem (0, &semHalt, 0, 1);

  /* We will need this all the time, so let's allocate it here: */
  pbmiBitmap = malloc (sizeof(BITMAPINFOHEADER2) + 256*sizeof(RGB2));

  _beginthread (ReadFile, 0, 8192, 0);           /* Start 2nd thread       */

 /*************************************************************************/
 /* Dispatching of messages:                                              */
 /*************************************************************************/
  while (WinGetMsg(hab, (PQMSG)&qmsg, (HWND)NULL, 0, 0))
    WinDispatchMsg(hab, (PQMSG)&qmsg);

 /*************************************************************************/
 /* Close application and release ressources:                             */
 /*************************************************************************/
  WinDestroyWindow(hwndFrame);          /* Kill window                     */
  WinDestroyMsgQueue(hmq);              /* Kill message queue              */
  WinTerminate(hab);                    /* And end...                      */
  return (0);
}
/**************************  End of main()  ********************************/



/********************  Start of the window procedure  **********************/
/*                                                                         */
/* The window procedure processes all messages from OS/2.                  */
/*                                                                         */
/***************************************************************************/

MRESULT EXPENTRY WinProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
{
  ULONG        ulTemp;

  switch(msg)                      /* In msg is the message                */
  {

    case WM_CREATE:                /* The window was just created          */
      Init(hwnd);                  /* --> call Init()                      */
      break;                       /* and break to default msg handler     */

    case WM_COMMAND:               /* One of the menus was selected        */
      switch (SHORT1FROMMP(mp1))   /* Which one is in mp1                  */
      {

        case IDM_OPEN:                              /* "File, open"        */
          OpenFile(hab, hdcMem, hwnd, &hbm);        /* --> call function   */
          break;

        case IDM_QUIT:              /* Application should end              */
          WinPostMsg(hwnd, WM_CLOSE, 0L, 0L);
          break;

        case IDM_ABOUT:            /* About dialog box                     */
          WinDlgBox(HWND_DESKTOP,  /* Desktop is parent (used for clipping)*/
                    hwnd,          /* hwnd gets the messages               */
                    (PFNWP)AboutDlgProc,  /* Dialog procedure              */
                    0,             /* Template is in EXE file              */
                    IDD_ABOUT,     /* ID of template                       */
                    NULL);         /* no more data                         */
          break;

        case IDM_FIT:                            /* All of the View xxx%   */
        case IDM_25:
        case IDM_50:
        case IDM_75:
        case IDM_100:
        case IDM_200:
        case IDM_400:
        case IDM_800:
          if (SHORT1FROMMP(mp1) == usMenuChecked)  /* Same as checked?     */
            break;
          switch (SHORT1FROMMP(mp1)) {             /* Set scale factor     */
            case IDM_FIT:
              lScaleFactor = 0;
              break;
            case IDM_25:
              lScaleFactor = 25;
              break;
            case IDM_50:
              lScaleFactor = 50;
              break;
            case IDM_75:
              lScaleFactor = 75;
              break;
            case IDM_100:
              lScaleFactor = 100;
              break;             
            case IDM_200:
              lScaleFactor = 200;
              break;
            case IDM_400:
              lScaleFactor = 400;
              break;
            case IDM_800:
              lScaleFactor = 800;
              break;
          }
                                                  /* Move the check mark   */
          WinCheckMenuItem (WinWindowFromID(hwndFrame, FID_MENU),
                            SHORT1FROMMP(mp1), TRUE);

          WinCheckMenuItem (WinWindowFromID(hwndFrame, FID_MENU),
                            usMenuChecked, FALSE);

          usMenuChecked = SHORT1FROMMP(mp1);      /* Set for next time     */

          SetScrollBars (lScaleFactor);           /* Init. scrollbars      */

          fFill = TRUE;                           /* Erase background      */
          WinInvalidateRect( hwnd, NULL, FALSE);  /* force redraw          */

          break;

        case IDM_PROP:             /* "Properties" dialog                  */
          WinDlgBox(HWND_DESKTOP,  /* Desktop is parent (user for clipping)*/
                    hwnd,          /* hwnd gets messages                   */
                    (PFNWP)PropDlgProc,   /* Dialog procedure              */
                    0,             /* Template is in EXE file              */
                    IDD_PROP,      /* ID of template                       */
                    NULL);         /* No more data                         */
          break;

        case IDM_HELPHELP:         /* "Help, Help for Help"                */
          Help(hwnd);              /* call Help()                          */
          break;

        default:                   /* Default: break out to default        */
          break;                   /* message processing                   */

      } /* End of "menu" switch */
      break;


    case WM_ERASEBACKGROUND:       /* Background has to be erased          */
      fFill = TRUE;                /* Set appropriate flag                 */
      return (MRESULT)(FALSE);     /* --> imShow will take care of it      */

    case WM_PAINT:                 /* A repaint is necessary               */
      Paint(hwnd);                 /*  --> call Paint()                    */
      break;

    case WM_SIZE:                  /* The window was resized               */
      cxClient = SHORT1FROMMP(mp2);
      cyClient = SHORT2FROMMP(mp2); /* Get new size                        */
                                                  /* Is a picture ready?   */
      if (DosWaitEventSem (semPictureReady, 0) != ERROR_TIMEOUT) {
        SetScrollBars (lScaleFactor);             /* Yes, set scrollbars,  */
        fFill = TRUE;                             /* erase background,     */
        WinInvalidateRect( hwnd, NULL, FALSE);    /* and force redraw      */
      }
      break;

    case WM_REALIZEPALETTE:        /* The palette has to be re-realized    */
      if (!fRealized)              /* If we don't have one currently:      */
        break;                     /* --> break to default                 */
      else
        if (WinRealizePalette (hwndClient, hps, &ulTemp)) /* Realize it    */
          WinInvalidateRect (hwnd, NULL, FALSE); /* and if necessary redraw*/
      return(0);

    case WM_VSCROLL:                /* Message from vertical scrollbar     */
      switch (SHORT2FROMMP (mp2)) { /* Message is in mp2                   */

        case SB_LINEUP:             /* Top arrow was clicked               */
          cyEdge--;                 /* Scroll one pixel                    */
          break;

        case SB_PAGEUP:             /* Click above slider                  */
          cyEdge -= cyCut;          /* Scroll one screen full              */
          break;

        case SB_PAGEDOWN:           /* Click below slider                  */
          cyEdge += cyCut;          /* Scroll one screen full              */
          break;

        case SB_LINEDOWN:           /* Bottom arrow was clicked            */
          cyEdge++;                 /* Scroll one pixel                    */
          break;

        case SB_SLIDERPOSITION:     /* Slider was dragged to the position  */
          cyEdge = SHORT1FROMMP (mp2);   /* given im mp2                   */
          break;                   

        default: return 0;          /* default: leave window procedure     */

      } /* End of vertical scrollbat switch                                */
        /* The following part is reached by all case's above (except the   */
        /* "default" part.                                                 */

      cyEdge = max (0, cyEdge);               /* Reset position to a valid */
      cyEdge = min ((LONG)ulSizeY-cyCut, cyEdge); /* one                   */

            /* Set the slider to the new position: */
        WinSendMsg (hwndVscroll, SBM_SETPOS, MPFROMSHORT (cyEdge), NULL);
            /* And force a redraw: */
        WinInvalidateRect (hwnd, NULL, FALSE);

      break; /* Get out of WM_VSCROLL */

    case WM_HSCROLL:                  /* Message from horizontal scrollbar */
      switch (SHORT2FROMMP (mp2)) {   /* The above comments apply also here*/

        case SB_LINELEFT:              /* Left arrow                       */
          cxEdge--;
          break;

        case SB_PAGELEFT:              /* Left from slider                 */
          cxEdge -= cxCut;
          break;

        case SB_PAGERIGHT:             /* Right from slider                */
          cxEdge += cxCut;
          break;

        case SB_LINERIGHT:             /* Right arrow                      */
          cxEdge++;
          break;

        case SB_SLIDERPOSITION:        /* New position                     */
          cxEdge = SHORT1FROMMP (mp2);  
          break;

        default: return 0;             /* Leave window procedure           */

      } /* End of horizontal scrollbar switch */

      /* Again the common scrollbar part:                                  */

      cxEdge = max (0, cxEdge);             
      cxEdge = min ((LONG)ulSizeX-cxCut, cxEdge);

        WinSendMsg (hwndHscroll, SBM_SETPOS, MPFROMSHORT (cxEdge), NULL);

        WinInvalidateRect (hwnd, NULL, FALSE);

      break; /* Leave VM_HSCROLL */

    default:      /* Default: Break to default window procedure            */
      break;

  } /* End of window procedure switch */

  /* Most of the messages are now also passed to the default window        */
  /* procedure. Of course, also all the not-processed messages get passed  */
  /* (by the default: break statement above).                              */

  return WinDefWindowProc( hwnd, msg, mp1, mp2 );
}
/**********************  End of the window procedure  **********************/



/**************************  Start of Init()  ******************************/
/*                                                                         */
/*  Initializations of the window.                                         */
/*                                                                         */
/*  The initializations for the window must be done here, since only at    */
/*  this place the window is already created, and one can ask e.g. the     */
/*  size of it.                                                            */
/*                                                                         */
/***************************************************************************/

VOID Init(HWND hwnd)    /* We only need hwnd from the WM_CREATE message.   */
{
  SIZEL sizlTemp;
  LONG  plTempArray[1];

  DosResetEventSem (semPictureReady, &ulSemHelper); /* Picture not ready   */

  /* We will need the scroll bars a lot, so we will get their ID's at this */
  /* place and put them in a global variable. Because hwndFrame isn't      */
  /* known yet, the somewhat complicated methot with QW_PARENT is used.    */
  hwndHscroll = WinWindowFromID
                 (WinQueryWindow (hwnd, QW_PARENT), FID_HORZSCROLL);
  hwndVscroll = WinWindowFromID
                 (WinQueryWindow (hwnd, QW_PARENT), FID_VERTSCROLL);

  /* We will also need the arrow and the clock pointer, so                 */
  /* we get their ID's also                                                */
  hptrArrow = WinQuerySysPointer (HWND_DESKTOP, SPTR_ARROW, FALSE);
  hptrWait  = WinQuerySysPointer (HWND_DESKTOP, SPTR_WAIT, FALSE);

  /* Now we set the titlebar text:                                         */
  WinSetWindowText (WinQueryWindow (hwnd, QW_PARENT), titleBar);

  /* The following gets the size of a maximized window:                    */
  sizlTemp.cx = (LONG)((WinQuerySysValue (HWND_DESKTOP, SV_CXFULLSCREEN)-
                        WinQuerySysValue (HWND_DESKTOP, SV_CXVSCROLL) + 1));
  sizlTemp.cy = (LONG)((WinQuerySysValue (HWND_DESKTOP, SV_CYFULLSCREEN)-
                        WinQuerySysValue (HWND_DESKTOP, SV_CYMENU)-
                        WinQuerySysValue (HWND_DESKTOP, SV_CYHSCROLL) + 1));

  /* Now we open a window device context to get some device capabilities: */
  hdc = WinOpenWindowDC (hwnd);  
    if (hdc == (HDC)NULL) exit(1);  /* Again, not very nice...            */

  DevQueryCaps (hdc, CAPS_COLORS, 1L, plTempArray);
  ulDisplayColors = (ULONG)plTempArray[0]; /* Number of display colors    */

  /* Is the palette manager supported?                                    */
  DevQueryCaps (hdc, CAPS_ADDITIONAL_GRAPHICS, 1L, plTempArray);
  bRealizeSupported = (BOOL)((plTempArray[0] & CAPS_PALETTE_MANAGER));

#ifdef DEBUG_NOPALETTE
  bRealizeSupported = FALSE;
#endif

  /* Now we create a presentation space (we will keep this one until      */
  /* the application ends).                                               */
  hps = GpiCreatePS (hab, hdc, &sizlTemp,
                     GPIA_ASSOC | PU_PELS | GPIF_DEFAULT);
  if (hps == (HPS)NULL) exit(1);

  /* Is there a file with the name specified on the command line?          */
  if (fCmdLine) {                         /* Was there an argument?        */
    if (fopen (szFileSource, "rb") == NULL) /* Try to open the file        */
      fCmdLine = FALSE;                   /* Nope, --> clear flag          */
    else {
      _fcloseall();                       /* Yes: Close file again         */
                                          /* and simulate a menu choice:   */
      WinPostMsg (hwndFrame, WM_COMMAND, MPFROMSHORT(IDM_OPEN), NULL);
    } /* End if (fopen...) */
  } /* End if (FCmdLine) */
} /* End Init() */

/***************************  End of Init()  *******************************/



/**************************  Start of Paint()  *****************************/
/*                                                                         */
/*  Paint() processes the WM_PAINT messages of OS/2 PM.                    */
/*                                                                         */
/*  We always get a WM_PAINT message if parts of the client area become    */
/*  invalid, e.g. by hiding and unhiding it by another window.             */
/*                                                                         */
/*  One can force a WM_PAINT message by declaring the window as invalid    */
/*  with the WinInvalidateRect call.                                       */
/*                                                                         */
/***************************************************************************/

VOID       Paint(HWND hwnd)  /* We only need the window handler            */
                             /* mp1 and mp2 are NULL anyway...             */
{
  RECTL rc;                  /* Update region.                             */
  RECTL rcSource, rcDest;    /* Source and target rectangles for bitmap    */


  WinQueryUpdateRect (hwnd, &rc); /* Get the update region                 */


/***************************************************************************/
/* We don't need the semaphore below for synchronisation, we just want     */
/* to know if there is a picture ready to display. We get this information */
/* by setting a timeout of 0, and then asking wether there was a timeout   */
/* or not.                                                                 */
/* If the semaphore was busy, then there is no picture ready yet, and we   */
/* simply draw the whole client area black.                                */
/***************************************************************************/
  if (DosWaitEventSem (semPictureReady, 0L) == ERROR_TIMEOUT)
    WinFillRect (hps, &rc, CLR_BLACK);

  else {                                /* Ok, there is a picture          */
    if (fFill)                          /* Do we have to reset the back-   */
                                        /* ground first?                   */
      WinFillRect (hps, &rc, CLR_BLACK);/* Yes: Fill it black              */

    fFill = FALSE;                     /* Reset flag                       */

    /* Next, we have to state which part of the bitmap we want to copy     */
    /* in what rectangle:                                                  */
    rcDest.xLeft = 0;                  /* Dest., bottom left               */
    rcDest.yBottom = 0;                /*                                  */

    if (cxCut > (LONG)ulSizeX) {       /* Picture is slimmer than window:  */
      rcDest.xRight = cxScaled;        /* Destination, right               */
      rcSource.xLeft = 0;              /* Source, left                     */
      rcSource.xRight = (LONG)ulSizeX; /* Source, right                    */
    } else {                           /* Picture is broader than window:  */
    rcDest.xRight = cxClient;          /* Destination, right:              */
    rcSource.xLeft = cxEdge;           /* Source, left                     */
    rcSource.xRight = cxCut + cxEdge;  /* Source, right                    */
    }

    if (cyCut > (LONG)ulSizeY) {       /* Picture is less high than window */
      rcDest.yTop = cyScaled;          /* Destination, top                 */
      rcSource.yBottom = 0;            /* Source, bottom                   */
      rcSource.yTop = (LONG)ulSizeY;   /* Source, top                      */
    } else {                           /* Picture is higher than window:   */
    rcDest.yTop = cyClient;            /* Destination, top                 */
    rcSource.yBottom = cyEdge;         /* Source, bottom                   */
    rcSource.yTop = cyCut + cyEdge;    /* Source, top                      */
    }

  /* The copy can take a while, so let's set the clock pointer:            */
  WinSetPointer (HWND_DESKTOP, hptrWait);

  WinDrawBitmap(hps,                      /* Draw the bitmap...            */
                hbm,                      /* From hbm to hps               */
                &rcSource,
                (PPOINTL)&rcDest,
                0,
                1,
                DBM_NORMAL | DBM_STRETCH  /* Stretch the bitmap            */
               );

  WinSetPointer (HWND_DESKTOP, hptrArrow); /* Reset the pointer again..    */

  }  /* End if */

  WinValidateRect (hwnd, &rc, FALSE); /* Window is now valid again.        */
}
/***************************  End of Paint()  ******************************/



/***********************  Start of SetScrollBars()  ************************/
/*                                                                         */
/* SetScrollBars() initializes the scrollbars after a new file was loaded, */
/* after a WM_SIZE message or when the scale rate was changed.             */
/*                                                                         */
/***************************************************************************/

VOID SetScrollBars(LONG lScaleFactor)
{
  cxEdge = 0;       /* Reset the bottom left corner of the visible         */
  cyEdge = 0;       /* bitmap                                              */


  if (ulSizeX == 0 || ulSizeY == 0) {           /* Wrong picture??         */
     cxScaled = 0;
     cyScaled = 0;
     cxCut = 0;
     cyCut = 0;
     ulSizeX = 0;
     ulSizeY = 0;
  } else {                                     /* Picture ok               */
    if (lScaleFactor == 0) {                   /* i.e. "fit in window"     */
      cyScaled = (LONG)ulSizeY * cxClient / (LONG)ulSizeX; /* Try to fit y */
      if (cyScaled > cyClient) {               /* Too big?                 */
        cyScaled = cyClient;                   /* let y be,                */
        cxScaled = (LONG)ulSizeX * cyClient / (LONG)ulSizeY; /* and fit x  */
      } else                                   /* fit y,                   */
        cxScaled = cxClient;                   /* and let x be             */
    } else {                                   /* other scale factor       */
      cxScaled = (lScaleFactor * (LONG)ulSizeX) / 100; /* scale x,         */
      cyScaled = (lScaleFactor * (LONG)ulSizeY) / 100; /* and y            */
    } /* End if lScaleFactor == 0 */

    /* Now we have to calculate the amount of pixels that can be seen at   */
    /* once. So if e.g. cxCut results to 30, 30 pixels of the source       */
    /* bitmap data will fit in the x direction. One could also say, cxCut  */
    /* is one "page" of the bitmap. If the window is 120 pixels            */
    /* wide, the image is 240 pixels wide, and the scale factor is 200%,   */
    /* then 30 pixels will fit. Easy, isn't it?                            */
    if (lScaleFactor == 0) { /* Again: option "fit in window"              */
      cxCut = (LONG)ulSizeX * cxClient / cxScaled; 
      cyCut = (LONG)ulSizeY * cyClient / cyScaled;
    } else {                                     /* Other scale factor:    */
      cxCut = (100 * cxClient) / lScaleFactor;   /* Obvious                */
      cyCut = (100 * cyClient) / lScaleFactor;
    } /* End if lScaleFactor == 0 */
  } /* End if Picture ok? */


  if (cxClient >= cxScaled)                  /* Window bigger than picture */
    WinEnableWindow (hwndHscroll, FALSE);    /* -> Switch off scrollbar    */
  else {                                     /* Picture bigger than window */
    WinEnableWindow (hwndHscroll, TRUE);     /* -> Switch on scrollbar     */

  /* Now we set the endpoints of the scrollbar. The value range goes from  */
  /* 0 to ulSizeX - cxCut. Why this? With 0, one can see the first cxCut   */
  /* pixels, and with ulSizeX-cxCut, one can see the last cxCut pixels...  */
  /* To go higher wouldn't make sense, one would have to put a blank bar   */
  /* to the left of the picture.                                           */
    WinSendMsg (hwndHscroll, SBM_SETSCROLLBAR, /* Set endpoints of scroll- */
                MPFROM2SHORT (0, 0),           /* bar and put slider on    */
                MPFROM2SHORT (0, (LONG)ulSizeX - cxCut));  /* position 0   */
  /* Then we set the slider size: It is set by giving the two values       */
  /* "visible part" and "total part", which is cxCut and ulSizeX here.     */
    WinSendMsg (hwndHscroll, SBM_SETTHUMBSIZE, /* Set the size of the      */
                MPFROM2SHORT (cxCut, ulSizeX), /* slider                   */
                MPFROM2SHORT (0,0));
  } /* End if window bigger as picture */

  if (cyClient >= cyScaled)                  /* Same stuff for the         */
    WinEnableWindow (hwndVscroll, FALSE);    /* vertical scrollbar         */
  else {
    WinEnableWindow (hwndVscroll, TRUE);
    WinSendMsg (hwndVscroll, SBM_SETSCROLLBAR,
                MPFROM2SHORT (0, 0),
                MPFROM2SHORT (0, (LONG)ulSizeY - cyCut));
    WinSendMsg (hwndVscroll, SBM_SETTHUMBSIZE,
                MPFROM2SHORT (cyCut, ulSizeY),
                MPFROM2SHORT (0,0));
  }
}

/*************************  End of SetScrollBars()  ************************/



/************************  Start of MakePalette()  *************************/
/*                                                                         */
/* MakePalette creates the palette.                                        */
/*                                                                         */
/***************************************************************************/

BOOL MakePalette (PBITMAPINFO2 pbmi, PLONG *plTable)
{
  ULONG i, ulTemp;

  switch (pbmi->cBitCount) {     /* Get the size of the table              */

    case 8:                      /* 8 bits, --> 256 color entries          */
      *plTable = malloc (256 * sizeof (LONG));
      break;

    case 4:                      /* 4 bits, --> 16 color entries           */
      *plTable = malloc (16 * sizeof (LONG));
      break;

    default:                     /* other value shouldn't appear...        */
      return FALSE;
  } /* Ende switch */

  if (*plTable == NULL)
    exit(1);

  for (i=0; i<usColors; i++) {   /* Fill the table with the colors         */
    (*plTable)[i] = (pbmi->argbColor[i].bRed   * 65536L +
                     pbmi->argbColor[i].bGreen *   256L +
                     pbmi->argbColor[i].bBlue);
  }
  hpal = GpiCreatePalette (hab, 0, LCOLF_CONSECRGB, /* Create palette      */
                           (LONG)usColors, (PULONG)*plTable);
  free (plTable);
  GpiSelectPalette (hps, hpal);                 /* Select palette into hps */
  WinRealizePalette (hwndClient, hps, &ulTemp); /* and realize it          */
  fRealized = TRUE;                             /* set flag                */
  return TRUE;
}
/**************************  End of MakePalette()  *************************/



/****************  Start of the About Box window procedure  ****************/
/*                                                                         */
/* Handles the messages to the "About" box.                                */
/*                                                                         */
/***************************************************************************/

MRESULT EXPENTRY AboutDlgProc(HWND hwndDlg, USHORT msg,
                              MPARAM mp1, MPARAM mp2)
{
  switch(msg)
  {
    case WM_INITDLG:         /* On init, the box will be centered.         */
      CentreDlgBox (hwndDlg, hwndClient);
      break;

    default: break;
  }
  return WinDefDlgProc(hwndDlg, msg, mp1, mp2);
}
/*****************  End of the About Box window procedure  *****************/



/***************  Start of the Cancel Box window procedure  ****************/
/*                                                                         */
/* Processes the messages to the "Cancel" box.                             */
/*                                                                         */
/* This dialog box appears while loading a picture, and gives the option   */
/* to cancel the loading procedure.                                        */
/*                                                                         */
/***************************************************************************/

MRESULT EXPENTRY CancelDlgProc(HWND hwndDlg, USHORT msg,
                               MPARAM mp1, MPARAM mp2)
{
  switch(msg) {

  case WM_INITDLG:
    /* Set " % loaded" to 0:                                               */
    WinSetDlgItemShort (hwndDlg, IDD_PERCENT, 0, FALSE);
    CentreDlgBox (hwndDlg, hwndClient);  /* And center the dialog box      */
    break;

  case WM_COMMAND:
    switch(COMMANDMSG(&msg)->cmd) {
    case DID_CANCEL:          /* User wants to cancel the loading process  */
      DosResetEventSem (semHalt, &ulSemHelper); /* --> inform the thread   */
      return 0;
    }
    break;

  case WM_PROGRESS:
    /* The percent field must be updated                                   */
    WinSetDlgItemShort (hwndDlg, IDD_PERCENT, SHORT1FROMMP(mp1), FALSE);
    break;

  case WM_FINISHED:             /* The loading thread is finished          */
    WinDismissDlg(hwndDlg, TRUE); /* --> End the dialog                    */
    return 0;
  }
  return WinDefDlgProc(hwndDlg, msg, mp1, mp2);
}
/****************  End of the Cancel Box window procedure  *****************/



/****************  Start of the Param Box window procedure  ****************/
/*                                                                         */
/* Processes the messages to the "Param" dialog box.                       */
/*                                                                         */
/* The "Param" box displays the file properties.                           */
/*                                                                         */
/***************************************************************************/

MRESULT EXPENTRY PropDlgProc(HWND hwndDlg, USHORT msg,
                             MPARAM mp1, MPARAM mp2)

{
  switch(msg) {
  case WM_INITDLG:
    CentreDlgBox (hwndDlg, hwndClient); /* Centre box on init              */

    switch (usStorage) {  /* Set storage type                              */
    case IMRAW:
      WinSetDlgItemText (hwndDlg, IDD_STORAGE, "IMRAW");
      break;

    case IMRLE:
      WinSetDlgItemText (hwndDlg, IDD_STORAGE, "IMRLE");
      break;

    case IMLZW:
      WinSetDlgItemText (hwndDlg, IDD_STORAGE, "IMLZW");
      break;

    default:
      WinSetDlgItemText (hwndDlg, IDD_STORAGE, "?");
    }

    switch (usType) {                   /* Set file type                   */
    case OS2_101:
      WinSetDlgItemText (hwndDlg, IDD_TYPE, "OS/2, 1.1");
      WinSetDlgItemText (hwndDlg, IDD_STORAGE, "RAW");
      break;

    case WIN_3:
      WinSetDlgItemText (hwndDlg, IDD_TYPE, "WIN 3");
      WinSetDlgItemText (hwndDlg, IDD_STORAGE, "RAW");
      break;

    case GIF:
      WinSetDlgItemText (hwndDlg, IDD_TYPE, "GIF");
      WinSetDlgItemText (hwndDlg, IDD_STORAGE, "GIF");
      break;

    case OS2_200:
      WinSetDlgItemText (hwndDlg, IDD_TYPE, "OS/2, 2.0");

      switch (usStorage) {
      case BCA_UNCOMP:
        WinSetDlgItemText (hwndDlg, IDD_STORAGE, "RAW");
        break;

      case BCA_HUFFMAN1D:
        WinSetDlgItemText (hwndDlg, IDD_STORAGE, "HUFFM");
        break;

      case BCA_RLE4:
        WinSetDlgItemText (hwndDlg, IDD_STORAGE, "RLE4");
        break;

      case BCA_RLE8:
        WinSetDlgItemText (hwndDlg, IDD_STORAGE, "RLE8");
        break;

      case BCA_RLE24:
        WinSetDlgItemText (hwndDlg, IDD_STORAGE, "RLE24");
        break;

      default:
        WinSetDlgItemText (hwndDlg, IDD_STORAGE, "?");
      } /* endswitch */
      break;

    case IMGRY:
      WinSetDlgItemText (hwndDlg, IDD_TYPE, "IMGRY");
      break;

    case IMRGB:
      WinSetDlgItemText (hwndDlg, IDD_TYPE, "IMRGB");
      break;

    case IMMAP:
      WinSetDlgItemText (hwndDlg, IDD_TYPE, "IMMAP");
      break;

    case IMFLT:
      WinSetDlgItemText (hwndDlg, IDD_TYPE, "IMFLT");
      break;

    default:
      WinSetDlgItemText (hwndDlg, IDD_TYPE, "?");
    }



  WinSetDlgItemShort (hwndDlg, IDD_SIZEX,   (USHORT)ulSizeX, FALSE);
  WinSetDlgItemShort (hwndDlg, IDD_SIZEY,   (USHORT)ulSizeY, FALSE);
  WinSetDlgItemShort (hwndDlg, IDD_SAMPLES, usSamples,       FALSE);
  WinSetDlgItemShort (hwndDlg, IDD_DEPTH,   usDepth,         FALSE);
  WinSetDlgItemShort (hwndDlg, IDD_COLORS,  usColors,        FALSE);

  break;

  default: break;

  }
  return WinDefDlgProc(hwndDlg, msg, mp1, mp2);
}
/*****************  End of the Param Box window procedure  *****************/




/***********************  Start of CentreDlgBox()  *************************/
/*                                                                         */
/*  CentreDlgBox places the given dialog box in the center of the          */
/*  stated window.                                                         */
/*                                                                         */
/***************************************************************************/

VOID CentreDlgBox(HWND hwndDlg, HWND hwndParent)
{
  LONG  ix, iy;                     /* New position of the dialog box      */
  LONG  iwidth, idepth;             /* Size of the display                 */
  SWP   swp;                        /* Window informations                 */
  RECTL rect;                       /* Size of the window                  */

  /*************************************************************************/
  /* Get size of display                                                   */
  /*************************************************************************/
  iwidth = (SHORT)WinQuerySysValue (HWND_DESKTOP, SV_CXSCREEN );
  idepth = (SHORT)WinQuerySysValue (HWND_DESKTOP, SV_CYSCREEN );

  /*************************************************************************/
  /* Get size of window and norm it to screen                              */
  /*************************************************************************/
  WinQueryWindowRect (hwndParent, &rect);
  WinMapWindowPoints (hwndParent, HWND_DESKTOP, (PPOINTL)&rect, 2);

  /*************************************************************************/
  /* Get window informations of the dialog box                             */
  /*************************************************************************/
  WinQueryWindowPos (hwndDlg, (PSWP)&swp);

  /*************************************************************************/
  /* Calculate new position                                                */
  /*************************************************************************/
  ix = ((rect.xRight - rect.xLeft   - swp.cx) / 2) + rect.xLeft;
  iy = ((rect.yTop   - rect.yBottom - swp.cy) / 2) + rect.yBottom;

  /*************************************************************************/
  /* Make shure, that the box is totally visible                           */
  /*************************************************************************/
  ix = max (ix, 0);
  ix = min (ix, (iwidth - swp.cx));

  iy = max (iy, 0);
  iy = min (iy, (idepth - swp.cy));

  /*************************************************************************/
  /* Set the new position                                                  */
  /*************************************************************************/
  WinSetWindowPos( hwndDlg, HWND_TOP, ix, iy, 0, 0, SWP_MOVE );
}
/************************  End of CentreDlgBox()  **************************/



/***************************  Start of  Help()  ****************************/
/*                                                                         */
/*  There is no online help in this application (well, there's no offline  */
/*  help either...).                                                       */
/*  This function call merely gives this information.                      */
/*                                                                         */
/***************************************************************************/
VOID Help(HWND hwnd)
{
  char title[20];
  char help[200];

  WinLoadString(hab,             /* Get title of help box                  */
                0,
                IDS_HELPTITLE,
                20,
                title
               );
  WinLoadString(hab,             /* Get content string of help box         */
                0,
                IDS_HELP,
                200,
                help
               );
  WinMessageBox(HWND_DESKTOP,    /* Display help box                       */
                hwnd,
                help,
                title,
                0,
                MB_OK | MB_NOICON
               );
  /* Don't know how to remove the WC_SAVEBITS option, so the whole         */
  /* window has to be invalidated (see also imShow.rc)                     */
  WinInvalidateRect( hwnd, NULL, FALSE);       
}
/****************************  End of Help()  ******************************/



/**************************  Begin of OpenFile()  **************************/
/*                                                                         */
/* OpenFile() will be called when the user selects "File, open" or when    */
/* there was a valid command line argument.                                */
/*                                                                         */
/***************************************************************************/

BOOL OpenFile(HAB hab, HDC hdcMem, HWND hwnd, HBITMAP *hbm)
{
  ULONG       ulTemp;
  ULONG       ulIndex;
  LONG        lReturn;
  IMBYTE      *pimMap;
  SIZEL       size;                /* Size of presentation space           */


  if (fCmdLine) fCmdLine = FALSE;  /* Was there an argument on startup?    */
                                   /* No: show file dialogbox:             */
  else if (!GetFileName (szFileSource, HWND_DESKTOP, hwnd, TRUE))
         return 0;

  fFill = TRUE;                    /* Window has to be cleared afterwards  */

  usType = GetFileType (szFileSource, &hImage, &imBild); /* Get type       */

  /* The not used fields of pbmiBitmap MUST be 0, or OS/2 will choke       */
  memset (pbmiBitmap, 0, sizeof (BITMAPINFOHEADER2)+256*sizeof(RGB2));

  switch (usType) {
  case BITMAP:                     /* File is a bitmap                     */
    bmrhdr (hImage, pbmiBitmap);              /* Read in the header        */
  
    ulSizeX = pbmiBitmap->cx;      /* Copy some parameters into globals    */
    ulSizeY = pbmiBitmap->cy;
  
     /* Now distinguish the type even further:                             */
    if (pbmiBitmap->cbFix == 12)   /* Must be an OS/2 1.1 bitmap           */
      usType  = OS2_101;
    else if ((pbmiBitmap->cbFix < 41) &&  /* A Win 3 header is smaller     */
                                 /* and doesn't support compressed bitmaps */
             (pbmiBitmap->ulCompression == BCA_UNCOMP))
      usType = WIN_3;
    else usType = OS2_200;
    /* We stuff all bitmaps into a full header, leaving the not needed     */
    pbmiBitmap->cbFix = 64;        /* fields with a value of 0             */
    usStorage = (USHORT)pbmiBitmap->ulCompression; /* Copy more parameters */
    usDepth = pbmiBitmap->cBitCount;               /* as depth,            */
    usSamples = pbmiBitmap->cPlanes;               /* no. of planes,       */
    usColors = (USHORT)(1 << usDepth);             /* and no. of colors    */
    break;                                         /* finished with BMP's  */

  case GIF:                        /* File is a GIF                        */
    if ((lReturn = gifrhdr (hImage, pbmiBitmap)) == 0) { /* Get header     */
      ulSizeX = pbmiBitmap->cx;      /* Copy some parameters into globals  */
      ulSizeY = pbmiBitmap->cy;
      usStorage = 0;                 /* Not needed with GIF's              */
      usColors = (USHORT)(1 << pbmiBitmap->cBitCount); /* Get no. of colors*/
      switch (pbmiBitmap->cBitCount) { /* OS/2 needs bitcount of 1, 4, or 8*/
      case 2:
      case 3:
      case 4:
        pbmiBitmap->cBitCount = 4;
        break;
      case 5:
      case 6:
      case 7:
      case 8:
        pbmiBitmap->cBitCount = 8;
        break;
      } /* endswitch */
      usDepth = pbmiBitmap->cBitCount;
      usSamples = pbmiBitmap->cPlanes;
      if (usDepth != 24) { /* Don't know if there are 24 bit GIF's??       */
        pbmiBitmap->cclrUsed = usColors; /* Palette manager can maybe make */
        pbmiBitmap->cclrImportant = usColors; /* use of this fields        */
      } 
    } else { /* If we couldn't read the GIF header, display an error msg.  */
      ShowMsg (IDS_GIFERR, -lReturn, MB_ERROR);
      return 1;
    }
    break; /* Finished with GIF's */

  case IM:  /* File is an IM                                               */
    if (imrhdr (imBild)) {          /* Get header                          */
      ImErrorMsg (imerrnum);        /* If error, display message           */
      return 0;
    }
    ulSizeX = imgetxsize(imBild);   /* Get some properties of the picture  */
    ulSizeY = imgetysize(imBild);
    usType  = imgettype (imBild);
    if (usType == IMFLT) {          /* IMFLT can't be displayed            */
      ShowMsg (IDS_ERRTITLE, IDS_FLTERR, MB_ERROR);
      return 0;
    }
    usStorage = imgetstor (imBild); /* Get some more properties            */
    usDepth = imgetdepth (imBild);
    usSamples = imgetsamples (imBild);
    if (usType == IMGRY)            /* IMGRY gives wrong no. of colors     */
      usColors = (USHORT)(1 << usDepth);
    else
      usColors = (USHORT)imgetcolors (imBild);

                                /* Allocate row buffer                     */
    imbRowBuff = imrowalloc ((USHORT)ulSizeX, usSamples, usDepth);
    if (imbRowBuff == NULL) return 1;

    switch (usType) {  /* Further processing different for the IM types    */
    case IMRGB: /* IMRGB is a 24 bit, no colormapped, RGB picture          */
      /* Allocate room for IM header                                       */
      pbmiBitmap->cBitCount = 24;
      break;

    case IMGRY: /* IMGRY is a grayscale, with no explicit colormap         */
      if (usDepth < 3)                   /* Set bitdepth to 1,2,4 or 8     */
        pbmiBitmap->cBitCount = usDepth;
      else if (usDepth < 5)           
        pbmiBitmap->cBitCount = 4;
      else 
        pbmiBitmap->cBitCount = 8;
      pbmiBitmap->cclrUsed = usColors;
      pbmiBitmap->cclrImportant = usColors;
     /*********************************************************************/
     /* OS/2 doesn't know about grayscales with no explicit colormap,     */
     /* so we have to generate a gray colormap.                           */
     /*********************************************************************/
      ulTemp = (255 / ((1<<(ULONG)usDepth)-1)); /* Calculate increment     */
      for (ulIndex = 0; ulIndex < (1<<(ULONG)usDepth); ulIndex++) {
        pbmiBitmap->argbColor[ulIndex].bBlue  = (BYTE)(ulIndex * ulTemp);
        pbmiBitmap->argbColor[ulIndex].bGreen = (BYTE)(ulIndex * ulTemp);
        pbmiBitmap->argbColor[ulIndex].bRed   = (BYTE)(ulIndex * ulTemp);
      } /* End for */
      break;

    case IMMAP:
      pimMap = (IMBYTE *)immapalloc (usColors); /* Allocate colormap       */
      imsetcmap (imBild, (IMPIXEL *)pimMap);    /* Set pointer to colormap */
      if (imrcmap (imBild)) {                   /* Get colormap            */
        ImErrorMsg (imerrnum);
        return 0;
      }

      if (usColors > 257) {                     /* Too much colors...      */
        pbmiBitmap->cBitCount = 24;             /* So make an RGB picture  */

      } else {                                  /* No. colors ok           */
        if (usColors > 16)               /* 32 to 256 colors: 8 Bits       */
          pbmiBitmap->cBitCount = 8;     /*                                */
        else if (usColors > 4)           /*  8 to  16 colors: 4 Bits       */
          pbmiBitmap->cBitCount = 4;     /*                                */
        else if (usColors > 2)           /*         4 colors: 2 Bits       */
          pbmiBitmap->cBitCount = 2;
        else                     
          pbmiBitmap->cBitCount = 1;     /*         2 colors: 1 Bit        */
        pbmiBitmap->cclrUsed = usColors;
        pbmiBitmap->cclrImportant = usColors;
      } /* End if too much colors */

      /* Copy colormap into header:                                        */
      for (ulIndex = 0; ulIndex < usColors; ulIndex++) {
        pbmiBitmap->argbColor[ulIndex].bBlue  = *(pimMap + 4*ulIndex + 2);
        pbmiBitmap->argbColor[ulIndex].bGreen = *(pimMap + 4*ulIndex + 1);
        pbmiBitmap->argbColor[ulIndex].bRed   = *(pimMap + 4*ulIndex);
      } /* End for */
      free (pimMap);  /* Release IM colormap                               */
      break;
    } /* End switch */

    pbmiBitmap->cbFix     = 64; /* Again common for all IM types           */
    pbmiBitmap->cx        = ulSizeX;
    pbmiBitmap->cy        = ulSizeY;
    pbmiBitmap->cPlanes   = 1;
    break;

  case IMERROR:   /* Type was an IM, but there was some sort of error      */
    ImErrorMsg (imerrnum);         /* Display this error                   */
    return 0;

  case UNKNOWN:                    /* Picture type is not supported        */
    ShowMsg (IDS_ERRTITLE, IDS_TYPEERR, MB_ERROR);
    return 0;

  default:                         /* Shouldn't occur                      */
    return 0;
  } /* endswitch */

  /*************************************************************************/
  /*                                                                       */
  /* Until now we got the bitmap header. Next we will load the image       */
  /* data into a buffer.                                                   */
  /*                                                                       */
  /*************************************************************************/
  strcat (szFileSource, " (imShow 2.02)");
  WinSetWindowText (hwndFrame, szFileSource); /* Set window title          */

  /* We have to know this global variable:                                 */
  ulBytesPerLine = ((pbmiBitmap->cBitCount * pbmiBitmap->cx + 31) / 32
                      * pbmiBitmap->cPlanes * 4);

  pbBitmapData = malloc(ulBytesPerLine * pbmiBitmap->cy); /* Alloc Buffer  */
  if (pbBitmapData == NULL) exit(1);          /* No good error handling... */

 /************************************************************************/
 /* At this place was a very hard to find race condition: I cleared      */
 /* the semaphore (which tells the loading thread to start) before I     */
 /* created the dialog. On VERY small pictures, the thread was finished  */
 /* before the dialog box was properly initialized. The message from the */
 /* thread to the dialog (telling it is finished) couldn't be delivered, */
 /* and there we had the Trap D...                                       */
 /* Now it works.                                                        */
 /************************************************************************/
  hwndDlg = WinLoadDlg(HWND_DESKTOP,     /* Load "cancel" dialog         */
                       hwnd,
                       (PFNWP)CancelDlgProc,
                       0,
                       IDD_CANCEL,
                       NULL);

  DosResetEventSem (semPictureReady, &ulSemHelper); /* Picture not ready   */
  DosPostEventSem (semInputReady);       /* Loading thread can start       */

  WinProcessDlg (hwndDlg);               /* Process messages to dialog     */
  WinDestroyWindow (hwndDlg);            /* After finishing, clear it      */

  if (fPicReadOK == TRUE) {        /* The picture was read in successfully */
    /* Now we reset and allocate some ressources:                          */
    SetScrollBars(lScaleFactor);              /* Initialize scrollbars     */
  
    if (*hbm != (HBITMAP) NULL) {        /* Is there a bitmap from before? */
      if (hpal != NULLHANDLE) {            /* Did we have a palette?       */
        GpiSelectPalette (hps, NULLHANDLE);  /* Yes: Release it            */
        GpiDeletePalette (hpal);
        hpal = NULLHANDLE;
      }
      GpiSetBitmap (hps, (HBITMAP)NULL);      /* Release bitmap here..     */
      if (hpsMem != NULLHANDLE) {
        GpiSetBitmap (hpsMem, (HBITMAP)NULL);   /* or maybe here,          */
        GpiDeleteBitmap (*hbm);                 /* and delete it           */
        GpiAssociate (hpsMem, (HDC)NULL);       /* and old memory          */
        GpiDestroyPS (hpsMem);                  /*  presentation space     */
        hpsMem = NULLHANDLE;
        DevCloseDC(hdcMem);                     /* Close memory dev. cont. */
      } else
        GpiDeleteBitmap (*hbm);                 /* Else delete only bitmap */
    }
  
    GpiResetPS (hps, GRES_ALL);      /* Make shure the PS is "fresh" again */
  
    if (pbBitmapData == NULL)                          /* something wrong? */
      return (1);
  
    if (bRealizeSupported &&         /* Can we realize the palette?        */
        ((USHORT)ulDisplayColors >= (1 << pbmiBitmap->cBitCount))) {
      MakePalette (pbmiBitmap, &plColTable); /* Make new palette           */
      *hbm = GpiCreateBitmap(hps,            /* Make new bitmap            */
                             (PBITMAPINFOHEADER2)pbmiBitmap,
                             CBM_INIT,            /* Initialize it with    */
                             (PBYTE)pbBitmapData, /* data in buffer        */
                             pbmiBitmap
                            );
      GpiSetBitmap(hps, *hbm);                /* Select bitmap in HPS      */
      DosPostEventSem (semPictureReady);   /* Picture is now ready         */
      WinInvalidateRect (hwnd, NULL, FALSE);  /* Force redraw              */
  
    } else {                             /* We can't realize the palette   */
      /* First create a memory device context:                             */
      hdcMem = DevOpenDC(hab,                     /* Anchor block handler  */
                         OD_MEMORY,               /* Memory device context */
                         (PSZ)"*",                /* no device information */
                         0,                       /* no datablocks         */
                         NULL,                    /* no datablocks         */
                         NULLHANDLE             /* compatible to screen-DC */
                        );
      if (hdcMem == (HDC)NULL)                    /* Not a very nice error */
        exit(1);                                  /* handling...           */

      hpsMem = GpiCreatePS(hab,               /* We need a memory PS       */
                    hdcMem,                   /*  for hdcMem               */
                    (PSIZEL)&size,
                    (LONG)PU_PELS | GPIT_NORMAL | GPIA_ASSOC | GPIF_DEFAULT
                          );
      if (hpsMem == (HPS)NULL) exit(1); /* Some day I will improve this..  */

      *hbm = GpiCreateBitmap(hpsMem,          /* Create bitmap             */
                             (PBITMAPINFOHEADER2)pbmiBitmap,
                             0L,              /* Don't init it             */
                             (PBYTE)NULL,
                             0
                            );
      if (*hbm == (HBITMAP)NULL) exit(1);

      GpiSetBitmap(hpsMem, *hbm);             /* Select bitmap into PS     */

       /* and now set the bitmap bits into the bitmap                     */
       /* This can take a while, so let's set the clock pointer:          */
      WinSetPointer (HWND_DESKTOP, hptrWait);
      GpiSetBitmapBits (hpsMem, 0, (LONG)ulSizeY, pbBitmapData, pbmiBitmap);
      WinSetPointer (HWND_DESKTOP, hptrArrow); /* Reset the pointer..     */

       /* If we realized before, this will undo it:                       */
      WinRealizePalette (hwndClient, 0, &ulTemp);
      fRealized = FALSE;

      DosPostEventSem (semPictureReady);  /* Picture is ready to display   */
      WinInvalidateRect (hwnd, NULL, FALSE); /* Force redraw               */
    } /* End if "can we realize" */
  free (pbBitmapData);                       /* Release Buffer             */

  } else /* Picture was not read in successfully: */
      WinInvalidateRect (hwnd, NULL, FALSE); /* Clears client area         */

  return 1;
}
/***************************  End of OpenFile()  ***************************/


/**********************  Begin of the thread ReadFile  *********************/
/*                                                                         */
/* The thread ReadFile() reads in the picture data asynchronously.         */
/* It is implemented as an endless loop and is contolled via semaphores.   */
/*                                                                         */
/***************************************************************************/
VOID ReadFile (VOID* nothing)    /* We HAVE to to this for _beginthread..  */
{

PBYTE    pbTemp;                         /* 2nd pointer to bitmapdata      */
register ULONG   x,y;                    /* Loop variables                 */
ULONG    ulProgress;                     /* For the progress indicator     */
IMPIXEL  impPixel;                       /* One IM pixel                   */
ULONG    ulChunk;                        /* Size of a data block           */
         
  while (TRUE) {                         /* Start of endless loop          */
    DosWaitEventSem (semInputReady, SEM_INDEFINITE_WAIT);/* Wait here...   */
    DosResetEventSem (semInputReady, &ulSemHelper); /* Reset semaphore     */
    fPicReadOK = FALSE;                  /* Picture (not yet) ok           */

    switch (usType) {                     /* Read depending on type        */
      case OS2_101:                       /* The bitmap DATA is equal on   */
      case WIN_3:                         /* all types, only the header    */
      case OS2_200:                       /* differs                       */
        bmrrows (hImage, pbBitmapData, 0, ulSizeY); /* Get all at once     */
        WinPostMsg (hwndDlg, WM_PROGRESS, MPFROMSHORT(100), 0L); /* dummy  */
        fPicReadOK = TRUE;                /* read in ok                    */
      break;                              /* that's it...                  */

      case IMRGB:                         /* IM type IMRGB (24 bit)        */
        /*******************************************************************/
        /* Unfortunately, the picture data of IM is different from OS/2... */
        /*******************************************************************/
        for (y=ulSizeY; y>0; y--) {  /* OS/2 bitmaps stand on head...      */
          /* Here we test the semaphore semHalt. It will be set when the   */
          /* user chooses "ESC=Cancel" on the dialog. We don't want to     */
          /* wait for the semaphore, we just want to see if it is set:     */
          if (DosWaitEventSem (semHalt, 0L) == ERROR_TIMEOUT)
            goto Halt; /* Not very elegant, but by far the easiest way...  */
          /* Read one row of image data:                                   */
          if ((!imrrow (imBild, imbRowBuff) && imerrnum)) {
            ImErrorMsg (imerrnum);
            goto Halt;
          }
          pbTemp = pbBitmapData + (y-1)*ulBytesPerLine; /* Get this out of */
                                                    /* the inner loop      */
          for (x=0; x<ulSizeX; x++) {      /* Now we have to calculate:    */
            /***************************************************************/
            /* IM stores the image data sorted by color, i.e. RRRGGGBBB.   */
            /* OS/2 on the other hand stores it pixelwise, i.e. RGBRGBRGB. */
            /* So we have to shuffle it:                                   */
            /***************************************************************/
            *(pbTemp+x+x+x+2) = *(imbRowBuff+x                );
            *(pbTemp+x+x+x+1) = *(imbRowBuff+x+ulSizeX        );
            *(pbTemp+x+x+x  ) = *(imbRowBuff+x+ulSizeX+ulSizeX);
          } /* Ende for x */
          ulProgress = 100 - (100 * y / ulSizeY);  /* Calculate progress   */
          WinPostMsg (hwndDlg, WM_PROGRESS, MPFROMSHORT(ulProgress), 0L);
        } /* Ende for y */
        fPicReadOK = TRUE;
        break;

      case IMGRY:               /* The image data is the same with IMMAP   */
      case IMMAP:               /* and IMGRY.                              */
        if (usColors < 257) {   /* IMMAP can have more than 256 colors in  */
                                /* the colormap, OS/2 not.                 */
          for (y=ulSizeY; y>0; y--) {
            if (DosWaitEventSem (semHalt, 0L) == ERROR_TIMEOUT) /* Test    */
              goto Halt;
            imrrow (imBild, pbBitmapData+(y-1)*ulBytesPerLine); /* Get row */
            ulProgress = 100 - (100 * y / ulSizeY);        /* Set progress */
            WinPostMsg (hwndDlg, WM_PROGRESS, MPFROMSHORT(ulProgress), 0L);
          } /* Ende for y */
          fPicReadOK = TRUE;                           /* Finished..       */
        } else {                /* With more than 256 colors we have to    */
                                /* use a 24 bit picture in OS/2.           */
          for (y=ulSizeY; y>0; y--) {  /* Work row by row                  */
            if (DosWaitEventSem (semHalt, 0L) == ERROR_TIMEOUT) /* Test    */
              goto Halt;
            if ((!imrrow (imBild, imbRowBuff)) && imerrnum) { /* Get row   */
              ImErrorMsg (imerrnum);
              goto Halt;
            }
            /***************************************************************/
            /* Now we use IM's 'ideal pixel' function to calculate the 24  */
            /* bit value of each pixel and store it into the bitmap buffer */
            /***************************************************************/
            for (x=0; x<ulSizeX; x++) {    /* Work pixel for pixel         */
              impPixel = imrpix (imBild, imbRowBuff, (USHORT)x); 
              pbTemp = pbBitmapData + (y-1)*ulBytesPerLine + x+x+x;
              *(pbTemp+2) = (BYTE)((impPixel & 0x0000FF));
              *(pbTemp+1) = (BYTE)((impPixel & 0x00FF00) >>  8);
              *(pbTemp  ) = (BYTE)((impPixel & 0xFF0000) >> 16);
            }
            ulProgress = (USHORT) ((100L * (LONG)y) / (LONG)ulSizeY);
            WinPostMsg (hwndDlg, WM_PROGRESS, MPFROMSHORT(ulProgress), 0L);
          } /* Ende for y */
          fPicReadOK = TRUE;    /* That's it                               */
        } /* Ende if */
        break;
     case GIF:                      /* GIF File                            */
       ulChunk = ulSizeY / 20;      /* Read in in 20 "bites"               */
       for (y=1; y<21; y++) {       /* Check                               */
         if (DosWaitEventSem (semHalt, 0L) == ERROR_TIMEOUT)
           goto Halt;
         pbTemp = pbBitmapData + (ulSizeY - y * ulChunk) * ulBytesPerLine;
         gifrrows (hImage, pbTemp, ulSizeX, ulChunk); /* Get the rows      */
         ulProgress = (USHORT) y*5;                   /* Set progress      */
         WinPostMsg (hwndDlg, WM_PROGRESS, MPFROMSHORT(ulProgress), 0L);
       } /* endfor */
       ulChunk = ulSizeY % 20;        /* Do we have some last rows?        */
       if (ulChunk > 0)               /* If so, read them in               */
         gifrrows (hImage, pbBitmapData, ulSizeX, ulChunk);
       fPicReadOK = TRUE;             /* That's it                         */
       break;

    } /* End switch */

    Halt:                             /* To this place we jump after the   */
                                      /* read in was cancelled.            */

    DosPostEventSem (semHalt);        /* Erase semaphore                   */

    if (fPicReadOK == FALSE)          /* Picture was not read in           */
      free (pbBitmapData);            /* free the buffer                   */
    if (imbRowBuff != NULL)           /* If we have one, free this as      */
      free (imbRowBuff);              /*  well                             */

    switch (usType) {
      case OS2_101:
      case WIN_3:
      case OS2_200:
        bmclose (hImage);             /* Close the image files             */
        break;
      case GIF:
        gifclose (hImage);
        break;
      default: 
        imclose (imBild);
    }

    WinPostMsg (hwndDlg, WM_FINISHED, 0L, 0L);  /* Inform the dialog box   */
                                                /* that we are finished    */
  } /* End of endless loop */
}
/*************************  End of thread ReadFile  ************************/




/**************************  Start of ShowMsg  *****************************/
/*                                                                         */
/* The function ShowMsg display a message.                                 */
/*                                                                         */
/***************************************************************************/

VOID ShowMsg (LONG lTitle, LONG lMessage, ULONG ulIcon)
{
  char cTitle[32];
  char cMessage[255];

   /* First the message title and the message content will be loaded       */
   /* from the ressource part:                                             */
  WinLoadString (hab, 0, lTitle, sizeof cTitle, cTitle);
  WinLoadString (hab, 0, lMessage, sizeof cMessage, cMessage);

  WinMessageBox (HWND_DESKTOP,   /* Then the message will be displayed     */
                 hwndFrame,
                 cMessage,
                 cTitle,
                 IDD_ERRORS,
                 MB_OK | ulIcon);
}
/****************************  End of ShowMsg  *****************************/



/**************************  Start of ImErrorMsg  **************************/
/*                                                                         */
/* ImErrorMsg shows an error message of the IM library.                    */
/*                                                                         */
/***************************************************************************/

VOID ImErrorMsg (int iMessageNum)
{
  char cTitle[32];

   /* First we get the title of the message box:                           */
  WinLoadString (hab, 0, IDS_IMTITLE, sizeof cTitle, cTitle);

  WinMessageBox (HWND_DESKTOP,   /* Then we show the message               */
                 hwndFrame,
                 imerrors[iMessageNum],
                 cTitle,
                 IDD_ERRORS,
                 MB_OK | MB_ERROR);
}

/***************************  End of ImErrorMsg  ***************************/



/************************  Start of GetFileType  ***************************/
/*                                                                         */
/* GetFileType gives the type of the stated file.                          */
/*                                                                         */
/***************************************************************************/
USHORT GetFileType (CHAR *szFileName, HFILE *hImage, IMAGE **imBild)
{
  *hImage = bmopen (szFileName, "rb");           /* Try to open bitmap     */
  if (*hImage != 0)                              /* ok?                    */
    return (BITMAP);

  *hImage = gifopen (szFileName, "rb");          /* Try to open GIF        */
  if (*hImage != 0)                              /* ok?                    */
    return (GIF);

  *imBild = imopen (szFileName, "r");            /* Try to open IM         */
    if (*imBild == NULL)                         /* ok?                    */
      if (imerrnum == 5)                         /* error 5 is "not im"    */
        return (UNKNOWN);          /* So give back "unknown"               */
      else                         /* File is IM, but there's another      */
        return (IMERROR);          /*   problem                            */
    else return (IM);
}
/***************************  End of GetFileType  **************************/
