Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C++

Generating Fractals with SSE/SSE2

Rate me:
Please Sign up or sign in to vote.
4.94/5 (94 votes)
29 Nov 2005CPOL32 min read 448.5K   2.5K   87  
An article on generating Mandelbrot and Julia sets using Intel's Streaming SIMD Extensions (SSE, SSE2).
// (c) Kankowski Peter, 2004-2005. kankowski@narod.ru

#include "tech.h"
#define WM_MOUSEWHEEL	0x20A
#define TIMER_ZOOMIN  100
#define TIMER_ZOOMOUT 101

LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);

HWND hWnd = NULL, hDlg = NULL, hToolbar = NULL;
const char classname[] = "kankowski_fractals";
WNDCLASS wc = {CS_HREDRAW|CS_VREDRAW |CS_DBLCLKS, (WNDPROC)WndProc, 0, 0,
  NULL, NULL, NULL, NULL, NULL, classname};
WNDPROC OldProc; HACCEL hAccel;

BITMAPINFOHEADER bi = {sizeof(BITMAPINFOHEADER), 0, 0, 1, 32, BI_RGB, 0, 0, 0, 0, 0};
BITMAPINFOHEADER bis = {sizeof(BITMAPINFOHEADER), 0, 0, 1, 32, BI_RGB, 0, 0, 0, 0, 0};

HBITMAP hBMP, hsBMP;
long *bits=0, *sbits;
char buff[4096];
const double LEFTm = -2.375, RIGHTm = 0.775, TOPm = -1.1, BOTTOMm = 1.1;
const double LEFTj = -1.575, RIGHTj = 1.575, TOPj = -1.1, BOTTOMj = 1.1;
//const double LEFTm = -0.84, RIGHTm = -0.79, TOPm = 0.18, BOTTOMm = 0.21;
//const double LEFTj = -0.897, RIGHTj = -0.894, TOPj = 0.673, BOTTOMj = 0.675;
double LEFT = LEFTj, RIGHT = RIGHTj,
       TOP = TOPj, BOTTOM = BOTTOMj;
#define zoomspeed 400
int SSEmode=1, SSEsupported=0, SSE2supported=0, ms=zoomspeed;
#define ITER 64
COLORREF a[ITER+1];
LARGE_INTEGER freq;
DWORD bz; RECT clientrect = {0, 0, 0, 0};
HBITMAP holdBMP; HDC hmem;
LPDIRECTDRAW DD = 0;
LPDIRECTDRAWSURFACE PrimarySurface = 0;
LPDIRECTDRAWSURFACE OffscreenSurface = 0;
LPDIRECTDRAWCLIPPER Clipper = 0;
WPARAM WindowActive;
int DDsupported = 1, DDmode = 1, movadd = 1;

typedef HRESULT (WINAPI *DIRECTDRAWCREATE)(GUID*, LPDIRECTDRAW*, IUnknown*);
HMODULE ddraw = 0;
struct MYRGNDATA {
	RGNDATAHEADER rdh;
	RECT r[4];
};

extern "C" void PaintJuliaMandelSSE(int w, int h, float dx, float dy, long* bits,
					COLORREF* a, float left, float top, float px, float py, int piw,
					int type, int bw);
extern "C" void PaintJuliaMandelSSE2(int w, int h, double dx, double dy, long* bits,
					COLORREF* a, double left, double top, double px, double py, int piw,
					int type, int bw);
extern "C" int IsSSE();
extern "C" int IsSSE2();
extern "C" int IsMovAdd();


#define ID_ARROW 100
#define ID_ZOOM 101
#define ID_PIP 102
#define ID_MANDEL 103
#define ID_JULIA 104
#define ID_BW 105
#define ID_AXIS 106
#define ID_SSE 107
#define ID_HOME 108
#define IDC_TOOLBAR 666
char* tooltips[] = {"Table and graph of iterates for the selected point (F2)",
	"Zoom (F3)", "Picture in picture: correspondence between Mandelbrot and Julia sets (F4)",
	"Mandelbrot set (Space to toggle)", "Julia set (Space to toggle)",
	"Color on/off (F5)", "Axes on/off (F6)", "Drawing mode (F7)", "Back to initial size (F8)"};
ACCEL accel[] = {
	{FNOINVERT | FVIRTKEY, VK_F2, ID_ARROW},
	{FNOINVERT | FVIRTKEY, VK_F3, ID_ZOOM},
	{FNOINVERT | FVIRTKEY, VK_F4, ID_PIP},

	{FNOINVERT | FVIRTKEY, VK_F5, ID_BW},
	{FNOINVERT | FVIRTKEY, VK_F6, ID_AXIS},
	{FNOINVERT | FVIRTKEY, VK_F7, ID_SSE},
	{FNOINVERT | FVIRTKEY, VK_F8, ID_HOME}
};
int mode=ID_ARROW, type=ID_JULIA, bw = 0, axis = 0, fired, oldtype, beep=0;
HMENU menu, popup;
double PX = -0.12, PY = 0.74, X0 = 0, Y0 = 0;

const TBBUTTON toolbar[] = {
	{0, ID_ARROW, TBSTATE_ENABLED | TBSTATE_CHECKED, TBSTYLE_BUTTON, 0},
	{1, ID_ZOOM, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0},	
	{2, ID_PIP, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0},
	{-1, 0, 0, TBSTYLE_SEP, 0},
	{3, ID_JULIA, TBSTATE_ENABLED | TBSTATE_CHECKED, TBSTYLE_BUTTON, 0},
	{4, ID_MANDEL, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0},
	{-1, 0, 0, TBSTYLE_SEP, 0},
	{5, ID_BW, TBSTATE_ENABLED | TBSTATE_CHECKED, TBSTYLE_BUTTON, 0},
	{6, ID_AXIS, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0},
	{-1, 0, 0, TBSTYLE_SEP, 0},
	{7, ID_SSE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0},
	{8, ID_HOME, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0}
};

void PaintMandelbrotFPU(int w, int h, double dx, double dy, long* b, double left, double first, int piw) {
	int x, y, i; double x2, y2, px, py, zx, zy;
	py = first;
	if(bw) {
		for(y = 0; y < h; ++y) {
			px = left;
			for(x = 0; x < w; ++x) {
				zx = px, zy = py;
				for(i = 0; i < ITER; ++i) {
					x2 = zx * zx,  y2 = zy * zy;
					if(x2 + y2 > 4.0) {
						*b++ = 0x808080;
						goto NEXT;
					}
					zy = zx * zy * 2 + py;
					zx = x2 - y2 + px;
				}
				*b++ = 0;
NEXT:			px += dx;
			}
			b += piw / 4;
			py += dy;
		}
	}
	else {
		for(y = 0; y < h; ++y) {
			px = left;
			for(x = 0; x < w; ++x) {
				zx = px, zy = py;
				for(i = 0; i < ITER; ++i) {
					x2 = zx * zx,   y2 = zy * zy;
					if(x2 + y2 > 4.0)
						break;
					zy = zx * zy * 2 + py;
					zx = x2 - y2 + px;
				}
				*b++ = a[i];
				px += dx;
			}
			b += piw / 4;
			py += dy;
		}
	}
}

