1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Win32 EGL native display factory
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuWin32EGLNativeDisplayFactory.hpp"
25
26#include "egluDefs.hpp"
27#include "tcuWin32Window.hpp"
28#include "tcuWin32API.h"
29#include "tcuTexture.hpp"
30#include "deMemory.h"
31#include "deThread.h"
32#include "deClock.h"
33
34namespace tcu
35{
36namespace
37{
38
39enum
40{
41	DEFAULT_SURFACE_WIDTH		= 400,
42	DEFAULT_SURFACE_HEIGHT		= 300,
43	WAIT_WINDOW_VISIBLE_MS		= 500	//!< Time to wait before issuing screenshot after changing window visibility (hack for DWM)
44};
45
46static const eglu::NativeDisplay::Capability	DISPLAY_CAPABILITIES	= eglu::NativeDisplay::CAPABILITY_GET_DISPLAY_LEGACY;
47static const eglu::NativePixmap::Capability		BITMAP_CAPABILITIES		= eglu::NativePixmap::CAPABILITY_CREATE_SURFACE_LEGACY;
48static const eglu::NativeWindow::Capability		WINDOW_CAPABILITIES		= (eglu::NativeWindow::Capability)
49																		   (eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_LEGACY	|
50																		    eglu::NativeWindow::CAPABILITY_GET_SURFACE_SIZE			|
51																		    eglu::NativeWindow::CAPABILITY_GET_SCREEN_SIZE			|
52																			eglu::NativeWindow::CAPABILITY_READ_SCREEN_PIXELS		|
53																		    eglu::NativeWindow::CAPABILITY_SET_SURFACE_SIZE			|
54																			eglu::NativeWindow::CAPABILITY_CHANGE_VISIBILITY);
55
56class NativeDisplay : public eglu::NativeDisplay
57{
58public:
59									NativeDisplay			(void);
60	virtual							~NativeDisplay			(void) {}
61
62	virtual EGLNativeDisplayType	getLegacyNative			(void) { return m_deviceContext; }
63
64	HDC								getDeviceContext		(void) { return m_deviceContext; }
65
66private:
67	HDC								m_deviceContext;
68};
69
70class NativePixmapFactory : public eglu::NativePixmapFactory
71{
72public:
73								NativePixmapFactory		(void);
74								~NativePixmapFactory	(void) {}
75
76	virtual eglu::NativePixmap*	createPixmap			(eglu::NativeDisplay* nativeDisplay, int width, int height) const;
77	virtual eglu::NativePixmap*	createPixmap			(eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, int width, int height) const;
78};
79
80class NativePixmap : public eglu::NativePixmap
81{
82public:
83								NativePixmap			(NativeDisplay* nativeDisplay, int width, int height, int bitDepth);
84	virtual						~NativePixmap			(void);
85
86	EGLNativePixmapType			getLegacyNative			(void) { return m_bitmap; }
87
88private:
89	HBITMAP						m_bitmap;
90};
91
92class NativeWindowFactory : public eglu::NativeWindowFactory
93{
94public:
95								NativeWindowFactory		(HINSTANCE instance);
96	virtual						~NativeWindowFactory	(void) {}
97
98	virtual eglu::NativeWindow*	createWindow			(eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const;
99
100private:
101	const HINSTANCE				m_instance;
102};
103
104class NativeWindow : public eglu::NativeWindow
105{
106public:
107									NativeWindow			(NativeDisplay* nativeDisplay, HINSTANCE instance, const eglu::WindowParams& params);
108	virtual							~NativeWindow			(void);
109
110	EGLNativeWindowType				getLegacyNative			(void) { return m_window.getHandle(); }
111	virtual IVec2					getSurfaceSize			(void) const;
112	virtual IVec2					getScreenSize			(void) const { return getSurfaceSize(); }
113	virtual void					processEvents			(void);
114	virtual void					setSurfaceSize			(IVec2 size);
115	virtual void					setVisibility			(eglu::WindowParams::Visibility visibility);
116	virtual void					readScreenPixels		(tcu::TextureLevel* dst) const;
117
118private:
119	Win32Window						m_window;
120	eglu::WindowParams::Visibility	m_curVisibility;
121	deUint64						m_setVisibleTime;		//!< Time window was set visible.
122};
123
124// NativeDisplay
125
126NativeDisplay::NativeDisplay (void)
127	: eglu::NativeDisplay	(DISPLAY_CAPABILITIES)
128	, m_deviceContext		(EGL_DEFAULT_DISPLAY)
129{
130}
131
132// NativePixmap
133
134NativePixmap::NativePixmap (NativeDisplay* nativeDisplay, int width, int height, int bitDepth)
135	: eglu::NativePixmap	(BITMAP_CAPABILITIES)
136	, m_bitmap				(DE_NULL)
137{
138	const HDC		deviceCtx	= nativeDisplay->getDeviceContext();
139	BITMAPINFO		bitmapInfo;
140
141	memset(&bitmapInfo, 0, sizeof(bitmapInfo));
142
143	if (bitDepth != 24 && bitDepth != 32)
144		throw NotSupportedError("Unsupported pixmap bit depth", DE_NULL, __FILE__, __LINE__);
145
146	bitmapInfo.bmiHeader.biSize				= sizeof(bitmapInfo);
147	bitmapInfo.bmiHeader.biWidth			= width;
148	bitmapInfo.bmiHeader.biHeight			= height;
149	bitmapInfo.bmiHeader.biPlanes			= 1;
150	bitmapInfo.bmiHeader.biBitCount			= bitDepth;
151	bitmapInfo.bmiHeader.biCompression		= BI_RGB;
152	bitmapInfo.bmiHeader.biSizeImage		= 0;
153	bitmapInfo.bmiHeader.biXPelsPerMeter	= 1;
154	bitmapInfo.bmiHeader.biYPelsPerMeter	= 1;
155	bitmapInfo.bmiHeader.biClrUsed			= 0;
156	bitmapInfo.bmiHeader.biClrImportant		= 0;
157
158	void* bitmapPtr = DE_NULL;
159	m_bitmap = CreateDIBSection(deviceCtx, &bitmapInfo, DIB_RGB_COLORS, &bitmapPtr, NULL, 0);
160
161	if (!m_bitmap)
162		throw ResourceError("Failed to create bitmap", DE_NULL, __FILE__, __LINE__);
163}
164
165NativePixmap::~NativePixmap (void)
166{
167	DeleteObject(m_bitmap);
168}
169
170// NativePixmapFactory
171
172NativePixmapFactory::NativePixmapFactory (void)
173	: eglu::NativePixmapFactory	("bitmap", "Win32 Bitmap", BITMAP_CAPABILITIES)
174{
175}
176
177eglu::NativePixmap* NativePixmapFactory::createPixmap (eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, int width, int height) const
178{
179	int	redBits		= 0;
180	int	greenBits	= 0;
181	int	blueBits	= 0;
182	int	alphaBits	= 0;
183	int	bitSum		= 0;
184
185	DE_ASSERT(display != EGL_NO_DISPLAY);
186
187	EGLU_CHECK_CALL(eglGetConfigAttrib(display, config, EGL_RED_SIZE,	&redBits));
188	EGLU_CHECK_CALL(eglGetConfigAttrib(display, config, EGL_GREEN_SIZE,	&greenBits));
189	EGLU_CHECK_CALL(eglGetConfigAttrib(display, config, EGL_BLUE_SIZE,	&blueBits));
190	EGLU_CHECK_CALL(eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE,	&alphaBits));
191	bitSum = redBits+greenBits+blueBits+alphaBits;
192
193	return new NativePixmap(dynamic_cast<NativeDisplay*>(nativeDisplay), width, height, bitSum);
194}
195
196eglu::NativePixmap* NativePixmapFactory::createPixmap (eglu::NativeDisplay* nativeDisplay, int width, int height) const
197{
198	const int defaultDepth = 32;
199	return new NativePixmap(dynamic_cast<NativeDisplay*>(nativeDisplay), width, height, defaultDepth);
200}
201
202// NativeWindowFactory
203
204NativeWindowFactory::NativeWindowFactory (HINSTANCE instance)
205	: eglu::NativeWindowFactory	("window", "Win32 Window", WINDOW_CAPABILITIES)
206	, m_instance				(instance)
207{
208}
209
210eglu::NativeWindow* NativeWindowFactory::createWindow (eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const
211{
212	return new NativeWindow(dynamic_cast<NativeDisplay*>(nativeDisplay), m_instance, params);
213}
214
215// NativeWindow
216
217NativeWindow::NativeWindow (NativeDisplay* nativeDisplay, HINSTANCE instance, const eglu::WindowParams& params)
218	: eglu::NativeWindow	(WINDOW_CAPABILITIES)
219	, m_window				(instance,
220							 params.width	== eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_WIDTH	: params.width,
221							 params.height	== eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_HEIGHT	: params.height)
222	, m_curVisibility		(eglu::WindowParams::VISIBILITY_HIDDEN)
223	, m_setVisibleTime		(0)
224{
225	if (params.visibility != eglu::WindowParams::VISIBILITY_DONT_CARE)
226		setVisibility(params.visibility);
227}
228
229void NativeWindow::setVisibility (eglu::WindowParams::Visibility visibility)
230{
231	switch (visibility)
232	{
233		case eglu::WindowParams::VISIBILITY_HIDDEN:
234			m_window.setVisible(false);
235			m_curVisibility		= visibility;
236			break;
237
238		case eglu::WindowParams::VISIBILITY_VISIBLE:
239		case eglu::WindowParams::VISIBILITY_FULLSCREEN:
240			// \todo [2014-03-12 pyry] Implement FULLSCREEN, or at least SW_MAXIMIZE.
241			m_window.setVisible(true);
242			m_curVisibility		= eglu::WindowParams::VISIBILITY_VISIBLE;
243			m_setVisibleTime	= deGetMicroseconds();
244			break;
245
246		default:
247			DE_ASSERT(DE_FALSE);
248	}
249}
250
251NativeWindow::~NativeWindow (void)
252{
253}
254
255IVec2 NativeWindow::getSurfaceSize (void) const
256{
257	return m_window.getSize();
258}
259
260void NativeWindow::processEvents (void)
261{
262	m_window.processEvents();
263}
264
265void NativeWindow::setSurfaceSize (IVec2 size)
266{
267	m_window.setSize(size.x(), size.y());
268}
269
270void NativeWindow::readScreenPixels (tcu::TextureLevel* dst) const
271{
272	HDC			windowDC	= DE_NULL;
273	HDC			screenDC	= DE_NULL;
274	HDC			tmpDC		= DE_NULL;
275	HBITMAP		tmpBitmap	= DE_NULL;
276	RECT		rect;
277
278	TCU_CHECK_INTERNAL(m_curVisibility != eglu::WindowParams::VISIBILITY_HIDDEN);
279
280	// Hack for DWM: There is no way to wait for DWM animations to finish, so we just have to wait
281	// for a while before issuing screenshot if window was just made visible.
282	{
283		const deInt64 timeSinceVisibleUs = (deInt64)(deGetMicroseconds()-m_setVisibleTime);
284
285		if (timeSinceVisibleUs < (deInt64)WAIT_WINDOW_VISIBLE_MS*1000)
286			deSleep(WAIT_WINDOW_VISIBLE_MS - (deUint32)(timeSinceVisibleUs/1000));
287	}
288
289	TCU_CHECK(GetClientRect(m_window.getHandle(), &rect));
290
291	try
292	{
293		const int			width		= rect.right - rect.left;
294		const int			height		= rect.bottom - rect.top;
295		BITMAPINFOHEADER	bitmapInfo;
296
297		deMemset(&bitmapInfo, 0, sizeof(bitmapInfo));
298
299		screenDC = GetDC(DE_NULL);
300		TCU_CHECK(screenDC);
301
302		windowDC = GetDC(m_window.getHandle());
303		TCU_CHECK(windowDC);
304
305		tmpDC = CreateCompatibleDC(screenDC);
306		TCU_CHECK(tmpDC != DE_NULL);
307
308		MapWindowPoints(m_window.getHandle(), DE_NULL, (LPPOINT)&rect, 2);
309
310		tmpBitmap = CreateCompatibleBitmap(screenDC, width, height);
311		TCU_CHECK(tmpBitmap != DE_NULL);
312
313		TCU_CHECK(SelectObject(tmpDC, tmpBitmap) != DE_NULL);
314
315		TCU_CHECK(BitBlt(tmpDC, 0, 0, width, height, screenDC, rect.left, rect.top, SRCCOPY));
316
317
318		bitmapInfo.biSize			= sizeof(BITMAPINFOHEADER);
319		bitmapInfo.biWidth			= width;
320		bitmapInfo.biHeight			= -height;
321		bitmapInfo.biPlanes			= 1;
322		bitmapInfo.biBitCount		= 32;
323		bitmapInfo.biCompression	= BI_RGB;
324		bitmapInfo.biSizeImage		= 0;
325		bitmapInfo.biXPelsPerMeter	= 0;
326		bitmapInfo.biYPelsPerMeter	= 0;
327		bitmapInfo.biClrUsed		= 0;
328		bitmapInfo.biClrImportant	= 0;
329
330		dst->setStorage(TextureFormat(TextureFormat::BGRA, TextureFormat::UNORM_INT8), width, height);
331
332		TCU_CHECK(GetDIBits(screenDC, tmpBitmap, 0, height, dst->getAccess().getDataPtr(), (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS));
333
334		DeleteObject(tmpBitmap);
335		tmpBitmap = DE_NULL;
336
337		ReleaseDC(DE_NULL, screenDC);
338		screenDC = DE_NULL;
339
340		ReleaseDC(m_window.getHandle(), windowDC);
341		windowDC = DE_NULL;
342
343		DeleteDC(tmpDC);
344		tmpDC = DE_NULL;
345	}
346	catch (...)
347	{
348		if (screenDC)
349			ReleaseDC(DE_NULL, screenDC);
350
351		if (windowDC)
352			ReleaseDC(m_window.getHandle(), windowDC);
353
354		if (tmpBitmap)
355			DeleteObject(tmpBitmap);
356
357		if (tmpDC)
358			DeleteDC(tmpDC);
359
360		throw;
361	}
362}
363
364} // anonymous
365
366Win32EGLNativeDisplayFactory::Win32EGLNativeDisplayFactory (HINSTANCE instance)
367	: eglu::NativeDisplayFactory	("win32", "Native Win32 Display", DISPLAY_CAPABILITIES)
368	, m_instance					(instance)
369{
370	m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(m_instance));
371	m_nativePixmapRegistry.registerFactory(new NativePixmapFactory());
372}
373
374Win32EGLNativeDisplayFactory::~Win32EGLNativeDisplayFactory (void)
375{
376}
377
378eglu::NativeDisplay* Win32EGLNativeDisplayFactory::createDisplay (const EGLAttrib* attribList) const
379{
380	DE_UNREF(attribList);
381	return new NativeDisplay();
382}
383
384} // tcu
385