1416 lines
43 KiB
C
1416 lines
43 KiB
C
#include <windows.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <commdlg.h>
|
|
#include "mandel.h"
|
|
|
|
#undef FLOAT
|
|
#define FLOAT double
|
|
|
|
#if DBG
|
|
#define MESSAGEBOX(a, b, c, d) MessageBox((a), (b), (c), (d))
|
|
#else
|
|
#define MESSAGEBOX(a, b, c, d)
|
|
#endif
|
|
|
|
typedef struct _RECTF_ {
|
|
FLOAT left;
|
|
FLOAT top;
|
|
FLOAT right;
|
|
FLOAT bottom;
|
|
} RECTF, *PRECTF;
|
|
|
|
LONG WndProc(HWND, UINT, WPARAM, LPARAM);
|
|
VOID vDrawMandelbrot(HDC, RECTL *, RECTF *, PBYTE);
|
|
VOID vUpdateMenuState(HWND, FLONG);
|
|
HPALETTE hpalSetupPalette(HDC);
|
|
VOID vRotatePalette(HDC, HPALETTE, int);
|
|
BOOL bNewArea(SHORT, SHORT, SHORT, SHORT, RECTL *, RECTF *);
|
|
VOID vSetPaletteEntries(LPPALETTEENTRY, UINT, WPARAM);
|
|
BOOL bOpenFile(HWND, RECTF *);
|
|
BOOL bSaveFile(HWND, RECTF *);
|
|
BOOL bSaveTex(HWND, HDC, RECTL *, RECTF *);
|
|
BOOL bGetRGBQuads(HDC, RGBQUAD *, int, int);
|
|
HBITMAP hbmCreateBitmap(HDC, SIZEL, PVOID *);
|
|
VOID SaveBMP(LPTSTR, RGBQUAD *, HBITMAP, PVOID);
|
|
|
|
PALETTEENTRY gpale[256];
|
|
|
|
/******************************Public*Routine******************************\
|
|
* WinMain
|
|
*
|
|
* History:
|
|
* 03-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
int WINAPI
|
|
WinMain( HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPSTR lpCmdLine,
|
|
int nCmdShow
|
|
)
|
|
{
|
|
static char szAppName[] = "Mandelbrot Set";
|
|
HWND hwnd;
|
|
MSG msg;
|
|
RECT Rect;
|
|
WNDCLASS wndclass;
|
|
char ach[256];
|
|
int size;
|
|
|
|
if ( !hPrevInstance )
|
|
{
|
|
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
|
wndclass.lpfnWndProc = WndProc;
|
|
wndclass.cbClsExtra = 0;
|
|
wndclass.cbWndExtra = 0;
|
|
wndclass.hInstance = hInstance;
|
|
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
|
|
wndclass.hbrBackground = GetStockObject( WHITE_BRUSH );
|
|
wndclass.lpszMenuName = "MandelMenu";
|
|
wndclass.lpszClassName = szAppName;
|
|
wndclass.hIcon = NULL;
|
|
|
|
RegisterClass(&wndclass);
|
|
}
|
|
|
|
// Parse cmd line for size.
|
|
|
|
if (lpCmdLine && *lpCmdLine)
|
|
size = atoi(lpCmdLine);
|
|
else
|
|
size = 256;
|
|
|
|
// Specify a 100 x 100 client area.
|
|
|
|
Rect.left = 0;
|
|
Rect.top = 0;
|
|
Rect.right = size;
|
|
Rect.bottom = size;
|
|
|
|
// Adjust the rectangle based on style.
|
|
|
|
AdjustWindowRect( &Rect, WS_OVERLAPPEDWINDOW, TRUE );
|
|
|
|
// Create the window.
|
|
|
|
hwnd = CreateWindow( szAppName, // window class name
|
|
"Mandelbrot", // window caption
|
|
WS_OVERLAPPEDWINDOW
|
|
| WS_CLIPCHILDREN | WS_CLIPSIBLINGS, // window style
|
|
CW_USEDEFAULT, // initial x position
|
|
CW_USEDEFAULT, // initial y position
|
|
Rect.right - Rect.left, // initial x size
|
|
Rect.bottom - Rect.top, // initial y size
|
|
//GetDesktopWindow(), // parent window handle
|
|
NULL, // parent window handle
|
|
NULL, // window menu handle
|
|
hInstance, // program instance handle
|
|
NULL // creation parameter
|
|
);
|
|
|
|
// Show the window.
|
|
|
|
ShowWindow( hwnd, nCmdShow );
|
|
UpdateWindow( hwnd );
|
|
|
|
// Message loop.
|
|
|
|
while ( GetMessage( &msg, NULL, 0, 0 ))
|
|
{
|
|
TranslateMessage( &msg );
|
|
DispatchMessage( &msg );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* WndProc
|
|
*
|
|
* History:
|
|
* 03-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
LONG
|
|
WndProc ( HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
long lRet = 0;
|
|
PAINTSTRUCT ps;
|
|
HDC hdc;
|
|
RECTL rcl;
|
|
static RECTF rcfReset = { -2.25f, -1.75f, 1.00f, 1.75f };
|
|
static RECTF rcfPrev = { -2.25f, -1.75f, 1.00f, 1.75f };
|
|
static RECTF rcf = { -2.25f, -1.75f, 1.00f, 1.75f };
|
|
static SHORT xMouseStart, yMouseStart, xMouseCur, yMouseCur;
|
|
static BOOL bMouseCapture = FALSE;
|
|
static HPALETTE hpal;
|
|
static BOOL bFirst = TRUE;
|
|
static BOOL bTimer = FALSE;
|
|
static int iPalDelta = -1;
|
|
static FLONG fMenuState = MENUSTATE_COLORBANDS | MENUSTATE_DONTMOVE |
|
|
MENUSTATE_SLOW;
|
|
static UINT uiTick = 100;
|
|
|
|
switch ( message )
|
|
{
|
|
case WM_COMMAND:
|
|
switch (wParam)
|
|
{
|
|
case IDM_GRAYRAMP:
|
|
fMenuState &= ~MENUSTATE_PALMASK;
|
|
fMenuState |= MENUSTATE_GRAYRAMP;
|
|
break;
|
|
case IDM_GRAYBAND:
|
|
fMenuState &= ~MENUSTATE_PALMASK;
|
|
fMenuState |= MENUSTATE_GRAYBAND;
|
|
break;
|
|
case IDM_COLORBANDS:
|
|
fMenuState &= ~MENUSTATE_PALMASK;
|
|
fMenuState |= MENUSTATE_COLORBANDS;
|
|
break;
|
|
case IDM_COPPER:
|
|
fMenuState &= ~MENUSTATE_PALMASK;
|
|
fMenuState |= MENUSTATE_COPPER;
|
|
break;
|
|
|
|
case IDM_DONTMOVE:
|
|
fMenuState &= ~MENUSTATE_ROTMASK;
|
|
fMenuState |= MENUSTATE_DONTMOVE;
|
|
break;
|
|
case IDM_ROTATEUP:
|
|
fMenuState &= ~MENUSTATE_ROTMASK;
|
|
fMenuState |= MENUSTATE_ROTATEUP;
|
|
iPalDelta = 1;
|
|
break;
|
|
case IDM_ROTATEDOWN:
|
|
fMenuState &= ~MENUSTATE_ROTMASK;
|
|
fMenuState |= MENUSTATE_ROTATEDOWN;
|
|
iPalDelta = -1;
|
|
break;
|
|
|
|
case IDM_SLOW:
|
|
fMenuState &= ~MENUSTATE_SPEEDMASK;
|
|
fMenuState |= MENUSTATE_SLOW;
|
|
uiTick = 100;
|
|
break;
|
|
case IDM_MED:
|
|
fMenuState &= ~MENUSTATE_SPEEDMASK;
|
|
fMenuState |= MENUSTATE_MED;
|
|
uiTick = 50;
|
|
break;
|
|
case IDM_FAST:
|
|
fMenuState &= ~MENUSTATE_SPEEDMASK;
|
|
fMenuState |= MENUSTATE_FAST;
|
|
uiTick = 1;
|
|
break;
|
|
|
|
case IDM_RESET_POS:
|
|
rcf = rcfReset;
|
|
break;
|
|
case IDM_PREV_POS:
|
|
{
|
|
RECTF rcfTmp;
|
|
|
|
rcfTmp = rcf;
|
|
rcf = rcfPrev;
|
|
rcfPrev = rcfTmp;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
vUpdateMenuState(hwnd, fMenuState);
|
|
|
|
switch (wParam)
|
|
{
|
|
case IDM_OPENFILE:
|
|
{
|
|
RECTF rcfTmp = rcf;
|
|
|
|
if (bOpenFile(hwnd, &rcf))
|
|
{
|
|
rcfPrev = rcfTmp;
|
|
|
|
// Force redraw with new coordiates.
|
|
|
|
InvalidateRect(hwnd, NULL, FALSE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDM_SAVEFILE:
|
|
bSaveFile(hwnd, &rcf);
|
|
break;
|
|
|
|
case IDM_SAVETEX:
|
|
GetClientRect(hwnd, (LPRECT) &rcl);
|
|
hdc = GetDC(hwnd);
|
|
if (hdc)
|
|
{
|
|
bSaveTex(hwnd, hdc, &rcl, &rcf);
|
|
ReleaseDC(hwnd, hdc);
|
|
}
|
|
break;
|
|
|
|
case IDM_GRAYRAMP:
|
|
case IDM_GRAYBAND:
|
|
case IDM_COLORBANDS:
|
|
case IDM_COPPER:
|
|
vSetPaletteEntries(gpale, 256, wParam);
|
|
hdc = GetDC(hwnd);
|
|
if (hdc)
|
|
{
|
|
vRotatePalette(hdc, hpal, 0);
|
|
ReleaseDC(hwnd, hdc);
|
|
}
|
|
break;
|
|
|
|
case IDM_DONTMOVE:
|
|
if (bTimer)
|
|
{
|
|
KillTimer(hwnd, 1);
|
|
bTimer = FALSE;
|
|
}
|
|
break;
|
|
|
|
case IDM_ROTATEUP:
|
|
case IDM_ROTATEDOWN:
|
|
if (!bTimer)
|
|
{
|
|
SetTimer(hwnd, 1, uiTick, NULL);
|
|
bTimer = TRUE;
|
|
}
|
|
break;
|
|
|
|
case IDM_ROTRESET:
|
|
if (!bTimer)
|
|
{
|
|
hdc = GetDC(hwnd);
|
|
if (hdc)
|
|
{
|
|
vRotatePalette(hdc, hpal, 0);
|
|
ReleaseDC(hwnd, hdc);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDM_SLOW:
|
|
case IDM_MED:
|
|
case IDM_FAST:
|
|
if (bTimer)
|
|
{
|
|
KillTimer(hwnd, 1);
|
|
SetTimer(hwnd, 1, uiTick, NULL);
|
|
}
|
|
break;
|
|
|
|
case IDM_RESET_POS:
|
|
case IDM_PREV_POS:
|
|
|
|
// Force redraw with new coordiates.
|
|
|
|
InvalidateRect(hwnd, NULL, FALSE);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
#if 0
|
|
case WM_CREATE:
|
|
hdc = GetDC(hwnd);
|
|
if (hdc)
|
|
{
|
|
hpal = hpalSetupPalette(hdc);
|
|
ReleaseDC(hwnd, hdc);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case WM_PAINT:
|
|
if (bFirst)
|
|
{
|
|
hdc = GetDC(hwnd);
|
|
if (hdc)
|
|
{
|
|
hpal = hpalSetupPalette(hdc);
|
|
ReleaseDC(hwnd, hdc);
|
|
}
|
|
bFirst = FALSE;
|
|
}
|
|
|
|
GetClientRect(hwnd, (LPRECT) &rcl);
|
|
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
if (hdc)
|
|
{
|
|
vDrawMandelbrot(hdc, &rcl, &rcf, NULL);
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
|
|
// Capture mouse.
|
|
|
|
SetCapture(hwnd);
|
|
bMouseCapture = TRUE;
|
|
|
|
xMouseStart = LOWORD(lParam);
|
|
yMouseStart = HIWORD(lParam);
|
|
xMouseCur = xMouseStart;
|
|
yMouseCur = yMouseStart;
|
|
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
GetClientRect(hwnd, (LPRECT) &rcl);
|
|
|
|
xMouseCur = LOWORD(lParam);
|
|
yMouseCur = HIWORD(lParam);
|
|
|
|
{
|
|
RECTF rcfTmp = rcf;
|
|
|
|
if ( bNewArea(xMouseStart, yMouseStart, xMouseCur, yMouseCur,
|
|
&rcl, &rcf) )
|
|
{
|
|
rcfPrev = rcfTmp;
|
|
|
|
// Force redraw with new coordinates.
|
|
|
|
InvalidateRect(hwnd, NULL, FALSE);
|
|
}
|
|
}
|
|
|
|
// Release mouse.
|
|
|
|
bMouseCapture = FALSE;
|
|
ReleaseCapture();
|
|
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if (bMouseCapture)
|
|
{
|
|
if ( hdc = GetDC(hwnd) )
|
|
{
|
|
// Erase previous rectangle if necessary.
|
|
|
|
if ( (xMouseStart != xMouseCur) &&
|
|
(yMouseStart != yMouseCur) )
|
|
{
|
|
BitBlt(hdc,
|
|
min(xMouseStart, xMouseCur),
|
|
min(yMouseStart, yMouseCur),
|
|
abs(xMouseCur - xMouseStart),
|
|
abs(yMouseCur - yMouseStart),
|
|
NULL,
|
|
0,
|
|
0,
|
|
DSTINVERT);
|
|
}
|
|
|
|
// Get current position.
|
|
|
|
xMouseCur = LOWORD(lParam);
|
|
yMouseCur = HIWORD(lParam);
|
|
|
|
// Draw new rectangle.
|
|
|
|
if ( (xMouseStart != xMouseCur) &&
|
|
(yMouseStart != yMouseCur) )
|
|
{
|
|
BitBlt(hdc,
|
|
min(xMouseStart, xMouseCur),
|
|
min(yMouseStart, yMouseCur),
|
|
abs(xMouseCur - xMouseStart),
|
|
abs(yMouseCur - yMouseStart),
|
|
NULL,
|
|
0,
|
|
0,
|
|
DSTINVERT);
|
|
}
|
|
|
|
ReleaseDC(hwnd, hdc);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
hdc = GetDC(hwnd);
|
|
if (hdc)
|
|
{
|
|
vRotatePalette(hdc, hpal, iPalDelta);
|
|
ReleaseDC(hwnd, hdc);
|
|
}
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
switch (wParam)
|
|
{
|
|
case VK_ESCAPE: // <ESC> is quick exit
|
|
|
|
PostMessage(hwnd, WM_DESTROY, 0, 0);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
DeleteObject(hpal);
|
|
if (bTimer)
|
|
KillTimer(hwnd, 1);
|
|
PostQuitMessage( 0 );
|
|
break;
|
|
|
|
default:
|
|
lRet = DefWindowProc( hwnd, message, wParam, lParam );
|
|
break;
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* vUpdateMenuState
|
|
*
|
|
* Make menu consistent with given state (checked/unchecked, grayed/ungrayed,
|
|
* etc.).
|
|
*
|
|
* History:
|
|
* 03-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID vUpdateMenuState(HWND hwnd, FLONG flState)
|
|
{
|
|
HMENU hmen;
|
|
|
|
hmen = GetMenu(hwnd);
|
|
|
|
CheckMenuItem(hmen, IDM_GRAYRAMP , (flState & MENUSTATE_GRAYRAMP ) ? MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmen, IDM_GRAYBAND , (flState & MENUSTATE_GRAYBAND ) ? MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmen, IDM_COLORBANDS, (flState & MENUSTATE_COLORBANDS) ? MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmen, IDM_COPPER , (flState & MENUSTATE_COPPER ) ? MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmen, IDM_DONTMOVE , (flState & MENUSTATE_DONTMOVE ) ? MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmen, IDM_ROTATEUP , (flState & MENUSTATE_ROTATEUP ) ? MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmen, IDM_ROTATEDOWN, (flState & MENUSTATE_ROTATEDOWN) ? MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmen, IDM_SLOW , (flState & MENUSTATE_SLOW ) ? MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmen, IDM_MED , (flState & MENUSTATE_MED ) ? MF_CHECKED : MF_UNCHECKED);
|
|
CheckMenuItem(hmen, IDM_FAST , (flState & MENUSTATE_FAST ) ? MF_CHECKED : MF_UNCHECKED);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* vDrawMandelbrot
|
|
*
|
|
* Plot Mandelbrot set in the complex region described by prcf. The prcl
|
|
* describes the dimensions of the window bound to the hdc.
|
|
*
|
|
* !!!dbug
|
|
* There is a problem with using SetPixel with the memdc (unknown at this
|
|
* time whether it is an app or GDI bug). As a workaround, the pjBits
|
|
* and iDir parameters are added. If pjBits is non-NULL, then we draw
|
|
* directly into the bitmap array.
|
|
*
|
|
* History:
|
|
* 03-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
void vDrawMandelbrot(HDC hdc, RECTL *prcl, RECTF *prcf, PBYTE pjBits)
|
|
{
|
|
FLOAT x, y, x2, y2, dp, dq, p;
|
|
FLOAT *q;
|
|
LONG xWnd, yWnd;
|
|
LONG cx, cy;
|
|
LONG jColor, jMax = 256;
|
|
|
|
cx = prcl->right - prcl->left;
|
|
cy = prcl->bottom - prcl->top;
|
|
|
|
if ( !cx || !cy )
|
|
return;
|
|
|
|
q = (FLOAT *) LocalAlloc(LMEM_FIXED, sizeof(FLOAT) * cy);
|
|
if (!q)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("vDrawMandelbrot: LocalAlloc failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
return;
|
|
}
|
|
|
|
dp = (prcf->right - prcf->left) / cx;
|
|
dq = (prcf->bottom - prcf->top) / cy;
|
|
|
|
for (yWnd = 0; yWnd < cy; yWnd++)
|
|
q[yWnd] = prcf->top + (yWnd * dq);
|
|
|
|
for (xWnd = 0; xWnd < cx; xWnd++)
|
|
{
|
|
p = prcf->left + (xWnd * dp);
|
|
|
|
for (yWnd = 0; yWnd < cy; yWnd++)
|
|
{
|
|
x = 0.0f;
|
|
y = 0.0f;
|
|
|
|
for (jColor = 0; jColor < jMax; jColor++)
|
|
{
|
|
x2 = x * x;
|
|
y2 = y * y;
|
|
|
|
if ( (x2 + y2) > 4.0f )
|
|
break;
|
|
|
|
y = (2 * x * y) + q[yWnd];
|
|
x = x2 - y2 + p;
|
|
}
|
|
|
|
//!!!dbug -- SetPixel does not seem to work when used with my
|
|
//!!!dbug memdc. I don't know if this is an NT bug or an
|
|
//!!!dbug app bug. For now, pass pointer to bitmap bits in
|
|
//!!!dbug and draw directly into bitmap.
|
|
|
|
if (pjBits)
|
|
{
|
|
PBYTE pjDst;
|
|
|
|
// Note: bitmap has bottom-up orientation, hence we "flip"
|
|
// the y.
|
|
|
|
pjDst = pjBits + xWnd + (((prcl->bottom - prcl->top - 1) - yWnd)
|
|
* (prcl->right - prcl->left));
|
|
*pjDst = (BYTE) (jColor & 255);
|
|
}
|
|
else
|
|
SetPixel(hdc, xWnd, yWnd, PALETTEINDEX(jColor & 255));
|
|
}
|
|
}
|
|
|
|
LocalFree(q);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* hpalSetupPalette
|
|
*
|
|
* Create and select palette into DC.
|
|
*
|
|
* Returns:
|
|
* Palette handle if successful, NULL otherwise.
|
|
*
|
|
* History:
|
|
* 03-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
HPALETTE hpalSetupPalette(HDC hdc)
|
|
{
|
|
HPALETTE hpal = (HPALETTE) NULL;
|
|
LOGPALETTE *ppal;
|
|
int i;
|
|
|
|
ppal = (PLOGPALETTE) LocalAlloc(LMEM_FIXED, sizeof(LOGPALETTE) +
|
|
256 * sizeof(PALETTEENTRY));
|
|
if (ppal)
|
|
{
|
|
ppal->palVersion = 0x300;
|
|
ppal->palNumEntries = 256;
|
|
|
|
vSetPaletteEntries(ppal->palPalEntry, 256, IDM_COLORBANDS);
|
|
vSetPaletteEntries(gpale, 256, IDM_COLORBANDS);
|
|
|
|
hpal = CreatePalette(ppal);
|
|
if (hpal)
|
|
{
|
|
SelectPalette(hdc, hpal, FALSE);
|
|
RealizePalette(hdc);
|
|
}
|
|
|
|
LocalFree(ppal);
|
|
}
|
|
|
|
return hpal;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* vRotatePalette
|
|
*
|
|
* Animate palette by shifting palette by given increment (can be +/-).
|
|
* If the given increment is 0, reset the offset back to 0.
|
|
*
|
|
* History:
|
|
* 03-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID vRotatePalette(HDC hdc, HPALETTE hpal, int iIncr)
|
|
{
|
|
PALETTEENTRY pale[256];
|
|
int i;
|
|
static int iOffset = 0;
|
|
|
|
if (iIncr)
|
|
iOffset = (iOffset + iIncr) & 255;
|
|
else
|
|
iOffset = 0;
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
pale[(i + iOffset) & 255] = gpale[i];
|
|
}
|
|
|
|
AnimatePalette(hpal, 0, 256, pale);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* bNewArea
|
|
*
|
|
* Compute new complex region corresponding to the given window coordinates.
|
|
*
|
|
* Returns:
|
|
* TRUE if new region is valid, FALSE otherwise.
|
|
*
|
|
* History:
|
|
* 03-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL bNewArea(SHORT xMouseStart, SHORT yMouseStart,
|
|
SHORT xMouseCur, SHORT yMouseCur,
|
|
RECTL *prcl, RECTF *prcf)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
FLOAT fStart, fCur;
|
|
|
|
if ( (xMouseCur != xMouseStart) && (yMouseCur != yMouseStart) )
|
|
{
|
|
// Compute new left/right complex coordinates.
|
|
|
|
fStart = prcf->left + ((((FLOAT) (xMouseStart - prcl->left)) /
|
|
((FLOAT) (prcl->right - prcl->left))) *
|
|
(prcf->right - prcf->left));
|
|
fCur = prcf->left + ((((FLOAT) (xMouseCur - prcl->left)) /
|
|
((FLOAT) (prcl->right - prcl->left))) *
|
|
(prcf->right - prcf->left));
|
|
|
|
if (xMouseCur > xMouseStart)
|
|
{
|
|
prcf->left = fStart;
|
|
prcf->right = fCur;
|
|
}
|
|
else
|
|
{
|
|
prcf->left = fCur;
|
|
prcf->right = fStart;
|
|
}
|
|
|
|
// Compute new top/bottom complex coordinates.
|
|
|
|
fStart = prcf->top + ((((FLOAT) (yMouseStart - prcl->top)) /
|
|
((FLOAT) (prcl->bottom - prcl->top))) *
|
|
(prcf->bottom - prcf->top));
|
|
fCur = prcf->top + ((((FLOAT) (yMouseCur - prcl->top)) /
|
|
((FLOAT) (prcl->bottom - prcl->top))) *
|
|
(prcf->bottom - prcf->top));
|
|
|
|
if (yMouseCur > yMouseStart)
|
|
{
|
|
prcf->top = fStart;
|
|
prcf->bottom = fCur;
|
|
}
|
|
else
|
|
{
|
|
prcf->top = fCur;
|
|
prcf->bottom = fStart;
|
|
}
|
|
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* vSetRamp
|
|
*
|
|
* Initialize the specified PALETTEENTRY array starting with the index
|
|
* specified by iStart and for the number of entries specified by cpal.
|
|
* The data is a linear interpolation of the colors between the start
|
|
* and end colors specified by crStart and crEnd, respectively.
|
|
*
|
|
* History:
|
|
* 04-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID vSetRamp(LPPALETTEENTRY ppal, int iStart, int cpal,
|
|
COLORREF crStart, COLORREF crEnd)
|
|
{
|
|
BYTE rStart, gStart, bStart;
|
|
BYTE rEnd, gEnd, bEnd;
|
|
int i;
|
|
|
|
rStart = GetRValue(crStart);
|
|
gStart = GetGValue(crStart);
|
|
bStart = GetBValue(crStart);
|
|
|
|
rEnd = GetRValue(crEnd);
|
|
gEnd = GetGValue(crEnd);
|
|
bEnd = GetBValue(crEnd);
|
|
|
|
for (i = 0; i < cpal; i++)
|
|
{
|
|
ppal[i+iStart].peRed = rStart + (i * (rEnd-rStart) / (cpal-1));
|
|
ppal[i+iStart].peGreen = gStart + (i * (gEnd-gStart) / (cpal-1));
|
|
ppal[i+iStart].peBlue = bStart + (i * (bEnd-bStart) / (cpal-1));
|
|
ppal[i+iStart].peFlags = PC_RESERVED | PC_NOCOLLAPSE;
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* vSetPaletteEntries
|
|
*
|
|
* Intialize the specified PALETTEENTRY array (pointed to by ppal and cpal
|
|
* entries long) with the type of color data indicated by wType.
|
|
*
|
|
* wType:
|
|
*
|
|
* IDM_GRAYRAMP a simple gray scale ramp from black to white
|
|
*
|
|
* IDM_GRAYBAND a gray scale from black to white and then back to black
|
|
*
|
|
* IDM_COLORBANDS several different color bands each of which ramp from
|
|
* black to max and back to black.
|
|
*
|
|
* History:
|
|
* 03-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID vSetPaletteEntries(LPPALETTEENTRY ppal, UINT cpal, WPARAM wType)
|
|
{
|
|
int i, iStep;
|
|
COLORREF crBlack, crMixed;
|
|
|
|
switch (wType)
|
|
{
|
|
case IDM_GRAYRAMP:
|
|
vSetRamp(ppal, 0, cpal, RGB(0,0,0), RGB(255,255,255));
|
|
break;
|
|
|
|
case IDM_GRAYBAND:
|
|
vSetRamp(ppal, 0, cpal/2, RGB(0,0,0), RGB(255,255,255));
|
|
vSetRamp(ppal, cpal/2, cpal/2, RGB(255,255,255), RGB(0,0,0));
|
|
break;
|
|
|
|
case IDM_COLORBANDS:
|
|
i = 0;
|
|
iStep = cpal/16;
|
|
crBlack = RGB(0,0,0);
|
|
|
|
crMixed = RGB(255,0,0);
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
crMixed = RGB(255,127,0);
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
crMixed = RGB(255,255,0);
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
crMixed = RGB(0,255,0);
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
crMixed = RGB(0,255,255);
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
crMixed = RGB(0,0,255);
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
crMixed = RGB(127,0,255);
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
crMixed = RGB(255,0,255);
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
break;
|
|
|
|
case IDM_COPPER:
|
|
i = 0;
|
|
iStep = cpal/16;
|
|
crBlack = RGB(0,0,0);
|
|
|
|
crMixed = RGB(255,127,0);
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
vSetRamp(ppal, i , iStep, crBlack, crMixed);
|
|
vSetRamp(ppal, i + iStep, iStep, crMixed, crBlack);
|
|
i += 2*iStep;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* bOpenFile
|
|
*
|
|
* Prompt user to open a file. Initialize the specified RECTF file with the
|
|
* contents of the file.
|
|
*
|
|
* History:
|
|
* 04-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL bOpenFile(HWND hwnd, RECTF *prcf)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
HANDLE hMap = (HANDLE) NULL;
|
|
PVOID pvFile = (PVOID) NULL;
|
|
static OPENFILENAME ofn;
|
|
static TCHAR achFileName[MAX_PATH] = "";
|
|
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwnd;
|
|
ofn.hInstance = NULL;
|
|
ofn.lpstrFilter = TEXT("Mandelbrot Region (*.MND)\0*.MND\0");
|
|
ofn.lpstrCustomFilter = (LPTSTR) NULL;
|
|
ofn.nMaxCustFilter = 0;
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrFile = achFileName;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.lpstrFileTitle = (LPTSTR) NULL;
|
|
ofn.nMaxFileTitle = 0;
|
|
ofn.lpstrInitialDir = (LPTSTR) NULL;
|
|
ofn.lpstrTitle = TEXT("Open Mandelbrot Region");
|
|
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_HIDEREADONLY;
|
|
ofn.nFileOffset = 0;
|
|
ofn.nFileExtension = 0;
|
|
ofn.lpstrDefExt = TEXT("mnd");
|
|
ofn.lCustData = 0;
|
|
ofn.lpfnHook = (LPOFNHOOKPROC) NULL;
|
|
ofn.lpTemplateName = (LPTSTR) NULL;
|
|
|
|
if (!GetOpenFileName(&ofn))
|
|
{
|
|
goto bOpenFile_exit;
|
|
}
|
|
|
|
hFile = CreateFile(achFileName, GENERIC_READ,
|
|
FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("bOpenFile: CreateFile failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
goto bOpenFile_exit;
|
|
}
|
|
|
|
hMap = CreateFileMapping(hFile, (LPSECURITY_ATTRIBUTES) NULL,
|
|
PAGE_READONLY, 0, 0, (LPCTSTR) NULL);
|
|
if (!hMap)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("bOpenFile: CreateFileMapping failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
goto bOpenFile_exit;
|
|
}
|
|
|
|
pvFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
|
if (!pvFile)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("bOpenFile: MapViewOfFile failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
goto bOpenFile_exit;
|
|
}
|
|
|
|
*prcf = *((RECTF *) pvFile);
|
|
|
|
bRet = TRUE;
|
|
|
|
bOpenFile_exit:
|
|
|
|
if (pvFile)
|
|
UnmapViewOfFile(pvFile);
|
|
|
|
if (hMap)
|
|
CloseHandle(hMap);
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hFile);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* bSaveFile
|
|
*
|
|
* Prompt user for a save file name. Save the specified RECTF file to the
|
|
* file, creating or overwriting as necessary.
|
|
*
|
|
* History:
|
|
* 04-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL bSaveFile(HWND hwnd, RECTF *prcf)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
HANDLE hMap = (HANDLE) NULL;
|
|
PVOID pvFile = (PVOID) NULL;
|
|
DWORD dwBytes;
|
|
static OPENFILENAME ofn;
|
|
static TCHAR achFileName[MAX_PATH] = "";
|
|
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwnd;
|
|
ofn.hInstance = NULL;
|
|
ofn.lpstrFilter = TEXT("Mandelbrot Region (*.MND)\0*.MND\0");
|
|
ofn.lpstrCustomFilter = (LPTSTR) NULL;
|
|
ofn.nMaxCustFilter = 0;
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrFile = achFileName;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.lpstrFileTitle = (LPTSTR) NULL;
|
|
ofn.nMaxFileTitle = 0;
|
|
ofn.lpstrInitialDir = (LPTSTR) NULL;
|
|
ofn.lpstrTitle = TEXT("Save Mandelbrot Region");
|
|
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_HIDEREADONLY;
|
|
ofn.nFileOffset = 0;
|
|
ofn.nFileExtension = 0;
|
|
ofn.lpstrDefExt = TEXT("mnd");
|
|
ofn.lCustData = 0;
|
|
ofn.lpfnHook = (LPOFNHOOKPROC) NULL;
|
|
ofn.lpTemplateName = (LPTSTR) NULL;
|
|
|
|
if (!GetSaveFileName(&ofn))
|
|
{
|
|
goto bSaveFile_exit;
|
|
}
|
|
|
|
hFile = CreateFile(achFileName, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("bSaveFile: CreateFile failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
goto bSaveFile_exit;
|
|
}
|
|
|
|
if (WriteFile(hFile, (LPCVOID) prcf, sizeof(*prcf), &dwBytes,
|
|
(LPOVERLAPPED) NULL) && dwBytes)
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("bSaveFile: WriteFile failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
}
|
|
|
|
bSaveFile_exit:
|
|
|
|
if (pvFile)
|
|
UnmapViewOfFile(pvFile);
|
|
|
|
if (hMap)
|
|
CloseHandle(hMap);
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hFile);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* bSaveTex
|
|
*
|
|
* History:
|
|
* 04-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
LPTSTR achExt[] = { TEXT("a8"), TEXT("bmp") };
|
|
LPTSTR achDotExt[] = { TEXT(".a8"), TEXT(".bmp") };
|
|
|
|
BOOL bSaveTex(HWND hwnd, HDC hdc, RECTL *prcl, RECTF *prcf)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
HDC hdcMem = (HDC) NULL;
|
|
HBITMAP hbm = (HBITMAP) NULL;
|
|
PVOID pvBits;
|
|
SIZEL sizl;
|
|
RGBQUAD rgb[256];
|
|
TCHAR achFileName[MAX_PATH] = TEXT("");
|
|
static OPENFILENAME ofn;
|
|
static DWORD nFilterIndex = 1;
|
|
|
|
// Query user for name of texture file.
|
|
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwnd;
|
|
ofn.hInstance = NULL;
|
|
ofn.lpstrFilter = TEXT("8 bit Alpha-Texture Format (*.A8)\0*.A8\0Bitmap Format (*.BMP)\0*.BMP\0");
|
|
ofn.lpstrCustomFilter = (LPTSTR) NULL;
|
|
ofn.nMaxCustFilter = 0;
|
|
ofn.nFilterIndex = nFilterIndex;
|
|
ofn.lpstrFile = achFileName;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.lpstrFileTitle = (LPTSTR) NULL;
|
|
ofn.nMaxFileTitle = 0;
|
|
ofn.lpstrInitialDir = (LPTSTR) NULL;
|
|
ofn.lpstrTitle = TEXT("Save Image");
|
|
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_HIDEREADONLY;
|
|
ofn.nFileOffset = 0;
|
|
ofn.nFileExtension = 0;
|
|
ofn.lpstrDefExt = (LPTSTR) NULL;
|
|
ofn.lCustData = 0;
|
|
ofn.lpfnHook = (LPOFNHOOKPROC) NULL;
|
|
ofn.lpTemplateName = (LPTSTR) NULL;
|
|
|
|
if (!GetSaveFileName(&ofn))
|
|
{
|
|
goto bSaveTex_exit;
|
|
}
|
|
nFilterIndex = ofn.nFilterIndex; // save for next time
|
|
|
|
// Append the correct extension if needed.
|
|
|
|
if (ofn.nFileExtension)
|
|
{
|
|
// If the file extension offset points to a NULL, then filename has
|
|
// no extension. Use the filter index (1-based) to choose the correct
|
|
// extension to append.
|
|
|
|
if (!achFileName[ofn.nFileExtension])
|
|
lstrcpy(&achFileName[ofn.nFileExtension], achDotExt[ofn.nFilterIndex - 1]);
|
|
}
|
|
else
|
|
{
|
|
// Filename has no extension and ends in a period (eg., "foo.").
|
|
|
|
lstrcat(achFileName, achExt[ofn.nFilterIndex - 1]);
|
|
}
|
|
|
|
// Create a memory DC and DIB section into which we will draw the
|
|
// Mandelbrot plot.
|
|
|
|
hdcMem = CreateCompatibleDC(hdc);
|
|
if (!hdcMem)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("bSaveTex: CreateCompatibleDC failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
goto bSaveTex_exit;
|
|
}
|
|
|
|
sizl.cx = prcl->right - prcl->left;
|
|
sizl.cy = prcl->bottom - prcl->top;
|
|
|
|
hbm = hbmCreateBitmap(hdc, sizl, &pvBits);
|
|
if (!hbm)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("bSaveTex: hbmCreateBitmap failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
goto bSaveTex_exit;
|
|
}
|
|
|
|
if (!SelectObject(hdcMem, hbm))
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("bSaveTex: SelectObject failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
goto bSaveTex_exit;
|
|
}
|
|
|
|
// Draw and save the results to the file using the palettized text format (A8).
|
|
|
|
vDrawMandelbrot(hdcMem, prcl, prcf, (PBYTE) pvBits);
|
|
bGetRGBQuads(hdc, rgb, 0, 256);
|
|
if (ofn.nFilterIndex == 1)
|
|
SaveA8(achFileName, sizl.cx, sizl.cy, rgb, (DWORD) -1, pvBits);
|
|
else
|
|
SaveBMP(achFileName, rgb, hbm, pvBits);
|
|
|
|
bRet = TRUE;
|
|
|
|
bSaveTex_exit:
|
|
if (hbm)
|
|
DeleteObject(hbm);
|
|
|
|
if (hdcMem)
|
|
DeleteDC(hdcMem);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* hbmCreateBitmap
|
|
*
|
|
* Create an 8bpp DIB section with a color table equal to the logical
|
|
* palette in the specified DC.
|
|
*
|
|
* Returns:
|
|
* If successful, returns a valid bitmap handle and sets ppvBits to point
|
|
* to the DIB section memory. Otherwise, returns NULL.
|
|
*
|
|
* History:
|
|
* 04-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
HBITMAP hbmCreateBitmap(HDC hdc, SIZEL sizl, PVOID *ppvBits)
|
|
{
|
|
BITMAPINFO *pbmi;
|
|
HBITMAP hbmRet = (HBITMAP) NULL;
|
|
size_t cjbmi;
|
|
|
|
// Allocate the BITMAPINFO structure and color table.
|
|
|
|
*ppvBits = (PVOID) NULL;
|
|
cjbmi = sizeof(BITMAPINFO) + 256*sizeof(RGBQUAD);
|
|
pbmi = LocalAlloc(LMEM_FIXED, cjbmi);
|
|
if (pbmi)
|
|
{
|
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pbmi->bmiHeader.biWidth = sizl.cx;
|
|
pbmi->bmiHeader.biHeight = sizl.cy;
|
|
pbmi->bmiHeader.biPlanes = 1;
|
|
pbmi->bmiHeader.biBitCount = 8;
|
|
pbmi->bmiHeader.biCompression = BI_RGB;
|
|
pbmi->bmiHeader.biSizeImage = 0;
|
|
pbmi->bmiHeader.biXPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biYPelsPerMeter = 0;
|
|
pbmi->bmiHeader.biClrUsed = 0;
|
|
pbmi->bmiHeader.biClrImportant = 0;
|
|
|
|
// Initialize DIB color table.
|
|
|
|
if (bGetRGBQuads(hdc, &pbmi->bmiColors[0], 0, 256))
|
|
{
|
|
// Create DIB section.
|
|
|
|
hbmRet = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
|
|
if ( hbmRet == (HBITMAP) NULL )
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("hbmCreateBitmap: CreateDIBSection failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("hbmCreateBitmap: bGetRGBQuads failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
}
|
|
|
|
LocalFree(pbmi);
|
|
}
|
|
else
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("hbmCreateBitmap: LocalAlloc failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
}
|
|
|
|
return hbmRet;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* bGetRBBQuads
|
|
*
|
|
* Fills the specified RGBQUAD array with the contents of the logical palette
|
|
* selected into the specified HDC.
|
|
*
|
|
* History:
|
|
* 04-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
BOOL bGetRGBQuads(HDC hdc, RGBQUAD *prgb, int iStart, int crgb)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
PALETTEENTRY lppe[256];
|
|
|
|
if ( GetPaletteEntries(GetCurrentObject(hdc, OBJ_PAL),
|
|
iStart, crgb, lppe) )
|
|
{
|
|
UINT i;
|
|
|
|
// Convert to RGBQUAD.
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
prgb[i].rgbRed = lppe[i].peRed;
|
|
prgb[i].rgbGreen = lppe[i].peGreen;
|
|
prgb[i].rgbBlue = lppe[i].peBlue;
|
|
prgb[i].rgbReserved = 0;
|
|
}
|
|
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("bGetRGBQuads: GetPaletteEntries failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* SaveBMP
|
|
*
|
|
* Save bitmap to a bitmap (.BMP) file. Only 8bpp is supported.
|
|
*
|
|
* History:
|
|
* 05-Jan-1996 -by- Gilman Wong [gilmanw]
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
|
|
VOID SaveBMP(LPTSTR achFileName, RGBQUAD *rgb, HBITMAP hbm, PVOID pvBits)
|
|
{
|
|
BITMAPFILEHEADER bmf;
|
|
BITMAPINFO bmi;
|
|
BITMAP bm;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
|
|
// Open the file.
|
|
|
|
hFile = CreateFile(achFileName, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("SaveBMP: CreateFile failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
goto SaveBMP_exit;
|
|
}
|
|
|
|
// Get bitmap info.
|
|
|
|
if (GetObject(hbm, sizeof(bm), &bm))
|
|
{
|
|
// Only bother supporting 8bpp for now.
|
|
|
|
if (bm.bmBitsPixel == 8)
|
|
{
|
|
DWORD dwBytes;
|
|
|
|
bmf.bfType = 0x4d42; // 'BM'
|
|
bmf.bfReserved1 = 0;
|
|
bmf.bfReserved2 = 0;
|
|
bmf.bfOffBits = sizeof(bmf) + offsetof(BITMAPINFO, bmiColors)
|
|
+ 256*sizeof(RGBQUAD);
|
|
bmf.bfSize = bmf.bfOffBits + (bm.bmWidthBytes * bm.bmHeight);
|
|
|
|
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
bmi.bmiHeader.biWidth = bm.bmWidth;
|
|
bmi.bmiHeader.biHeight = bm.bmHeight;
|
|
bmi.bmiHeader.biPlanes = 1;
|
|
bmi.bmiHeader.biBitCount = bm.bmBitsPixel;
|
|
bmi.bmiHeader.biCompression = BI_RGB;
|
|
bmi.bmiHeader.biSizeImage = 0;
|
|
bmi.bmiHeader.biXPelsPerMeter = 0;
|
|
bmi.bmiHeader.biYPelsPerMeter = 0;
|
|
bmi.bmiHeader.biClrUsed = 0;
|
|
bmi.bmiHeader.biClrImportant = 0;
|
|
|
|
if (!WriteFile(hFile, (LPCVOID) &bmf, sizeof(bmf),
|
|
&dwBytes, (LPOVERLAPPED) NULL) && dwBytes)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("SaveBMP: WriteFile BITMAPFILEHEADER failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
}
|
|
|
|
if (!WriteFile(hFile, (LPCVOID) &bmi, offsetof(BITMAPINFO, bmiColors),
|
|
&dwBytes, (LPOVERLAPPED) NULL) && dwBytes)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("SaveBMP: WriteFile BITMAPINFO failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
}
|
|
|
|
if (!WriteFile(hFile, (LPCVOID) rgb, 256*sizeof(RGBQUAD),
|
|
&dwBytes, (LPOVERLAPPED) NULL) && dwBytes)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("SaveBMP: WriteFile color table failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
}
|
|
|
|
if (!WriteFile(hFile, (LPCVOID) pvBits, bm.bmWidthBytes * bm.bmHeight,
|
|
&dwBytes, (LPOVERLAPPED) NULL) && dwBytes)
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("SaveBMP: WriteFile bitmap bits failed"),
|
|
TEXT("ERROR"), MB_OK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MESSAGEBOX(NULL, TEXT("SaveBMP: only 8bpp supported"),
|
|
TEXT("ERROR"), MB_OK);
|
|
}
|
|
}
|
|
|
|
SaveBMP_exit:
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hFile);
|
|
}
|