void PaintJuliaFPU(int w, int h, double dx, double dy, long* b, double left, double first, int piw) {
	int x, y, i; double x2, y2, zx2, zy2, zx, zy;
	zy2 = first;
	if(bw) {
		for(y = 0; y < h; ++y) {
			zx2 = left;
			for(x = 0; x < w; ++x) {
				zx = zx2, zy = zy2;
				for(i = 0; i < ITER; ++i) {
					x2 = zx * zx,   y2 = zy * zy;
					if(x2 + y2 > 4.0) {
						*b++ = 0x808080;
						goto NEXT;
					}
					zy = zx * zy * 2 + PY;
					zx = x2 - y2 + PX;
				}
				*b++ = 0;
NEXT:			zx2 += dx;
			}
			b += piw / 4;
			zy2 += dy;
		}
	}
	else {
		for(y = 0; y < h; ++y) {
			zx2 = left;
			for(x = 0; x < w; ++x) {
				zx = zx2, zy = zy2;
				for(i = 0; i < ITER; ++i) {
					x2 = zx * zx,   y2 = zy * zy;
					if(x2 + y2 > 4.0)
						break;
					zy = zx * zy * 2 + PY;
					zx = x2 - y2 + PX;
				}
				*b++ = a[i];
				zx2 += dx;
			}
			b += piw / 4;
			zy2 += dy;
		}
	}
}

double atod(char*& p) { // String to double
	unsigned sign = 0, v; double d = 0.0, factor = 0.1; int exp = 0; char *a = p;
	while(' ' == *a) // Skip leading spaces
		a++;
	if('+' == *a) a++;
	if('-' == *a) a++, sign = 1;
	while((v = *a - '0') <= 9) // Integer part
		a++, d = 10.0 * d + v;
	if('.' == *a || ',' == *a) { // Fractional part
		a++;
		while((v = *a - '0') <= 9)
			a++, d += v * factor, factor *= 0.1;
	}
	if((*a & 0xDF) == 'E') { // Exponent
		a++;
		if('+' == *a) a++, factor = 10;
		if('-' == *a) a++, factor = 0.1;
		while((v = *a - '0') <= 9)
			a++, exp = 10 * exp + v;
		while(exp) {
			if(exp & 1)
				d *= factor;
			exp >>= 1;
			factor *= factor; // factor = factor ^ 2
		}
	}
	p = a;
	return sign ? -d : d;
}

void __forceinline atox(char* a, double& x, double&y) { // String to complex number
	while(' ' == *a) // Skip spaces
		a++;
	if('i' == *a) { // i without number = 1 * i
		y = 1, x = 0;
		return;
	}
	x = atod(a); // Real part
	while(' ' == *a) // Skip spaces
		a++;
	if('i' == *a) { // Imaginary part goes first
		a++, y = x, x = atod(a);
		if('i' == *a)
			x = 0;
		return;
	}
	y = atod(a); // Imaginary part
	if('i' != *a)
		y = 0;
}

double ceil(double x) {
	__asm {
		FLD x
		FRNDINT
		// FISTP QWORD PTR [TEMP]
		// FILD QWORD PTR [TEMP]
	}
}

unsigned inline dtou(double x) {
	int y;
	__asm {
		FLD x
		FISTP y
		MOV eax, DWORD PTR y
	}
}

int inline dtoi(double x) {
	int y;
	__asm {
		FLD x
		FISTP y
		MOV eax, DWORD PTR y
	}
}

void dtoa(double x, char*& a) { // Double to string
	unsigned v, i; char *p = a;
	if(x < 0)
		x = -x, *p++ = '-';
	v = dtou(x); // Get integer part to v
	do {
		*p++ = char('0' + (v % 10)); // Output integer part
		v /= 10;
	} while(v);
	x = x - ceil(x); // Leave only fractional part
	if(x) {
		x *= 10;
		*p++ = '.';
		for(i = 7; i && x; i--)
			v = dtou(x), *p++ = char('0' + v), x = x - ceil(x), x *= 10;
	}
	*p = '\0';
	a = p;
}

char* xtoa(char* a, double x, double y) {
	dtoa(x, a);
	if(y >= 0)
		*a++ = '+';
	dtoa(y, a);
	set2ch(a, 'i', '\0');
	return a+1;
}

inline char* xxxtoa(int x, char* a) { // Iteration number
	*a++ = 'Z';
	if(x / 10) // If the is the second figure
		*a++ = char(x / 10 + '0');
	*a++ = char(x % 10 + '0'); // The first figure
	*a++ = '=';
	return a;
}

void EnableDisable(WPARAM id1, WPARAM id2, WPARAM id3) {
	SendMessage(hToolbar, TB_SETSTATE, id1, TBSTATE_CHECKED | TBSTATE_ENABLED);
	SendMessage(hToolbar, TB_SETSTATE, id2, TBSTATE_ENABLED);
	SendMessage(hToolbar, TB_SETSTATE, id3, TBSTATE_ENABLED);
}
void CheckUncheck(int id1, int id2, int id3) {
	CheckMenuItem(popup, id1, MF_BYCOMMAND | MF_CHECKED);
	CheckMenuItem(popup, id2, MF_BYCOMMAND | MF_UNCHECKED);
	CheckMenuItem(popup, id3, MF_BYCOMMAND | MF_UNCHECKED);
}

double flp2(double x) { // ��������� ���� �� ��������� ������� 2
	unsigned a[2];
	a[0] = *(unsigned*)&x;
	a[1] = *((unsigned*)&x+1);
	a[0] = 0;
	a[1] &= 0xFFF00000;
	return *(double*)a;
}

// From complex plane coordinates to screen coordinates
inline void plane2screen(double X, double Y, int& x, int& y, int right, int bottom) {
	x = dtoi(((X - LEFT) * (double)right) / (RIGHT - LEFT));
	y = dtoi(((Y - BOTTOM) * (double)bottom) / (TOP - BOTTOM));
}
inline void plane2screendx(double X, double Y, int& x, int& y, int right, int bottom) {
	x = dtoi((X * (double)right) / (RIGHT - LEFT));
	y = dtoi((Y * (double)bottom) / (TOP - BOTTOM));
}
inline void screen2plane(double& X, double& Y, int x, int y, int right, int bottom) {
	X = x * (RIGHT - LEFT) / right + LEFT;
	Y = y * (TOP - BOTTOM) / bottom + BOTTOM;
}

