home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1991 / 07 / pcx_cpp.asc < prev    next >
Text File  |  1991-06-11  |  36KB  |  1,108 lines

  1. _A C++ PCX FILE VIEWER FOR WINDOWS 3_
  2. by Paul Chui
  3.  
  4.  
  5.  
  6. [LISTING ONE]
  7.  
  8. #include <windows.h>
  9. #include "pcxwin.h"
  10.  
  11. #include <io.h>
  12.  
  13. #define ALIGN_DWORD(x) (((x)+3)/4 * 4)
  14.  
  15. struct PCXRGB { BYTE r, g, b; };
  16.  
  17. struct PCXHEADER {
  18.         BYTE     pcxManufacturer;
  19.         BYTE     pcxVersion;
  20.         BYTE     pcxEncoding;
  21.         BYTE     pcxBitsPixel;
  22.         int      pcxXmin, pcxYmin;
  23.         int      pcxXmax, pcxYmax;
  24.         int      pcxHres, pcxVres;
  25.         PCXRGB   pcxPalette[16];
  26.         BYTE     pcxReserved;
  27.         BYTE     pcxPlanes;
  28.         int      pcxBytesLine;
  29.         int      pcxPaletteInfo;
  30.         BYTE     pcxFiller[58];
  31. };
  32.  
  33. ///////////////////////////////////////////////////////////////////////////
  34. // NOTES: Decoder creates a DIB and possibly a PALETTE, but does not delete 
  35. // either. It is the responsibility of the Decoder's user to delete them.
  36. ///////////////////////////////////////////////////////////////////////////
  37. class DecodePCX {
  38. public:
  39.         DecodePCX(int hfile, PCXHEADER& pcxHeader);
  40. virtual HBITMAP  MakeDIB(HDC hdc) = 0;
  41.         HPALETTE Palette();
  42. protected:
  43.         WORD     read_pcx_line(BYTE huge* pLine);
  44.         BOOL NEAR PASCAL next_data();
  45.  
  46.         int      hFile;               // Handle to the open PCX file
  47.         HPALETTE hPalette;            // Handle to Palette
  48.  
  49.         PCXHEADER header;
  50.         int      BytesLine;           // Bytes/Line in PCX file
  51.         WORD     width;               // width in pixels
  52.         WORD     height;              // height in scan lines
  53.  
  54.         BYTE     byData;              // Current data byte
  55.         int      iDataBytes;          // Current unread data buffer size
  56. };
  57.  
  58. HPALETTE DecodePCX::Palette() { return hPalette; }
  59. class DecodeMonoPCX : public DecodePCX {
  60. public:
  61.         DecodeMonoPCX(int hfile, PCXHEADER& pcxHeader) :
  62.             DecodePCX(hfile, pcxHeader) { }
  63.         HBITMAP  MakeDIB(HDC hdc);
  64. };
  65. class Decode16PCX: public DecodePCX {
  66. public:
  67.         Decode16PCX(int hfile, PCXHEADER& pcxHeader) :
  68.             DecodePCX(hfile, pcxHeader) { }
  69.         HBITMAP  MakeDIB(HDC hdc);
  70. };
  71. class Decode256PCX: public DecodePCX {
  72. public:
  73.         Decode256PCX(int hfile, PCXHEADER& pcxHeader) :
  74.             DecodePCX(hfile, pcxHeader) { }
  75.         HBITMAP  MakeDIB(HDC hdc);
  76. private:
  77.         HPALETTE make_palette(RGBQUAD* pColors);
  78. };
  79.  
  80. ///////////////////////////////////////////////////////////////////////////
  81. // PCX Methods
  82. ///////////////////////////////////////////////////////////////////////////
  83. PCX::PCX()
  84. {
  85.     hBitmap     = 0;
  86.     hPalette    = 0;
  87.     hFile       = 0;
  88.  
  89.     wWidth      = 0;
  90.     wHeight     = 0;
  91. }
  92. PCX::~PCX()
  93. {
  94.     if (hBitmap) DeleteObject(hBitmap);
  95.     if (hPalette) DeleteObject(hPalette);
  96. }
  97.  
  98. /****************************************************************************
  99.     METHOD: BOOL PCX::Read(LPSTR lpszFileName, HDC hdc)
  100.     PURPOSE: Creates a DIB from a PCX file
  101.     PARAMETERS: LPSTR lpszFileName    PCX file name
  102.                 HDC   hdc             A compatible DC for the DIB
  103.     RETURN: TRUE if DIB was created, otherwise FALSE
  104.     NOTES: ZSoft documents a CGA palette type that is not support here.
  105. ****************************************************************************/
  106. BOOL PCX::Read(LPSTR lpszFileName, HDC hdc)
  107. {
  108.     // Delete the bitmap and reset variables
  109.     if (hBitmap)
  110.     {
  111.         DeleteObject(hBitmap);
  112.         hBitmap = 0;            // So we know the bitmap has been deleted
  113.     }
  114.     if (hPalette)
  115.     {
  116.         DeleteObject(hPalette);
  117.         hPalette = 0;           // So we know the palette has been deleted
  118.     }
  119.     wWidth  = 0;
  120.     wHeight = 0;
  121.     OFSTRUCT OfStruct;
  122.     if ((hFile=OpenFile(lpszFileName, &OfStruct, OF_READ)) == -1)
  123.     {
  124.         ErrorMessage("Unable to open file.");
  125.         return FALSE;
  126.     }
  127.     PCXHEADER header;
  128.     if (_lread(hFile,(LPSTR)&header,sizeof(PCXHEADER)) != sizeof(PCXHEADER))
  129.     {
  130.         ErrorMessage("Error reading PCX file header.");
  131.         return FALSE;
  132.     }
  133.     if(header.pcxManufacturer != 0x0a)
  134.     {
  135.         _lclose(hFile);
  136.         ErrorMessage("Not a PCX file.");
  137.         return FALSE;
  138.     }
  139.     wWidth  = header.pcxXmax - header.pcxXmin + 1;
  140.     wHeight = header.pcxYmax - header.pcxYmin + 1;
  141.  
  142.     DecodePCX* Decoder;
  143.  
  144.     /* Determine PCX file type and create a decoder */
  145.  
  146.     // 256-color file
  147.     if (header.pcxBitsPixel == 8 && header.pcxPlanes == 1)
  148.         Decoder = new Decode256PCX(hFile, header);
  149.     else
  150.     // 16-color file
  151.     if (header.pcxBitsPixel == 1 && header.pcxPlanes == 4)
  152.         Decoder = new Decode16PCX(hFile, header);
  153.     else
  154.     // monochrome file
  155.     if (header.pcxBitsPixel == 1 && header.pcxPlanes == 1)
  156.         Decoder = new DecodeMonoPCX(hFile, header);
  157.     else
  158.         ErrorMessage("Unsupported PCX format.");
  159.  
  160.     if (!Decoder)
  161.     {
  162.         ErrorMessage("Cannot create PCX decoder.");
  163.         _lclose(hFile);
  164.         return FALSE;
  165.     }
  166.     SetCursor( LoadCursor(NULL,IDC_WAIT) );
  167.     // Create the bitmap
  168.     hBitmap = Decoder->MakeDIB(hdc);
  169.     hPalette = Decoder->Palette();
  170.     SetCursor( LoadCursor(NULL,IDC_ARROW) );
  171.     delete Decoder;
  172.     _lclose(hFile);
  173.     return (hBitmap) ? TRUE : FALSE;
  174. }
  175.  
  176. /****************************************************************************
  177.     METHOD: BOOL PCX::Display(HDC hdc, POINT& pos, RECT& rect)
  178.     PURPOSE: Displays the DIB
  179.     PARAMETERS: HDC   hdc             DC on which DIB is displayed
  180.                 POINT pos             Destination positions
  181.                 RECT  rect            Clipping rectangle on source
  182.     RETURN: TRUE if DIB was displayed, otherwise FALSE
  183.     NOTES: Works for MM_TEXT mode only
  184. ****************************************************************************/
  185. BOOL PCX::Display(HDC hdc, POINT& pos, RECT& rect)
  186. {
  187.     BOOL bltOk = FALSE;
  188.     if (hBitmap)
  189.     {
  190.         HBITMAP hdcBitmap  = CreateCompatibleDC(hdc);
  191.         HBITMAP hOldBitmap = SelectObject(hdcBitmap, hBitmap);
  192.         bltOk = BitBlt(hdc, rect.left,rect.top,rect.right,rect.bottom,
  193.                                              hdcBitmap,pos.x,pos.y, SRCCOPY);
  194.         SelectObject(hdcBitmap, hOldBitmap);
  195.         DeleteDC(hdcBitmap);
  196.     }
  197.     return bltOk;
  198. }
  199.  
  200. ///////////////////////////////////////////////////////////////////////////
  201. // DecodePCX Methods
  202. ///////////////////////////////////////////////////////////////////////////
  203.  
  204. /****************************************************************************
  205.     METHOD: DecodePCX::DecodePCX(int hfile, PCXHEADER& pcxHeader)
  206.     PURPOSE: Constructor
  207.     PARAMETERS: int       hfile            Handle to open PCX file
  208.                 PCXHEADER pcxHeader        PCX header
  209. ****************************************************************************/
  210. DecodePCX::DecodePCX(int hfile, PCXHEADER& pcxHeader)
  211. {
  212.     hFile = hfile;
  213.     // Reset file pointer
  214.     if (_llseek(hFile, sizeof(PCXHEADER), 0) == -1)
  215.         ErrorMessage("Error positioning past header.");
  216.     header      = pcxHeader;
  217.     hPalette    = 0;
  218.     BytesLine   = header.pcxBytesLine;
  219.     width       = header.pcxXmax - header.pcxXmin + 1;
  220.     height      = header.pcxYmax - header.pcxYmin + 1;
  221.     byData      = 0;
  222.     iDataBytes  = 0;
  223. }
  224.  
  225. /****************************************************************************
  226.     METHOD: WORD DecodePCX::read_pcx_line(BYTE huge* lpLineImage)
  227.     PURPOSE: Decode a PCX RLE scanline
  228.     PARAMETERS: BYTE huge* lpLineImage   Destination of decoded scanline
  229.     RETURN: Number of bytes decoded
  230. ****************************************************************************/
  231. WORD DecodePCX::read_pcx_line(BYTE huge* lpLineImage)
  232. {
  233.     for (WORD n=0; n<BytesLine; )
  234.     {
  235.         if (!next_data()) return n;
  236.         // If the two high bits are set...
  237.         if (byData >= 0xc0)
  238.         {
  239.             // Get duplication count from lower bits
  240.             BYTE run_len = byData & 0x3f;
  241.             // Set run_len bytes
  242.             if (!next_data()) return n;
  243.             while(run_len--) lpLineImage[n++]=byData;
  244.         }
  245.         else
  246.             // Set this byte
  247.             lpLineImage[n++] = byData;
  248.     }
  249.     if (n != BytesLine)
  250.         ErrorMessage("PCX Read Error.");
  251.     return n;
  252. }
  253.  
  254. /****************************************************************************
  255.     METHOD: BOOL NEAR PASCAL DecodePCX::next_data()
  256.     PURPOSE: Read a byte from the file and set to byData
  257.     RETURN: FALSE on read error
  258.     NOTES: The output byte is written to byData
  259. ****************************************************************************/
  260. BOOL NEAR PASCAL DecodePCX::next_data()
  261. {
  262.     static BYTE  fileBuf[5120];
  263.     static int   index = 0;
  264.     if (iDataBytes == 0)
  265.     {
  266.         if ((iDataBytes = _read(hFile, fileBuf, sizeof(fileBuf))) <= 0)
  267.             return FALSE;
  268.         index = 0;
  269.     }
  270.     --iDataBytes;
  271.     byData = *(fileBuf+(index++));
  272.     return TRUE;
  273. }
  274.  
  275. ///////////////////////////////////////////////////////////////////////////
  276. // DecodeMonoPCX Methods
  277. ///////////////////////////////////////////////////////////////////////////
  278.  
  279. /****************************************************************************
  280.     METHOD: HBITMAP DecodeMonoPCX::MakeDIB(HDC hdc)
  281.     PURPOSE: Make monochrome DIB
  282.     PARAMETERS: HDC hdc           Handle to compatible DC
  283.     RETURNS: Handle to DIB, NULL on error
  284. ****************************************************************************/
  285. HBITMAP DecodeMonoPCX::MakeDIB(HDC hdc)
  286. {
  287.     int h;
  288.     LONG lDIBBytesLine = ALIGN_DWORD(BytesLine);
  289.     LONG image_size = lDIBBytesLine*height;
  290.     // Allocate memory for the image buffer
  291.     GlobalCompact(image_size);
  292.     HANDLE hImageMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, image_size);
  293.     if (!hImageMem)
  294.     {
  295.         ErrorMessage("Out of memory."); return NULL;
  296.     }
  297.     BYTE huge* lpImage = (BYTE huge*) GlobalLock(hImageMem);
  298.     if (!lpImage)
  299.     {
  300.         ErrorMessage("Memory error.");  return NULL;
  301.     }
  302.     for (h=height-1; h>=0; --h)
  303.         read_pcx_line(lpImage+(lDIBBytesLine*h));
  304.     // Create the DIB header
  305.     PBITMAPINFO pBmi = (PBITMAPINFO)
  306.         new BYTE[ sizeof(BITMAPINFOHEADER)+2*sizeof(RGBQUAD) ];
  307.     if (!pBmi)
  308.     {
  309.         ErrorMessage("Out of memory.");
  310.         GlobalUnlock(hImageMem);
  311.         GlobalFree(hImageMem);
  312.         return NULL;
  313.     }
  314.     PBITMAPINFOHEADER pBi = &pBmi->bmiHeader;
  315.     pBi->biSize          = sizeof(BITMAPINFOHEADER);
  316.     pBi->biWidth         = width;
  317.     pBi->biHeight        = height;
  318.     pBi->biPlanes        = 1;
  319.     pBi->biBitCount      = 1;
  320.     pBi->biCompression   = 0L;
  321.     pBi->biSizeImage     = 0L;
  322.     pBi->biXPelsPerMeter = 0L;
  323.     pBi->biYPelsPerMeter = 0L;
  324.     pBi->biClrUsed       = 0L;
  325.     pBi->biClrImportant  = 0L;
  326.     // Copy PCX Palette into DIB color table
  327.     pBmi->bmiColors[0].rgbBlue     = header.pcxPalette[0].b;
  328.     pBmi->bmiColors[0].rgbGreen    = header.pcxPalette[0].g;
  329.     pBmi->bmiColors[0].rgbRed      = header.pcxPalette[0].r;
  330.     pBmi->bmiColors[1].rgbBlue     = header.pcxPalette[1].b;
  331.     pBmi->bmiColors[1].rgbGreen    = header.pcxPalette[1].g;
  332.     pBmi->bmiColors[1].rgbRed      = header.pcxPalette[1].r;
  333.     HBITMAP hBitmap = CreateDIBitmap(hdc, pBi, CBM_INIT,
  334.                                         (LPSTR)lpImage, pBmi, DIB_RGB_COLORS);
  335.     delete pBmi;
  336.     // Free image buffer
  337.     GlobalUnlock(hImageMem);
  338.     GlobalFree(hImageMem);
  339.     return hBitmap;
  340. }
  341.  
  342. ///////////////////////////////////////////////////////////////////////////
  343. // Decode16PCX Methods
  344. ///////////////////////////////////////////////////////////////////////////
  345.  
  346. /****************************************************************************
  347.     METHOD: HBITMAP Decode16PCX::MakeDIB(HDC hdc)
  348.     PURPOSE: Make 16-color DIB
  349.     PARAMETERS: HDC hdc           Handle to compatible DC
  350.     RETURNS: Handle to DIB, NULL on error
  351. ****************************************************************************/
  352. HBITMAP Decode16PCX::MakeDIB(HDC hdc)
  353. {
  354.     LONG lDIBBytesLine = ALIGN_DWORD( (width+1)/2 );
  355.     LONG image_size = lDIBBytesLine*height;
  356.     // Allocate memory for the image buffer
  357.     GlobalCompact(image_size);
  358.     HANDLE hImageMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, image_size);
  359.     if (!hImageMem)
  360.     {
  361.         ErrorMessage("Out of memory."); return NULL;
  362.     }
  363.     BYTE huge* lpImage = (BYTE huge*) GlobalLock(hImageMem);
  364.     if (!lpImage)
  365.     {
  366.         ErrorMessage("Memory error.");  return NULL;
  367.     }
  368.     // 16 color PCX files interleve scanlines for each color
  369.     BYTE *npPlane[4];
  370.     for (int h=0; h<4; ++h)
  371.         npPlane[h] = new BYTE[BytesLine];
  372.     if (!npPlane[0] || !npPlane[1] || !npPlane[2] || !npPlane[3])
  373.     {
  374.         GlobalUnlock(hImageMem);
  375.         GlobalFree(hImageMem);
  376.         return NULL;
  377.     }
  378.     // 16 color DIB bitmaps have 4 bits per pixel
  379.     for (h=height-1; h>=0; --h)
  380.     {
  381.         read_pcx_line(npPlane[0]);
  382.         read_pcx_line(npPlane[1]);
  383.         read_pcx_line(npPlane[2]);
  384.         read_pcx_line(npPlane[3]);
  385.         LONG l = (LONG) h * lDIBBytesLine;
  386.         for (int m=0; m<BytesLine; ++m)
  387.         {
  388.             BYTE r = npPlane[0][m];
  389.             BYTE g = npPlane[1][m];
  390.             BYTE b = npPlane[2][m];
  391.             BYTE i = npPlane[3][m];
  392.             // Combine a bit from each 4 scan lines into a 4-bit nibble
  393.             BYTE nibbles = 0;
  394.             for (int k=0; k<4; ++k)
  395.             {
  396.                 nibbles = 0;
  397.                 // If the most significant bit is set...
  398.                 // Set the appropriate bit in the higher order nibble
  399.                 if (r & '\x80') nibbles |= 0x10;
  400.                 if (g & '\x80') nibbles |= 0x20;
  401.                 if (b & '\x80') nibbles |= 0x40;
  402.                 if (i & '\x80') nibbles |= 0x80;
  403.                 r<<=1; g<<=1; b<<=1; i<<=1;
  404.                 // Repeat for the lower order nibble
  405.                 if (r & '\x80') nibbles |= 0x01;
  406.                 if (g & '\x80') nibbles |= 0x02;
  407.                 if (b & '\x80') nibbles |= 0x04;
  408.                 if (i & '\x80') nibbles |= 0x08;
  409.                 r<<=1; g<<=1; b<<=1; i<<=1;
  410.                 *(lpImage + l++) = nibbles;
  411.             }
  412.         }
  413.     }
  414.     for (h=0; h<4; ++h)
  415.         delete npPlane[h];
  416.     // Create the DIB header
  417.     PBITMAPINFO pBmi = (PBITMAPINFO)
  418.        new BYTE[ sizeof(BITMAPINFOHEADER)+16*sizeof(RGBQUAD) ];
  419.     if (!pBmi)
  420.     {
  421.         ErrorMessage("Out of memory.");
  422.         GlobalUnlock(hImageMem);
  423.         GlobalFree(hImageMem);
  424.         return NULL;
  425.     }
  426.     PBITMAPINFOHEADER pBi = &pBmi->bmiHeader;
  427.     pBi->biSize          = sizeof(BITMAPINFOHEADER);
  428.     pBi->biWidth         = width;
  429.     pBi->biHeight        = height;
  430.     pBi->biPlanes        = 1;
  431.     pBi->biBitCount      = 4;
  432.     pBi->biCompression   = 0L;
  433.     pBi->biSizeImage     = 0L;
  434.     pBi->biXPelsPerMeter = 0L;
  435.     pBi->biYPelsPerMeter = 0L;
  436.     pBi->biClrUsed       = 0L;
  437.     pBi->biClrImportant  = 0L;
  438.     if (header.pcxVersion == 3)
  439.     // No PCX palette, use literal color values
  440.     {
  441.         DWORD* clrTab = (DWORD*)pBmi->bmiColors;
  442.         clrTab[0]  = 0x000000L;
  443.         clrTab[1]  = 0x000080L;
  444.         clrTab[2]  = 0x008000L;
  445.         clrTab[3]  = 0x008080L;
  446.         clrTab[4]  = 0x800000L;
  447.         clrTab[5]  = 0x800080L;
  448.         clrTab[6]  = 0x808000L;
  449.         clrTab[7]  = 0x808080L;
  450.         clrTab[8]  = 0xc0c0c0L;
  451.         clrTab[9]  = 0x0000ffL;
  452.         clrTab[10] = 0x00ff00L;
  453.         clrTab[11] = 0x00ffffL;
  454.         clrTab[12] = 0xff0000L;
  455.         clrTab[13] = 0xff00ffL;
  456.         clrTab[14] = 0xffff00L;
  457.         clrTab[15] = 0xffffffL;
  458.     }
  459.     else
  460.     // Copy PCX palette to DIB color table
  461.     {
  462.         for (int i=0; i<16; ++i)
  463.         {
  464.             pBmi->bmiColors[i].rgbBlue     = header.pcxPalette[i].b;
  465.             pBmi->bmiColors[i].rgbGreen    = header.pcxPalette[i].g;
  466.             pBmi->bmiColors[i].rgbRed      = header.pcxPalette[i].r;
  467.             pBmi->bmiColors[i].rgbReserved = 0;
  468.         }
  469.     }
  470.     HBITMAP hBitmap = CreateDIBitmap(hdc, pBi, CBM_INIT,
  471.                                         (LPSTR)lpImage, pBmi, DIB_RGB_COLORS);
  472.     delete pBmi;
  473.     // Free image buffer
  474.     GlobalUnlock(hImageMem);
  475.     GlobalFree(hImageMem);
  476.     return hBitmap;
  477. }
  478.  
  479. ///////////////////////////////////////////////////////////////////////////
  480. // Decode256PCX Methods
  481. ///////////////////////////////////////////////////////////////////////////
  482.  
  483. /****************************************************************************
  484.     METHOD: HBITMAP Decode256PCX::MakeDIB(HDC hdc)
  485.     PURPOSE: Make 256-color DIB
  486.     PARAMETERS: HDC hdc           Handle to compatible DC
  487.     RETURNS: Handle to DIB, NULL on error
  488. ****************************************************************************/
  489. HANDLE Decode256PCX::MakeDIB(HDC hdc)
  490. {
  491.     LONG lDIBBytesLine = ALIGN_DWORD(BytesLine);
  492.     LONG image_size = lDIBBytesLine*height;
  493.     // Allocate memory for the image buffer
  494.     GlobalCompact(image_size);
  495.     HANDLE hImageMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, image_size);
  496.     if (!hImageMem)
  497.     {
  498.         ErrorMessage("Out of memory."); return NULL;
  499.     }
  500.     BYTE huge* lpImage = (BYTE huge*) GlobalLock(hImageMem);
  501.     if (!lpImage)
  502.     {
  503.         ErrorMessage("Memory error.");  return NULL;
  504.     }
  505.     for (int h=height-1; h>=0; --h)
  506.         read_pcx_line(lpImage+(lDIBBytesLine*h));
  507.     // Create the DIB header
  508.     PBITMAPINFO pBmi = (PBITMAPINFO)
  509.         new BYTE[ sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD) ];
  510.     if (!pBmi)
  511.     {
  512.         ErrorMessage("Out of memory.");
  513.         GlobalUnlock(hImageMem);
  514.         GlobalFree(hImageMem);
  515.         return NULL;
  516.     }
  517.     PBITMAPINFOHEADER pBi = &pBmi->bmiHeader;
  518.     pBi->biSize          = sizeof(BITMAPINFOHEADER);
  519.     pBi->biWidth         = width;
  520.     pBi->biHeight        = height;
  521.     pBi->biPlanes        = 1;
  522.     pBi->biBitCount      = 8;
  523.     pBi->biCompression   = 0L;
  524.     pBi->biSizeImage     = 0L;
  525.     pBi->biXPelsPerMeter = 0L;
  526.     pBi->biYPelsPerMeter = 0L;
  527.     pBi->biClrUsed       = 0L;
  528.     pBi->biClrImportant  = 0L;
  529.     // Look for the palette at the end of the file
  530.     if (_llseek(hFile, -769L, 2) == -1)
  531.         ErrorMessage("Error seeking palette.");
  532.     // It should start with a 0Ch byte
  533.     BYTE Id256Pal;
  534.     if (!(_read(hFile, &Id256Pal, 1) == 1 && Id256Pal == '\xc'))
  535.         ErrorMessage("No palette found.");
  536.     PCXRGB* PalPCX = new PCXRGB[256];
  537.     if (_read(hFile, PalPCX, 768) != 768)
  538.         ErrorMessage("Error reading palette.");
  539.     // Copy PCX palette to DIB color table
  540.     for (int i=0; i<256; ++i)
  541.     {
  542.         pBmi->bmiColors[i].rgbBlue     = PalPCX[i].b;
  543.         pBmi->bmiColors[i].rgbGreen    = PalPCX[i].g;
  544.         pBmi->bmiColors[i].rgbRed      = PalPCX[i].r;
  545.         pBmi->bmiColors[i].rgbReserved = 0;
  546.     }
  547.     delete PalPCX;
  548.     if (hPalette)
  549.         DeleteObject(hPalette);
  550.     // Create and set logical palette
  551.     if ((hPalette = make_palette(pBmi->bmiColors)) != NULL)
  552.     {
  553.         SelectPalette(hdc, hPalette, 0);
  554.         RealizePalette(hdc);
  555.     }
  556.     else
  557.     {
  558.         ErrorMessage("Cannot create palette");
  559.     }
  560.     HBITMAP hBitmap = CreateDIBitmap(hdc, pBi, CBM_INIT,
  561.                                         (LPSTR)lpImage, pBmi, DIB_RGB_COLORS);
  562.     delete pBmi;
  563.     // Free image buffer
  564.     GlobalUnlock(hImageMem);
  565.     GlobalFree(hImageMem);
  566.     return hBitmap;
  567. }
  568.  
  569. /****************************************************************************
  570.     METHOD: HPALETTE Decode256PCX::make_palette(RGBQUAD* pColors)
  571.     PURPOSE: Make 256-color Logical Palette
  572.     PARAMETERS: RGBQUAD[256] pColors       Palette colors
  573.     RETURNS: Handle to Palette, NULL on error
  574. ****************************************************************************/
  575. HPALETTE Decode256PCX::make_palette(RGBQUAD* pColors)
  576. {
  577.     if (!pColors)
  578.         return NULL;
  579.     PLOGPALETTE pPal = (PLOGPALETTE)
  580.         new BYTE[ sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY)];
  581.     if (!pPal)
  582.         return NULL;
  583.     pPal->palNumEntries = 256;
  584.     pPal->palVersion    = 0x300;
  585.     for (int i=0; i<256; ++i)
  586.     {
  587.         pPal->palPalEntry[i].peRed   = pColors[i].rgbRed;
  588.         pPal->palPalEntry[i].peGreen = pColors[i].rgbGreen;
  589.         pPal->palPalEntry[i].peBlue  = pColors[i].rgbBlue;
  590.         pPal->palPalEntry[i].peFlags = 0;
  591.     }
  592.     HPALETTE hPal = CreatePalette(pPal);
  593.     delete pPal;
  594.     return hPal;
  595. }
  596.  
  597.  
  598.  
  599.  
  600. [LISTING TWO]
  601.  
  602. #include <windows.h>
  603. #include "pcxwin.h"
  604.  
  605. #include <stdlib.h>
  606.  
  607. static char szAppName[] = "PCXWIN";
  608.  
  609. #define MAXPATH 80
  610. static char szFileName[MAXPATH+1] = "";
  611. static char szUntitled[] = "PCXWIN - (Untitled)";
  612.  
  613. // Function Prototypes
  614. int DoKeyDown(HWND hwnd, WORD wVkey);
  615. int DoFileOpenDlg(HANDLE hInst, HWND hwnd);
  616.  
  617. LONG FAR PASCAL _export WndProc(HWND hwnd, WORD message, 
  618.                                                      WORD wParam, LONG lParam);
  619. BOOL FAR PASCAL _export FileOpenDlgProc(HWND hDlg, WORD message, 
  620.                                                      WORD wParam, LONG lParam);
  621. int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR, int nCmdShow)
  622. {
  623.     if (!hPrevInstance)
  624.     {
  625.         WNDCLASS wndclass;
  626.         wndclass.style          = CS_HREDRAW | CS_VREDRAW;
  627.         wndclass.lpfnWndProc    = WndProc;
  628.         wndclass.cbClsExtra     = 0;
  629.         wndclass.cbWndExtra     = 0;
  630.         wndclass.hInstance      = hInstance;
  631.         wndclass.hIcon          = LoadIcon(hInstance, "PCXWIN");
  632.         wndclass.hCursor        = LoadCursor(NULL, IDC_ARROW);
  633.         wndclass.hbrBackground  = GetStockObject(WHITE_BRUSH);
  634.         wndclass.lpszMenuName   = "PCXWIN";
  635.         wndclass.lpszClassName  = szAppName;
  636.         RegisterClass(&wndclass);
  637.     }
  638.     HWND hwnd = CreateWindow(
  639.                     szAppName, szUntitled,
  640.                     WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
  641.                     CW_USEDEFAULT, CW_USEDEFAULT,
  642.                     CW_USEDEFAULT, CW_USEDEFAULT,
  643.                     NULL, NULL, hInstance, NULL
  644.                 );
  645.     ShowWindow(hwnd, nCmdShow);
  646.     UpdateWindow(hwnd);
  647.     HANDLE hAccel = LoadAccelerators(hInstance, szAppName);
  648.     MSG msg;
  649.     while(GetMessage(&msg, NULL, 0, 0))
  650.     {
  651.         if (!TranslateAccelerator(hwnd, hAccel, &msg))
  652.         {
  653.             TranslateMessage(&msg);
  654.             DispatchMessage(&msg);
  655.         }
  656.     }
  657.     return msg.wParam;
  658. }
  659. LONG FAR PASCAL _export WndProc(HWND hwnd, WORD message, 
  660.                                                      WORD wParam, LONG lParam)
  661. {
  662.     static HANDLE hInst;
  663.     static PCX* pcx;
  664.     static Scroller* scroll;
  665.     HDC    hdc;
  666.     switch(message)
  667.     {
  668.         case WM_CREATE :
  669.             hInst = ((LPCREATESTRUCT) lParam)->hInstance;
  670.             pcx    = new PCX;
  671.             scroll = new Scroller(hwnd);
  672.             return 0L;
  673.         case WM_DESTROY :
  674.             delete pcx;
  675.             delete scroll;
  676.             PostQuitMessage(0);
  677.             return 0L;
  678.         case WM_PAINT :
  679.             PAINTSTRUCT ps;
  680.             hdc = BeginPaint(hwnd, &ps);
  681.             RECT rcClient;
  682.             GetClientRect(hwnd, &rcClient);
  683.             pcx->Display(hdc, scroll->Pos(), rcClient);
  684.             EndPaint(hwnd, &ps);
  685.             return 0L;
  686.         case WM_QUERYNEWPALETTE:
  687.             if (pcx->Palette())
  688.             {
  689.                 hdc = GetDC(hwnd);
  690.                 SelectPalette(hdc, pcx->Palette(), 0);
  691.                 BOOL b = RealizePalette(hdc);
  692.                 ReleaseDC(hwnd, hdc);
  693.                 if (b)
  694.                 {
  695.                     InvalidateRect(hwnd, NULL, 1);
  696.                     return 1L;
  697.                 }
  698.             }
  699.             return 0L;
  700.         case WM_SIZE :
  701.             scroll->Size(pcx->Size());
  702.             return 0L;
  703.         case WM_VSCROLL :
  704.             scroll->Vert(wParam, LOWORD(lParam));
  705.             return 0L;
  706.         case WM_HSCROLL :
  707.             scroll->Horz(wParam, LOWORD(lParam));
  708.             return 0L;
  709.         case WM_KEYDOWN :
  710.             return DoKeyDown(hwnd, wParam);
  711.         case WM_COMMAND :
  712.             switch (wParam)
  713.             {
  714.                 case IDM_OPEN :
  715.                     if (DoFileOpenDlg(hInst, hwnd))
  716.                     {
  717.                         hdc = GetDC(hwnd);
  718.                         if (pcx->Read(szFileName, hdc))
  719.                         {
  720.                           char wtext[70];
  721.                           wsprintf(wtext, "PcxWin - %.40s (%u x %u)", 
  722.                             AnsiUpper(szFileName),pcx->Width(), pcx->Height());
  723.                           SetWindowText(hwnd, wtext);
  724.                         }
  725.                         else
  726.                         {
  727.                             SetWindowText(hwnd, szUntitled);
  728.                         }
  729.                         ReleaseDC(hwnd, hdc);
  730.                         POINT ptNewPos = {0,0};
  731.                         scroll->Pos(ptNewPos);
  732.                         scroll->Size(pcx->Size());
  733.                     }
  734.                     InvalidateRect(hwnd, NULL, TRUE);
  735.                     break;
  736.                 case IDM_ABOUT:
  737.                     MessageBox(hwnd, "PCXWIN (c) Paul Chui, 1991",
  738.                         "About PCXWIN...", MB_OK | MB_ICONINFORMATION);
  739.                     break;
  740.                 case IDM_EXIT :
  741.                     DestroyWindow(hwnd);
  742.                     break;
  743.                 case IDM_COPY :
  744.                     OpenClipboard(hwnd);
  745.                     EmptyClipboard();
  746.                     SetClipboardData(CF_BITMAP, pcx->Bitmap());
  747.                     CloseClipboard();
  748.                 }
  749.                 return 0L;
  750.     }
  751.     return DefWindowProc(hwnd, message, wParam, lParam);
  752. }
  753. int DoKeyDown(HWND hwnd, WORD wVkey)
  754. {
  755.     switch (wVkey)
  756.     {
  757.     case VK_HOME  : SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0L);      break;
  758.     case VK_END   : SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0L);   break;
  759.     case VK_PRIOR : SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0L);   break;
  760.     case VK_NEXT  : SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0L); break;
  761.     case VK_UP    : SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0L);   break;
  762.     case VK_DOWN  : SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0L); break;
  763.     case VK_LEFT  : SendMessage(hwnd, WM_HSCROLL, SB_PAGEUP, 0L);   break;
  764.     case VK_RIGHT : SendMessage(hwnd, WM_HSCROLL, SB_PAGEDOWN, 0L); break;
  765.     }
  766.     return 0;
  767. }
  768. BOOL DoFileOpenDlg(HANDLE hInst, HWND hwnd)
  769. {
  770.     FARPROC lpfnFileOpenDlgProc = MakeProcInstance((FARPROC)FileOpenDlgProc, 
  771.                                                                         hInst);
  772.     BOOL bReturn = DialogBox(hInst, "FileOpen", hwnd, lpfnFileOpenDlgProc);
  773.     FreeProcInstance(lpfnFileOpenDlgProc);
  774.     return bReturn;
  775. }
  776. BOOL FAR PASCAL FileOpenDlgProc(HWND hDlg, WORD message, WORD wParam, LONG)
  777. {
  778.     switch(message)
  779.     {
  780.         case WM_INITDIALOG :
  781.             SendDlgItemMessage(hDlg, IDD_FNAME, EM_LIMITTEXT, MAXPATH, 0L);
  782.             SetDlgItemText(hDlg, IDD_FNAME, szFileName);
  783.             return TRUE;
  784.         case WM_COMMAND :
  785.             switch(wParam)
  786.             {
  787.                 case IDOK :
  788.                     GetDlgItemText(hDlg, IDD_FNAME, szFileName, MAXPATH);
  789.                     EndDialog(hDlg, TRUE);
  790.                     return TRUE;
  791.                 case IDCANCEL :
  792.                     szFileName[0] = '\0';       // erase the string
  793.                     EndDialog(hDlg, FALSE);
  794.                     return TRUE;
  795.             }
  796.     }
  797.     return FALSE;
  798. }
  799.  
  800.  
  801.  
  802.  
  803. [LISTING THREE]
  804.  
  805. #ifndef PCXWIN_H
  806. #define PCXWIN_H
  807.  
  808. #define IDM_OPEN    0x10
  809. #define IDM_EXIT    0x11
  810. #define IDM_ABOUT   0x12
  811. #define IDM_COPY    0x20
  812. #define IDD_FNAME   0x20
  813.  
  814. class PCX {
  815. public:
  816.         PCX();
  817.        ~PCX();
  818. virtual BOOL     Read(LPSTR lpszFileName, HDC theHdc);
  819. virtual BOOL     Display(HDC hdc, POINT& pos, RECT& rect);
  820.         POINT    Size();
  821.         WORD     Width();
  822.         WORD     Height();
  823.         HBITMAP  Bitmap();
  824.         HPALETTE Palette();
  825. private:
  826.         WORD     wWidth, wHeight;
  827.         HBITMAP  hBitmap;
  828.         HPALETTE hPalette;
  829.         int      hFile;               // Input file handle
  830. };
  831. inline POINT    PCX::Size()    { POINT p = {wWidth,wHeight}; return p; }
  832. inline WORD     PCX::Width()   { return wWidth; }
  833. inline WORD     PCX::Height()  { return wHeight; }
  834. inline HBITMAP  PCX::Bitmap()  { return hBitmap; }
  835. inline HPALETTE PCX::Palette() { return hPalette; }
  836.  
  837. class Scroller {
  838. public:
  839.         Scroller(HWND hwnd);
  840.         int      Size(POINT& ptImgSize);
  841.         int      Vert(WORD wSBcode, WORD wSBPos);
  842.         int      Horz(WORD wSBcode, WORD wSBPos);
  843.         POINT    Pos();
  844.         POINT    Pos(POINT& ptNewPos);
  845. private:
  846.         HWND     hClientWnd;
  847.         POINT    ptPos;               // Current Scroll position
  848.         POINT    ptMax;               // Max Scroll range
  849.         POINT    ptInc;               // Scroll increment
  850.         POINT    ptClient;            // Size of client window
  851. };
  852. inline POINT Scroller::Pos() { return ptPos; }
  853. inline POINT Scroller::Pos(POINT& ptNewPos) { return ptPos = ptNewPos; }
  854. inline void ErrorMessage(PSTR message)
  855. {
  856.     MessageBox(NULL, (LPSTR) message, (LPSTR) "Error", MB_OK|MB_ICONEXCLAMATION);
  857. }
  858. /* The standard max and min macros are undefined by BC++ because
  859.    they may conflict with class-defined macros with the same names. */
  860. #define MAX(a,b)            (((a) > (b)) ? (a) : (b))
  861. #define MIN(a,b)            (((a) < (b)) ? (a) : (b))
  862. #endif
  863.  
  864.  
  865.  
  866.  
  867. [LISTING FOUR]
  868.  
  869. #include <windows.h>
  870. #include "pcxwin.h"
  871.  
  872. ////////////////////////  Class Scroller  ////////////////////////////////
  873. Scroller::Scroller(HWND hwnd)
  874. {
  875.     ptPos.x = 0; ptPos.y = 0;
  876.     ptMax.x = 0; ptMax.y = 0;
  877.     ptInc.x = 0; ptInc.y = 0;
  878.  
  879.     RECT rect;
  880.     GetClientRect(hwnd, &rect);
  881.     ptClient.x = rect.right; ptClient.y = rect.bottom;
  882.     hClientWnd = hwnd;
  883. }
  884. int Scroller::Size(POINT& ptImgSize)
  885. {
  886.     RECT rect;
  887.     GetClientRect(hClientWnd, &rect);
  888.     ptClient.x = rect.right; ptClient.y = rect.bottom;
  889.     ptMax.x = MAX(0, ptImgSize.x - ptClient.x);
  890.     ptPos.x = MIN(ptPos.x, ptMax.x);
  891.     SetScrollRange(hClientWnd, SB_HORZ, 0, ptMax.x, FALSE);
  892.     SetScrollPos(hClientWnd, SB_HORZ, ptPos.x, TRUE);
  893.     ptMax.y = MAX(0, ptImgSize.y - ptClient.y);
  894.     ptPos.y = MIN(ptPos.y, ptMax.y);
  895.     SetScrollRange(hClientWnd, SB_VERT, 0, ptMax.y, FALSE);
  896.     SetScrollPos(hClientWnd, SB_VERT, ptPos.y, TRUE);
  897.     return 0;
  898. }
  899. int Scroller::Vert(WORD wSBcode, WORD wSBPos)
  900. {
  901.     switch (wSBcode)
  902.     {
  903.         case SB_LINEUP :
  904.             ptInc.y = -1;
  905.             break;
  906.         case SB_LINEDOWN :
  907.             ptInc.y = 1;
  908.             break;
  909.         case SB_PAGEUP :
  910.             ptInc.y = MIN(-1, -ptClient.y/4);
  911.             break;
  912.         case SB_PAGEDOWN :
  913.             ptInc.y = MAX(1, ptClient.y/4);
  914.             break;
  915.         case SB_TOP :
  916.             ptInc.y = -ptInc.y;
  917.             break;
  918.         case SB_BOTTOM :
  919.             ptInc.y = ptMax.y - ptPos.y;
  920.             break;
  921.         case SB_THUMBPOSITION :
  922.             ptInc.y = wSBPos - ptPos.y;
  923.             break;
  924.         default :
  925.             ptInc.y = 0;
  926.     }
  927.     if (( ptInc.y = MAX(-ptPos.y, MIN(ptInc.y, ptMax.y - ptPos.y)) ) != 0)
  928.     {
  929.         ptPos.y += ptInc.y;
  930.         ScrollWindow(hClientWnd, 0, -ptInc.y, NULL, NULL);
  931.         SetScrollPos(hClientWnd, SB_VERT, ptPos.y, TRUE);
  932.         UpdateWindow(hClientWnd);
  933.     }
  934.     return 0;
  935. }
  936. int Scroller::Horz(WORD wSBcode, WORD wSBPos)
  937. {
  938.     switch (wSBcode)
  939.     {
  940.         case SB_LINEUP :
  941.             ptInc.x = -1;
  942.             break;
  943.         case SB_LINEDOWN :
  944.             ptInc.x = 1;
  945.             break;
  946.         case SB_PAGEUP :
  947.             ptInc.x = MIN(-1, -ptClient.x/4);
  948.             break;
  949.         case SB_PAGEDOWN :
  950.             ptInc.x = MAX(1, ptClient.x/4);
  951.             break;
  952.         case SB_THUMBPOSITION :
  953.             ptInc.x = wSBPos - ptPos.x;
  954.             break;
  955.         default :
  956.             ptInc.x = 0;
  957.     }
  958.     if (( ptInc.x = MAX(-ptPos.x, MIN(ptInc.x, ptMax.x - ptPos.x)) ) != 0)
  959.     {
  960.         ptPos.x += ptInc.x;
  961.         ScrollWindow(hClientWnd, -ptInc.x, 0, NULL, NULL);
  962.         SetScrollPos(hClientWnd, SB_HORZ, ptPos.x, TRUE);
  963.         UpdateWindow(hClientWnd);
  964.     }
  965.     return 0;
  966. }
  967.  
  968.  
  969.  
  970. [LISTING FIVE]
  971.  
  972. NAME        PCXWIN
  973.  
  974. DESCRIPTION 'PCX Viewer (c) Paul Chui, 1991'
  975. EXETYPE     WINDOWS
  976. STUB        'WINSTUB.EXE'
  977. CODE        PRELOAD MOVEABLE DISCARDABLE
  978. DATA        PRELOAD MOVABLE MULTIPLE
  979. HEAPSIZE    1046
  980. STACKSIZE   8192
  981. PROTMODE
  982.  
  983.  
  984.  
  985. [LISTING SIX]
  986.  
  987. #include <windows.h>
  988. #include "pcxwin.h"
  989.  
  990. PCXWin MENU
  991. BEGIN
  992.    POPUP   "&File"
  993.    BEGIN
  994.     MENUITEM        "&Open"                 IDM_OPEN
  995.     MENUITEM        SEPARATOR
  996.     MENUITEM        "E&xit"                 IDM_EXIT
  997.     MENUITEM        "A&bout PCXWIN..."      IDM_ABOUT
  998.     END
  999.    POPUP   "&Edit"
  1000.    BEGIN
  1001.         MENUITEM        "&Copy\tCtrl+Ins"     IDM_COPY
  1002.     END
  1003. END
  1004. FILEOPEN DIALOG DISCARDABLE LOADONCALL PURE MOVEABLE 10, 35, 129, 56
  1005. STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | 0x80L
  1006. CAPTION "Open File"
  1007. BEGIN
  1008.   CONTROL "File &name:" -1, "STATIC", WS_CHILD | WS_VISIBLE, 8, 7, 56, 12
  1009.   CONTROL "" IDD_FNAME, "EDIT", WS_CHILD | WS_VISIBLE | WS_BORDER | 
  1010.                                             WS_TABSTOP | 0x80L, 7, 15, 116, 12
  1011.   CONTROL "OK" IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | 
  1012.                                             WS_TABSTOP, 15, 36, 40, 12
  1013.   CONTROL "Cancel" IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | 
  1014.                                             WS_TABSTOP, 69, 36, 40, 12
  1015. END
  1016. PCXWin ACCELERATORS
  1017. {
  1018.     VK_INSERT, IDM_COPY,    VIRTKEY, CONTROL
  1019. }
  1020.  
  1021.  
  1022.  
  1023. [LISTING SEVEN]
  1024.  
  1025. .AUTODEPEND
  1026. #       *Translator Definitions*
  1027. CC = bccx +PCXWIN.CFG
  1028. TASM = TASM
  1029. TLINK = tlink
  1030. #       *Implicit Rules*
  1031. .c.obj:
  1032.   $(CC) -c {$< }
  1033. .cpp.obj:
  1034.   $(CC) -c {$< }
  1035. #       *List Macros*
  1036. Link_Exclude =  \
  1037.   pcxwin.res
  1038. Link_Include =  \
  1039.   pcxwin.obj \
  1040.   showpcx.obj \
  1041.   scroller.obj \
  1042.   pcxwin.def
  1043. #       *Explicit Rules*
  1044. pcxwin.exe: pcxwin.cfg $(Link_Include) $(Link_Exclude)
  1045.   $(TLINK) /v/x/n/c/Twe/P-/LC:\CPP\LIB @&&|
  1046. c0ws.obj+
  1047. pcxwin.obj+
  1048. showpcx.obj+
  1049. scroller.obj
  1050. pcxwin
  1051.         # no map file
  1052. cwins.lib+
  1053. import.lib+
  1054. maths.lib+
  1055. cs.lib
  1056. pcxwin.def
  1057. |
  1058.   RC -T pcxwin.res pcxwin.exe
  1059. #       *Individual File Dependencies*
  1060. pcxwin.obj: pcxwin.cpp 
  1061. showpcx.obj: showpcx.cpp 
  1062. scroller.obj: scroller.cpp 
  1063. pcxwin.res: pcxwin.rc 
  1064.         RC -R -IC:\CPP\INCLUDE -FO pcxwin.res PCXWIN.RC
  1065. #       *Compiler Configuration File*
  1066. pcxwin.cfg: pcxwin.mak
  1067.   copy &&|
  1068. -v
  1069. -W
  1070. -H=PCXWIN.SYM
  1071. -IC:\CPP\INCLUDE
  1072. -LC:\CPP\LIB
  1073. | pcxwin.cfg
  1074.  
  1075.  
  1076.  
  1077. Example 1: 
  1078.  
  1079.     BYTE huge* lpImage = new BYTE[lImageSize];
  1080.     int h, line, plane;
  1081.     for (h=0, line=0; h<wHeight; ++h, line+=byPlanes)
  1082.         for (plane=0; plane<byPlanes; ++plane)
  1083.             read_pcx_line(lpImage+(lBmpBytesLine*(line+plane)));
  1084.     HBITMAP hBitmap = CreateBitmap(wWidth, wHeight, byPlanes, 1, lpImage);
  1085.  
  1086.  
  1087.  
  1088. Example 2:
  1089.  
  1090.     BYTE huge* lpImage = new BYTE[lImageSize];
  1091.     int h, line, plane;
  1092.     if (lImageSize < 65535L)
  1093.     // Interlaced color scanlines
  1094.     for (h=0, line=0; h<wHeight; ++h, line+=byPlanes)
  1095.         for (plane=0; plane<byPlanes; ++plane)
  1096. read_pcx_line(lpImage+(LONG(iBmpBytesLine)*(line+plane)));
  1097.     else
  1098.     // Interlaced color planes
  1099.     for (h=0, line=0; h<wHeight; ++h, line+=wHeight)
  1100.         for (plane=0; plane<byPlanes; ++plane)
  1101.            read_pcx_line(lpImage+(lBmpBytesLine*(plane*wHeight+h)));
  1102.     HBITMAP hBitmap = CreateBitmap(wWidth, wHeight, byPlanes, 1, lpImage);
  1103.  
  1104.  
  1105.  
  1106.  
  1107.  
  1108.