1 /**
2  * Copyright: Mike Wey 2011
3  * License:   zlib (See accompanying LICENSE file)
4  * Authors:   Mike Wey
5  */
6  
7 module dmagick.internal.Windows;
8 
9 version(Windows):
10 
11 import core.sys.windows.windows;
12 
13 import dmagick.Image;
14 import dmagick.Exception;
15 import dmagick.Geometry;
16 
17 class Window
18 {
19 	Image image;
20 	Image[] imageList;
21 	size_t index;
22 	int height;
23 	int width;
24 
25 	WNDCLASS   wndclass;
26 	HINSTANCE  hInstance;
27 	BITMAPINFO bmi;          // bitmap header
28 	HWND       hwnd;
29 	MSG        msg;
30 
31 	static Window[HWND] windows;
32 
33 	/**
34 	 * Create an window foe displaying an image.
35 	 */
36 	this(Image image)
37 	{
38 		this.image = image;
39 
40 		height = cast(int)image.rows;
41 		width  = cast(int)image.columns;
42 
43 		hInstance = cast(HINSTANCE) GetModuleHandleA(null);
44 
45 		wndclass.style         = CS_HREDRAW | CS_VREDRAW;
46 		wndclass.lpfnWndProc   = &WndProc;
47 		wndclass.cbClsExtra    = 0;
48 		wndclass.cbWndExtra    = 0;
49 		wndclass.hInstance     = hInstance;
50 		wndclass.hIcon         = LoadIcon(null, IDI_APPLICATION);
51 		wndclass.hCursor       = LoadCursor(null, IDC_ARROW);
52 		wndclass.hbrBackground = null;
53 		wndclass.lpszMenuName  = null;
54 		wndclass.lpszClassName = "DMagick";
55 
56 		if (!RegisterClass(&wndclass))
57 			throw new DMagickException("Displaying images requires Windows NT!");
58 
59 		RECT rect = RECT(0,0, width,height);
60 		AdjustWindowRect(&rect, WS_CAPTION | WS_SYSMENU, false);
61 
62 		hwnd = CreateWindow("DMagick", "DMagick", WS_CAPTION | WS_SYSMENU,
63 			CW_USEDEFAULT, CW_USEDEFAULT, rect.right-rect.left, rect.bottom-rect.top,
64 			null, null, hInstance, null);
65 
66 		// setup bitmap info
67 		bmi.bmiHeader.biSize        = BITMAPINFOHEADER.sizeof;
68 		bmi.bmiHeader.biWidth       = width;
69 		bmi.bmiHeader.biHeight      = -height;  // must be inverted so Y axis is at top
70 		bmi.bmiHeader.biPlanes      = 1;
71 		bmi.bmiHeader.biBitCount    = 32;      // four 8-bit components
72 		bmi.bmiHeader.biCompression = BI_RGB;
73 		bmi.bmiHeader.biSizeImage   = width * height * 4;
74 
75 		windows[hwnd] = this;
76 	}
77 
78 	/**
79 	 * Create an window foe displaying an animation
80 	  * or a collection of images.
81 	 */
82 	this(Image[] images)
83 	{
84 		this(images[0]);
85 
86 		imageList = images;
87 		index = 0;
88 	}
89 
90 	/**
91 	 * Open the window and display the image.
92 	 */
93 	void display()
94 	{
95 		ShowWindow(hwnd, SW_SHOWNORMAL);
96 		UpdateWindow(hwnd);
97 
98 		if ( imageList !is null )
99 		{
100 			UINT delay = cast(UINT)image.animationDelay.total!"msecs"();
101 			
102 			if ( delay == 0 )
103 				delay = 1000;
104 			
105 			SetTimer(hwnd, 0, delay, null);
106 		}
107 
108 		while (GetMessageA(&msg, null, 0, 0))
109 		{
110 			TranslateMessage(&msg);
111 			DispatchMessageA(&msg);
112 		}
113 	}
114 
115 	extern(Windows) nothrow static LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
116 	{
117 		try
118 		{
119 			switch (message)
120 			{
121 				case WM_ERASEBKGND:  // don't redraw bg
122 					return 1;
123 
124 				case WM_PAINT:
125 					windows[hwnd].draw();
126 					return 0;
127 
128 				case WM_TIMER:
129 					windows[hwnd].nextFrame();
130 					return 0;
131 
132 				case WM_DESTROY:
133 					windows[hwnd] = null;
134 					PostQuitMessage(0);
135 					return 0;
136 
137 				default:
138 			}
139 		}
140 		catch(Exception e){}
141 		
142 		return DefWindowProcA(hwnd, message, wParam, lParam);
143 	}
144 
145 	/**
146 	 * Draw the image on the window.
147 	 */
148 	void draw()
149 	{
150 		HDC hdc;                              // handle of the DC we will create
151 		HDC hdcwnd;                           // DC for the window
152 		HBITMAP hbitmap;                      // bitmap handle
153 		PAINTSTRUCT ps;
154 		VOID*  pvBits;                        // pointer to DIB section
155 		ULONG  ulWindowWidth, ulWindowHeight; // window width/height
156 		RECT   rt;                            // used for getting window dimensions
157 
158 		// get window dimensions
159 		GetClientRect(hwnd, &rt);
160 
161 		// calculate window width/height
162 		ulWindowWidth  = rt.right - rt.left;
163 		ulWindowHeight = rt.bottom - rt.top;
164 
165 		// make sure we have at least some window size
166 		if (ulWindowWidth < 1 || ulWindowHeight < 1)
167 			return;
168 
169 		// Get DC for window
170 		hdcwnd = BeginPaint(hwnd, &ps);
171 
172 		// create a DC for our bitmap -- the source DC for BitBlt
173 		hdc = CreateCompatibleDC(hdcwnd);
174 
175 		// create our DIB section and select the bitmap into the dc
176 		hbitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pvBits, null, 0x0);
177 		SelectObject(hdc, hbitmap);
178 
179 		enum channels = "BGRA";  // win32 uses BGR(A)
180 		Geometry area = Geometry(width, height);
181 		byte[] arr = (cast(byte*)pvBits)[0 .. (area.width * area.height) * channels.length];
182 		image.exportPixels(area, arr, channels);
183 
184 		BitBlt(hdcwnd, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
185 
186 		DeleteObject(hbitmap);
187 		DeleteDC(hdc);
188 		EndPaint(hwnd, &ps);
189 	}
190 
191 	/**
192 	 * Setup the next frame, and invalidate the window so its repainted.
193 	 */
194 	void nextFrame()
195 	{
196 		if (++index == imageList.length)
197 			index = 0;
198 
199 		image = imageList[index];
200 
201 		UINT delay = cast(UINT)image.animationDelay.total!"msecs"();
202 		
203 		if ( delay == 0 )
204 			delay = 1000;
205 				
206 		SetTimer(hwnd, 0, delay, null);
207 		InvalidateRect(hwnd,null,false);
208 	}
209 }
210 
211 pragma(lib, "gdi32.lib");
212 
213 const AC_SRC_OVER  = 0x00;
214 const AC_SRC_ALPHA = 0x01;
215 
216 enum DWORD BI_RGB = 0;
217 enum UINT DIB_RGB_COLORS = 0;
218 
219 extern(Windows) BOOL BitBlt(HDC, int, int, int, int, HDC, int, int, DWORD);
220 extern(Windows) HBITMAP CreateCompatibleBitmap(HDC, int, int);
221 extern(Windows) HBITMAP CreateDIBSection(HDC, const(BITMAPINFO)*, UINT, void**, HANDLE, DWORD);
222 extern(Windows) UINT_PTR SetTimer(HWND, UINT_PTR, UINT, TIMERPROC);