void SetZoomPos() {
	char *a = buff;
	set4ch(a, 'x',' ','f','r'); a+=4;
	set4ch(a, 'o','m',' ',0); a+=3;
	dtoa(LEFT, a);
	set4ch(a,' ','t','o',' '); a+=4;
	dtoa(RIGHT, a);
	set2ch(a, 0x0D, 0x0A); a+=2;

	set4ch(a,'y',' ','f','r'); a+=4;
	set4ch(a,'o','m',' ',0); a+=3;
	dtoa(TOP, a);
	set4ch(a,' ','t','o',' '); a+=4;
	dtoa(BOTTOM, a);
	*a = '\0';
	SendDlgItemMessage(hDlg, IDC_X1, WM_SETTEXT, 0, (LPARAM)buff);
}

void __forceinline DrawPolyline(HDC hdc, RECT r) {
	int x, y, i; double ZX, ZY, X2, Y2, DX, DY;
	plane2screen(X0, Y0, x, y, r.right, r.bottom);
	char*p = xtoa(buff, X0, Y0);
	SetBkMode(hdc, TRANSPARENT);
	SetTextColor(hdc, RGB(0xFF, 0xFF, 0xFF));
	TextOut(hdc, x, y, buff, p - buff);
	MoveToEx(hdc, x, y, NULL);
	//SetROP2(hdc, R2_NOT);
	SelectObject(hdc, GetStockObject(WHITE_PEN));
	p = buff;
	if(ID_JULIA == type)
		ZX = X0, ZY = Y0, DX = PX, DY = PY;
	else // ID_MANDEL
		ZX = 0, ZY = 0, DX = X0, DY = Y0;
	for(i = 0; i < ITER; ++i) {
		X2 = ZX * ZX, Y2 = ZY * ZY;
		if(X2 + Y2 > 4.0)
			goto STOP;
		ZY = ZX * ZY * 2 + DY;
		ZX = X2 - Y2 + DX;
		plane2screen(ZX, ZY, x, y, r.right, r.bottom);
		LineTo(hdc, x, y);
		if(i < 13) {
			p = xxxtoa(i + 1, p);
			p = xtoa(p, ZX, ZY);
			*(short*)p = 0x0A0D;
			p+=2;
		}
	}		
	set4ch(p,'.','.','.',0xD); p+=4;
	*p++ = 0xA;
	p = xxxtoa(ITER, p);
	p = xtoa(p, ZX, ZY);
STOP:
	*p = '\0';
	SendDlgItemMessage(hDlg, IDC_X1, WM_SETTEXT, 0, (LPARAM)buff);
}

void __forceinline DrawAxis(HDC hmem, RECT r) {
	int x, y, dx, dy, xorigin, yorigin;
	double X, Y, DX, DY;
	// Get screen coordinates of the origin
	plane2screen(0, 0, xorigin, yorigin, r.right, r.bottom);
	SetTextColor(hmem, RGB(0, 0xFF, 0));
	SetBkMode(hmem, TRANSPARENT);
	HPEN hPen = CreatePen(PS_SOLID, 0, RGB(0, 0xFF, 0));
	HPEN hOldPen = (HPEN) SelectObject(hmem, hPen);
	// Draw axis
	MoveToEx(hmem, xorigin, 0, NULL);
	LineTo(hmem, xorigin, r.bottom);
	MoveToEx(hmem, 0, yorigin, NULL);
	LineTo(hmem, r.right, yorigin);
	// Draw numbers
	DX = flp2(RIGHT - LEFT) / 4;
	DY = flp2(BOTTOM - TOP) / 4;
	X = ceil(LEFT / DX) * DX;
	Y = ceil(BOTTOM / DY) * DY;
	plane2screendx(DX, DY, dx, dy, r.right, r.bottom);
	plane2screen  ( X,  Y,  x,  y, r.right, r.bottom);
	SIZE size;
	for(; x < r.right; x += dx, X += DX) {
		char* p = buff;
		dtoa(X, p);
		if(X) {
			GetTextExtentPoint(hmem, buff, p - buff, &size);
			MoveToEx(hmem, x, yorigin-1, NULL);
			LineTo(hmem, x, yorigin+2);
			TextOut(hmem, x - size.cx / 2, yorigin, buff, p - buff);
		}
	}
	size.cy /= 2;
	for(; y < r.bottom; y -= dy, Y -= DY) {
		char* p = buff;
		dtoa(Y, p);
		MoveToEx(hmem, xorigin-1, y, NULL);
		LineTo(hmem, xorigin+2, y);
		TextOut(hmem, xorigin + 4, y - size.cy, buff, p - buff);
	}
	SelectObject(hmem, hOldPen);
	DeleteObject(hPen);
}

void __forceinline DrawOrangePoint(HDC hdc, RECT r) {
	int x,y;
	SelectObject(hdc, GetStockObject(WHITE_PEN));
	HBRUSH brush = CreateSolidBrush(RGB(0xFF, 0xA5, 0x00));
	HBRUSH oldbrush = (HBRUSH) SelectObject(hdc, brush);
	plane2screen(PX, PY, x, y, r.right, r.bottom);
	Ellipse(hdc, x - 3, y - 3, x + 3, y + 3);
	SelectObject(hdc, oldbrush);
	DeleteObject(brush);
}

void ReDraw() {
	LARGE_INTEGER t, t2; int w, h, piw; long* b; HDC hdc; double dx, dy, first;
#ifdef FPSdisplay
	__asm{
		XOR eax, eax
		CPUID
		RDTSC
		MOV bz, eax
	}
#endif
	if(DDmode) {
		DDSURFACEDESC sdesc;
		sdesc.dwSize = sizeof sdesc;
		if(DD_OK != OffscreenSurface->Lock(NULL, &sdesc, DDLOCK_WAIT | DDLOCK_NOSYSLOCK |
			DDLOCK_WRITEONLY | DDLOCK_SURFACEMEMORYPTR, NULL)) {
			DDmode = DDsupported = 0;
			return;
		}
		assert(sdesc.ddpfPixelFormat.dwFlags | DDPF_RGB);
		assert(sdesc.ddpfPixelFormat.dwRGBBitCount == 32);
		w = sdesc.dwWidth, h = sdesc.dwHeight;
		b = (long*)sdesc.lpSurface;
		piw = sdesc.lPitch - sdesc.dwWidth * 4;
		dx = (RIGHT - LEFT) / w;
		dy = (TOP - BOTTOM) / h;
		first = BOTTOM;
	}
	else { // GDI
		w = ceil(clientrect.right, 4), h = clientrect.bottom,
		b = ceil(bits, 16), piw = 0,
		dx = (RIGHT - LEFT) / w,
		dy = (BOTTOM - TOP) / h,
		first = TOP;
		if(!w || !h)
			return;
	}

	QueryPerformanceCounter(&t);
	if(SSEmode == 1)
		PaintJuliaMandelSSE(w, h, float(dx), float(dy), b, a, float(LEFT), float(first),
			float(PX), float(PY), piw, type - ID_MANDEL, bw);
	else if(SSEmode == 2)
		PaintJuliaMandelSSE2(w, h, dx, dy, b, a, LEFT, first,
			PX, PY, piw, type - ID_MANDEL, bw);
	else {
		if(ID_JULIA == type)
			PaintJuliaFPU(w, h, dx, dy, b, LEFT, first, piw);
		else
			PaintMandelbrotFPU(w, h, dx, dy, b, LEFT, first, piw);
	}
	if(QueryPerformanceCounter(&t2)) {
		ms = int(((t2.QuadPart - t.QuadPart) * 1000) / freq.QuadPart) / 4;
	}
	if(DDmode) {
		OffscreenSurface->Unlock(NULL);
		OffscreenSurface->GetDC(&hdc);
	}
	else {
		SetDIBits(hmem, hBMP, 0, clientrect.bottom, b,
			(BITMAPINFO*)&bi, DIB_RGB_COLORS);
		hdc = hmem;
	}
	if(axis)
		DrawAxis(hdc, clientrect);
	if(ID_MANDEL == type) // Orange point
		DrawOrangePoint(hdc, clientrect);
	if(DDmode)
		OffscreenSurface->ReleaseDC(hdc);
#ifdef FPSdisplay
	__asm{
		XOR eax, eax
		CPUID
		RDTSC
		SUB eax, bz
		MOV bz, eax
	}
#endif
	if(mode == ID_ZOOM)
		SetZoomPos();
}

