_A C++ PCX FILE VIEWER FOR WINDOWS 3_ by Paul Chui [LISTING ONE] #include #include "pcxwin.h" #include #define ALIGN_DWORD(x) (((x)+3)/4 * 4) struct PCXRGB { BYTE r, g, b; }; struct PCXHEADER { BYTE pcxManufacturer; BYTE pcxVersion; BYTE pcxEncoding; BYTE pcxBitsPixel; int pcxXmin, pcxYmin; int pcxXmax, pcxYmax; int pcxHres, pcxVres; PCXRGB pcxPalette[16]; BYTE pcxReserved; BYTE pcxPlanes; int pcxBytesLine; int pcxPaletteInfo; BYTE pcxFiller[58]; }; /////////////////////////////////////////////////////////////////////////// // NOTES: Decoder creates a DIB and possibly a PALETTE, but does not delete // either. It is the responsibility of the Decoder's user to delete them. /////////////////////////////////////////////////////////////////////////// class DecodePCX { public: DecodePCX(int hfile, PCXHEADER& pcxHeader); virtual HBITMAP MakeDIB(HDC hdc) = 0; HPALETTE Palette(); protected: WORD read_pcx_line(BYTE huge* pLine); BOOL NEAR PASCAL next_data(); int hFile; // Handle to the open PCX file HPALETTE hPalette; // Handle to Palette PCXHEADER header; int BytesLine; // Bytes/Line in PCX file WORD width; // width in pixels WORD height; // height in scan lines BYTE byData; // Current data byte int iDataBytes; // Current unread data buffer size }; HPALETTE DecodePCX::Palette() { return hPalette; } class DecodeMonoPCX : public DecodePCX { public: DecodeMonoPCX(int hfile, PCXHEADER& pcxHeader) : DecodePCX(hfile, pcxHeader) { } HBITMAP MakeDIB(HDC hdc); }; class Decode16PCX: public DecodePCX { public: Decode16PCX(int hfile, PCXHEADER& pcxHeader) : DecodePCX(hfile, pcxHeader) { } HBITMAP MakeDIB(HDC hdc); }; class Decode256PCX: public DecodePCX { public: Decode256PCX(int hfile, PCXHEADER& pcxHeader) : DecodePCX(hfile, pcxHeader) { } HBITMAP MakeDIB(HDC hdc); private: HPALETTE make_palette(RGBQUAD* pColors); }; /////////////////////////////////////////////////////////////////////////// // PCX Methods /////////////////////////////////////////////////////////////////////////// PCX::PCX() { hBitmap = 0; hPalette = 0; hFile = 0; wWidth = 0; wHeight = 0; } PCX::~PCX() { if (hBitmap) DeleteObject(hBitmap); if (hPalette) DeleteObject(hPalette); } /**************************************************************************** METHOD: BOOL PCX::Read(LPSTR lpszFileName, HDC hdc) PURPOSE: Creates a DIB from a PCX file PARAMETERS: LPSTR lpszFileName PCX file name HDC hdc A compatible DC for the DIB RETURN: TRUE if DIB was created, otherwise FALSE NOTES: ZSoft documents a CGA palette type that is not support here. ****************************************************************************/ BOOL PCX::Read(LPSTR lpszFileName, HDC hdc) { // Delete the bitmap and reset variables if (hBitmap) { DeleteObject(hBitmap); hBitmap = 0; // So we know the bitmap has been deleted } if (hPalette) { DeleteObject(hPalette); hPalette = 0; // So we know the palette has been deleted } wWidth = 0; wHeight = 0; OFSTRUCT OfStruct; if ((hFile=OpenFile(lpszFileName, &OfStruct, OF_READ)) == -1) { ErrorMessage("Unable to open file."); return FALSE; } PCXHEADER header; if (_lread(hFile,(LPSTR)&header,sizeof(PCXHEADER)) != sizeof(PCXHEADER)) { ErrorMessage("Error reading PCX file header."); return FALSE; } if(header.pcxManufacturer != 0x0a) { _lclose(hFile); ErrorMessage("Not a PCX file."); return FALSE; } wWidth = header.pcxXmax - header.pcxXmin + 1; wHeight = header.pcxYmax - header.pcxYmin + 1; DecodePCX* Decoder; /* Determine PCX file type and create a decoder */ // 256-color file if (header.pcxBitsPixel == 8 && header.pcxPlanes == 1) Decoder = new Decode256PCX(hFile, header); else // 16-color file if (header.pcxBitsPixel == 1 && header.pcxPlanes == 4) Decoder = new Decode16PCX(hFile, header); else // monochrome file if (header.pcxBitsPixel == 1 && header.pcxPlanes == 1) Decoder = new DecodeMonoPCX(hFile, header); else ErrorMessage("Unsupported PCX format."); if (!Decoder) { ErrorMessage("Cannot create PCX decoder."); _lclose(hFile); return FALSE; } SetCursor( LoadCursor(NULL,IDC_WAIT) ); // Create the bitmap hBitmap = Decoder->MakeDIB(hdc); hPalette = Decoder->Palette(); SetCursor( LoadCursor(NULL,IDC_ARROW) ); delete Decoder; _lclose(hFile); return (hBitmap) ? TRUE : FALSE; } /**************************************************************************** METHOD: BOOL PCX::Display(HDC hdc, POINT& pos, RECT& rect) PURPOSE: Displays the DIB PARAMETERS: HDC hdc DC on which DIB is displayed POINT pos Destination positions RECT rect Clipping rectangle on source RETURN: TRUE if DIB was displayed, otherwise FALSE NOTES: Works for MM_TEXT mode only ****************************************************************************/ BOOL PCX::Display(HDC hdc, POINT& pos, RECT& rect) { BOOL bltOk = FALSE; if (hBitmap) { HBITMAP hdcBitmap = CreateCompatibleDC(hdc); HBITMAP hOldBitmap = SelectObject(hdcBitmap, hBitmap); bltOk = BitBlt(hdc, rect.left,rect.top,rect.right,rect.bottom, hdcBitmap,pos.x,pos.y, SRCCOPY); SelectObject(hdcBitmap, hOldBitmap); DeleteDC(hdcBitmap); } return bltOk; } /////////////////////////////////////////////////////////////////////////// // DecodePCX Methods /////////////////////////////////////////////////////////////////////////// /**************************************************************************** METHOD: DecodePCX::DecodePCX(int hfile, PCXHEADER& pcxHeader) PURPOSE: Constructor PARAMETERS: int hfile Handle to open PCX file PCXHEADER pcxHeader PCX header ****************************************************************************/ DecodePCX::DecodePCX(int hfile, PCXHEADER& pcxHeader) { hFile = hfile; // Reset file pointer if (_llseek(hFile, sizeof(PCXHEADER), 0) == -1) ErrorMessage("Error positioning past header."); header = pcxHeader; hPalette = 0; BytesLine = header.pcxBytesLine; width = header.pcxXmax - header.pcxXmin + 1; height = header.pcxYmax - header.pcxYmin + 1; byData = 0; iDataBytes = 0; } /**************************************************************************** METHOD: WORD DecodePCX::read_pcx_line(BYTE huge* lpLineImage) PURPOSE: Decode a PCX RLE scanline PARAMETERS: BYTE huge* lpLineImage Destination of decoded scanline RETURN: Number of bytes decoded ****************************************************************************/ WORD DecodePCX::read_pcx_line(BYTE huge* lpLineImage) { for (WORD n=0; n= 0xc0) { // Get duplication count from lower bits BYTE run_len = byData & 0x3f; // Set run_len bytes if (!next_data()) return n; while(run_len--) lpLineImage[n++]=byData; } else // Set this byte lpLineImage[n++] = byData; } if (n != BytesLine) ErrorMessage("PCX Read Error."); return n; } /**************************************************************************** METHOD: BOOL NEAR PASCAL DecodePCX::next_data() PURPOSE: Read a byte from the file and set to byData RETURN: FALSE on read error NOTES: The output byte is written to byData ****************************************************************************/ BOOL NEAR PASCAL DecodePCX::next_data() { static BYTE fileBuf[5120]; static int index = 0; if (iDataBytes == 0) { if ((iDataBytes = _read(hFile, fileBuf, sizeof(fileBuf))) <= 0) return FALSE; index = 0; } --iDataBytes; byData = *(fileBuf+(index++)); return TRUE; } /////////////////////////////////////////////////////////////////////////// // DecodeMonoPCX Methods /////////////////////////////////////////////////////////////////////////// /**************************************************************************** METHOD: HBITMAP DecodeMonoPCX::MakeDIB(HDC hdc) PURPOSE: Make monochrome DIB PARAMETERS: HDC hdc Handle to compatible DC RETURNS: Handle to DIB, NULL on error ****************************************************************************/ HBITMAP DecodeMonoPCX::MakeDIB(HDC hdc) { int h; LONG lDIBBytesLine = ALIGN_DWORD(BytesLine); LONG image_size = lDIBBytesLine*height; // Allocate memory for the image buffer GlobalCompact(image_size); HANDLE hImageMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, image_size); if (!hImageMem) { ErrorMessage("Out of memory."); return NULL; } BYTE huge* lpImage = (BYTE huge*) GlobalLock(hImageMem); if (!lpImage) { ErrorMessage("Memory error."); return NULL; } for (h=height-1; h>=0; --h) read_pcx_line(lpImage+(lDIBBytesLine*h)); // Create the DIB header PBITMAPINFO pBmi = (PBITMAPINFO) new BYTE[ sizeof(BITMAPINFOHEADER)+2*sizeof(RGBQUAD) ]; if (!pBmi) { ErrorMessage("Out of memory."); GlobalUnlock(hImageMem); GlobalFree(hImageMem); return NULL; } PBITMAPINFOHEADER pBi = &pBmi->bmiHeader; pBi->biSize = sizeof(BITMAPINFOHEADER); pBi->biWidth = width; pBi->biHeight = height; pBi->biPlanes = 1; pBi->biBitCount = 1; pBi->biCompression = 0L; pBi->biSizeImage = 0L; pBi->biXPelsPerMeter = 0L; pBi->biYPelsPerMeter = 0L; pBi->biClrUsed = 0L; pBi->biClrImportant = 0L; // Copy PCX Palette into DIB color table pBmi->bmiColors[0].rgbBlue = header.pcxPalette[0].b; pBmi->bmiColors[0].rgbGreen = header.pcxPalette[0].g; pBmi->bmiColors[0].rgbRed = header.pcxPalette[0].r; pBmi->bmiColors[1].rgbBlue = header.pcxPalette[1].b; pBmi->bmiColors[1].rgbGreen = header.pcxPalette[1].g; pBmi->bmiColors[1].rgbRed = header.pcxPalette[1].r; HBITMAP hBitmap = CreateDIBitmap(hdc, pBi, CBM_INIT, (LPSTR)lpImage, pBmi, DIB_RGB_COLORS); delete pBmi; // Free image buffer GlobalUnlock(hImageMem); GlobalFree(hImageMem); return hBitmap; } /////////////////////////////////////////////////////////////////////////// // Decode16PCX Methods /////////////////////////////////////////////////////////////////////////// /**************************************************************************** METHOD: HBITMAP Decode16PCX::MakeDIB(HDC hdc) PURPOSE: Make 16-color DIB PARAMETERS: HDC hdc Handle to compatible DC RETURNS: Handle to DIB, NULL on error ****************************************************************************/ HBITMAP Decode16PCX::MakeDIB(HDC hdc) { LONG lDIBBytesLine = ALIGN_DWORD( (width+1)/2 ); LONG image_size = lDIBBytesLine*height; // Allocate memory for the image buffer GlobalCompact(image_size); HANDLE hImageMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, image_size); if (!hImageMem) { ErrorMessage("Out of memory."); return NULL; } BYTE huge* lpImage = (BYTE huge*) GlobalLock(hImageMem); if (!lpImage) { ErrorMessage("Memory error."); return NULL; } // 16 color PCX files interleve scanlines for each color BYTE *npPlane[4]; for (int h=0; h<4; ++h) npPlane[h] = new BYTE[BytesLine]; if (!npPlane[0] || !npPlane[1] || !npPlane[2] || !npPlane[3]) { GlobalUnlock(hImageMem); GlobalFree(hImageMem); return NULL; } // 16 color DIB bitmaps have 4 bits per pixel for (h=height-1; h>=0; --h) { read_pcx_line(npPlane[0]); read_pcx_line(npPlane[1]); read_pcx_line(npPlane[2]); read_pcx_line(npPlane[3]); LONG l = (LONG) h * lDIBBytesLine; for (int m=0; mbmiHeader; pBi->biSize = sizeof(BITMAPINFOHEADER); pBi->biWidth = width; pBi->biHeight = height; pBi->biPlanes = 1; pBi->biBitCount = 4; pBi->biCompression = 0L; pBi->biSizeImage = 0L; pBi->biXPelsPerMeter = 0L; pBi->biYPelsPerMeter = 0L; pBi->biClrUsed = 0L; pBi->biClrImportant = 0L; if (header.pcxVersion == 3) // No PCX palette, use literal color values { DWORD* clrTab = (DWORD*)pBmi->bmiColors; clrTab[0] = 0x000000L; clrTab[1] = 0x000080L; clrTab[2] = 0x008000L; clrTab[3] = 0x008080L; clrTab[4] = 0x800000L; clrTab[5] = 0x800080L; clrTab[6] = 0x808000L; clrTab[7] = 0x808080L; clrTab[8] = 0xc0c0c0L; clrTab[9] = 0x0000ffL; clrTab[10] = 0x00ff00L; clrTab[11] = 0x00ffffL; clrTab[12] = 0xff0000L; clrTab[13] = 0xff00ffL; clrTab[14] = 0xffff00L; clrTab[15] = 0xffffffL; } else // Copy PCX palette to DIB color table { for (int i=0; i<16; ++i) { pBmi->bmiColors[i].rgbBlue = header.pcxPalette[i].b; pBmi->bmiColors[i].rgbGreen = header.pcxPalette[i].g; pBmi->bmiColors[i].rgbRed = header.pcxPalette[i].r; pBmi->bmiColors[i].rgbReserved = 0; } } HBITMAP hBitmap = CreateDIBitmap(hdc, pBi, CBM_INIT, (LPSTR)lpImage, pBmi, DIB_RGB_COLORS); delete pBmi; // Free image buffer GlobalUnlock(hImageMem); GlobalFree(hImageMem); return hBitmap; } /////////////////////////////////////////////////////////////////////////// // Decode256PCX Methods /////////////////////////////////////////////////////////////////////////// /**************************************************************************** METHOD: HBITMAP Decode256PCX::MakeDIB(HDC hdc) PURPOSE: Make 256-color DIB PARAMETERS: HDC hdc Handle to compatible DC RETURNS: Handle to DIB, NULL on error ****************************************************************************/ HANDLE Decode256PCX::MakeDIB(HDC hdc) { LONG lDIBBytesLine = ALIGN_DWORD(BytesLine); LONG image_size = lDIBBytesLine*height; // Allocate memory for the image buffer GlobalCompact(image_size); HANDLE hImageMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, image_size); if (!hImageMem) { ErrorMessage("Out of memory."); return NULL; } BYTE huge* lpImage = (BYTE huge*) GlobalLock(hImageMem); if (!lpImage) { ErrorMessage("Memory error."); return NULL; } for (int h=height-1; h>=0; --h) read_pcx_line(lpImage+(lDIBBytesLine*h)); // Create the DIB header PBITMAPINFO pBmi = (PBITMAPINFO) new BYTE[ sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD) ]; if (!pBmi) { ErrorMessage("Out of memory."); GlobalUnlock(hImageMem); GlobalFree(hImageMem); return NULL; } PBITMAPINFOHEADER pBi = &pBmi->bmiHeader; pBi->biSize = sizeof(BITMAPINFOHEADER); pBi->biWidth = width; pBi->biHeight = height; pBi->biPlanes = 1; pBi->biBitCount = 8; pBi->biCompression = 0L; pBi->biSizeImage = 0L; pBi->biXPelsPerMeter = 0L; pBi->biYPelsPerMeter = 0L; pBi->biClrUsed = 0L; pBi->biClrImportant = 0L; // Look for the palette at the end of the file if (_llseek(hFile, -769L, 2) == -1) ErrorMessage("Error seeking palette."); // It should start with a 0Ch byte BYTE Id256Pal; if (!(_read(hFile, &Id256Pal, 1) == 1 && Id256Pal == '\xc')) ErrorMessage("No palette found."); PCXRGB* PalPCX = new PCXRGB[256]; if (_read(hFile, PalPCX, 768) != 768) ErrorMessage("Error reading palette."); // Copy PCX palette to DIB color table for (int i=0; i<256; ++i) { pBmi->bmiColors[i].rgbBlue = PalPCX[i].b; pBmi->bmiColors[i].rgbGreen = PalPCX[i].g; pBmi->bmiColors[i].rgbRed = PalPCX[i].r; pBmi->bmiColors[i].rgbReserved = 0; } delete PalPCX; if (hPalette) DeleteObject(hPalette); // Create and set logical palette if ((hPalette = make_palette(pBmi->bmiColors)) != NULL) { SelectPalette(hdc, hPalette, 0); RealizePalette(hdc); } else { ErrorMessage("Cannot create palette"); } HBITMAP hBitmap = CreateDIBitmap(hdc, pBi, CBM_INIT, (LPSTR)lpImage, pBmi, DIB_RGB_COLORS); delete pBmi; // Free image buffer GlobalUnlock(hImageMem); GlobalFree(hImageMem); return hBitmap; } /**************************************************************************** METHOD: HPALETTE Decode256PCX::make_palette(RGBQUAD* pColors) PURPOSE: Make 256-color Logical Palette PARAMETERS: RGBQUAD[256] pColors Palette colors RETURNS: Handle to Palette, NULL on error ****************************************************************************/ HPALETTE Decode256PCX::make_palette(RGBQUAD* pColors) { if (!pColors) return NULL; PLOGPALETTE pPal = (PLOGPALETTE) new BYTE[ sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY)]; if (!pPal) return NULL; pPal->palNumEntries = 256; pPal->palVersion = 0x300; for (int i=0; i<256; ++i) { pPal->palPalEntry[i].peRed = pColors[i].rgbRed; pPal->palPalEntry[i].peGreen = pColors[i].rgbGreen; pPal->palPalEntry[i].peBlue = pColors[i].rgbBlue; pPal->palPalEntry[i].peFlags = 0; } HPALETTE hPal = CreatePalette(pPal); delete pPal; return hPal; } [LISTING TWO] #include #include "pcxwin.h" #include static char szAppName[] = "PCXWIN"; #define MAXPATH 80 static char szFileName[MAXPATH+1] = ""; static char szUntitled[] = "PCXWIN - (Untitled)"; // Function Prototypes int DoKeyDown(HWND hwnd, WORD wVkey); int DoFileOpenDlg(HANDLE hInst, HWND hwnd); LONG FAR PASCAL _export WndProc(HWND hwnd, WORD message, WORD wParam, LONG lParam); BOOL FAR PASCAL _export FileOpenDlgProc(HWND hDlg, WORD message, WORD wParam, LONG lParam); int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR, int nCmdShow) { if (!hPrevInstance) { WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(hInstance, "PCXWIN"); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = "PCXWIN"; wndclass.lpszClassName = szAppName; RegisterClass(&wndclass); } HWND hwnd = CreateWindow( szAppName, szUntitled, WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); HANDLE hAccel = LoadAccelerators(hInstance, szAppName); MSG msg; while(GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(hwnd, hAccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } LONG FAR PASCAL _export WndProc(HWND hwnd, WORD message, WORD wParam, LONG lParam) { static HANDLE hInst; static PCX* pcx; static Scroller* scroll; HDC hdc; switch(message) { case WM_CREATE : hInst = ((LPCREATESTRUCT) lParam)->hInstance; pcx = new PCX; scroll = new Scroller(hwnd); return 0L; case WM_DESTROY : delete pcx; delete scroll; PostQuitMessage(0); return 0L; case WM_PAINT : PAINTSTRUCT ps; hdc = BeginPaint(hwnd, &ps); RECT rcClient; GetClientRect(hwnd, &rcClient); pcx->Display(hdc, scroll->Pos(), rcClient); EndPaint(hwnd, &ps); return 0L; case WM_QUERYNEWPALETTE: if (pcx->Palette()) { hdc = GetDC(hwnd); SelectPalette(hdc, pcx->Palette(), 0); BOOL b = RealizePalette(hdc); ReleaseDC(hwnd, hdc); if (b) { InvalidateRect(hwnd, NULL, 1); return 1L; } } return 0L; case WM_SIZE : scroll->Size(pcx->Size()); return 0L; case WM_VSCROLL : scroll->Vert(wParam, LOWORD(lParam)); return 0L; case WM_HSCROLL : scroll->Horz(wParam, LOWORD(lParam)); return 0L; case WM_KEYDOWN : return DoKeyDown(hwnd, wParam); case WM_COMMAND : switch (wParam) { case IDM_OPEN : if (DoFileOpenDlg(hInst, hwnd)) { hdc = GetDC(hwnd); if (pcx->Read(szFileName, hdc)) { char wtext[70]; wsprintf(wtext, "PcxWin - %.40s (%u x %u)", AnsiUpper(szFileName),pcx->Width(), pcx->Height()); SetWindowText(hwnd, wtext); } else { SetWindowText(hwnd, szUntitled); } ReleaseDC(hwnd, hdc); POINT ptNewPos = {0,0}; scroll->Pos(ptNewPos); scroll->Size(pcx->Size()); } InvalidateRect(hwnd, NULL, TRUE); break; case IDM_ABOUT: MessageBox(hwnd, "PCXWIN (c) Paul Chui, 1991", "About PCXWIN...", MB_OK | MB_ICONINFORMATION); break; case IDM_EXIT : DestroyWindow(hwnd); break; case IDM_COPY : OpenClipboard(hwnd); EmptyClipboard(); SetClipboardData(CF_BITMAP, pcx->Bitmap()); CloseClipboard(); } return 0L; } return DefWindowProc(hwnd, message, wParam, lParam); } int DoKeyDown(HWND hwnd, WORD wVkey) { switch (wVkey) { case VK_HOME : SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0L); break; case VK_END : SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0L); break; case VK_PRIOR : SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0L); break; case VK_NEXT : SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0L); break; case VK_UP : SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0L); break; case VK_DOWN : SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0L); break; case VK_LEFT : SendMessage(hwnd, WM_HSCROLL, SB_PAGEUP, 0L); break; case VK_RIGHT : SendMessage(hwnd, WM_HSCROLL, SB_PAGEDOWN, 0L); break; } return 0; } BOOL DoFileOpenDlg(HANDLE hInst, HWND hwnd) { FARPROC lpfnFileOpenDlgProc = MakeProcInstance((FARPROC)FileOpenDlgProc, hInst); BOOL bReturn = DialogBox(hInst, "FileOpen", hwnd, lpfnFileOpenDlgProc); FreeProcInstance(lpfnFileOpenDlgProc); return bReturn; } BOOL FAR PASCAL FileOpenDlgProc(HWND hDlg, WORD message, WORD wParam, LONG) { switch(message) { case WM_INITDIALOG : SendDlgItemMessage(hDlg, IDD_FNAME, EM_LIMITTEXT, MAXPATH, 0L); SetDlgItemText(hDlg, IDD_FNAME, szFileName); return TRUE; case WM_COMMAND : switch(wParam) { case IDOK : GetDlgItemText(hDlg, IDD_FNAME, szFileName, MAXPATH); EndDialog(hDlg, TRUE); return TRUE; case IDCANCEL : szFileName[0] = '\0'; // erase the string EndDialog(hDlg, FALSE); return TRUE; } } return FALSE; } [LISTING THREE] #ifndef PCXWIN_H #define PCXWIN_H #define IDM_OPEN 0x10 #define IDM_EXIT 0x11 #define IDM_ABOUT 0x12 #define IDM_COPY 0x20 #define IDD_FNAME 0x20 class PCX { public: PCX(); ~PCX(); virtual BOOL Read(LPSTR lpszFileName, HDC theHdc); virtual BOOL Display(HDC hdc, POINT& pos, RECT& rect); POINT Size(); WORD Width(); WORD Height(); HBITMAP Bitmap(); HPALETTE Palette(); private: WORD wWidth, wHeight; HBITMAP hBitmap; HPALETTE hPalette; int hFile; // Input file handle }; inline POINT PCX::Size() { POINT p = {wWidth,wHeight}; return p; } inline WORD PCX::Width() { return wWidth; } inline WORD PCX::Height() { return wHeight; } inline HBITMAP PCX::Bitmap() { return hBitmap; } inline HPALETTE PCX::Palette() { return hPalette; } class Scroller { public: Scroller(HWND hwnd); int Size(POINT& ptImgSize); int Vert(WORD wSBcode, WORD wSBPos); int Horz(WORD wSBcode, WORD wSBPos); POINT Pos(); POINT Pos(POINT& ptNewPos); private: HWND hClientWnd; POINT ptPos; // Current Scroll position POINT ptMax; // Max Scroll range POINT ptInc; // Scroll increment POINT ptClient; // Size of client window }; inline POINT Scroller::Pos() { return ptPos; } inline POINT Scroller::Pos(POINT& ptNewPos) { return ptPos = ptNewPos; } inline void ErrorMessage(PSTR message) { MessageBox(NULL, (LPSTR) message, (LPSTR) "Error", MB_OK|MB_ICONEXCLAMATION); } /* The standard max and min macros are undefined by BC++ because they may conflict with class-defined macros with the same names. */ #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif [LISTING FOUR] #include #include "pcxwin.h" //////////////////////// Class Scroller //////////////////////////////// Scroller::Scroller(HWND hwnd) { ptPos.x = 0; ptPos.y = 0; ptMax.x = 0; ptMax.y = 0; ptInc.x = 0; ptInc.y = 0; RECT rect; GetClientRect(hwnd, &rect); ptClient.x = rect.right; ptClient.y = rect.bottom; hClientWnd = hwnd; } int Scroller::Size(POINT& ptImgSize) { RECT rect; GetClientRect(hClientWnd, &rect); ptClient.x = rect.right; ptClient.y = rect.bottom; ptMax.x = MAX(0, ptImgSize.x - ptClient.x); ptPos.x = MIN(ptPos.x, ptMax.x); SetScrollRange(hClientWnd, SB_HORZ, 0, ptMax.x, FALSE); SetScrollPos(hClientWnd, SB_HORZ, ptPos.x, TRUE); ptMax.y = MAX(0, ptImgSize.y - ptClient.y); ptPos.y = MIN(ptPos.y, ptMax.y); SetScrollRange(hClientWnd, SB_VERT, 0, ptMax.y, FALSE); SetScrollPos(hClientWnd, SB_VERT, ptPos.y, TRUE); return 0; } int Scroller::Vert(WORD wSBcode, WORD wSBPos) { switch (wSBcode) { case SB_LINEUP : ptInc.y = -1; break; case SB_LINEDOWN : ptInc.y = 1; break; case SB_PAGEUP : ptInc.y = MIN(-1, -ptClient.y/4); break; case SB_PAGEDOWN : ptInc.y = MAX(1, ptClient.y/4); break; case SB_TOP : ptInc.y = -ptInc.y; break; case SB_BOTTOM : ptInc.y = ptMax.y - ptPos.y; break; case SB_THUMBPOSITION : ptInc.y = wSBPos - ptPos.y; break; default : ptInc.y = 0; } if (( ptInc.y = MAX(-ptPos.y, MIN(ptInc.y, ptMax.y - ptPos.y)) ) != 0) { ptPos.y += ptInc.y; ScrollWindow(hClientWnd, 0, -ptInc.y, NULL, NULL); SetScrollPos(hClientWnd, SB_VERT, ptPos.y, TRUE); UpdateWindow(hClientWnd); } return 0; } int Scroller::Horz(WORD wSBcode, WORD wSBPos) { switch (wSBcode) { case SB_LINEUP : ptInc.x = -1; break; case SB_LINEDOWN : ptInc.x = 1; break; case SB_PAGEUP : ptInc.x = MIN(-1, -ptClient.x/4); break; case SB_PAGEDOWN : ptInc.x = MAX(1, ptClient.x/4); break; case SB_THUMBPOSITION : ptInc.x = wSBPos - ptPos.x; break; default : ptInc.x = 0; } if (( ptInc.x = MAX(-ptPos.x, MIN(ptInc.x, ptMax.x - ptPos.x)) ) != 0) { ptPos.x += ptInc.x; ScrollWindow(hClientWnd, -ptInc.x, 0, NULL, NULL); SetScrollPos(hClientWnd, SB_HORZ, ptPos.x, TRUE); UpdateWindow(hClientWnd); } return 0; } [LISTING FIVE] NAME PCXWIN DESCRIPTION 'PCX Viewer (c) Paul Chui, 1991' EXETYPE WINDOWS STUB 'WINSTUB.EXE' CODE PRELOAD MOVEABLE DISCARDABLE DATA PRELOAD MOVABLE MULTIPLE HEAPSIZE 1046 STACKSIZE 8192 PROTMODE [LISTING SIX] #include #include "pcxwin.h" PCXWin MENU BEGIN POPUP "&File" BEGIN MENUITEM "&Open" IDM_OPEN MENUITEM SEPARATOR MENUITEM "E&xit" IDM_EXIT MENUITEM "A&bout PCXWIN..." IDM_ABOUT END POPUP "&Edit" BEGIN MENUITEM "&Copy\tCtrl+Ins" IDM_COPY END END FILEOPEN DIALOG DISCARDABLE LOADONCALL PURE MOVEABLE 10, 35, 129, 56 STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | 0x80L CAPTION "Open File" BEGIN CONTROL "File &name:" -1, "STATIC", WS_CHILD | WS_VISIBLE, 8, 7, 56, 12 CONTROL "" IDD_FNAME, "EDIT", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | 0x80L, 7, 15, 116, 12 CONTROL "OK" IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 15, 36, 40, 12 CONTROL "Cancel" IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 69, 36, 40, 12 END PCXWin ACCELERATORS { VK_INSERT, IDM_COPY, VIRTKEY, CONTROL } [LISTING SEVEN] .AUTODEPEND # *Translator Definitions* CC = bccx +PCXWIN.CFG TASM = TASM TLINK = tlink # *Implicit Rules* .c.obj: $(CC) -c {$< } .cpp.obj: $(CC) -c {$< } # *List Macros* Link_Exclude = \ pcxwin.res Link_Include = \ pcxwin.obj \ showpcx.obj \ scroller.obj \ pcxwin.def # *Explicit Rules* pcxwin.exe: pcxwin.cfg $(Link_Include) $(Link_Exclude) $(TLINK) /v/x/n/c/Twe/P-/LC:\CPP\LIB @&&| c0ws.obj+ pcxwin.obj+ showpcx.obj+ scroller.obj pcxwin # no map file cwins.lib+ import.lib+ maths.lib+ cs.lib pcxwin.def | RC -T pcxwin.res pcxwin.exe # *Individual File Dependencies* pcxwin.obj: pcxwin.cpp showpcx.obj: showpcx.cpp scroller.obj: scroller.cpp pcxwin.res: pcxwin.rc RC -R -IC:\CPP\INCLUDE -FO pcxwin.res PCXWIN.RC # *Compiler Configuration File* pcxwin.cfg: pcxwin.mak copy &&| -v -W -H=PCXWIN.SYM -IC:\CPP\INCLUDE -LC:\CPP\LIB | pcxwin.cfg Example 1: BYTE huge* lpImage = new BYTE[lImageSize]; int h, line, plane; for (h=0, line=0; h