1 /*
2  * Tests for the D3DX9 core interfaces
3  *
4  * Copyright 2009 Tony Wasserka
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #define COBJMACROS
22 #include "wine/test.h"
23 #include <dxerr9.h>
24 #include "d3dx9core.h"
25 
26 static inline int get_ref(IUnknown *obj)
27 {
28     IUnknown_AddRef(obj);
29     return IUnknown_Release(obj);
30 }
31 
32 #define check_ref(obj, exp) _check_ref(__LINE__, obj, exp)
33 static inline void _check_ref(unsigned int line, IUnknown *obj, int exp)
34 {
35     int ref = get_ref(obj);
36     ok_(__FILE__, line)(exp == ref, "Invalid refcount. Expected %d, got %d\n", exp, ref);
37 }
38 
39 #define check_release(obj, exp) _check_release(__LINE__, obj, exp)
40 static inline void _check_release(unsigned int line, IUnknown *obj, int exp)
41 {
42     int ref = IUnknown_Release(obj);
43     ok_(__FILE__, line)(ref == exp, "Invalid refcount. Expected %d, got %d\n", exp, ref);
44 }
45 
46 #define admitted_error 0.0001f
47 static inline void check_mat(D3DXMATRIX got, D3DXMATRIX exp)
48 {
49     int i, j, equal=1;
50     for (i=0; i<4; i++)
51         for (j=0; j<4; j++)
52             if (fabs(U(exp).m[i][j]-U(got).m[i][j]) > admitted_error)
53                 equal=0;
54 
55     ok(equal, "Got matrix\n\t(%f,%f,%f,%f\n\t %f,%f,%f,%f\n\t %f,%f,%f,%f\n\t %f,%f,%f,%f)\n"
56        "Expected matrix=\n\t(%f,%f,%f,%f\n\t %f,%f,%f,%f\n\t %f,%f,%f,%f\n\t %f,%f,%f,%f)\n",
57        U(got).m[0][0],U(got).m[0][1],U(got).m[0][2],U(got).m[0][3],
58        U(got).m[1][0],U(got).m[1][1],U(got).m[1][2],U(got).m[1][3],
59        U(got).m[2][0],U(got).m[2][1],U(got).m[2][2],U(got).m[2][3],
60        U(got).m[3][0],U(got).m[3][1],U(got).m[3][2],U(got).m[3][3],
61        U(exp).m[0][0],U(exp).m[0][1],U(exp).m[0][2],U(exp).m[0][3],
62        U(exp).m[1][0],U(exp).m[1][1],U(exp).m[1][2],U(exp).m[1][3],
63        U(exp).m[2][0],U(exp).m[2][1],U(exp).m[2][2],U(exp).m[2][3],
64        U(exp).m[3][0],U(exp).m[3][1],U(exp).m[3][2],U(exp).m[3][3]);
65 }
66 
67 static void test_ID3DXBuffer(void)
68 {
69     ID3DXBuffer *buffer;
70     HRESULT hr;
71     ULONG count;
72     DWORD size;
73 
74     hr = D3DXCreateBuffer(10, NULL);
75     ok(hr == D3DERR_INVALIDCALL, "D3DXCreateBuffer failed, got %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
76 
77     hr = D3DXCreateBuffer(0, &buffer);
78     ok(hr == D3D_OK, "D3DXCreateBuffer failed, got %#x, expected %#x\n", hr, D3D_OK);
79 
80     size = ID3DXBuffer_GetBufferSize(buffer);
81     ok(!size, "GetBufferSize failed, got %u, expected %u\n", size, 0);
82 
83     count = ID3DXBuffer_Release(buffer);
84     ok(!count, "ID3DXBuffer has %u references left\n", count);
85 
86     hr = D3DXCreateBuffer(3, &buffer);
87     ok(hr == D3D_OK, "D3DXCreateBuffer failed, got %#x, expected %#x\n", hr, D3D_OK);
88 
89     size = ID3DXBuffer_GetBufferSize(buffer);
90     ok(size == 3, "GetBufferSize failed, got %u, expected %u\n", size, 3);
91 
92     count = ID3DXBuffer_Release(buffer);
93     ok(!count, "ID3DXBuffer has %u references left\n", count);
94 }
95 
96 static void test_ID3DXSprite(IDirect3DDevice9 *device)
97 {
98     ID3DXSprite *sprite;
99     IDirect3D9 *d3d;
100     IDirect3DDevice9 *cmpdev;
101     IDirect3DTexture9 *tex1, *tex2;
102     D3DXMATRIX mat, cmpmat;
103     D3DVIEWPORT9 vp;
104     RECT rect;
105     D3DXVECTOR3 pos, center;
106     HRESULT hr;
107 
108     IDirect3DDevice9_GetDirect3D(device, &d3d);
109     hr = IDirect3D9_CheckDeviceFormat(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_DYNAMIC, D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8);
110     IDirect3D9_Release(d3d);
111     ok (hr == D3D_OK, "D3DFMT_A8R8G8B8 not supported\n");
112     if (FAILED(hr)) return;
113 
114     hr = IDirect3DDevice9_CreateTexture(device, 64, 64, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &tex1, NULL);
115     ok (hr == D3D_OK, "Failed to create first texture (error code: %#x)\n", hr);
116     if (FAILED(hr)) return;
117 
118     hr = IDirect3DDevice9_CreateTexture(device, 32, 32, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &tex2, NULL);
119     ok (hr == D3D_OK, "Failed to create second texture (error code: %#x)\n", hr);
120     if (FAILED(hr)) {
121         IDirect3DTexture9_Release(tex1);
122         return;
123     }
124 
125     /* Test D3DXCreateSprite */
126     hr = D3DXCreateSprite(device, NULL);
127     ok (hr == D3DERR_INVALIDCALL, "D3DXCreateSprite returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
128 
129     hr = D3DXCreateSprite(NULL, &sprite);
130     ok (hr == D3DERR_INVALIDCALL, "D3DXCreateSprite returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
131 
132     hr = D3DXCreateSprite(device, &sprite);
133     ok (hr == D3D_OK, "D3DXCreateSprite returned %#x, expected %#x\n", hr, D3D_OK);
134 
135 
136     /* Test ID3DXSprite_GetDevice */
137     hr = ID3DXSprite_GetDevice(sprite, NULL);
138     ok (hr == D3DERR_INVALIDCALL, "GetDevice returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
139 
140     hr = ID3DXSprite_GetDevice(sprite, &cmpdev);  /* cmpdev == NULL */
141     ok (hr == D3D_OK, "GetDevice returned %#x, expected %#x\n", hr, D3D_OK);
142 
143     hr = ID3DXSprite_GetDevice(sprite, &cmpdev);  /* cmpdev != NULL */
144     ok (hr == D3D_OK, "GetDevice returned %#x, expected %#x\n", hr, D3D_OK);
145 
146     IDirect3DDevice9_Release(device);
147     IDirect3DDevice9_Release(device);
148 
149 
150     /* Test ID3DXSprite_GetTransform */
151     hr = ID3DXSprite_GetTransform(sprite, NULL);
152     ok (hr == D3DERR_INVALIDCALL, "GetTransform returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
153     hr = ID3DXSprite_GetTransform(sprite, &mat);
154     ok (hr == D3D_OK, "GetTransform returned %#x, expected %#x\n", hr, D3D_OK);
155     if(SUCCEEDED(hr)) {
156         D3DXMATRIX identity;
157         D3DXMatrixIdentity(&identity);
158         check_mat(mat, identity);
159     }
160 
161     /* Test ID3DXSprite_SetTransform */
162     /* Set a transform and test if it gets returned correctly */
163     U(mat).m[0][0]=2.1f;  U(mat).m[0][1]=6.5f;  U(mat).m[0][2]=-9.6f; U(mat).m[0][3]=1.7f;
164     U(mat).m[1][0]=4.2f;  U(mat).m[1][1]=-2.5f; U(mat).m[1][2]=2.1f;  U(mat).m[1][3]=5.5f;
165     U(mat).m[2][0]=-2.6f; U(mat).m[2][1]=0.3f;  U(mat).m[2][2]=8.6f;  U(mat).m[2][3]=8.4f;
166     U(mat).m[3][0]=6.7f;  U(mat).m[3][1]=-5.1f; U(mat).m[3][2]=6.1f;  U(mat).m[3][3]=2.2f;
167 
168     hr = ID3DXSprite_SetTransform(sprite, NULL);
169     ok (hr == D3DERR_INVALIDCALL, "SetTransform returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
170 
171     hr = ID3DXSprite_SetTransform(sprite, &mat);
172     ok (hr == D3D_OK, "SetTransform returned %#x, expected %#x\n", hr, D3D_OK);
173     if(SUCCEEDED(hr)) {
174         hr=ID3DXSprite_GetTransform(sprite, &cmpmat);
175         if(SUCCEEDED(hr)) check_mat(cmpmat, mat);
176         else skip("GetTransform returned %#x\n", hr);
177     }
178 
179     /* Test ID3DXSprite_SetWorldViewLH/RH */
180     todo_wine {
181         hr = ID3DXSprite_SetWorldViewLH(sprite, &mat, &mat);
182         ok (hr == D3D_OK, "SetWorldViewLH returned %#x, expected %#x\n", hr, D3D_OK);
183         hr = ID3DXSprite_SetWorldViewLH(sprite, NULL, &mat);
184         ok (hr == D3D_OK, "SetWorldViewLH returned %#x, expected %#x\n", hr, D3D_OK);
185         hr = ID3DXSprite_SetWorldViewLH(sprite, &mat, NULL);
186         ok (hr == D3D_OK, "SetWorldViewLH returned %#x, expected %#x\n", hr, D3D_OK);
187         hr = ID3DXSprite_SetWorldViewLH(sprite, NULL, NULL);
188         ok (hr == D3D_OK, "SetWorldViewLH returned %#x, expected %#x\n", hr, D3D_OK);
189 
190         hr = ID3DXSprite_SetWorldViewRH(sprite, &mat, &mat);
191         ok (hr == D3D_OK, "SetWorldViewRH returned %#x, expected %#x\n", hr, D3D_OK);
192         hr = ID3DXSprite_SetWorldViewRH(sprite, NULL, &mat);
193         ok (hr == D3D_OK, "SetWorldViewRH returned %#x, expected %#x\n", hr, D3D_OK);
194         hr = ID3DXSprite_SetWorldViewRH(sprite, &mat, NULL);
195         ok (hr == D3D_OK, "SetWorldViewRH returned %#x, expected %#x\n", hr, D3D_OK);
196         hr = ID3DXSprite_SetWorldViewRH(sprite, NULL, NULL);
197         ok (hr == D3D_OK, "SetWorldViewRH returned %#x, expected %#x\n", hr, D3D_OK);
198     }
199     IDirect3DDevice9_BeginScene(device);
200 
201     /* Test ID3DXSprite_Begin*/
202     hr = ID3DXSprite_Begin(sprite, 0);
203     ok (hr == D3D_OK, "Begin returned %#x, expected %#x\n", hr, D3D_OK);
204 
205     IDirect3DDevice9_GetTransform(device, D3DTS_WORLD, &mat);
206     D3DXMatrixIdentity(&cmpmat);
207     check_mat(mat, cmpmat);
208 
209     IDirect3DDevice9_GetTransform(device, D3DTS_VIEW, &mat);
210     check_mat(mat, cmpmat);
211 
212     IDirect3DDevice9_GetTransform(device, D3DTS_PROJECTION, &mat);
213     IDirect3DDevice9_GetViewport(device, &vp);
214     D3DXMatrixOrthoOffCenterLH(&cmpmat, vp.X+0.5f, (float)vp.Width+vp.X+0.5f, (float)vp.Height+vp.Y+0.5f, vp.Y+0.5f, vp.MinZ, vp.MaxZ);
215     check_mat(mat, cmpmat);
216 
217     /* Test ID3DXSprite_Flush and ID3DXSprite_End */
218     hr = ID3DXSprite_Flush(sprite);
219     ok (hr == D3D_OK, "Flush returned %#x, expected %#x\n", hr, D3D_OK);
220 
221     hr = ID3DXSprite_End(sprite);
222     ok (hr == D3D_OK, "End returned %#x, expected %#x\n", hr, D3D_OK);
223 
224     hr = ID3DXSprite_Flush(sprite); /* May not be called before next Begin */
225     ok (hr == D3DERR_INVALIDCALL, "Flush returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
226     hr = ID3DXSprite_End(sprite);
227     ok (hr == D3DERR_INVALIDCALL, "End returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
228 
229     /* Test ID3DXSprite_Draw */
230     hr = ID3DXSprite_Begin(sprite, 0);
231     ok (hr == D3D_OK, "Begin returned %#x, expected %#x\n", hr, D3D_OK);
232 
233     if(FAILED(hr)) skip("Couldn't ID3DXSprite_Begin, can't test ID3DXSprite_Draw\n");
234     else { /* Feed the sprite batch */
235         int texref1, texref2;
236 
237         SetRect(&rect, 53, 12, 142, 165);
238         pos.x    =  2.2f; pos.y    = 4.5f; pos.z    = 5.1f;
239         center.x = 11.3f; center.y = 3.4f; center.z = 1.2f;
240 
241         texref1 = get_ref((IUnknown*)tex1);
242         texref2 = get_ref((IUnknown*)tex2);
243 
244         hr = ID3DXSprite_Draw(sprite, NULL, &rect, &center, &pos, D3DCOLOR_XRGB(255, 255, 255));
245         ok (hr == D3DERR_INVALIDCALL, "Draw returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
246 
247         hr = ID3DXSprite_Draw(sprite, tex1, &rect, &center, &pos, D3DCOLOR_XRGB(255, 255, 255));
248         ok (hr == D3D_OK, "Draw returned %#x, expected %#x\n", hr, D3D_OK);
249         hr = ID3DXSprite_Draw(sprite, tex2, &rect, &center, &pos, D3DCOLOR_XRGB(  3,  45,  66));
250         ok (hr == D3D_OK, "Draw returned %#x, expected %#x\n", hr, D3D_OK);
251         hr = ID3DXSprite_Draw(sprite, tex1,  NULL, &center, &pos, D3DCOLOR_XRGB(255, 255, 255));
252         ok (hr == D3D_OK, "Draw returned %#x, expected %#x\n", hr, D3D_OK);
253         hr = ID3DXSprite_Draw(sprite, tex1, &rect,    NULL, &pos, D3DCOLOR_XRGB(255, 255, 255));
254         ok (hr == D3D_OK, "Draw returned %#x, expected %#x\n", hr, D3D_OK);
255         hr = ID3DXSprite_Draw(sprite, tex1, &rect, &center, NULL, D3DCOLOR_XRGB(255, 255, 255));
256         ok (hr == D3D_OK, "Draw returned %#x, expected %#x\n", hr, D3D_OK);
257         hr = ID3DXSprite_Draw(sprite, tex1,  NULL,    NULL, NULL,                            0);
258         ok (hr == D3D_OK, "Draw returned %#x, expected %#x\n", hr, D3D_OK);
259 
260         check_ref((IUnknown*)tex1, texref1+5); check_ref((IUnknown*)tex2, texref2+1);
261         hr = ID3DXSprite_Flush(sprite);
262         ok (hr == D3D_OK, "Flush returned %#x, expected %#x\n", hr, D3D_OK);
263         hr = ID3DXSprite_Flush(sprite);   /* Flushing twice should work */
264         ok (hr == D3D_OK, "Flush returned %#x, expected %#x\n", hr, D3D_OK);
265         check_ref((IUnknown*)tex1, texref1);   check_ref((IUnknown*)tex2, texref2);
266 
267         hr = ID3DXSprite_End(sprite);
268         ok (hr == D3D_OK, "End returned %#x, expected %#x\n", hr, D3D_OK);
269     }
270 
271     /* Test ID3DXSprite_OnLostDevice and ID3DXSprite_OnResetDevice */
272     /* Both can be called twice */
273     hr = ID3DXSprite_OnLostDevice(sprite);
274     ok (hr == D3D_OK, "OnLostDevice returned %#x, expected %#x\n", hr, D3D_OK);
275     hr = ID3DXSprite_OnLostDevice(sprite);
276     ok (hr == D3D_OK, "OnLostDevice returned %#x, expected %#x\n", hr, D3D_OK);
277     hr = ID3DXSprite_OnResetDevice(sprite);
278     ok (hr == D3D_OK, "OnResetDevice returned %#x, expected %#x\n", hr, D3D_OK);
279     hr = ID3DXSprite_OnResetDevice(sprite);
280     ok (hr == D3D_OK, "OnResetDevice returned %#x, expected %#x\n", hr, D3D_OK);
281 
282     /* Make sure everything works like before */
283     hr = ID3DXSprite_Begin(sprite, 0);
284     ok (hr == D3D_OK, "Begin returned %#x, expected %#x\n", hr, D3D_OK);
285     hr = ID3DXSprite_Draw(sprite, tex2, &rect, &center, &pos, D3DCOLOR_XRGB(255, 255, 255));
286     ok (hr == D3D_OK, "Draw returned %#x, expected %#x\n", hr, D3D_OK);
287     hr = ID3DXSprite_Flush(sprite);
288     ok (hr == D3D_OK, "Flush returned %#x, expected %#x\n", hr, D3D_OK);
289     hr = ID3DXSprite_End(sprite);
290     ok (hr == D3D_OK, "End returned %#x, expected %#x\n", hr, D3D_OK);
291 
292     /* OnResetDevice makes the interface "forget" the Begin call */
293     hr = ID3DXSprite_Begin(sprite, 0);
294     ok (hr == D3D_OK, "Begin returned %#x, expected %#x\n", hr, D3D_OK);
295     hr = ID3DXSprite_OnResetDevice(sprite);
296     ok (hr == D3D_OK, "OnResetDevice returned %#x, expected %#x\n", hr, D3D_OK);
297     hr = ID3DXSprite_End(sprite);
298     ok (hr == D3DERR_INVALIDCALL, "End returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
299 
300     IDirect3DDevice9_EndScene(device);
301     check_release((IUnknown*)sprite, 0);
302     check_release((IUnknown*)tex2, 0);
303     check_release((IUnknown*)tex1, 0);
304 }
305 
306 static void test_ID3DXFont(IDirect3DDevice9 *device)
307 {
308     static const WCHAR testW[] = L"test";
309     static const char long_text[] = "Example text to test clipping and other related things";
310     static const WCHAR long_textW[] = L"Example text to test clipping and other related things";
311     static const MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
312     static const struct
313     {
314         int font_height;
315         unsigned int expected_size;
316         unsigned int expected_levels;
317     }
318     tests[] =
319     {
320         {   2,  32,  2 },
321         {   6, 128,  4 },
322         {  10, 256,  5 },
323         {  12, 256,  5 },
324         {  72, 256,  8 },
325         { 250, 256,  9 },
326         { 258, 512, 10 },
327         { 512, 512, 10 },
328     };
329     const unsigned int size = ARRAY_SIZE(testW);
330     TEXTMETRICA metrics, expmetrics;
331     IDirect3DTexture9 *texture;
332     D3DSURFACE_DESC surf_desc;
333     IDirect3DDevice9 *bufdev;
334     GLYPHMETRICS glyph_metrics;
335     D3DXFONT_DESCA desc;
336     ID3DXSprite *sprite;
337     RECT rect, blackbox;
338     DWORD count, levels;
339     int ref, i, height;
340     ID3DXFont *font;
341     TEXTMETRICW tm;
342     POINT cellinc;
343     HRESULT hr;
344     WORD glyph;
345     BOOL ret;
346     HDC hdc;
347     char c;
348 
349     /* D3DXCreateFont */
350     ref = get_ref((IUnknown*)device);
351     hr = D3DXCreateFontA(device, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
352     ok(hr == D3D_OK, "D3DXCreateFont returned %#x, expected %#x\n", hr, D3D_OK);
353     check_ref((IUnknown*)device, ref + 1);
354     check_release((IUnknown*)font, 0);
355     check_ref((IUnknown*)device, ref);
356 
357     hr = D3DXCreateFontA(device, 0, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
358     ok(hr == D3D_OK, "D3DXCreateFont returned %#x, expected %#x\n", hr, D3D_OK);
359     ID3DXFont_Release(font);
360 
361     hr = D3DXCreateFontA(device, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, NULL, &font);
362     ok(hr == D3D_OK, "D3DXCreateFont returned %#x, expected %#x\n", hr, D3D_OK);
363     ID3DXFont_Release(font);
364 
365     hr = D3DXCreateFontA(device, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "", &font);
366     ok(hr == D3D_OK, "D3DXCreateFont returned %#x, expected %#x\n", hr, D3D_OK);
367     ID3DXFont_Release(font);
368 
369     hr = D3DXCreateFontA(NULL, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
370     ok(hr == D3DERR_INVALIDCALL, "D3DXCreateFont returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
371 
372     hr = D3DXCreateFontA(device, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", NULL);
373     ok(hr == D3DERR_INVALIDCALL, "D3DXCreateFont returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
374 
375     hr = D3DXCreateFontA(NULL, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", NULL);
376     ok(hr == D3DERR_INVALIDCALL, "D3DXCreateFont returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
377 
378 
379     /* D3DXCreateFontIndirect */
380     desc.Height = 12;
381     desc.Width = 0;
382     desc.Weight = FW_DONTCARE;
383     desc.MipLevels = 0;
384     desc.Italic = FALSE;
385     desc.CharSet = DEFAULT_CHARSET;
386     desc.OutputPrecision = OUT_DEFAULT_PRECIS;
387     desc.Quality = DEFAULT_QUALITY;
388     desc.PitchAndFamily = DEFAULT_PITCH;
389     strcpy(desc.FaceName, "Tahoma");
390     hr = D3DXCreateFontIndirectA(device, &desc, &font);
391     ok(hr == D3D_OK, "D3DXCreateFontIndirect returned %#x, expected %#x\n", hr, D3D_OK);
392     ID3DXFont_Release(font);
393 
394     hr = D3DXCreateFontIndirectA(NULL, &desc, &font);
395     ok(hr == D3DERR_INVALIDCALL, "D3DXCreateFontIndirect returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
396 
397     hr = D3DXCreateFontIndirectA(device, NULL, &font);
398     ok(hr == D3DERR_INVALIDCALL, "D3DXCreateFontIndirect returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
399 
400     hr = D3DXCreateFontIndirectA(device, &desc, NULL);
401     ok(hr == D3DERR_INVALIDCALL, "D3DXCreateFontIndirect returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
402 
403 
404     /* ID3DXFont_GetDevice */
405     hr = D3DXCreateFontA(device, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
406             DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
407     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
408 
409     hr = ID3DXFont_GetDevice(font, NULL);
410     ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#x.\n", hr);
411 
412     ref = get_ref((IUnknown *)device);
413     hr = ID3DXFont_GetDevice(font, &bufdev);
414     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
415     check_release((IUnknown *)bufdev, ref);
416 
417     ID3DXFont_Release(font);
418 
419 
420     /* ID3DXFont_GetDesc */
421     hr = D3DXCreateFontA(device, 12, 8, FW_BOLD, 2, TRUE, ANSI_CHARSET, OUT_RASTER_PRECIS,
422             ANTIALIASED_QUALITY, VARIABLE_PITCH, "Tahoma", &font);
423     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
424 
425     hr = ID3DXFont_GetDescA(font, NULL);
426     ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#x.\n", hr);
427 
428     hr = ID3DXFont_GetDescA(font, &desc);
429     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
430 
431     ok(desc.Height == 12, "Got unexpected height %d.\n", desc.Height);
432     ok(desc.Width == 8, "Got unexpected width %u.\n", desc.Width);
433     ok(desc.Weight == FW_BOLD, "Got unexpected weight %u.\n", desc.Weight);
434     ok(desc.MipLevels == 2, "Got unexpected miplevels %u.\n", desc.MipLevels);
435     ok(desc.Italic == TRUE, "Got unexpected italic %#x.\n", desc.Italic);
436     ok(desc.CharSet == ANSI_CHARSET, "Got unexpected charset %u.\n", desc.CharSet);
437     ok(desc.OutputPrecision == OUT_RASTER_PRECIS, "Got unexpected output precision %u.\n", desc.OutputPrecision);
438     ok(desc.Quality == ANTIALIASED_QUALITY, "Got unexpected quality %u.\n", desc.Quality);
439     ok(desc.PitchAndFamily == VARIABLE_PITCH, "Got unexpected pitch and family %#x.\n", desc.PitchAndFamily);
440     ok(!strcmp(desc.FaceName, "Tahoma"), "Got unexpected facename %s.\n", debugstr_a(desc.FaceName));
441 
442     ID3DXFont_Release(font);
443 
444 
445     /* ID3DXFont_GetDC + ID3DXFont_GetTextMetrics */
446     hr = D3DXCreateFontA(device, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
447             DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
448     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
449 
450     hdc = ID3DXFont_GetDC(font);
451     ok(!!hdc, "Got unexpected hdc %p.\n", hdc);
452 
453     ret = ID3DXFont_GetTextMetricsA(font, &metrics);
454     ok(ret, "Got unexpected ret %#x.\n", ret);
455     ret = GetTextMetricsA(hdc, &expmetrics);
456     ok(ret, "Got unexpected ret %#x.\n", ret);
457 
458     ok(metrics.tmHeight == expmetrics.tmHeight, "Got unexpected height %d, expected %d.\n",
459             metrics.tmHeight, expmetrics.tmHeight);
460     ok(metrics.tmAscent == expmetrics.tmAscent, "Got unexpected ascent %d, expected %d.\n",
461             metrics.tmAscent, expmetrics.tmAscent);
462     ok(metrics.tmDescent == expmetrics.tmDescent, "Got unexpected descent %d, expected %d.\n",
463             metrics.tmDescent, expmetrics.tmDescent);
464     ok(metrics.tmInternalLeading == expmetrics.tmInternalLeading, "Got unexpected internal leading %d, expected %d.\n",
465             metrics.tmInternalLeading, expmetrics.tmInternalLeading);
466     ok(metrics.tmExternalLeading == expmetrics.tmExternalLeading, "Got unexpected external leading %d, expected %d.\n",
467             metrics.tmExternalLeading, expmetrics.tmExternalLeading);
468     ok(metrics.tmAveCharWidth == expmetrics.tmAveCharWidth, "Got unexpected average char width %d, expected %d.\n",
469             metrics.tmAveCharWidth, expmetrics.tmAveCharWidth);
470     ok(metrics.tmMaxCharWidth == expmetrics.tmMaxCharWidth, "Got unexpected maximum char width %d, expected %d.\n",
471             metrics.tmMaxCharWidth, expmetrics.tmMaxCharWidth);
472     ok(metrics.tmWeight == expmetrics.tmWeight, "Got unexpected weight %d, expected %d.\n",
473             metrics.tmWeight, expmetrics.tmWeight);
474     ok(metrics.tmOverhang == expmetrics.tmOverhang, "Got unexpected overhang %d, expected %d.\n",
475             metrics.tmOverhang, expmetrics.tmOverhang);
476     ok(metrics.tmDigitizedAspectX == expmetrics.tmDigitizedAspectX, "Got unexpected digitized x aspect %d, expected %d.\n",
477             metrics.tmDigitizedAspectX, expmetrics.tmDigitizedAspectX);
478     ok(metrics.tmDigitizedAspectY == expmetrics.tmDigitizedAspectY, "Got unexpected digitized y aspect %d, expected %d.\n",
479             metrics.tmDigitizedAspectY, expmetrics.tmDigitizedAspectY);
480     ok(metrics.tmFirstChar == expmetrics.tmFirstChar, "Got unexpected first char %u, expected %u.\n",
481             metrics.tmFirstChar, expmetrics.tmFirstChar);
482     ok(metrics.tmLastChar == expmetrics.tmLastChar, "Got unexpected last char %u, expected %u.\n",
483             metrics.tmLastChar, expmetrics.tmLastChar);
484     ok(metrics.tmDefaultChar == expmetrics.tmDefaultChar, "Got unexpected default char %u, expected %u.\n",
485             metrics.tmDefaultChar, expmetrics.tmDefaultChar);
486     ok(metrics.tmBreakChar == expmetrics.tmBreakChar, "Got unexpected break char %u, expected %u.\n",
487             metrics.tmBreakChar, expmetrics.tmBreakChar);
488     ok(metrics.tmItalic == expmetrics.tmItalic, "Got unexpected italic %u, expected %u.\n",
489             metrics.tmItalic, expmetrics.tmItalic);
490     ok(metrics.tmUnderlined == expmetrics.tmUnderlined, "Got unexpected underlined %u, expected %u.\n",
491             metrics.tmUnderlined, expmetrics.tmUnderlined);
492     ok(metrics.tmStruckOut == expmetrics.tmStruckOut, "Got unexpected struck out %u, expected %u.\n",
493             metrics.tmStruckOut, expmetrics.tmStruckOut);
494     ok(metrics.tmPitchAndFamily == expmetrics.tmPitchAndFamily, "Got unexpected pitch and family %u, expected %u.\n",
495             metrics.tmPitchAndFamily, expmetrics.tmPitchAndFamily);
496     ok(metrics.tmCharSet == expmetrics.tmCharSet, "Got unexpected charset %u, expected %u.\n",
497             metrics.tmCharSet, expmetrics.tmCharSet);
498 
499     ID3DXFont_Release(font);
500 
501 
502     /* ID3DXFont_PreloadText */
503     hr = D3DXCreateFontA(device, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
504             DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
505     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
506 
507     hr = ID3DXFont_PreloadTextA(font, NULL, -1);
508     ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#x.\n", hr);
509     hr = ID3DXFont_PreloadTextA(font, NULL, 0);
510     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
511     hr = ID3DXFont_PreloadTextA(font, NULL, 1);
512     ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#x.\n", hr);
513     hr = ID3DXFont_PreloadTextA(font, "test", -1);
514     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
515     hr = ID3DXFont_PreloadTextA(font, "", 0);
516     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
517     hr = ID3DXFont_PreloadTextA(font, "", -1);
518     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
519 
520     hr = ID3DXFont_PreloadTextW(font, NULL, -1);
521     ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#x.\n", hr);
522     hr = ID3DXFont_PreloadTextW(font, NULL, 0);
523     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
524     hr = ID3DXFont_PreloadTextW(font, NULL, 1);
525     ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#x.\n", hr);
526     hr = ID3DXFont_PreloadTextW(font, testW, -1);
527     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
528     hr = ID3DXFont_PreloadTextW(font, L"", 0);
529     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
530     hr = ID3DXFont_PreloadTextW(font, L"", -1);
531     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
532 
533     check_release((IUnknown*)font, 0);
534 
535 
536     /* ID3DXFont_GetGlyphData, ID3DXFont_PreloadGlyphs, ID3DXFont_PreloadCharacters */
537     hr = D3DXCreateFontA(device, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
538             DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
539     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
540 
541     hdc = ID3DXFont_GetDC(font);
542     ok(!!hdc, "Got unexpected hdc %p.\n", hdc);
543 
544     hr = ID3DXFont_GetGlyphData(font, 0, NULL, &blackbox, &cellinc);
545     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
546     hr = ID3DXFont_GetGlyphData(font, 0, &texture, NULL, &cellinc);
547     check_release((IUnknown *)texture, 1);
548     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
549     hr = ID3DXFont_GetGlyphData(font, 0, &texture, &blackbox, NULL);
550     check_release((IUnknown *)texture, 1);
551     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
552 
553     hr = ID3DXFont_PreloadCharacters(font, 'b', 'a');
554     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
555     hr = ID3DXFont_PreloadGlyphs(font, 1, 0);
556     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
557 
558     hr = ID3DXFont_PreloadCharacters(font, 'a', 'a');
559     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
560 
561     for (c = 'b'; c <= 'z'; ++c)
562     {
563         count = GetGlyphIndicesA(hdc, &c, 1, &glyph, 0);
564         ok(count != GDI_ERROR, "Got unexpected count %u.\n", count);
565 
566         hr = ID3DXFont_GetGlyphData(font, glyph, &texture, &blackbox, &cellinc);
567         ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
568 
569         levels = IDirect3DTexture9_GetLevelCount(texture);
570         todo_wine ok(levels == 5, "Character %c: got unexpected levels %u.\n", c, levels);
571         hr = IDirect3DTexture9_GetLevelDesc(texture, 0, &surf_desc);
572         ok(hr == D3D_OK, "Character %c: got unexpected hr %#x.\n", c, hr);
573         ok(surf_desc.Format == D3DFMT_A8R8G8B8, "Character %c: got unexpected format %#x.\n", c, surf_desc.Format);
574         ok(surf_desc.Usage == 0, "Character %c: got unexpected usage %#x.\n", c, surf_desc.Usage);
575         ok(surf_desc.Width == 256, "Character %c: got unexpected width %u.\n", c, surf_desc.Width);
576         ok(surf_desc.Height == 256, "Character %c: got unexpected height %u.\n", c, surf_desc.Height);
577         ok(surf_desc.Pool == D3DPOOL_MANAGED, "Character %c: got unexpected pool %u.\n", c, surf_desc.Pool);
578 
579         count = GetGlyphOutlineW(hdc, glyph, GGO_GLYPH_INDEX | GGO_METRICS, &glyph_metrics, 0, NULL, &mat);
580         ok(count != GDI_ERROR, "Got unexpected count %#x.\n", count);
581 
582         ret = ID3DXFont_GetTextMetricsW(font, &tm);
583         ok(ret, "Got unexpected ret %#x.\n", ret);
584 
585         todo_wine ok(blackbox.right - blackbox.left == glyph_metrics.gmBlackBoxX + 2, "Character %c: got %d, expected %d.\n",
586                 c, blackbox.right - blackbox.left, glyph_metrics.gmBlackBoxX + 2);
587         todo_wine ok(blackbox.bottom - blackbox.top == glyph_metrics.gmBlackBoxY + 2, "Character %c: got %d, expected %d.\n",
588                 c, blackbox.bottom - blackbox.top, glyph_metrics.gmBlackBoxY + 2);
589         ok(cellinc.x == glyph_metrics.gmptGlyphOrigin.x - 1, "Character %c: got %d, expected %d.\n",
590                 c, cellinc.x, glyph_metrics.gmptGlyphOrigin.x - 1);
591         ok(cellinc.y == tm.tmAscent - glyph_metrics.gmptGlyphOrigin.y - 1, "Character %c: got %d, expected %d.\n",
592                 c, cellinc.y, tm.tmAscent - glyph_metrics.gmptGlyphOrigin.y - 1);
593 
594         check_release((IUnknown *)texture, 1);
595     }
596 
597     hr = ID3DXFont_PreloadCharacters(font, 'a', 'z');
598     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
599 
600     /* Test multiple textures */
601     hr = ID3DXFont_PreloadGlyphs(font, 0, 1000);
602     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
603 
604     /* Test glyphs that are not rendered */
605     for (glyph = 1; glyph < 4; ++glyph)
606     {
607         texture = (IDirect3DTexture9 *)0xdeadbeef;
608         hr = ID3DXFont_GetGlyphData(font, glyph, &texture, &blackbox, &cellinc);
609         ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
610         ok(!texture, "Got unexpected texture %p.\n", texture);
611     }
612 
613     check_release((IUnknown *)font, 0);
614 
615     c = 'a';
616     for (i = 0; i < ARRAY_SIZE(tests); ++i)
617     {
618         hr = D3DXCreateFontA(device, tests[i].font_height, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET,
619                 OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
620         ok(hr == D3D_OK, "Test %u: got unexpected hr %#x.\n", i, hr);
621 
622         hdc = ID3DXFont_GetDC(font);
623         ok(!!hdc, "Test %u: got unexpected hdc %p.\n", i, hdc);
624 
625         count = GetGlyphIndicesA(hdc, &c, 1, &glyph, 0);
626         ok(count != GDI_ERROR, "Test %u: got unexpected count %u.\n", i, count);
627 
628         hr = ID3DXFont_GetGlyphData(font, glyph, &texture, NULL, NULL);
629         ok(hr == D3D_OK, "Test %u: got unexpected hr %#x.\n", i, hr);
630 
631         levels = IDirect3DTexture9_GetLevelCount(texture);
632         todo_wine_if(tests[i].expected_levels < 9)
633         ok(levels == tests[i].expected_levels, "Test %u: got unexpected levels %u.\n", i, levels);
634 
635         hr = IDirect3DTexture9_GetLevelDesc(texture, 0, &surf_desc);
636         ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
637         ok(surf_desc.Format == D3DFMT_A8R8G8B8, "Test %u: got unexpected format %#x.\n", i, surf_desc.Format);
638         ok(surf_desc.Usage == 0, "Test %u: got unexpected usage %#x.\n", i, surf_desc.Usage);
639         ok(surf_desc.Width == tests[i].expected_size, "Test %u: got unexpected width %u.\n", i, surf_desc.Width);
640         ok(surf_desc.Height == tests[i].expected_size, "Test %u: got unexpected height %u.\n", i, surf_desc.Height);
641         ok(surf_desc.Pool == D3DPOOL_MANAGED, "Test %u: got unexpected pool %u.\n", i, surf_desc.Pool);
642 
643         IDirect3DTexture9_Release(texture);
644 
645         /* ID3DXFontImpl_DrawText */
646         D3DXCreateSprite(device, &sprite);
647         SetRect(&rect, 0, 0, 640, 480);
648 
649         IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0xff000000, 1.0f, 0);
650 
651         IDirect3DDevice9_BeginScene(device);
652         hr = ID3DXSprite_Begin(sprite, D3DXSPRITE_ALPHABLEND);
653         ok (hr == D3D_OK, "Test %d: got unexpected hr %#x.\n", i, hr);
654 
655         height = ID3DXFont_DrawTextW(font, sprite, testW, -1, &rect, DT_TOP, 0xffffffff);
656         ok(height == tests[i].font_height, "Test %d: got unexpected height %u.\n", i, height);
657         height = ID3DXFont_DrawTextW(font, sprite, testW, size, &rect, DT_TOP, 0xffffffff);
658         ok(height == tests[i].font_height, "Test %d: got unexpected height %u.\n", i, height);
659         height = ID3DXFont_DrawTextW(font, sprite, testW, size, &rect, DT_RIGHT, 0xffffffff);
660         ok(height == tests[i].font_height, "Test %d: got unexpected height %u.\n", i, height);
661         height = ID3DXFont_DrawTextW(font, sprite, testW, size, &rect, DT_LEFT | DT_NOCLIP, 0xffffffff);
662         ok(height == tests[i].font_height, "Test %d: got unexpected height %u.\n", i, height);
663 
664         SetRectEmpty(&rect);
665         height = ID3DXFont_DrawTextW(font, sprite, testW, size, &rect,
666                 DT_LEFT | DT_CALCRECT, 0xffffffff);
667         ok(height == tests[i].font_height, "Test %d: got unexpected height %u.\n", i, height);
668         ok(!rect.left, "Test %d: got unexpected rect left %d.\n", i, rect.left);
669         ok(!rect.top, "Test %d: got unexpected rect top %d.\n", i, rect.top);
670         todo_wine ok(rect.right, "Test %d: got unexpected rect right %d.\n", i, rect.right);
671         todo_wine ok(rect.bottom == tests[i].font_height, "Test %d: got unexpected rect bottom %d.\n", i, rect.bottom);
672 
673         hr = ID3DXSprite_End(sprite);
674         ok (hr == D3D_OK, "Test %d: got unexpected hr %#x.\n", i, hr);
675         IDirect3DDevice9_EndScene(device);
676         ID3DXSprite_Release(sprite);
677 
678         ID3DXFont_Release(font);
679     }
680 
681 
682     /* ID3DXFont_DrawTextA, ID3DXFont_DrawTextW */
683     hr = D3DXCreateFontA(device, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
684             DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
685     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
686 
687     SetRect(&rect, 10, 10, 200, 200);
688 
689     height = ID3DXFont_DrawTextA(font, NULL, "test", -2, &rect, 0, 0xFF00FF);
690     ok(height == 12, "Got unexpected height %d.\n", height);
691 
692     height = ID3DXFont_DrawTextA(font, NULL, "test", -1, &rect, 0, 0xFF00FF);
693     ok(height == 12, "Got unexpected height %d.\n", height);
694 
695     height = ID3DXFont_DrawTextA(font, NULL, "test", 0, &rect, 0, 0xFF00FF);
696     ok(height == 0, "Got unexpected height %d.\n", height);
697 
698     height = ID3DXFont_DrawTextA(font, NULL, "test", 1, &rect, 0, 0xFF00FF);
699     ok(height == 12, "Got unexpected height %d.\n", height);
700 
701     height = ID3DXFont_DrawTextA(font, NULL, "test", 2, &rect, 0, 0xFF00FF);
702     ok(height == 12, "Got unexpected height %d.\n", height);
703 
704     height = ID3DXFont_DrawTextA(font, NULL, "", 0, &rect, 0, 0xff00ff);
705     ok(height == 0, "Got unexpected height %d.\n", height);
706 
707     height = ID3DXFont_DrawTextA(font, NULL, "", -1, &rect, 0, 0xff00ff);
708     ok(height == 0, "Got unexpected height %d.\n", height);
709 
710     height = ID3DXFont_DrawTextA(font, NULL, "test", -1, NULL, 0, 0xFF00FF);
711     ok(height == 12, "Got unexpected height %d.\n", height);
712 
713     height = ID3DXFont_DrawTextA(font, NULL, "test", -1, NULL, DT_CALCRECT, 0xFF00FF);
714     ok(height == 12, "Got unexpected height %d.\n", height);
715 
716     height = ID3DXFont_DrawTextA(font, NULL, NULL, -1, NULL, 0, 0xFF00FF);
717     ok(height == 0, "Got unexpected height %d.\n", height);
718 
719     SetRect(&rect, 10, 10, 50, 50);
720 
721     height = ID3DXFont_DrawTextA(font, NULL, long_text, -1, &rect, DT_WORDBREAK, 0xff00ff);
722     todo_wine ok(height == 60, "Got unexpected height %d.\n", height);
723 
724     height = ID3DXFont_DrawTextA(font, NULL, long_text, -1, &rect, DT_WORDBREAK | DT_NOCLIP, 0xff00ff);
725     ok(height == 96, "Got unexpected height %d.\n", height);
726 
727     SetRect(&rect, 10, 10, 200, 200);
728 
729     height = ID3DXFont_DrawTextW(font, NULL, testW, -1, &rect, 0, 0xFF00FF);
730     ok(height == 12, "Got unexpected height %d.\n", height);
731 
732     height = ID3DXFont_DrawTextW(font, NULL, testW, 0, &rect, 0, 0xFF00FF);
733     ok(height == 0, "Got unexpected height %d.\n", height);
734 
735     height = ID3DXFont_DrawTextW(font, NULL, testW, 1, &rect, 0, 0xFF00FF);
736     ok(height == 12, "Got unexpected height %d.\n", height);
737 
738     height = ID3DXFont_DrawTextW(font, NULL, testW, 2, &rect, 0, 0xFF00FF);
739     ok(height == 12, "Got unexpected height %d.\n", height);
740 
741     height = ID3DXFont_DrawTextW(font, NULL, L"", 0, &rect, 0, 0xff00ff);
742     ok(height == 0, "Got unexpected height %d.\n", height);
743 
744     height = ID3DXFont_DrawTextW(font, NULL, L"", -1, &rect, 0, 0xff00ff);
745     ok(height == 0, "Got unexpected height %d.\n", height);
746 
747     height = ID3DXFont_DrawTextW(font, NULL, testW, -1, NULL, 0, 0xFF00FF);
748     ok(height == 12, "Got unexpected height %d.\n", height);
749 
750     height = ID3DXFont_DrawTextW(font, NULL, testW, -1, NULL, DT_CALCRECT, 0xFF00FF);
751     ok(height == 12, "Got unexpected height %d.\n", height);
752 
753     height = ID3DXFont_DrawTextW(font, NULL, NULL, -1, NULL, 0, 0xFF00FF);
754     ok(height == 0, "Got unexpected height %d.\n", height);
755 
756     SetRect(&rect, 10, 10, 50, 50);
757 
758     height = ID3DXFont_DrawTextW(font, NULL, long_textW, -1, &rect, DT_WORDBREAK, 0xff00ff);
759     todo_wine ok(height == 60, "Got unexpected height %d.\n", height);
760 
761     height = ID3DXFont_DrawTextW(font, NULL, long_textW, -1, &rect, DT_WORDBREAK | DT_NOCLIP, 0xff00ff);
762     ok(height == 96, "Got unexpected height %d.\n", height);
763 
764     height = ID3DXFont_DrawTextW(font, NULL, L"a\na", -1, &rect, 0, 0xff00ff);
765     ok(height == 24, "Got unexpected height %d.\n", height);
766 
767     height = ID3DXFont_DrawTextW(font, NULL, L"a\r\na", -1, &rect, 0, 0xff00ff);
768     ok(height == 24, "Got unexpected height %d.\n", height);
769 
770     height = ID3DXFont_DrawTextW(font, NULL, L"a\ra", -1, &rect, 0, 0xff00ff);
771     ok(height == 12, "Got unexpected height %d.\n", height);
772 
773     height = ID3DXFont_DrawTextW(font, NULL, L"a\na", -1, &rect, DT_SINGLELINE, 0xff00ff);
774     ok(height == 12, "Got unexpected height %d.\n", height);
775 
776     height = ID3DXFont_DrawTextW(font, NULL, L"a\naaaaa aaaa", -1, &rect, 0, 0xff00ff);
777     ok(height == 24, "Got unexpected height %d.\n", height);
778 
779     height = ID3DXFont_DrawTextW(font, NULL, L"a\naaaaa aaaa", -1, &rect, DT_WORDBREAK, 0xff00ff);
780     ok(height == 36, "Got unexpected height %d.\n", height);
781 
782     height = ID3DXFont_DrawTextW(font, NULL, L"1\n2\n3\n4\n5\n6", -1, &rect, 0, 0xff00ff);
783     ok(height == 48, "Got unexpected height %d.\n", height);
784 
785     height = ID3DXFont_DrawTextW(font, NULL, L"1\n2\n3\n4\n5\n6", -1, &rect, DT_NOCLIP, 0xff00ff);
786     ok(height == 72, "Got unexpected height %d.\n", height);
787 
788     height = ID3DXFont_DrawTextW(font, NULL, L"\t\t\t\t\t\t\t\t\t\t", -1, &rect, DT_WORDBREAK, 0xff00ff);
789     todo_wine ok(height == 0, "Got unexpected height %d.\n", height);
790 
791     height = ID3DXFont_DrawTextW(font, NULL, L"\t\t\t\t\t\t\t\t\t\ta", -1, &rect, DT_WORDBREAK, 0xff00ff);
792     todo_wine ok(height == 12, "Got unexpected height %d.\n", height);
793 
794     height = ID3DXFont_DrawTextW(font, NULL, L"\taaaaaaaaaa", -1, &rect, DT_WORDBREAK, 0xff00ff);
795     todo_wine ok(height == 24, "Got unexpected height %d.\n", height);
796 
797     height = ID3DXFont_DrawTextW(font, NULL, L"\taaaaaaaaaa", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
798     ok(height == 36, "Got unexpected height %d.\n", height);
799 
800     height = ID3DXFont_DrawTextW(font, NULL, L"\taaa\taaa\taaa", -1, &rect, DT_WORDBREAK, 0xff00ff);
801     ok(height == 24, "Got unexpected height %d.\n", height);
802 
803     height = ID3DXFont_DrawTextW(font, NULL, L"\taaa\taaa\taaa", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
804     todo_wine ok(height == 48, "Got unexpected height %d.\n", height);
805 
806     height = ID3DXFont_DrawTextW(font, NULL, L"\t\t\t\t\t\t\t\t\t\t", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
807     todo_wine ok(height == 60, "Got unexpected height %d.\n", height);
808 
809     height = ID3DXFont_DrawTextW(font, NULL, L"a\ta", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
810     ok(height == 12, "Got unexpected height %d.\n", height);
811 
812     height = ID3DXFont_DrawTextW(font, NULL, L"a\ta\ta", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
813     todo_wine ok(height == 24, "Got unexpected height %d.\n", height);
814 
815     height = ID3DXFont_DrawTextW(font, NULL, L"aaaaaaaaaaaaaaaaaaaa", -1, &rect, DT_WORDBREAK, 0xff00ff);
816     ok(height == 36, "Got unexpected height %d.\n", height);
817 
818     height = ID3DXFont_DrawTextW(font, NULL, L"a                        a", -1, &rect, DT_WORDBREAK, 0xff00ff);
819     ok(height == 36, "Got unexpected height %d.\n", height);
820 
821     height = ID3DXFont_DrawTextW(font, NULL, L"aaaa              aaaa", -1, &rect, DT_WORDBREAK, 0xff00ff);
822     ok(height == 36, "Got unexpected height %d.\n", height);
823 
824     height = ID3DXFont_DrawTextW(font, NULL, L"aaaa              aaaa", -1, &rect, DT_WORDBREAK | DT_RIGHT, 0xff00ff);
825     ok(height == 36, "Got unexpected height %d.\n", height);
826 
827     height = ID3DXFont_DrawTextW(font, NULL, L"aaaa              aaaa", -1, &rect, DT_WORDBREAK | DT_RIGHT, 0xff00ff);
828     ok(height == 36, "Got unexpected height %d.\n", height);
829 
830     height = ID3DXFont_DrawTextW(font, NULL, L"aaaa\naaaa", -1, &rect, DT_BOTTOM, 0xff00ff);
831     todo_wine ok(height == 40, "Got unexpected height %d.\n", height);
832 
833     height = ID3DXFont_DrawTextW(font, NULL, L"aaaa\naaaa", -1, &rect, DT_VCENTER, 0xff00ff);
834     todo_wine ok(height == 32, "Got unexpected height %d.\n", height);
835 
836     height = ID3DXFont_DrawTextW(font, NULL, L"aaaa\naaaa", -1, &rect, DT_RIGHT, 0xff00ff);
837     ok(height == 24, "Got unexpected height %d.\n", height);
838 
839     height = ID3DXFont_DrawTextW(font, NULL, L"aaaa\naaaa", -1, &rect, DT_CENTER, 0xff00ff);
840     ok(height == 24, "Got unexpected height %d.\n", height);
841 
842     ID3DXFont_Release(font);
843 }
844 
845 static void test_D3DXCreateRenderToSurface(IDirect3DDevice9 *device)
846 {
847     int i;
848     HRESULT hr;
849     ULONG ref_count;
850     D3DXRTS_DESC desc;
851     ID3DXRenderToSurface *render = (void *)0xdeadbeef;
852     static const D3DXRTS_DESC tests[] =
853     {
854         { 0, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN },
855         { 256, 0, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN },
856         { 256, 0, D3DFMT_A8R8G8B8, FALSE, D3DFMT_D24S8 },
857         { 256, 256, D3DFMT_UNKNOWN, FALSE, D3DFMT_R8G8B8 },
858         { 0, 0, D3DFMT_UNKNOWN, FALSE, D3DFMT_UNKNOWN },
859         { -1, -1, MAKEFOURCC('B','A','D','F'), TRUE, MAKEFOURCC('B','A','D','F') }
860     };
861 
862     hr = D3DXCreateRenderToSurface(NULL /* device */, 256, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN, &render);
863     ok(hr == D3DERR_INVALIDCALL, "D3DXCreateRenderToSurface returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
864     ok(render == (void *)0xdeadbeef, "Got %p, expected %p\n", render, (void *)0xdeadbeef);
865 
866     hr = D3DXCreateRenderToSurface(device, 256, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN, NULL /* out */);
867     ok(hr == D3DERR_INVALIDCALL, "D3DXCreateRenderToSurface returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
868 
869     for (i = 0; i < ARRAY_SIZE(tests); i++)
870     {
871         hr = D3DXCreateRenderToSurface(device, tests[i].Width, tests[i].Height, tests[i].Format, tests[i].DepthStencil,
872                 tests[i].DepthStencilFormat, &render);
873         ok(hr == D3D_OK, "%d: D3DXCreateRenderToSurface returned %#x, expected %#x\n", i, hr, D3D_OK);
874         if (SUCCEEDED(hr))
875         {
876             hr = ID3DXRenderToSurface_GetDesc(render, &desc);
877             ok(hr == D3D_OK, "%d: GetDesc failed %#x\n", i, hr);
878             if (SUCCEEDED(hr))
879             {
880                 ok(desc.Width == tests[i].Width, "%d: Got width %u, expected %u\n", i, desc.Width, tests[i].Width);
881                 ok(desc.Height == tests[i].Height, "%d: Got height %u, expected %u\n", i, desc.Height, tests[i].Height);
882                 ok(desc.Format == tests[i].Format, "%d: Got format %#x, expected %#x\n", i, desc.Format, tests[i].Format);
883                 ok(desc.DepthStencil == tests[i].DepthStencil, "%d: Got depth stencil %d, expected %d\n",
884                         i, desc.DepthStencil, tests[i].DepthStencil);
885                 ok(desc.DepthStencilFormat == tests[i].DepthStencilFormat, "%d: Got depth stencil format %#x, expected %#x\n",
886                         i, desc.DepthStencilFormat, tests[i].DepthStencilFormat);
887             }
888             ID3DXRenderToSurface_Release(render);
889         }
890     }
891 
892     /* check device ref count */
893     ref_count = get_ref((IUnknown *)device);
894     hr = D3DXCreateRenderToSurface(device, 0, 0, D3DFMT_UNKNOWN, FALSE, D3DFMT_UNKNOWN, &render);
895     check_ref((IUnknown *)device, ref_count + 1);
896     if (SUCCEEDED(hr)) ID3DXRenderToSurface_Release(render);
897 }
898 
899 /* runs a set of tests for the ID3DXRenderToSurface interface created with given parameters */
900 static void check_ID3DXRenderToSurface(IDirect3DDevice9 *device, UINT width, UINT height, D3DFORMAT format,
901         BOOL depth_stencil, D3DFORMAT depth_stencil_format, BOOL render_target)
902 {
903     HRESULT hr;
904     D3DFORMAT fmt;
905     HRESULT expected_value;
906     IDirect3DSurface9 *surface;
907     ID3DXRenderToSurface *render;
908     D3DVIEWPORT9 viewport = { 0, 0, width, height, 0.0, 1.0 };
909 
910     hr = D3DXCreateRenderToSurface(device, width, height, format, depth_stencil, depth_stencil_format, &render);
911     if (FAILED(hr))
912     {
913         skip("Failed to create ID3DXRenderToSurface\n");
914         return;
915     }
916 
917     if (render_target)
918         hr = IDirect3DDevice9_CreateRenderTarget(device, width, height, format, D3DMULTISAMPLE_NONE, 0, FALSE, &surface, NULL);
919     else
920         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, width, height, format, D3DPOOL_DEFAULT, &surface, NULL);
921     if (FAILED(hr))
922     {
923         skip("Failed to create surface\n");
924         ID3DXRenderToSurface_Release(render);
925         return;
926     }
927 
928     /* viewport */
929     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
930     ok(hr == D3D_OK, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3D_OK);
931     check_ref((IUnknown *)surface, 2);
932     if (SUCCEEDED(hr)) ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
933 
934     /* invalid viewport */
935     viewport.Width = 2 * width;
936     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
937     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
938 
939     viewport.X = width / 2;
940     viewport.Width = width;
941     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
942     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
943 
944     viewport.X = width;
945     viewport.Width = width;
946     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
947     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
948 
949     /* rendering to a part of a surface is only allowed for render target surfaces */
950     expected_value = render_target ? D3D_OK : D3DERR_INVALIDCALL;
951 
952     viewport.X = 0;
953     viewport.Width = width / 2;
954     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
955     ok(hr == expected_value, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, expected_value);
956     if (SUCCEEDED(hr)) ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
957 
958     viewport.X = width / 2;
959     viewport.Width = width - width / 2;
960     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
961     ok(hr == expected_value, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, expected_value);
962     if (SUCCEEDED(hr)) ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
963 
964     check_release((IUnknown *)surface, 0);
965 
966     /* surfaces with different sizes */
967     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, width / 2, width / 2, format, D3DPOOL_DEFAULT, &surface, NULL);
968     if (FAILED(hr))
969     {
970         skip("Failed to create surface\n");
971         ID3DXRenderToSurface_Release(render);
972         return;
973     }
974     hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
975     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
976     check_release((IUnknown *)surface, 0);
977 
978     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 2 * width, 2 * height, format, D3DPOOL_DEFAULT, &surface, NULL);
979     if (FAILED(hr))
980     {
981         skip("Failed to create surface\n");
982         ID3DXRenderToSurface_Release(render);
983         return;
984     }
985     hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
986     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
987     viewport.X = 0;
988     viewport.Y = 0;
989     viewport.Width = width;
990     viewport.Height = height;
991     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
992     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
993     check_release((IUnknown *)surface, 0);
994 
995     /* surfaces with different formats */
996     for (fmt = D3DFMT_A8R8G8B8; fmt <= D3DFMT_X8R8G8B8; fmt++)
997     {
998         HRESULT expected_result = (fmt != format) ? D3DERR_INVALIDCALL : D3D_OK;
999 
1000         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, width, height, fmt, D3DPOOL_DEFAULT, &surface, NULL);
1001         if (FAILED(hr))
1002         {
1003             skip("Failed to create surface\n");
1004             continue;
1005         }
1006 
1007         hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
1008         ok(hr == expected_result, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, expected_result);
1009 
1010         if (SUCCEEDED(hr)) ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
1011         check_release((IUnknown *)surface, 0);
1012     }
1013 
1014     check_release((IUnknown *)render, 0);
1015 }
1016 
1017 struct device_state
1018 {
1019     IDirect3DSurface9 *render_target;
1020     IDirect3DSurface9 *depth_stencil;
1021     D3DVIEWPORT9 viewport;
1022 };
1023 
1024 static void release_device_state(struct device_state *state)
1025 {
1026     if (state->render_target) IDirect3DSurface9_Release(state->render_target);
1027     if (state->depth_stencil) IDirect3DSurface9_Release(state->depth_stencil);
1028     memset(state, 0, sizeof(*state));
1029 }
1030 
1031 static HRESULT retrieve_device_state(IDirect3DDevice9 *device, struct device_state *state)
1032 {
1033     HRESULT hr;
1034 
1035     memset(state, 0, sizeof(*state));
1036 
1037     hr = IDirect3DDevice9_GetRenderTarget(device, 0, &state->render_target);
1038     if (FAILED(hr)) goto cleanup;
1039 
1040     hr = IDirect3DDevice9_GetDepthStencilSurface(device, &state->depth_stencil);
1041     if (hr == D3DERR_NOTFOUND)
1042         state->depth_stencil = NULL;
1043     else if (FAILED(hr))
1044         goto cleanup;
1045 
1046     hr = IDirect3DDevice9_GetViewport(device, &state->viewport);
1047     if (SUCCEEDED(hr)) return hr;
1048 
1049 cleanup:
1050     release_device_state(state);
1051     return hr;
1052 }
1053 
1054 static HRESULT apply_device_state(IDirect3DDevice9 *device, struct device_state *state)
1055 {
1056     HRESULT hr;
1057     HRESULT status = D3D_OK;
1058 
1059     hr = IDirect3DDevice9_SetRenderTarget(device, 0, state->render_target);
1060     if (FAILED(hr)) status = hr;
1061 
1062     hr = IDirect3DDevice9_SetDepthStencilSurface(device, state->depth_stencil);
1063     if (FAILED(hr)) status = hr;
1064 
1065     hr = IDirect3DDevice9_SetViewport(device, &state->viewport);
1066     if (FAILED(hr)) status = hr;
1067 
1068     return status;
1069 }
1070 
1071 static void compare_device_state(struct device_state *state1, struct device_state *state2, BOOL equal)
1072 {
1073     BOOL cmp;
1074     const char *message = equal ? "differs" : "is the same";
1075 
1076     cmp = state1->render_target == state2->render_target;
1077     ok(equal ? cmp : !cmp, "Render target %s %p, %p\n", message, state1->render_target, state2->render_target);
1078 
1079     cmp = state1->depth_stencil == state2->depth_stencil;
1080     ok(equal ? cmp : !cmp, "Depth stencil surface %s %p, %p\n", message, state1->depth_stencil, state2->depth_stencil);
1081 
1082     cmp = state1->viewport.X == state2->viewport.X && state1->viewport.Y == state2->viewport.Y
1083             && state1->viewport.Width == state2->viewport.Width && state1->viewport.Height == state2->viewport.Height;
1084     ok(equal ? cmp : !cmp, "Viewport %s (%u, %u, %u, %u), (%u, %u, %u, %u)\n", message,
1085             state1->viewport.X, state1->viewport.Y, state1->viewport.Width, state1->viewport.Height,
1086             state2->viewport.X, state2->viewport.Y, state2->viewport.Width, state2->viewport.Height);
1087 }
1088 
1089 static void test_ID3DXRenderToSurface_device_state(IDirect3DDevice9 *device)
1090 {
1091     HRESULT hr;
1092     IDirect3DSurface9 *surface = NULL;
1093     ID3DXRenderToSurface *render = NULL;
1094     struct device_state pre_state;
1095     struct device_state current_state;
1096     IDirect3DSurface9 *depth_stencil_surface;
1097 
1098     /* make sure there is a depth stencil surface present */
1099     hr = IDirect3DDevice9_GetDepthStencilSurface(device, &depth_stencil_surface);
1100     if (SUCCEEDED(hr))
1101     {
1102         IDirect3DSurface9_Release(depth_stencil_surface);
1103         depth_stencil_surface = NULL;
1104     }
1105     else if (hr == D3DERR_NOTFOUND)
1106     {
1107         hr = IDirect3DDevice9_CreateDepthStencilSurface(device, 256, 256, D3DFMT_D24X8,
1108                 D3DMULTISAMPLE_NONE, 0, TRUE, &depth_stencil_surface, NULL);
1109         if (SUCCEEDED(hr)) IDirect3DDevice9_SetDepthStencilSurface(device, depth_stencil_surface);
1110     }
1111 
1112     if (FAILED(hr))
1113     {
1114         skip("Failed to create depth stencil surface\n");
1115         return;
1116     }
1117 
1118     hr = IDirect3DDevice9_CreateRenderTarget(device, 256, 256, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0,
1119         FALSE, &surface, NULL);
1120     if (FAILED(hr))
1121     {
1122         skip("Failed to create render target\n");
1123         goto cleanup;
1124     }
1125 
1126     hr = retrieve_device_state(device, &pre_state);
1127     ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1128 
1129     hr = D3DXCreateRenderToSurface(device, 256, 256, D3DFMT_A8R8G8B8, TRUE, D3DFMT_D24X8, &render);
1130     ok(hr == D3D_OK, "D3DXCreateRenderToSurface returned %#x, expected %#x\n", hr, D3D_OK);
1131     if (SUCCEEDED(hr))
1132     {
1133         hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
1134         ok(hr == D3D_OK, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3D_OK);
1135 
1136         hr = retrieve_device_state(device, &current_state);
1137         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1138         compare_device_state(&current_state, &pre_state, FALSE);
1139         release_device_state(&current_state);
1140 
1141         hr = ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
1142         ok(hr == D3D_OK, "ID3DXRenderToSurface::EndScene returned %#x, expected %#x\n", hr, D3D_OK);
1143 
1144         hr = retrieve_device_state(device, &current_state);
1145         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1146         compare_device_state(&current_state, &pre_state, TRUE);
1147         release_device_state(&current_state);
1148 
1149         check_release((IUnknown *)render, 0);
1150     }
1151 
1152     hr = D3DXCreateRenderToSurface(device, 256, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN, &render);
1153     if (SUCCEEDED(hr))
1154     {
1155         hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
1156         ok(hr == D3D_OK, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3D_OK);
1157 
1158         hr = retrieve_device_state(device, &current_state);
1159         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1160         compare_device_state(&current_state, &pre_state, FALSE);
1161         release_device_state(&current_state);
1162 
1163         hr = ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
1164         ok(hr == D3D_OK, "ID3DXRenderToSurface::EndScene returned %#x, expected %#x\n", hr, D3D_OK);
1165 
1166         hr = retrieve_device_state(device, &current_state);
1167         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1168         compare_device_state(&current_state, &pre_state, TRUE);
1169         release_device_state(&current_state);
1170 
1171         hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
1172         ok(hr == D3D_OK, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3D_OK);
1173 
1174         hr = retrieve_device_state(device, &current_state);
1175         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1176         compare_device_state(&current_state, &pre_state, FALSE);
1177         release_device_state(&current_state);
1178 
1179         check_release((IUnknown *)render, 0);
1180 
1181         /* if EndScene isn't called, the device state isn't restored */
1182         hr = retrieve_device_state(device, &current_state);
1183         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1184         compare_device_state(&current_state, &pre_state, FALSE);
1185         release_device_state(&current_state);
1186 
1187         hr = apply_device_state(device, &pre_state);
1188         ok(SUCCEEDED(hr), "Failed to restore previous device state\n");
1189 
1190         IDirect3DDevice9_EndScene(device);
1191     }
1192 
1193     release_device_state(&pre_state);
1194 
1195 cleanup:
1196     if (depth_stencil_surface)
1197     {
1198         IDirect3DDevice9_SetDepthStencilSurface(device, NULL);
1199         IDirect3DSurface9_Release(depth_stencil_surface);
1200     }
1201 
1202     if (surface) check_release((IUnknown *)surface, 0);
1203 }
1204 
1205 static void test_ID3DXRenderToSurface(IDirect3DDevice9 *device)
1206 {
1207     int i;
1208     HRESULT hr;
1209     ULONG ref_count;
1210     IDirect3DDevice9 *out_device;
1211     ID3DXRenderToSurface *render;
1212     IDirect3DSurface9 *surface;
1213     D3DVIEWPORT9 viewport = { 0, 0, 256, 256, 0.0, 1.0 };
1214     D3DXRTS_DESC tests[] = {
1215         { 256, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN },
1216         { 256, 256, D3DFMT_A8R8G8B8, TRUE, D3DFMT_D24S8 },
1217         { 256, 256, D3DFMT_A8R8G8B8, TRUE, D3DFMT_D24X8 },
1218         { 512, 512, D3DFMT_X8R8G8B8, FALSE, D3DFMT_X8R8G8B8 },
1219         { 1024, 1024, D3DFMT_X8R8G8B8, TRUE, D3DFMT_D24S8 }
1220     };
1221 
1222     hr = D3DXCreateRenderToSurface(device, 256, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN, &render);
1223     ok(hr == D3D_OK, "D3DXCreateRenderToSurface returned %#x, expected %#x\n", hr, D3D_OK);
1224     if (FAILED(hr)) return;
1225 
1226     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 256, 256, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &surface, NULL);
1227     if (SUCCEEDED(hr))
1228     {
1229         ID3DXRenderToSurface *render_surface;
1230 
1231         /* GetDevice */
1232         hr = ID3DXRenderToSurface_GetDevice(render, NULL /* device */);
1233         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::GetDevice returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1234 
1235         ref_count = get_ref((IUnknown *)device);
1236         hr = ID3DXRenderToSurface_GetDevice(render, &out_device);
1237         ok(hr == D3D_OK, "ID3DXRenderToSurface::GetDevice returned %#x, expected %#x\n", hr, D3D_OK);
1238         check_release((IUnknown *)out_device, ref_count);
1239 
1240         /* BeginScene and EndScene */
1241         hr = ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
1242         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::EndScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1243 
1244         hr = ID3DXRenderToSurface_BeginScene(render, NULL /* surface */, &viewport);
1245         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1246 
1247         ref_count = get_ref((IUnknown *)surface);
1248         hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
1249         ok(hr == D3D_OK, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3D_OK);
1250         if (SUCCEEDED(hr))
1251         {
1252             check_ref((IUnknown *)surface, ref_count + 1);
1253 
1254             hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
1255             ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1256 
1257             hr = ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
1258             ok(hr == D3D_OK, "ID3DXRenderToSurface::EndScene returned %#x, expected %#x\n", hr, D3D_OK);
1259 
1260             check_ref((IUnknown *)surface, ref_count);
1261         }
1262 
1263         /* error handling is deferred to BeginScene */
1264         hr = D3DXCreateRenderToSurface(device, 256, 256, D3DFMT_A8R8G8B8, TRUE, D3DFMT_UNKNOWN, &render_surface);
1265         ok(hr == D3D_OK, "D3DXCreateRenderToSurface returned %#x, expected %#x\n", hr, D3D_OK);
1266         hr = ID3DXRenderToSurface_BeginScene(render_surface, surface, NULL);
1267         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1268         check_release((IUnknown *)render_surface, 0);
1269 
1270         check_release((IUnknown *)surface, 0);
1271     }
1272     else skip("Failed to create surface\n");
1273 
1274     check_release((IUnknown *)render, 0);
1275 
1276     for (i = 0; i < ARRAY_SIZE(tests); i++)
1277     {
1278         check_ID3DXRenderToSurface(device, tests[i].Width, tests[i].Height, tests[i].Format, tests[i].DepthStencil, tests[i].DepthStencilFormat, TRUE);
1279         check_ID3DXRenderToSurface(device, tests[i].Width, tests[i].Height, tests[i].Format, tests[i].DepthStencil, tests[i].DepthStencilFormat, FALSE);
1280     }
1281 
1282     test_ID3DXRenderToSurface_device_state(device);
1283 }
1284 
1285 static void test_D3DXCreateRenderToEnvMap(IDirect3DDevice9 *device)
1286 {
1287     int i;
1288     HRESULT hr;
1289     ULONG ref_count;
1290     D3DXRTE_DESC desc;
1291     ID3DXRenderToEnvMap *render;
1292     static const struct {
1293         D3DXRTE_DESC parameters;
1294         D3DXRTE_DESC expected_values;
1295     } tests[] = {
1296         { {   0,   0, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN }, {   1, 1, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN } },
1297         { { 256,   0, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN }, { 256, 9, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN } },
1298         { { 256,   4, D3DFMT_A8R8G8B8, FALSE, D3DFMT_D24S8   }, { 256, 4, D3DFMT_A8R8G8B8, FALSE, D3DFMT_D24S8   } },
1299         { { 256, 256, D3DFMT_UNKNOWN,  FALSE, D3DFMT_R8G8B8  }, { 256, 9, D3DFMT_A8R8G8B8, FALSE, D3DFMT_R8G8B8  } },
1300         { {  -1,  -1, D3DFMT_A8R8G8B8, TRUE,  D3DFMT_DXT1    }, { 256, 9, D3DFMT_A8R8G8B8, TRUE,  D3DFMT_DXT1    } },
1301         { { 256,   1, D3DFMT_X8R8G8B8, TRUE,  D3DFMT_UNKNOWN }, { 256, 1, D3DFMT_X8R8G8B8, TRUE,  D3DFMT_UNKNOWN } }
1302     };
1303 
1304     for (i = 0; i < ARRAY_SIZE(tests); i++)
1305     {
1306         const D3DXRTE_DESC *parameters = &tests[i].parameters;
1307         const D3DXRTE_DESC *expected  = &tests[i].expected_values;
1308         hr = D3DXCreateRenderToEnvMap(device, parameters->Size, parameters->MipLevels, parameters->Format,
1309                 parameters->DepthStencil, parameters->DepthStencilFormat, &render);
1310         ok(hr == D3D_OK, "%d: D3DXCreateRenderToEnvMap returned %#x, expected %#x\n", i, hr, D3D_OK);
1311         if (SUCCEEDED(hr))
1312         {
1313             hr = ID3DXRenderToEnvMap_GetDesc(render, &desc);
1314             ok(hr == D3D_OK, "%d: GetDesc failed %#x\n", i, hr);
1315             if (SUCCEEDED(hr))
1316             {
1317                 ok(desc.Size == expected->Size, "%d: Got size %u, expected %u\n", i, desc.Size, expected->Size);
1318                 ok(desc.MipLevels == expected->MipLevels, "%d: Got miplevels %u, expected %u\n", i, desc.MipLevels, expected->MipLevels);
1319                 ok(desc.Format == expected->Format, "%d: Got format %#x, expected %#x\n", i, desc.Format, expected->Format);
1320                 ok(desc.DepthStencil == expected->DepthStencil, "%d: Got depth stencil %d, expected %d\n",
1321                         i, expected->DepthStencil, expected->DepthStencil);
1322                 ok(desc.DepthStencilFormat == expected->DepthStencilFormat, "%d: Got depth stencil format %#x, expected %#x\n",
1323                         i, expected->DepthStencilFormat, expected->DepthStencilFormat);
1324             }
1325             check_release((IUnknown *)render, 0);
1326         }
1327     }
1328 
1329     /* check device ref count */
1330     ref_count = get_ref((IUnknown *)device);
1331     hr = D3DXCreateRenderToEnvMap(device, 0, 0, D3DFMT_UNKNOWN, FALSE, D3DFMT_UNKNOWN, &render);
1332     check_ref((IUnknown *)device, ref_count + 1);
1333     if (SUCCEEDED(hr)) ID3DXRenderToEnvMap_Release(render);
1334 }
1335 
1336 static void test_ID3DXRenderToEnvMap_cube_map(IDirect3DDevice9 *device)
1337 {
1338     HRESULT hr;
1339     IDirect3DCubeTexture9 *cube_texture = NULL;
1340     ID3DXRenderToEnvMap *render = NULL;
1341     struct device_state pre_state;
1342     struct device_state current_state;
1343 
1344     hr = IDirect3DDevice9_CreateCubeTexture(device, 256, 0, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
1345         &cube_texture, NULL);
1346     if (FAILED(hr))
1347     {
1348         skip("Failed to create cube texture\n");
1349         return;
1350     }
1351 
1352     hr = retrieve_device_state(device, &pre_state);
1353     ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1354 
1355     hr = D3DXCreateRenderToEnvMap(device, 256, 0, D3DFMT_A8R8G8B8, TRUE, D3DFMT_D24X8, &render);
1356     ok(hr == D3D_OK, "D3DCreateRenderToEnvMap returned %#x, expected %#x\n", hr, D3D_OK);
1357     if (SUCCEEDED(hr))
1358     {
1359         DWORD face;
1360 
1361         hr = ID3DXRenderToEnvMap_End(render, D3DX_FILTER_NONE);
1362         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::End returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1363 
1364         hr = ID3DXRenderToEnvMap_BeginCube(render, cube_texture);
1365         ok(hr == D3D_OK, "ID3DXRenderToEnvMap::BeginCube returned %#x, expected %#x\n", hr, D3D_OK);
1366 
1367         hr = retrieve_device_state(device, &current_state);
1368         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1369         compare_device_state(&current_state, &pre_state, TRUE);
1370         release_device_state(&current_state);
1371 
1372         for (face = D3DCUBEMAP_FACE_POSITIVE_X; face <= D3DCUBEMAP_FACE_NEGATIVE_Z; face++)
1373         {
1374             hr = ID3DXRenderToEnvMap_Face(render, face, D3DX_FILTER_POINT);
1375             ok(hr == D3D_OK, "ID3DXRenderToEnvMap::Face returned %#x, expected %#x\n", hr, D3D_OK);
1376 
1377             hr = retrieve_device_state(device, &current_state);
1378             ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1379             compare_device_state(&current_state, &pre_state, FALSE);
1380             release_device_state(&current_state);
1381         }
1382 
1383         hr = ID3DXRenderToEnvMap_End(render, D3DX_FILTER_POINT);
1384         ok(hr == D3D_OK, "ID3DXRenderToEnvMap::End returned %#x, expected %#x\n", hr, D3D_OK);
1385 
1386         hr = retrieve_device_state(device, &current_state);
1387         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1388         compare_device_state(&current_state, &pre_state, TRUE);
1389         release_device_state(&current_state);
1390 
1391         check_release((IUnknown *)render, 0);
1392     }
1393 
1394     release_device_state(&pre_state);
1395 
1396     check_release((IUnknown *)cube_texture, 0);
1397 }
1398 
1399 static void test_ID3DXRenderToEnvMap(IDirect3DDevice9 *device)
1400 {
1401     HRESULT hr;
1402     ID3DXRenderToEnvMap *render;
1403     IDirect3DSurface9 *depth_stencil_surface;
1404 
1405     hr = D3DXCreateRenderToEnvMap(device, 256, 0, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN, &render);
1406     if (SUCCEEDED(hr))
1407     {
1408         ULONG ref_count;
1409         IDirect3DDevice9 *out_device;
1410 
1411         hr = ID3DXRenderToEnvMap_GetDesc(render, NULL);
1412         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::GetDesc returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1413 
1414         hr = ID3DXRenderToEnvMap_GetDevice(render, NULL);
1415         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::GetDevice returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1416 
1417         ref_count = get_ref((IUnknown *)device);
1418         hr = ID3DXRenderToEnvMap_GetDevice(render, &out_device);
1419         ok(hr == D3D_OK, "ID3DXRenderToEnvMap::GetDevice returned %#x, expected %#x\n", hr, D3D_OK);
1420         ok(out_device == device, "ID3DXRenderToEnvMap::GetDevice returned different device\n");
1421         check_release((IUnknown *)device, ref_count);
1422 
1423         hr = ID3DXRenderToEnvMap_End(render, D3DX_FILTER_NONE);
1424         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::End returned %#x, expected %#x\n", hr, D3D_OK);
1425 
1426         hr = ID3DXRenderToEnvMap_BeginCube(render, NULL);
1427         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::BeginCube returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1428 
1429         hr = ID3DXRenderToEnvMap_BeginHemisphere(render, NULL, NULL);
1430         todo_wine ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::BeginHemisphere returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1431 
1432         hr = ID3DXRenderToEnvMap_BeginParabolic(render, NULL, NULL);
1433         todo_wine ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::BeginParabolic returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1434 
1435         hr = ID3DXRenderToEnvMap_BeginSphere(render, NULL);
1436         todo_wine ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::BeginSphere returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1437 
1438         check_release((IUnknown *)render, 0);
1439     } else skip("Failed to create ID3DXRenderToEnvMap\n");
1440 
1441     /* make sure there is a depth stencil surface present */
1442     hr = IDirect3DDevice9_GetDepthStencilSurface(device, &depth_stencil_surface);
1443     if (SUCCEEDED(hr))
1444     {
1445         IDirect3DSurface9_Release(depth_stencil_surface);
1446         depth_stencil_surface = NULL;
1447     }
1448     else if (hr == D3DERR_NOTFOUND)
1449     {
1450         hr = IDirect3DDevice9_CreateDepthStencilSurface(device, 256, 256, D3DFMT_D24X8,
1451                 D3DMULTISAMPLE_NONE, 0, TRUE, &depth_stencil_surface, NULL);
1452         if (SUCCEEDED(hr)) IDirect3DDevice9_SetDepthStencilSurface(device, depth_stencil_surface);
1453     }
1454 
1455     if (FAILED(hr))
1456     {
1457         skip("Failed to create depth stencil surface\n");
1458         return;
1459     }
1460 
1461     test_ID3DXRenderToEnvMap_cube_map(device);
1462 
1463     if (depth_stencil_surface)
1464     {
1465         IDirect3DDevice9_SetDepthStencilSurface(device, NULL);
1466         IDirect3DSurface9_Release(depth_stencil_surface);
1467     }
1468 }
1469 
1470 START_TEST(core)
1471 {
1472     HWND wnd;
1473     IDirect3D9 *d3d;
1474     IDirect3DDevice9 *device;
1475     D3DPRESENT_PARAMETERS d3dpp;
1476     HRESULT hr;
1477 
1478     if (!(wnd = CreateWindowA("static", "d3dx9_test", WS_OVERLAPPEDWINDOW, 0, 0,
1479             640, 480, NULL, NULL, NULL, NULL)))
1480     {
1481         skip("Couldn't create application window\n");
1482         return;
1483     }
1484     if (!(d3d = Direct3DCreate9(D3D_SDK_VERSION)))
1485     {
1486         skip("Couldn't create IDirect3D9 object\n");
1487         DestroyWindow(wnd);
1488         return;
1489     }
1490 
1491     ZeroMemory(&d3dpp, sizeof(d3dpp));
1492     d3dpp.Windowed   = TRUE;
1493     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1494     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &device);
1495     if(FAILED(hr)) {
1496         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1497         IDirect3D9_Release(d3d);
1498         DestroyWindow(wnd);
1499         return;
1500     }
1501 
1502     test_ID3DXBuffer();
1503     test_ID3DXSprite(device);
1504     test_ID3DXFont(device);
1505     test_D3DXCreateRenderToSurface(device);
1506     test_ID3DXRenderToSurface(device);
1507     test_D3DXCreateRenderToEnvMap(device);
1508     test_ID3DXRenderToEnvMap(device);
1509 
1510     check_release((IUnknown*)device, 0);
1511     check_release((IUnknown*)d3d, 0);
1512     if (wnd) DestroyWindow(wnd);
1513 }
1514