void SetModes() {
	TCHAR *p = buff;
	set4ch(p, 'F','r','a','c'); p += 4;
	set4ch(p, 't','a','l','s'); p += 4;
	set4ch(p, ' ','(','\0','\0'); p += 2;
	if(!SSEmode) {
		set4ch(p, 'F','P','U',','); p += 4;	}
	else {
		if(SSEmode == 1) {
			set4ch(p, 'S','S','E',','); p += 4;	}
		else if(SSEmode == 2) {
			set4ch(p, 'S','S','E','2'); p += 4;
			*p++ = ',';	}
		if(movadd) {
			set4ch(p, 'M','o','v','A'); p += 4;
			set4ch(p, 'd','d',',','\0'); p += 3; }
		else {
			set4ch(p, 'V','o','d',','); p += 4;	}
	}
	if(DDmode) {
		set4ch(p, 'D','i','r','e'); p += 4;
		set4ch(p, 'c','t','D','r'); p += 4;
		set4ch(p, 'a','w',')','\0'); p += 3; }
	else {
		set4ch(p, 'G','D','I',')'); p += 4;
		*p = '\0'; }
	SetWindowText(hWnd, buff);
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) {
	PAINTSTRUCT ps; HDC hdc; double a;
#ifdef FPSdisplay
	DWORD b2;
#endif
	switch (msg) {
	case WM_CREATE: {
			menu = LoadMenu(wc.hInstance, (LPCSTR)IDR_MENU);
			popup = GetSubMenu(menu, 0);
			ddraw = LoadLibrary(TEXT("ddraw.dll"));
			const short cw = 0xF7F; const unsigned mxcsr = 0x9F80;
			SSEsupported = IsSSE();
			if(SSEsupported) {
				__asm {
					LDMXCSR mxcsr  ; set Flush-To-Zero mode
				}
				movadd = IsMovAdd();
				SSE2supported = IsSSE2();
				if(!SSE2supported)
					EnableMenuItem(popup, ID_C_SSE2, MF_BYCOMMAND | MF_GRAYED);
			}
			else {
				SSEmode = 0;
				EnableMenuItem(popup, ID_C_SSE, MF_BYCOMMAND | MF_GRAYED);
				EnableMenuItem(popup, ID_C_SSE2, MF_BYCOMMAND | MF_GRAYED);
				CheckUncheck(ID_C_FPU, ID_C_SSE, ID_C_SSE2);
			}
			__asm {
				FLDCW cw  ; set rounding mode toward zero (truncate)
			}
			DIRECTDRAWCREATE directdrawcreate =
				(DIRECTDRAWCREATE)GetProcAddress(ddraw, TEXT("DirectDrawCreate"));
			if(!directdrawcreate) {
DDERR:			DDsupported = DDmode = 0;
				EnableMenuItem(popup, ID_C_DIRECTDRAW, MF_BYCOMMAND | MF_GRAYED);
				CheckMenuItem(popup, ID_C_DIRECTDRAW, MF_BYCOMMAND | MF_UNCHECKED);
				return 0;
			}
			if(DD_OK != directdrawcreate(NULL, &DD, NULL)) goto DDERR;
			DD->SetCooperativeLevel(hWnd, DDSCL_NORMAL | DDSCL_FPUSETUP);
			DDSURFACEDESC sdesc;
			sdesc.dwSize = sizeof sdesc;
			sdesc.dwFlags = DDSD_CAPS;
			sdesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
			if(DD_OK != DD->CreateSurface(&sdesc, &PrimarySurface, NULL)) goto DDERR;
			sdesc.ddpfPixelFormat.dwSize = sizeof sdesc.ddpfPixelFormat;
			if(DD_OK != PrimarySurface->GetPixelFormat(&sdesc.ddpfPixelFormat)) goto DDERR;
			if((sdesc.ddpfPixelFormat.dwFlags & DDPF_RGB) == 0 ||
				sdesc.ddpfPixelFormat.dwRGBBitCount != 32) goto DDERR;
			if(DD_OK != DD->CreateClipper(0, &Clipper, NULL)) goto DDERR;
			if(DD_OK != Clipper->SetHWnd(0, hWnd)) goto DDERR;
			return 0;
			}
	case WM_DESTROY:
			if(Clipper)
				Clipper->Release();
			if(PrimarySurface)
				PrimarySurface->Release();
			if(OffscreenSurface)
				OffscreenSurface->Release();
			if(DD)
				DD->Release();
			free(bits);
			SelectObject(hmem, holdBMP);
			DeleteDC(hmem);
			DeleteObject(hBMP);
			FreeLibrary(ddraw);
			DestroyMenu(menu);
			DestroyAcceleratorTable(hAccel);
			PostQuitMessage(0);
			return 0;
    case WM_ACTIVATEAPP:
			WindowActive = wParam;
			return 0;
	case WM_SIZE:
			if(DDmode) {
				if(LOWORD(lParam) == 0 || HIWORD(lParam) == 0)
					return 0;
				clientrect.right = LOWORD(lParam), clientrect.bottom = HIWORD(lParam);
				if(OffscreenSurface) {
					OffscreenSurface->Release();
					OffscreenSurface = NULL;
				}
				DDSURFACEDESC sdesc;
				sdesc.dwSize = sizeof sdesc;
				sdesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
				sdesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
				sdesc.dwWidth = ceil(clientrect.right, 4), sdesc.dwHeight = clientrect.bottom;
				if(DD_OK != DD->CreateSurface(&sdesc, &OffscreenSurface, NULL)) goto DDERR;
				if(DD_OK != PrimarySurface->SetClipper(Clipper)) goto DDERR;
			}
			else {
				hdc = GetDC(hWnd);
				if(hBMP) {
					SelectObject(hmem, holdBMP);
					DeleteObject(hBMP);
					DeleteDC(hmem);
				}
				hBMP = CreateCompatibleBitmap(hdc, ceil(LOWORD(lParam),4), HIWORD(lParam));
				bi.biWidth = ceil(LOWORD(lParam),4), bi.biHeight = HIWORD(lParam),
				bi.biSizeImage = bi.biWidth * bi.biHeight * 4;
				// bi.biBitCount = GetDeviceCaps(hdc, BITSPIXEL);
				bits = (long*)realloc(bits, bi.biSizeImage + 16 * 3);
				hmem = CreateCompatibleDC(hdc);
				holdBMP = (HBITMAP) SelectObject(hmem, hBMP);
				ReleaseDC(hWnd, hdc);
			}
			clientrect.right = LOWORD(lParam), clientrect.bottom = HIWORD(lParam);
			ReDraw();
			return 0;
	case WM_PAINT:
#ifdef FPSdisplay
				__asm{
					XOR eax, eax
					CPUID
					RDTSC
					MOV b2, eax
				}
#endif
			if(DDmode) {
				RECT r = {0, 0, clientrect.right, clientrect.bottom};
				ClientToScreen(hWnd, (LPPOINT)&r);
 				r.right += r.left, r.bottom += r.top;
				for(;;) {
					if(DDERR_SURFACELOST != PrimarySurface->Blt(&r, OffscreenSurface,
						NULL, DDBLT_WAIT, 0))
						break;
					if(DD_OK != PrimarySurface->Restore())
						break;
					ReDraw();
				}
				PAINTSTRUCT ps;
				hdc = BeginPaint(hWnd, &ps);
			}
			else {
				hdc = BeginPaint(hWnd, &ps);
				BitBlt(hdc, 0, 0, clientrect.right, clientrect.bottom, hmem, 0, 0, SRCCOPY);
			}
#ifdef FPSdisplay
				__asm{
					XOR eax, eax
					CPUID
					RDTSC
					SUB eax, b2
					MOV b2, eax
				}
				b2 = wsprintf(buff, "%10d %10d %10d ms", bz, b2, ms);
				SetBkMode(hdc, TRANSPARENT);
				SetTextColor(hdc, RGB(0xFF, 0xFF, 0xFF));
				TextOut(hdc, 0, 0, buff, b2);
#endif
			if(ID_ARROW == mode) // Draw polyline
				DrawPolyline(hdc, clientrect);
			EndPaint(hWnd, &ps);
			return 0;
	case WM_KEYUP:
			switch(wParam) {
			case VK_PRIOR:
				a = (TOP - BOTTOM);	TOP -= a, BOTTOM -= a;
				goto REDRAW;
			case VK_NEXT:
				a = (TOP - BOTTOM);	TOP += a, BOTTOM += a;
				goto REDRAW;
			case VK_HOME:
				a = (RIGHT - LEFT);	LEFT -= a, RIGHT -= a;
				goto REDRAW;
			case VK_END:
				a = (RIGHT - LEFT);	LEFT += a, RIGHT += a;
				goto REDRAW;
			case VK_UP:
				a = (TOP - BOTTOM)/16;	TOP -= a, BOTTOM -= a;
				goto REDRAW;
			case VK_DOWN:
				a = (TOP - BOTTOM)/16;	TOP += a, BOTTOM += a;
				goto REDRAW;
			case VK_LEFT:
				a = (RIGHT - LEFT)/16;	LEFT -= a, RIGHT -= a;
				goto REDRAW;
			case VK_RIGHT:
				a = (RIGHT - LEFT)/16;	LEFT += a, RIGHT += a;
				goto REDRAW;
			case VK_RETURN: // Enter
				ShowWindow(hDlg, SW_SHOW);
				SetFocus(hDlg);
				break;
			case VK_SPACE: {
				if(mode == ID_PIP)
					break;
				oldtype = type;
				type = type == ID_MANDEL ? ID_JULIA : ID_MANDEL;
				if(ID_MANDEL == type)
					LEFT = LEFTm, RIGHT = RIGHTm, TOP = TOPm, BOTTOM = BOTTOMm;
				else
					LEFT = LEFTj, RIGHT = RIGHTj, TOP = TOPj, BOTTOM = BOTTOMj;
				EnableDisable(type, oldtype, oldtype);
				ReDraw();
				InvalidateRect(hWnd, NULL, FALSE);
				break;
				}
			}
			return 0;
	case WM_CHAR:
			if(wParam == 27) SendMessage(hWnd,WM_CLOSE,0,0);
			else if(wParam == '+')	{
				a = (TOP - BOTTOM) / 3;
				TOP -= a, BOTTOM += a;
				a = (RIGHT - LEFT) / 3;
				RIGHT -= a, LEFT += a;
				goto REDRAW;
			}
			else if(wParam == '-')	{
				if(RIGHT - LEFT > 2.0) {
					if(beep < 2)
						Beep(0x100, 100);
					beep++;
					return 0;
				}
				beep = 0;
				a = (TOP - BOTTOM);
				TOP += a, BOTTOM -= a;
				a = (RIGHT - LEFT);
				RIGHT += a, LEFT -= a;
REDRAW:			ReDraw();
				InvalidateRect(hWnd, NULL, FALSE);
			}
			return 0;
	case WM_MOUSEWHEEL:
			a = ((TOP - BOTTOM)/8) * double((signed short)HIWORD(wParam) / 120);
			TOP -= a, BOTTOM -= a;
			ReDraw();
			InvalidateRect(hWnd, NULL, FALSE);
			return 0;
	case WM_MOUSEMOVE:
			if(0 == (wParam & MK_LBUTTON) || ID_ARROW != mode)
				return 0;
	case WM_LBUTTONDOWN:
			if(ID_ARROW == mode) {
				screen2plane(X0, Y0, LOWORD(lParam), HIWORD(lParam), clientrect.right, clientrect.bottom);
				InvalidateRect(hWnd, NULL, FALSE);
				return 0;
			}
			else {// if ID_ZOOM
				fired = 0;
				SetTimer(hWnd, TIMER_ZOOMIN, ms, NULL);
			}
			return 0;
	case WM_RBUTTONDOWN: {
			if (ID_ARROW == mode) {
				if(type == ID_MANDEL) { // Switch to Julia
					screen2plane(PX, PY, LOWORD(lParam), HIWORD(lParam), clientrect.right, clientrect.bottom);
					type = ID_JULIA;
					X0 = PX, Y0 = PY;
					xtoa(buff, PX, PY);
					SendDlgItemMessage(hDlg, IDC_C, WM_SETTEXT, 0, (LPARAM)buff);
					EnableDisable(ID_JULIA, ID_MANDEL, ID_MANDEL);
					ReDraw();
					InvalidateRect(hWnd, NULL, FALSE);
				}
				else { // if ID_JULIA
					type = ID_MANDEL;
					X0 = PX, Y0 = PY;
					EnableDisable(ID_MANDEL, ID_JULIA, ID_JULIA);
					ReDraw();
					InvalidateRect(hWnd, NULL, FALSE);
				}
			}
			else {
				fired = 0;
				SetTimer(hWnd, TIMER_ZOOMOUT, ms, NULL);
			}
			return 0;
		}
	case WM_TIMER: {
TIMER:		POINT p; GetCursorPos(&p);
			ScreenToClient(hWnd, &p);
			double X, Y, scale;
			screen2plane(X, Y, p.x, p.y, clientrect.right, clientrect.bottom);
			if(wParam == TIMER_ZOOMIN) {
				scale = zoomspeed / double(ms + zoomspeed);
				if(scale >= 1)
					scale = 0.9;
			}
			else {
				if(RIGHT - LEFT > 2.0)
					return 0;
				scale = double(ms + zoomspeed) / zoomspeed;
				if(scale <= 1)
					scale = 1.1;
			}
			a = (BOTTOM - TOP) * scale;
			TOP = Y - (Y - TOP)* scale, BOTTOM = TOP + a;
			a = (RIGHT - LEFT) * scale;
			LEFT = X - (X - LEFT) * scale, RIGHT = LEFT + a;
			ReDraw();
			InvalidateRect(hWnd, NULL, FALSE);
			if(fired)
				SetTimer(hWnd, wParam, ms, NULL);
			fired = 1;
			return 0;
			  }
	case WM_LBUTTONUP:
			if(ID_ARROW != mode) {
				KillTimer(hWnd, TIMER_ZOOMIN);
				if(!fired) {
					wParam = TIMER_ZOOMIN;
					goto TIMER;
				}
			}
			return 0;
	case WM_RBUTTONUP:
			if(ID_ARROW != mode) {
				KillTimer(hWnd, TIMER_ZOOMOUT);
				if(!fired) {
					wParam = TIMER_ZOOMOUT;
					goto TIMER;
				}
			}
			return 0;
	case WM_SETCURSOR:
		if(mode != ID_ARROW) {
			POINT pt; GetCursorPos(&pt);
			RECT r = clientrect;
			ClientToScreen(hWnd, (LPPOINT)&r);
			ClientToScreen(hWnd, (LPPOINT)&r.right);
			if(!PtInRect(&r, pt))
				return DefWindowProc(hWnd,msg,wParam,lParam);
			SetCursor(LoadCursor(wc.hInstance, (LPCSTR)IDC_ZOOM));
			return 0;
		}
	default:
		return DefWindowProc(hWnd,msg,wParam,lParam);
	}
}

