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