LRESULT APIENTRY SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  {
	if (uMsg == WM_KEYUP) {
		switch(wParam) {
			case VK_ESCAPE:
				SendMessage(hDlg, WM_CLOSE, 0, 0);
				SetFocus(hWnd);
				return 0;
			case VK_RETURN:
				SetFocus(hWnd);
				return 0;
		}
	  }
	return CallWindowProc(OldProc, hwnd, uMsg, wParam, lParam);
}

void RecoverPiP() {
	if(mode == ID_PIP) {
		SendMessage(hToolbar, TB_SETSTATE, ID_JULIA, oldtype == ID_JULIA ? TBSTATE_CHECKED | TBSTATE_ENABLED : TBSTATE_ENABLED);
		SendMessage(hToolbar, TB_SETSTATE, ID_MANDEL, oldtype == ID_MANDEL ? TBSTATE_CHECKED | TBSTATE_ENABLED : TBSTATE_ENABLED);
		if(type != oldtype) {
			type = oldtype;
			ReDraw();
			InvalidateRect(hWnd, NULL, FALSE);
		}
	}
}

const char* strings[] = {"0.1+0.7i (Fatu's dust)", "-0.74543+0.11301i (sea-horse)",
	"-0.74543-0.11301i (sea-horse)",               "-0.481762+0.531657i (\"comb\")",
	"-0.123175+0.56508i (deformed circle)",        "-0.12+0.74i (Duadi's rabbit)",
	"-0.15652-1.03225i (\"nucleus of a cell\")",   "-0.194+0.6557i (\"rivers and lakes\")",
	"0.3325+0.5753176i (tree)",                    "-0.6203+0.416i (\"exotic flower\")",
    "-1.7568+0.0065i (\"constellation\")",         "-1.1416+0.2434i (\"rabbits\")",
	"0.2617+0.0014i (\"helix\")",                  "0.3760+0.1481i (\"palm\")",
    "-0.7725+0.0014i (\"potatoes\")",	    	   "0.40566+0.33679i (\"mandagora\")",
	"-0.3737+0.6597i (\"crab claws\")",            "-0.3559+0.6577i (\"swirl\")"
};

const double left = -2.0, right = 0.5, top = -1.0, bottom = 1.0;
BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
	switch(msg) {
		case WM_INITDIALOG: {
			HWND hC = GetDlgItem(hDlg, IDC_C);
			POINT p = {5, 5};
			hC = ChildWindowFromPoint(hC, p);
			OldProc = (WNDPROC) SetWindowLong(hC,
               GWL_WNDPROC, (LONG)SubclassProc); // Subclassing
			hToolbar = CreateToolbarEx(hDlg, WS_CHILD | CCS_TOP | TBSTYLE_TOOLTIPS | WS_VISIBLE,
				IDC_TOOLBAR, 12, wc.hInstance, IDR_TOOLBAR, &toolbar[0],
				sizeof(toolbar) / sizeof(TBBUTTON), 16, 16, 16, 16, sizeof(TBBUTTON));
			SendMessage(hToolbar, TB_SETSTATE, ID_ARROW,
					   TBSTATE_CHECKED | TBSTATE_ENABLED);
			SendMessage(hToolbar, TB_SETSTATE, ID_JULIA,
					   TBSTATE_CHECKED | TBSTATE_ENABLED);
			for(int i = 0; i < sizeof(strings) / sizeof(char*); ++i)
				SendDlgItemMessage(hDlg, IDC_C, CB_ADDSTRING, 0, (LPARAM)strings[i]);
			SendDlgItemMessage(hDlg, IDC_C, CB_SETCURSEL, 1, 0);
			HWND hswnd = GetDlgItem(hDlg, IDC_X1);
			HDC hdc = GetDC(hswnd); // Create bitmap for the small picture of the Mandelbrot set
			RECT r;	GetClientRect(hswnd, &r);
			hsBMP = CreateCompatibleBitmap(hdc, r.right, r.bottom);
			bis.biWidth = r.right, bis.biHeight = r.bottom;
			bis.biSizeImage = r.right * r.bottom * 4;
			// bi.biBitCount = GetDeviceCaps(hdc, BITSPIXEL);
			sbits = (long*) malloc(bis.biSizeImage + 16 * 3);
			const double left = -2.0, right = 0.5, top = -1.0, bottom = 1.0;
			double DX = (right - left) / r.right;
			double DY = (bottom - top) / r.bottom;
			PaintMandelbrotFPU(r.right, r.bottom, DX, DY, ceil(sbits, 16), left, top, 0);
			SetDIBits(hdc, hsBMP, 0, r.bottom, ceil(sbits, 16),
					(BITMAPINFO*)&bis, DIB_RGB_COLORS);
			ReleaseDC(hswnd, hdc);
			return TRUE;
		}
		case WM_DESTROY: {
			HWND hC = GetDlgItem(hDlg, IDC_C);
			POINT p = {5, 5};
			hC = ChildWindowFromPoint(hC, p);
			SetWindowLong(hC, GWL_WNDPROC, (LONG)OldProc);
			free(sbits);
			DeleteObject(hsBMP);
			return TRUE;
			}
		case WM_CLOSE:
			ShowWindow(hDlg, SW_HIDE);
			return TRUE;
		case WM_SETCURSOR:
			if(mode != ID_PIP)
				break;
			RECT r;
			POINT pt; GetCursorPos(&pt);
			GetWindowRect(GetDlgItem(hDlg, IDC_X1), &r);  // Absolute coordinates to client
			if(!PtInRect(&r, pt))
				break;
			SetCursor(LoadCursor(NULL, IDC_CROSS));
			return TRUE;
		case WM_PAINT: // The small picture of the Mandelbrot set
			if(mode == ID_PIP) {
				PAINTSTRUCT ps; RECT r;
				HWND hw = GetDlgItem(hDlg, IDC_X1);
				HDC hdc = BeginPaint(hw, &ps);
				GetClientRect(hw, &r);
				SelectClipRgn(hdc, CreateRectRgn(r.left, r.top, r.right, r.bottom));
				HDC hmem = CreateCompatibleDC(hdc);
				HBITMAP holdBMP = (HBITMAP) SelectObject(hmem, hsBMP);
				BitBlt(hdc, 0, 0, r.right, r.bottom, hmem, 0, 0, SRCCOPY);
				// Orange point
				int x,y;
				SelectObject(hdc, GetStockObject(WHITE_PEN));
				HBRUSH brush = CreateSolidBrush(RGB(0xFF, 0xA5, 0x00));
				HBRUSH oldbrush = (HBRUSH) SelectObject(hdc, brush);
				x = dtoi(((PX - left) * double(r.right)) / (right - left)); // From complex plane coordinates to screen coordinates
				y = dtoi(((PY - bottom) * double(r.bottom)) / (top - bottom));
				Ellipse(hdc, x - 2, y - 2, x + 2, y + 2);
				// Destroying objects
				SelectObject(hdc, oldbrush);
				DeleteObject(brush);
				SelectObject(hmem, holdBMP);
				EndPaint(hWnd, &ps);
				DeleteDC(hmem);
			}
			break;
		case WM_MOUSEMOVE:
			if(0 == (wParam & MK_LBUTTON))
				break;
		case WM_LBUTTONUP: // Clicking the small picture
			if(mode == ID_PIP) {
				RECT r;
				POINT pt; GetCursorPos(&pt);
				GetWindowRect(GetDlgItem(hDlg, IDC_X1), &r);  // Absolute coordinates to client coordinates
				if(!PtInRect(&r, pt))
					break;
				r.right -= r.left;
				r.bottom -= r.top;
				PX = double(pt.x - r.left) * (right - left) / r.right + left;
				PY = double(pt.y - r.top) * (top - bottom) / r.bottom + bottom;
				xtoa(buff, PX, PY);
				SendDlgItemMessage(hDlg, IDC_C, WM_SETTEXT, 0, (LPARAM)buff);
				ReDraw();
				InvalidateRect(hWnd, NULL, FALSE);
				InvalidateRect(hDlg, NULL, FALSE);
			}
			break;
		case WM_NOTIFY:
			if( ((NMHDR *)lParam) ->code == TTN_NEEDTEXT )
				((TOOLTIPTEXT *)lParam) ->lpszText = tooltips[((NMHDR *)lParam) ->idFrom - ID_ARROW];
			break;
		case WM_COMMAND: 
            switch (wParam) {
			case MAKEWPARAM(IDC_C, CBN_EDITCHANGE):
				SendDlgItemMessage(hDlg, IDC_C, WM_GETTEXT, sizeof(buff), (LPARAM)buff);
				goto SK;
			case MAKEWPARAM(IDC_C, CBN_SELENDOK):
				SendDlgItemMessage(hDlg, IDC_C, CB_GETLBTEXT, 
					SendDlgItemMessage(hDlg, IDC_C, CB_GETCURSEL, 0, 0), (LPARAM)buff);
SK:				atox(buff, PX, PY);
				if(type == ID_MANDEL)
					X0 = PX, Y0 = PY;
				ReDraw();
				InvalidateRect(hWnd, NULL, FALSE);
				InvalidateRect(hDlg, NULL, FALSE);
				return FALSE;
			}
			switch(LOWORD(wParam)) {
			case ID_MANDEL:
				EnableDisable(ID_MANDEL, ID_JULIA, ID_JULIA);
				//ShowWindow(GetDlgItem(hDlg, IDC_C), SW_HIDE);
				//ShowWindow(GetDlgItem(hDlg, IDC_CSTATIC), SW_HIDE);
				LEFT = LEFTm, RIGHT = RIGHTm, TOP = TOPm, BOTTOM = BOTTOMm;
				goto REDRAW;
			case ID_JULIA:
				EnableDisable(ID_JULIA, ID_MANDEL, ID_MANDEL);
				LEFT = LEFTj, RIGHT = RIGHTj, TOP = TOPj, BOTTOM = BOTTOMj;
				//ShowWindow(GetDlgItem(hDlg, IDC_C), SW_SHOW);
				//ShowWindow(GetDlgItem(hDlg, IDC_CSTATIC), SW_SHOW);
REDRAW:			type = LOWORD(wParam);
				ReDraw();
				InvalidateRect(hWnd, NULL, FALSE);
				break;
			case ID_ARROW:
				RecoverPiP();
				mode = ID_ARROW;
				EnableDisable(ID_ARROW, ID_ZOOM, ID_PIP);
				InvalidateRect(hWnd, NULL, FALSE);
				break;
			case ID_ZOOM:
				RecoverPiP();
				mode = ID_ZOOM;
				EnableDisable(ID_ZOOM, ID_ARROW, ID_PIP);
				InvalidateRect(hWnd, NULL, FALSE);
				SetZoomPos();
				break;
			case ID_PIP:
				mode = ID_PIP;				
				SendMessage(hToolbar, TB_SETSTATE, ID_JULIA, type == ID_JULIA ? TBSTATE_CHECKED : 0);
				SendMessage(hToolbar, TB_SETSTATE, ID_MANDEL, type == ID_MANDEL ? TBSTATE_CHECKED : 0);
				oldtype = type, type = ID_JULIA;
				EnableDisable(ID_PIP, ID_ARROW, ID_ZOOM);
				LEFT = LEFTj, RIGHT = RIGHTj, TOP = TOPj, BOTTOM = BOTTOMj;
				ReDraw();
				InvalidateRect(hWnd, NULL, FALSE);
				InvalidateRect(hDlg, NULL, FALSE);
				break;
			case ID_BW: {
				bw = !bw;
				SendMessage(hToolbar, TB_SETSTATE, ID_BW, bw ? TBSTATE_ENABLED : TBSTATE_CHECKED | TBSTATE_ENABLED);
				ReDraw();
				InvalidateRect(hWnd, NULL, FALSE);
				// Redraw the small picture
				HWND hswnd = GetDlgItem(hDlg, IDC_X1);
				HDC hdc = GetDC(hswnd);
				RECT r;	GetClientRect(hswnd, &r);
				const double left = -2.0, right = 0.5, top = -1.0, bottom = 1.0;
				double DX = (right - left) / r.right;
				double DY = (bottom - top) / r.bottom;
				PaintMandelbrotFPU(r.right, r.bottom, DX, DY, ceil(sbits, 16), left, top, 0);
				bis.biWidth = r.right, bis.biHeight = r.bottom;
				SetDIBits(hdc, hsBMP, 0, r.bottom, ceil(sbits,16),
					(BITMAPINFO*)&bis, DIB_RGB_COLORS);
				ReleaseDC(hswnd, hdc);
				InvalidateRect(hDlg, NULL, FALSE);
				break;
				}
			case ID_AXIS:
				axis = !axis;
				SendMessage(hToolbar, TB_SETSTATE, ID_AXIS, axis ?
					TBSTATE_CHECKED | TBSTATE_ENABLED : TBSTATE_ENABLED);
				ReDraw();
				InvalidateRect(hWnd, NULL, FALSE);
				break;
			case ID_HOME:
				if(type == ID_MANDEL)
					LEFT = LEFTm, RIGHT = RIGHTm, TOP = TOPm, BOTTOM = BOTTOMm;
				else
					LEFT = LEFTj, RIGHT = RIGHTj, TOP = TOPj, BOTTOM = BOTTOMj;
				ReDraw();
				InvalidateRect(hWnd, NULL, FALSE);
				break;
			case ID_SSE: {
				RECT r; SendMessage(hToolbar, TB_GETITEMRECT, 10, (LPARAM)&r);
				ClientToScreen(hDlg, (LPPOINT)&r.right);
				r.bottom += GetSystemMetrics(SM_CYEDGE);
				TrackPopupMenu(popup, TPM_RIGHTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
					r.right, r.bottom, 0, hDlg, NULL);
				break;
						 }
			case ID_C_DIRECTDRAW:
				if(DDsupported) {
					DDmode = !DDmode;
					CheckMenuItem(popup, ID_C_DIRECTDRAW,
						DDmode ? (MF_BYCOMMAND | MF_CHECKED) : (MF_BYCOMMAND | MF_UNCHECKED));
					SendMessage(hWnd, WM_SIZE, SIZE_RESTORED,
						MAKELPARAM(clientrect.right, clientrect.bottom));
					InvalidateRect(hWnd, NULL, FALSE);
					SetModes();
				}
				break;
			case ID_C_FPU:
				SSEmode = 0;
				CheckUncheck(ID_C_FPU, ID_C_SSE, ID_C_SSE2);
				goto SSEM;
			case ID_C_SSE:
				if(SSEsupported) {
					SSEmode = 1;
					CheckUncheck(ID_C_SSE, ID_C_FPU, ID_C_SSE2);
SSEM:				ReDraw();
					InvalidateRect(hWnd, NULL, FALSE);
					SetModes();
				}
				break;
			case ID_C_SSE2:
				if(SSE2supported) {
					SSEmode = 2;
					CheckUncheck(ID_C_SSE2, ID_C_FPU, ID_C_SSE);
					goto SSEM;
				}
				break;
			}
	}
	return FALSE;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) {
	hInstance; hPrevInstance; nCmdShow; lpCmdLine;
	MSG msg;
	InitCommonControls();
	_CrtSetDbgFlag( // Locating memory leaks and GPFs
    _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) |
    _CRTDBG_CHECK_ALWAYS_DF |
    _CRTDBG_LEAK_CHECK_DF
	);
	//heap = GetProcessHeap();
	GetSystemInfo(&si);
	QueryPerformanceFrequency(&freq);
	int	addR = 3 << 16, addG = 4 << 8, addB = -2, r = 50 << 16, g = 50 << 8, b = 50;
	for(unsigned i=0; i < ITER; ++i) {
		a[i] = r | g | b;
		r += addR, g += addG, b += addB;
		if (r < 0)
			addR = -addR, r = -r;
		else if (r > (0xF0 << 16))
			addR = -addR, r += addR;
		if (g < 0)
			addG = -addG, g = -g;
		else if (g > (0xF0 << 8))
			addG = -addG, g += addG;
		if (b < 0)
			addB = -addB, b = -b;
		else if (b > 0xF0)
			addB = -addB, b += addB;
	}
	a[ITER] = 0;
	wc.hInstance = GetModuleHandle(NULL);
	wc.hIcon = LoadIcon(wc.hInstance,(LPCSTR)IDI_FRACTALS);
	if(!RegisterClass(&wc))
		return 0;
	hWnd = CreateWindow(classname, "Fractals", WS_OVERLAPPEDWINDOW | WS_MAXIMIZE,
		CW_USEDEFAULT,0,455,195,
		NULL, NULL,	wc.hInstance, NULL);
	SetModes();
	hDlg = CreateDialogParam(wc.hInstance, MAKEINTRESOURCE(IDD_PARAMS),
		hWnd, DlgProc, 0);
	hAccel = CreateAcceleratorTable(accel, sizeof(accel) / sizeof(ACCEL));
	// Set Options dialog position
	RECT rwnd, rdlg;
	GetWindowRect(hDlg, &rdlg);
	GetWindowRect(hWnd, &rwnd);
	rwnd.bottom -= (rdlg.bottom - rdlg.top);
	SetWindowPos(hDlg, NULL, 20, rwnd.bottom - 20, 0, 0, SWP_NOOWNERZORDER |
		SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);

	ShowWindow(hWnd,SW_SHOWMAXIMIZED);
	SetFocus(hDlg);
	while(GetMessage(&msg,NULL,0,0)) {
		if(!TranslateAccelerator(hDlg, hAccel, &msg)) {
			if(!IsDialogMessage(hDlg, &msg)) {
				TranslateMessage(&msg);
				DispatchMessage(&msg);
				}
		}
	}
	return msg.wParam;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Czech Republic Czech Republic
Peter is the developer of Aba Search and Replace, a tool for replacing text in multiple files. He likes to program in C with a bit of C++, also in x86 assembly language, Python, and PHP.

Comments and Discussions