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     todo_wine {
508     hr = ID3DXFont_PreloadTextA(font, NULL, -1);
509     ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#x.\n", hr);
510     hr = ID3DXFont_PreloadTextA(font, NULL, 0);
511     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
512     hr = ID3DXFont_PreloadTextA(font, NULL, 1);
513     ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#x.\n", hr);
514     hr = ID3DXFont_PreloadTextA(font, "test", -1);
515     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
516     hr = ID3DXFont_PreloadTextA(font, "", 0);
517     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
518     hr = ID3DXFont_PreloadTextA(font, "", -1);
519     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
520 
521     hr = ID3DXFont_PreloadTextW(font, NULL, -1);
522     ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#x.\n", hr);
523     hr = ID3DXFont_PreloadTextW(font, NULL, 0);
524     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
525     hr = ID3DXFont_PreloadTextW(font, NULL, 1);
526     ok(hr == D3DERR_INVALIDCALL, "Got unexpected hr %#x.\n", hr);
527     hr = ID3DXFont_PreloadTextW(font, testW, -1);
528     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
529     hr = ID3DXFont_PreloadTextW(font, L"", 0);
530     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
531     hr = ID3DXFont_PreloadTextW(font, L"", -1);
532     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
533     }
534 
535     check_release((IUnknown*)font, 0);
536 
537 
538     /* ID3DXFont_GetGlyphData, ID3DXFont_PreloadGlyphs, ID3DXFont_PreloadCharacters */
539     hr = D3DXCreateFontA(device, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
540             DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
541     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
542 
543     hdc = ID3DXFont_GetDC(font);
544     ok(!!hdc, "Got unexpected hdc %p.\n", hdc);
545 
546     todo_wine {
547     hr = ID3DXFont_GetGlyphData(font, 0, NULL, &blackbox, &cellinc);
548     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
549     hr = ID3DXFont_GetGlyphData(font, 0, &texture, NULL, &cellinc);
550     if(SUCCEEDED(hr)) check_release((IUnknown *)texture, 1);
551     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
552     hr = ID3DXFont_GetGlyphData(font, 0, &texture, &blackbox, NULL);
553     if(SUCCEEDED(hr)) check_release((IUnknown *)texture, 1);
554     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
555     }
556     hr = ID3DXFont_PreloadCharacters(font, 'b', 'a');
557     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
558     hr = ID3DXFont_PreloadGlyphs(font, 1, 0);
559     todo_wine ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
560 
561     hr = ID3DXFont_PreloadCharacters(font, 'a', 'a');
562     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
563 
564     for (c = 'b'; c <= 'z'; ++c)
565     {
566         count = GetGlyphIndicesA(hdc, &c, 1, &glyph, 0);
567         ok(count != GDI_ERROR, "Got unexpected count %u.\n", count);
568 
569         hr = ID3DXFont_GetGlyphData(font, glyph, &texture, &blackbox, &cellinc);
570         todo_wine ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
571         if (FAILED(hr))
572             continue;
573 
574         levels = IDirect3DTexture9_GetLevelCount(texture);
575         ok(levels == 5, "Character %c: got unexpected levels %u.\n", c, levels);
576         hr = IDirect3DTexture9_GetLevelDesc(texture, 0, &surf_desc);
577         ok(hr == D3D_OK, "Character %c: got unexpected hr %#x.\n", c, hr);
578         ok(surf_desc.Format == D3DFMT_A8R8G8B8, "Character %c: got unexpected format %#x.\n", c, surf_desc.Format);
579         ok(surf_desc.Usage == 0, "Character %c: got unexpected usage %#x.\n", c, surf_desc.Usage);
580         ok(surf_desc.Width == 256, "Character %c: got unexpected width %u.\n", c, surf_desc.Width);
581         ok(surf_desc.Height == 256, "Character %c: got unexpected height %u.\n", c, surf_desc.Height);
582         ok(surf_desc.Pool == D3DPOOL_MANAGED, "Character %c: got unexpected pool %u.\n", c, surf_desc.Pool);
583 
584         count = GetGlyphOutlineW(hdc, glyph, GGO_GLYPH_INDEX | GGO_METRICS, &glyph_metrics, 0, NULL, &mat);
585         ok(count != GDI_ERROR, "Got unexpected count %#x.\n", count);
586 
587         ret = ID3DXFont_GetTextMetricsW(font, &tm);
588         ok(ret, "Got unexpected ret %#x.\n", ret);
589         ok(blackbox.right - blackbox.left == glyph_metrics.gmBlackBoxX + 2, "Character %c: got %d, expected %d.\n",
590                 c, blackbox.right - blackbox.left, glyph_metrics.gmBlackBoxX + 2);
591         ok(blackbox.bottom - blackbox.top == glyph_metrics.gmBlackBoxY + 2, "Character %c: got %d, expected %d.\n",
592                 c, blackbox.bottom - blackbox.top, glyph_metrics.gmBlackBoxY + 2);
593         ok(cellinc.x == glyph_metrics.gmptGlyphOrigin.x - 1, "Character %c: got %d, expected %d.\n",
594                 c, cellinc.x, glyph_metrics.gmptGlyphOrigin.x - 1);
595         ok(cellinc.y == tm.tmAscent - glyph_metrics.gmptGlyphOrigin.y - 1, "Character %c: got %d, expected %d.\n",
596                 c, cellinc.y, tm.tmAscent - glyph_metrics.gmptGlyphOrigin.y - 1);
597 
598         check_release((IUnknown *)texture, 1);
599     }
600 
601     hr = ID3DXFont_PreloadCharacters(font, 'a', 'z');
602     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
603 
604     /* Test multiple textures */
605     hr = ID3DXFont_PreloadGlyphs(font, 0, 1000);
606     todo_wine ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
607 
608     /* Test glyphs that are not rendered */
609     for (glyph = 1; glyph < 4; ++glyph)
610     {
611         texture = (IDirect3DTexture9 *)0xdeadbeef;
612         hr = ID3DXFont_GetGlyphData(font, glyph, &texture, &blackbox, &cellinc);
613         todo_wine ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
614         todo_wine ok(!texture, "Got unexpected texture %p.\n", texture);
615     }
616 
617     check_release((IUnknown *)font, 0);
618 
619     c = 'a';
620     for (i = 0; i < ARRAY_SIZE(tests); ++i)
621     {
622         hr = D3DXCreateFontA(device, tests[i].font_height, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET,
623                 OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
624         ok(hr == D3D_OK, "Test %u: got unexpected hr %#x.\n", i, hr);
625 
626         hdc = ID3DXFont_GetDC(font);
627         ok(!!hdc, "Test %u: got unexpected hdc %p.\n", i, hdc);
628 
629         count = GetGlyphIndicesA(hdc, &c, 1, &glyph, 0);
630         ok(count != GDI_ERROR, "Test %u: got unexpected count %u.\n", i, count);
631 
632         hr = ID3DXFont_GetGlyphData(font, glyph, &texture, NULL, NULL);
633         todo_wine ok(hr == D3D_OK, "Test %u: got unexpected hr %#x.\n", i, hr);
634         if(SUCCEEDED(hr)) {
635             DWORD levels;
636             D3DSURFACE_DESC desc;
637 
638             levels = IDirect3DTexture9_GetLevelCount(texture);
639             ok(levels == tests[i].expected_levels, "Test %u: got unexpected levels %u.\n", i, levels);
640             hr = IDirect3DTexture9_GetLevelDesc(texture, 0, &desc);
641             ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
642             ok(desc.Format == D3DFMT_A8R8G8B8, "Test %u: got unexpected format %#x.\n", i, desc.Format);
643             ok(desc.Usage == 0, "Test %u: got unexpected usage %#x.\n", i, desc.Usage);
644             ok(desc.Width == tests[i].expected_size, "Test %u: got unexpected width %u.\n", i, desc.Width);
645             ok(desc.Height == tests[i].expected_size, "Test %u: got unexpected height %u.\n", i, desc.Height);
646             ok(desc.Pool == D3DPOOL_MANAGED, "Test %u: got unexpected pool %u.\n", i, desc.Pool);
647 
648             IDirect3DTexture9_Release(texture);
649         }
650 
651         /* ID3DXFontImpl_DrawText */
652         D3DXCreateSprite(device, &sprite);
653         SetRect(&rect, 0, 0, 640, 480);
654 
655         IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0xff000000, 1.0f, 0);
656 
657         IDirect3DDevice9_BeginScene(device);
658         hr = ID3DXSprite_Begin(sprite, D3DXSPRITE_ALPHABLEND);
659         ok (hr == D3D_OK, "Test %d: got unexpected hr %#x.\n", i, hr);
660 
661         todo_wine
662         {
663         height = ID3DXFont_DrawTextW(font, sprite, testW, -1, &rect, DT_TOP, 0xffffffff);
664         ok(height == tests[i].font_height, "Test %d: got unexpected height %u.\n", i, height);
665         height = ID3DXFont_DrawTextW(font, sprite, testW, size, &rect, DT_TOP, 0xffffffff);
666         ok(height == tests[i].font_height, "Test %d: got unexpected height %u.\n", i, height);
667         height = ID3DXFont_DrawTextW(font, sprite, testW, size, &rect, DT_RIGHT, 0xffffffff);
668         ok(height == tests[i].font_height, "Test %d: got unexpected height %u.\n", i, height);
669         height = ID3DXFont_DrawTextW(font, sprite, testW, size, &rect, DT_LEFT | DT_NOCLIP, 0xffffffff);
670         ok(height == tests[i].font_height, "Test %d: got unexpected height %u.\n", i, height);
671         }
672 
673         SetRectEmpty(&rect);
674         height = ID3DXFont_DrawTextW(font, sprite, testW, size, &rect,
675                 DT_LEFT | DT_CALCRECT, 0xffffffff);
676         todo_wine ok(height == tests[i].font_height, "Test %d: got unexpected height %u.\n", i, height);
677         ok(!rect.left, "Test %d: got unexpected rect left %d.\n", i, rect.left);
678         ok(!rect.top, "Test %d: got unexpected rect top %d.\n", i, rect.top);
679         todo_wine ok(rect.right, "Test %d: got unexpected rect right %d.\n", i, rect.right);
680         todo_wine ok(rect.bottom == tests[i].font_height, "Test %d: got unexpected rect bottom %d.\n", i, rect.bottom);
681 
682         hr = ID3DXSprite_End(sprite);
683         ok (hr == D3D_OK, "Test %d: got unexpected hr %#x.\n", i, hr);
684         IDirect3DDevice9_EndScene(device);
685         ID3DXSprite_Release(sprite);
686 
687         ID3DXFont_Release(font);
688     }
689 
690 
691     /* ID3DXFont_DrawTextA, ID3DXFont_DrawTextW */
692     hr = D3DXCreateFontA(device, 12, 0, FW_DONTCARE, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
693             DEFAULT_QUALITY, DEFAULT_PITCH, "Tahoma", &font);
694     ok(hr == D3D_OK, "Got unexpected hr %#x.\n", hr);
695 
696     todo_wine {
697     SetRect(&rect, 10, 10, 200, 200);
698 
699     height = ID3DXFont_DrawTextA(font, NULL, "test", -2, &rect, 0, 0xFF00FF);
700     ok(height == 12, "Got unexpected height %d.\n", height);
701 
702     height = ID3DXFont_DrawTextA(font, NULL, "test", -1, &rect, 0, 0xFF00FF);
703     ok(height == 12, "Got unexpected height %d.\n", height);
704 
705     height = ID3DXFont_DrawTextA(font, NULL, "test", 0, &rect, 0, 0xFF00FF);
706     ok(height == 0, "Got unexpected height %d.\n", height);
707 
708     height = ID3DXFont_DrawTextA(font, NULL, "test", 1, &rect, 0, 0xFF00FF);
709     ok(height == 12, "Got unexpected height %d.\n", height);
710 
711     height = ID3DXFont_DrawTextA(font, NULL, "test", 2, &rect, 0, 0xFF00FF);
712     ok(height == 12, "Got unexpected height %d.\n", height);
713 
714     height = ID3DXFont_DrawTextA(font, NULL, "", 0, &rect, 0, 0xff00ff);
715     ok(height == 0, "Got unexpected height %d.\n", height);
716 
717     height = ID3DXFont_DrawTextA(font, NULL, "", -1, &rect, 0, 0xff00ff);
718     ok(height == 0, "Got unexpected height %d.\n", height);
719 
720     height = ID3DXFont_DrawTextA(font, NULL, "test", -1, NULL, 0, 0xFF00FF);
721     ok(height == 12, "Got unexpected height %d.\n", height);
722 
723     height = ID3DXFont_DrawTextA(font, NULL, "test", -1, NULL, DT_CALCRECT, 0xFF00FF);
724     ok(height == 12, "Got unexpected height %d.\n", height);
725 
726     height = ID3DXFont_DrawTextA(font, NULL, NULL, -1, NULL, 0, 0xFF00FF);
727     ok(height == 0, "Got unexpected height %d.\n", height);
728 
729     SetRect(&rect, 10, 10, 50, 50);
730 
731     height = ID3DXFont_DrawTextA(font, NULL, long_text, -1, &rect, DT_WORDBREAK, 0xff00ff);
732     ok(height == 60, "Got unexpected height %d.\n", height);
733 
734     height = ID3DXFont_DrawTextA(font, NULL, long_text, -1, &rect, DT_WORDBREAK | DT_NOCLIP, 0xff00ff);
735     ok(height == 96, "Got unexpected height %d.\n", height);
736 
737     SetRect(&rect, 10, 10, 200, 200);
738 
739     height = ID3DXFont_DrawTextW(font, NULL, testW, -1, &rect, 0, 0xFF00FF);
740     ok(height == 12, "Got unexpected height %d.\n", height);
741 
742     height = ID3DXFont_DrawTextW(font, NULL, testW, 0, &rect, 0, 0xFF00FF);
743     ok(height == 0, "Got unexpected height %d.\n", height);
744 
745     height = ID3DXFont_DrawTextW(font, NULL, testW, 1, &rect, 0, 0xFF00FF);
746     ok(height == 12, "Got unexpected height %d.\n", height);
747 
748     height = ID3DXFont_DrawTextW(font, NULL, testW, 2, &rect, 0, 0xFF00FF);
749     ok(height == 12, "Got unexpected height %d.\n", height);
750 
751     height = ID3DXFont_DrawTextW(font, NULL, L"", 0, &rect, 0, 0xff00ff);
752     ok(height == 0, "Got unexpected height %d.\n", height);
753 
754     height = ID3DXFont_DrawTextW(font, NULL, L"", -1, &rect, 0, 0xff00ff);
755     ok(height == 0, "Got unexpected height %d.\n", height);
756 
757     height = ID3DXFont_DrawTextW(font, NULL, testW, -1, NULL, 0, 0xFF00FF);
758     ok(height == 12, "Got unexpected height %d.\n", height);
759 
760     height = ID3DXFont_DrawTextW(font, NULL, testW, -1, NULL, DT_CALCRECT, 0xFF00FF);
761     ok(height == 12, "Got unexpected height %d.\n", height);
762 
763     height = ID3DXFont_DrawTextW(font, NULL, NULL, -1, NULL, 0, 0xFF00FF);
764     ok(height == 0, "Got unexpected height %d.\n", height);
765 
766     SetRect(&rect, 10, 10, 50, 50);
767 
768     height = ID3DXFont_DrawTextW(font, NULL, long_textW, -1, &rect, DT_WORDBREAK, 0xff00ff);
769     ok(height == 60, "Got unexpected height %d.\n", height);
770 
771     height = ID3DXFont_DrawTextW(font, NULL, long_textW, -1, &rect, DT_WORDBREAK | DT_NOCLIP, 0xff00ff);
772     ok(height == 96, "Got unexpected height %d.\n", height);
773     }
774 
775     ID3DXFont_Release(font);
776 }
777 
778 static void test_D3DXCreateRenderToSurface(IDirect3DDevice9 *device)
779 {
780     int i;
781     HRESULT hr;
782     ULONG ref_count;
783     D3DXRTS_DESC desc;
784     ID3DXRenderToSurface *render = (void *)0xdeadbeef;
785     static const D3DXRTS_DESC tests[] =
786     {
787         { 0, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN },
788         { 256, 0, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN },
789         { 256, 0, D3DFMT_A8R8G8B8, FALSE, D3DFMT_D24S8 },
790         { 256, 256, D3DFMT_UNKNOWN, FALSE, D3DFMT_R8G8B8 },
791         { 0, 0, D3DFMT_UNKNOWN, FALSE, D3DFMT_UNKNOWN },
792         { -1, -1, MAKEFOURCC('B','A','D','F'), TRUE, MAKEFOURCC('B','A','D','F') }
793     };
794 
795     hr = D3DXCreateRenderToSurface(NULL /* device */, 256, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN, &render);
796     ok(hr == D3DERR_INVALIDCALL, "D3DXCreateRenderToSurface returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
797     ok(render == (void *)0xdeadbeef, "Got %p, expected %p\n", render, (void *)0xdeadbeef);
798 
799     hr = D3DXCreateRenderToSurface(device, 256, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN, NULL /* out */);
800     ok(hr == D3DERR_INVALIDCALL, "D3DXCreateRenderToSurface returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
801 
802     for (i = 0; i < ARRAY_SIZE(tests); i++)
803     {
804         hr = D3DXCreateRenderToSurface(device, tests[i].Width, tests[i].Height, tests[i].Format, tests[i].DepthStencil,
805                 tests[i].DepthStencilFormat, &render);
806         ok(hr == D3D_OK, "%d: D3DXCreateRenderToSurface returned %#x, expected %#x\n", i, hr, D3D_OK);
807         if (SUCCEEDED(hr))
808         {
809             hr = ID3DXRenderToSurface_GetDesc(render, &desc);
810             ok(hr == D3D_OK, "%d: GetDesc failed %#x\n", i, hr);
811             if (SUCCEEDED(hr))
812             {
813                 ok(desc.Width == tests[i].Width, "%d: Got width %u, expected %u\n", i, desc.Width, tests[i].Width);
814                 ok(desc.Height == tests[i].Height, "%d: Got height %u, expected %u\n", i, desc.Height, tests[i].Height);
815                 ok(desc.Format == tests[i].Format, "%d: Got format %#x, expected %#x\n", i, desc.Format, tests[i].Format);
816                 ok(desc.DepthStencil == tests[i].DepthStencil, "%d: Got depth stencil %d, expected %d\n",
817                         i, desc.DepthStencil, tests[i].DepthStencil);
818                 ok(desc.DepthStencilFormat == tests[i].DepthStencilFormat, "%d: Got depth stencil format %#x, expected %#x\n",
819                         i, desc.DepthStencilFormat, tests[i].DepthStencilFormat);
820             }
821             ID3DXRenderToSurface_Release(render);
822         }
823     }
824 
825     /* check device ref count */
826     ref_count = get_ref((IUnknown *)device);
827     hr = D3DXCreateRenderToSurface(device, 0, 0, D3DFMT_UNKNOWN, FALSE, D3DFMT_UNKNOWN, &render);
828     check_ref((IUnknown *)device, ref_count + 1);
829     if (SUCCEEDED(hr)) ID3DXRenderToSurface_Release(render);
830 }
831 
832 /* runs a set of tests for the ID3DXRenderToSurface interface created with given parameters */
833 static void check_ID3DXRenderToSurface(IDirect3DDevice9 *device, UINT width, UINT height, D3DFORMAT format,
834         BOOL depth_stencil, D3DFORMAT depth_stencil_format, BOOL render_target)
835 {
836     HRESULT hr;
837     D3DFORMAT fmt;
838     HRESULT expected_value;
839     IDirect3DSurface9 *surface;
840     ID3DXRenderToSurface *render;
841     D3DVIEWPORT9 viewport = { 0, 0, width, height, 0.0, 1.0 };
842 
843     hr = D3DXCreateRenderToSurface(device, width, height, format, depth_stencil, depth_stencil_format, &render);
844     if (FAILED(hr))
845     {
846         skip("Failed to create ID3DXRenderToSurface\n");
847         return;
848     }
849 
850     if (render_target)
851         hr = IDirect3DDevice9_CreateRenderTarget(device, width, height, format, D3DMULTISAMPLE_NONE, 0, FALSE, &surface, NULL);
852     else
853         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, width, height, format, D3DPOOL_DEFAULT, &surface, NULL);
854     if (FAILED(hr))
855     {
856         skip("Failed to create surface\n");
857         ID3DXRenderToSurface_Release(render);
858         return;
859     }
860 
861     /* viewport */
862     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
863     ok(hr == D3D_OK, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3D_OK);
864     check_ref((IUnknown *)surface, 2);
865     if (SUCCEEDED(hr)) ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
866 
867     /* invalid viewport */
868     viewport.Width = 2 * width;
869     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
870     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
871 
872     viewport.X = width / 2;
873     viewport.Width = width;
874     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
875     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
876 
877     viewport.X = width;
878     viewport.Width = width;
879     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
880     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
881 
882     /* rendering to a part of a surface is only allowed for render target surfaces */
883     expected_value = render_target ? D3D_OK : D3DERR_INVALIDCALL;
884 
885     viewport.X = 0;
886     viewport.Width = width / 2;
887     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
888     ok(hr == expected_value, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, expected_value);
889     if (SUCCEEDED(hr)) ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
890 
891     viewport.X = width / 2;
892     viewport.Width = width - width / 2;
893     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
894     ok(hr == expected_value, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, expected_value);
895     if (SUCCEEDED(hr)) ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
896 
897     check_release((IUnknown *)surface, 0);
898 
899     /* surfaces with different sizes */
900     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, width / 2, width / 2, format, D3DPOOL_DEFAULT, &surface, NULL);
901     if (FAILED(hr))
902     {
903         skip("Failed to create surface\n");
904         ID3DXRenderToSurface_Release(render);
905         return;
906     }
907     hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
908     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
909     check_release((IUnknown *)surface, 0);
910 
911     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 2 * width, 2 * height, format, D3DPOOL_DEFAULT, &surface, NULL);
912     if (FAILED(hr))
913     {
914         skip("Failed to create surface\n");
915         ID3DXRenderToSurface_Release(render);
916         return;
917     }
918     hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
919     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
920     viewport.X = 0;
921     viewport.Y = 0;
922     viewport.Width = width;
923     viewport.Height = height;
924     hr = ID3DXRenderToSurface_BeginScene(render, surface, &viewport);
925     ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
926     check_release((IUnknown *)surface, 0);
927 
928     /* surfaces with different formats */
929     for (fmt = D3DFMT_A8R8G8B8; fmt <= D3DFMT_X8R8G8B8; fmt++)
930     {
931         HRESULT expected_result = (fmt != format) ? D3DERR_INVALIDCALL : D3D_OK;
932 
933         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, width, height, fmt, D3DPOOL_DEFAULT, &surface, NULL);
934         if (FAILED(hr))
935         {
936             skip("Failed to create surface\n");
937             continue;
938         }
939 
940         hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
941         ok(hr == expected_result, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, expected_result);
942 
943         if (SUCCEEDED(hr)) ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
944         check_release((IUnknown *)surface, 0);
945     }
946 
947     check_release((IUnknown *)render, 0);
948 }
949 
950 struct device_state
951 {
952     IDirect3DSurface9 *render_target;
953     IDirect3DSurface9 *depth_stencil;
954     D3DVIEWPORT9 viewport;
955 };
956 
957 static void release_device_state(struct device_state *state)
958 {
959     if (state->render_target) IDirect3DSurface9_Release(state->render_target);
960     if (state->depth_stencil) IDirect3DSurface9_Release(state->depth_stencil);
961     memset(state, 0, sizeof(*state));
962 }
963 
964 static HRESULT retrieve_device_state(IDirect3DDevice9 *device, struct device_state *state)
965 {
966     HRESULT hr;
967 
968     memset(state, 0, sizeof(*state));
969 
970     hr = IDirect3DDevice9_GetRenderTarget(device, 0, &state->render_target);
971     if (FAILED(hr)) goto cleanup;
972 
973     hr = IDirect3DDevice9_GetDepthStencilSurface(device, &state->depth_stencil);
974     if (hr == D3DERR_NOTFOUND)
975         state->depth_stencil = NULL;
976     else if (FAILED(hr))
977         goto cleanup;
978 
979     hr = IDirect3DDevice9_GetViewport(device, &state->viewport);
980     if (SUCCEEDED(hr)) return hr;
981 
982 cleanup:
983     release_device_state(state);
984     return hr;
985 }
986 
987 static HRESULT apply_device_state(IDirect3DDevice9 *device, struct device_state *state)
988 {
989     HRESULT hr;
990     HRESULT status = D3D_OK;
991 
992     hr = IDirect3DDevice9_SetRenderTarget(device, 0, state->render_target);
993     if (FAILED(hr)) status = hr;
994 
995     hr = IDirect3DDevice9_SetDepthStencilSurface(device, state->depth_stencil);
996     if (FAILED(hr)) status = hr;
997 
998     hr = IDirect3DDevice9_SetViewport(device, &state->viewport);
999     if (FAILED(hr)) status = hr;
1000 
1001     return status;
1002 }
1003 
1004 static void compare_device_state(struct device_state *state1, struct device_state *state2, BOOL equal)
1005 {
1006     BOOL cmp;
1007     const char *message = equal ? "differs" : "is the same";
1008 
1009     cmp = state1->render_target == state2->render_target;
1010     ok(equal ? cmp : !cmp, "Render target %s %p, %p\n", message, state1->render_target, state2->render_target);
1011 
1012     cmp = state1->depth_stencil == state2->depth_stencil;
1013     ok(equal ? cmp : !cmp, "Depth stencil surface %s %p, %p\n", message, state1->depth_stencil, state2->depth_stencil);
1014 
1015     cmp = state1->viewport.X == state2->viewport.X && state1->viewport.Y == state2->viewport.Y
1016             && state1->viewport.Width == state2->viewport.Width && state1->viewport.Height == state2->viewport.Height;
1017     ok(equal ? cmp : !cmp, "Viewport %s (%u, %u, %u, %u), (%u, %u, %u, %u)\n", message,
1018             state1->viewport.X, state1->viewport.Y, state1->viewport.Width, state1->viewport.Height,
1019             state2->viewport.X, state2->viewport.Y, state2->viewport.Width, state2->viewport.Height);
1020 }
1021 
1022 static void test_ID3DXRenderToSurface_device_state(IDirect3DDevice9 *device)
1023 {
1024     HRESULT hr;
1025     IDirect3DSurface9 *surface = NULL;
1026     ID3DXRenderToSurface *render = NULL;
1027     struct device_state pre_state;
1028     struct device_state current_state;
1029     IDirect3DSurface9 *depth_stencil_surface;
1030 
1031     /* make sure there is a depth stencil surface present */
1032     hr = IDirect3DDevice9_GetDepthStencilSurface(device, &depth_stencil_surface);
1033     if (SUCCEEDED(hr))
1034     {
1035         IDirect3DSurface9_Release(depth_stencil_surface);
1036         depth_stencil_surface = NULL;
1037     }
1038     else if (hr == D3DERR_NOTFOUND)
1039     {
1040         hr = IDirect3DDevice9_CreateDepthStencilSurface(device, 256, 256, D3DFMT_D24X8,
1041                 D3DMULTISAMPLE_NONE, 0, TRUE, &depth_stencil_surface, NULL);
1042         if (SUCCEEDED(hr)) IDirect3DDevice9_SetDepthStencilSurface(device, depth_stencil_surface);
1043     }
1044 
1045     if (FAILED(hr))
1046     {
1047         skip("Failed to create depth stencil surface\n");
1048         return;
1049     }
1050 
1051     hr = IDirect3DDevice9_CreateRenderTarget(device, 256, 256, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0,
1052         FALSE, &surface, NULL);
1053     if (FAILED(hr))
1054     {
1055         skip("Failed to create render target\n");
1056         goto cleanup;
1057     }
1058 
1059     hr = retrieve_device_state(device, &pre_state);
1060     ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1061 
1062     hr = D3DXCreateRenderToSurface(device, 256, 256, D3DFMT_A8R8G8B8, TRUE, D3DFMT_D24X8, &render);
1063     ok(hr == D3D_OK, "D3DXCreateRenderToSurface returned %#x, expected %#x\n", hr, D3D_OK);
1064     if (SUCCEEDED(hr))
1065     {
1066         hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
1067         ok(hr == D3D_OK, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3D_OK);
1068 
1069         hr = retrieve_device_state(device, &current_state);
1070         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1071         compare_device_state(&current_state, &pre_state, FALSE);
1072         release_device_state(&current_state);
1073 
1074         hr = ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
1075         ok(hr == D3D_OK, "ID3DXRenderToSurface::EndScene returned %#x, expected %#x\n", hr, D3D_OK);
1076 
1077         hr = retrieve_device_state(device, &current_state);
1078         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1079         compare_device_state(&current_state, &pre_state, TRUE);
1080         release_device_state(&current_state);
1081 
1082         check_release((IUnknown *)render, 0);
1083     }
1084 
1085     hr = D3DXCreateRenderToSurface(device, 256, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN, &render);
1086     if (SUCCEEDED(hr))
1087     {
1088         hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
1089         ok(hr == D3D_OK, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3D_OK);
1090 
1091         hr = retrieve_device_state(device, &current_state);
1092         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1093         compare_device_state(&current_state, &pre_state, FALSE);
1094         release_device_state(&current_state);
1095 
1096         hr = ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
1097         ok(hr == D3D_OK, "ID3DXRenderToSurface::EndScene returned %#x, expected %#x\n", hr, D3D_OK);
1098 
1099         hr = retrieve_device_state(device, &current_state);
1100         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1101         compare_device_state(&current_state, &pre_state, TRUE);
1102         release_device_state(&current_state);
1103 
1104         hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
1105         ok(hr == D3D_OK, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3D_OK);
1106 
1107         hr = retrieve_device_state(device, &current_state);
1108         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1109         compare_device_state(&current_state, &pre_state, FALSE);
1110         release_device_state(&current_state);
1111 
1112         check_release((IUnknown *)render, 0);
1113 
1114         /* if EndScene isn't called, the device state isn't restored */
1115         hr = retrieve_device_state(device, &current_state);
1116         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1117         compare_device_state(&current_state, &pre_state, FALSE);
1118         release_device_state(&current_state);
1119 
1120         hr = apply_device_state(device, &pre_state);
1121         ok(SUCCEEDED(hr), "Failed to restore previous device state\n");
1122 
1123         IDirect3DDevice9_EndScene(device);
1124     }
1125 
1126     release_device_state(&pre_state);
1127 
1128 cleanup:
1129     if (depth_stencil_surface)
1130     {
1131         IDirect3DDevice9_SetDepthStencilSurface(device, NULL);
1132         IDirect3DSurface9_Release(depth_stencil_surface);
1133     }
1134 
1135     if (surface) check_release((IUnknown *)surface, 0);
1136 }
1137 
1138 static void test_ID3DXRenderToSurface(IDirect3DDevice9 *device)
1139 {
1140     int i;
1141     HRESULT hr;
1142     ULONG ref_count;
1143     IDirect3DDevice9 *out_device;
1144     ID3DXRenderToSurface *render;
1145     IDirect3DSurface9 *surface;
1146     D3DVIEWPORT9 viewport = { 0, 0, 256, 256, 0.0, 1.0 };
1147     D3DXRTS_DESC tests[] = {
1148         { 256, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN },
1149         { 256, 256, D3DFMT_A8R8G8B8, TRUE, D3DFMT_D24S8 },
1150         { 256, 256, D3DFMT_A8R8G8B8, TRUE, D3DFMT_D24X8 },
1151         { 512, 512, D3DFMT_X8R8G8B8, FALSE, D3DFMT_X8R8G8B8 },
1152         { 1024, 1024, D3DFMT_X8R8G8B8, TRUE, D3DFMT_D24S8 }
1153     };
1154 
1155     hr = D3DXCreateRenderToSurface(device, 256, 256, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN, &render);
1156     ok(hr == D3D_OK, "D3DXCreateRenderToSurface returned %#x, expected %#x\n", hr, D3D_OK);
1157     if (FAILED(hr)) return;
1158 
1159     hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 256, 256, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &surface, NULL);
1160     if (SUCCEEDED(hr))
1161     {
1162         ID3DXRenderToSurface *render_surface;
1163 
1164         /* GetDevice */
1165         hr = ID3DXRenderToSurface_GetDevice(render, NULL /* device */);
1166         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::GetDevice returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1167 
1168         ref_count = get_ref((IUnknown *)device);
1169         hr = ID3DXRenderToSurface_GetDevice(render, &out_device);
1170         ok(hr == D3D_OK, "ID3DXRenderToSurface::GetDevice returned %#x, expected %#x\n", hr, D3D_OK);
1171         check_release((IUnknown *)out_device, ref_count);
1172 
1173         /* BeginScene and EndScene */
1174         hr = ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
1175         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::EndScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1176 
1177         hr = ID3DXRenderToSurface_BeginScene(render, NULL /* surface */, &viewport);
1178         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1179 
1180         ref_count = get_ref((IUnknown *)surface);
1181         hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
1182         ok(hr == D3D_OK, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3D_OK);
1183         if (SUCCEEDED(hr))
1184         {
1185             check_ref((IUnknown *)surface, ref_count + 1);
1186 
1187             hr = ID3DXRenderToSurface_BeginScene(render, surface, NULL);
1188             ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1189 
1190             hr = ID3DXRenderToSurface_EndScene(render, D3DX_FILTER_NONE);
1191             ok(hr == D3D_OK, "ID3DXRenderToSurface::EndScene returned %#x, expected %#x\n", hr, D3D_OK);
1192 
1193             check_ref((IUnknown *)surface, ref_count);
1194         }
1195 
1196         /* error handling is deferred to BeginScene */
1197         hr = D3DXCreateRenderToSurface(device, 256, 256, D3DFMT_A8R8G8B8, TRUE, D3DFMT_UNKNOWN, &render_surface);
1198         ok(hr == D3D_OK, "D3DXCreateRenderToSurface returned %#x, expected %#x\n", hr, D3D_OK);
1199         hr = ID3DXRenderToSurface_BeginScene(render_surface, surface, NULL);
1200         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToSurface::BeginScene returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1201         check_release((IUnknown *)render_surface, 0);
1202 
1203         check_release((IUnknown *)surface, 0);
1204     }
1205     else skip("Failed to create surface\n");
1206 
1207     check_release((IUnknown *)render, 0);
1208 
1209     for (i = 0; i < ARRAY_SIZE(tests); i++)
1210     {
1211         check_ID3DXRenderToSurface(device, tests[i].Width, tests[i].Height, tests[i].Format, tests[i].DepthStencil, tests[i].DepthStencilFormat, TRUE);
1212         check_ID3DXRenderToSurface(device, tests[i].Width, tests[i].Height, tests[i].Format, tests[i].DepthStencil, tests[i].DepthStencilFormat, FALSE);
1213     }
1214 
1215     test_ID3DXRenderToSurface_device_state(device);
1216 }
1217 
1218 static void test_D3DXCreateRenderToEnvMap(IDirect3DDevice9 *device)
1219 {
1220     int i;
1221     HRESULT hr;
1222     ULONG ref_count;
1223     D3DXRTE_DESC desc;
1224     ID3DXRenderToEnvMap *render;
1225     static const struct {
1226         D3DXRTE_DESC parameters;
1227         D3DXRTE_DESC expected_values;
1228     } tests[] = {
1229         { {   0,   0, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN }, {   1, 1, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN } },
1230         { { 256,   0, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN }, { 256, 9, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN } },
1231         { { 256,   4, D3DFMT_A8R8G8B8, FALSE, D3DFMT_D24S8   }, { 256, 4, D3DFMT_A8R8G8B8, FALSE, D3DFMT_D24S8   } },
1232         { { 256, 256, D3DFMT_UNKNOWN,  FALSE, D3DFMT_R8G8B8  }, { 256, 9, D3DFMT_A8R8G8B8, FALSE, D3DFMT_R8G8B8  } },
1233         { {  -1,  -1, D3DFMT_A8R8G8B8, TRUE,  D3DFMT_DXT1    }, { 256, 9, D3DFMT_A8R8G8B8, TRUE,  D3DFMT_DXT1    } },
1234         { { 256,   1, D3DFMT_X8R8G8B8, TRUE,  D3DFMT_UNKNOWN }, { 256, 1, D3DFMT_X8R8G8B8, TRUE,  D3DFMT_UNKNOWN } }
1235     };
1236 
1237     for (i = 0; i < ARRAY_SIZE(tests); i++)
1238     {
1239         const D3DXRTE_DESC *parameters = &tests[i].parameters;
1240         const D3DXRTE_DESC *expected  = &tests[i].expected_values;
1241         hr = D3DXCreateRenderToEnvMap(device, parameters->Size, parameters->MipLevels, parameters->Format,
1242                 parameters->DepthStencil, parameters->DepthStencilFormat, &render);
1243         ok(hr == D3D_OK, "%d: D3DXCreateRenderToEnvMap returned %#x, expected %#x\n", i, hr, D3D_OK);
1244         if (SUCCEEDED(hr))
1245         {
1246             hr = ID3DXRenderToEnvMap_GetDesc(render, &desc);
1247             ok(hr == D3D_OK, "%d: GetDesc failed %#x\n", i, hr);
1248             if (SUCCEEDED(hr))
1249             {
1250                 ok(desc.Size == expected->Size, "%d: Got size %u, expected %u\n", i, desc.Size, expected->Size);
1251                 ok(desc.MipLevels == expected->MipLevels, "%d: Got miplevels %u, expected %u\n", i, desc.MipLevels, expected->MipLevels);
1252                 ok(desc.Format == expected->Format, "%d: Got format %#x, expected %#x\n", i, desc.Format, expected->Format);
1253                 ok(desc.DepthStencil == expected->DepthStencil, "%d: Got depth stencil %d, expected %d\n",
1254                         i, expected->DepthStencil, expected->DepthStencil);
1255                 ok(desc.DepthStencilFormat == expected->DepthStencilFormat, "%d: Got depth stencil format %#x, expected %#x\n",
1256                         i, expected->DepthStencilFormat, expected->DepthStencilFormat);
1257             }
1258             check_release((IUnknown *)render, 0);
1259         }
1260     }
1261 
1262     /* check device ref count */
1263     ref_count = get_ref((IUnknown *)device);
1264     hr = D3DXCreateRenderToEnvMap(device, 0, 0, D3DFMT_UNKNOWN, FALSE, D3DFMT_UNKNOWN, &render);
1265     check_ref((IUnknown *)device, ref_count + 1);
1266     if (SUCCEEDED(hr)) ID3DXRenderToEnvMap_Release(render);
1267 }
1268 
1269 static void test_ID3DXRenderToEnvMap_cube_map(IDirect3DDevice9 *device)
1270 {
1271     HRESULT hr;
1272     IDirect3DCubeTexture9 *cube_texture = NULL;
1273     ID3DXRenderToEnvMap *render = NULL;
1274     struct device_state pre_state;
1275     struct device_state current_state;
1276 
1277     hr = IDirect3DDevice9_CreateCubeTexture(device, 256, 0, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
1278         &cube_texture, NULL);
1279     if (FAILED(hr))
1280     {
1281         skip("Failed to create cube texture\n");
1282         return;
1283     }
1284 
1285     hr = retrieve_device_state(device, &pre_state);
1286     ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1287 
1288     hr = D3DXCreateRenderToEnvMap(device, 256, 0, D3DFMT_A8R8G8B8, TRUE, D3DFMT_D24X8, &render);
1289     ok(hr == D3D_OK, "D3DCreateRenderToEnvMap returned %#x, expected %#x\n", hr, D3D_OK);
1290     if (SUCCEEDED(hr))
1291     {
1292         DWORD face;
1293 
1294         hr = ID3DXRenderToEnvMap_End(render, D3DX_FILTER_NONE);
1295         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::End returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1296 
1297         hr = ID3DXRenderToEnvMap_BeginCube(render, cube_texture);
1298         ok(hr == D3D_OK, "ID3DXRenderToEnvMap::BeginCube returned %#x, expected %#x\n", hr, D3D_OK);
1299 
1300         hr = retrieve_device_state(device, &current_state);
1301         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1302         compare_device_state(&current_state, &pre_state, TRUE);
1303         release_device_state(&current_state);
1304 
1305         for (face = D3DCUBEMAP_FACE_POSITIVE_X; face <= D3DCUBEMAP_FACE_NEGATIVE_Z; face++)
1306         {
1307             hr = ID3DXRenderToEnvMap_Face(render, face, D3DX_FILTER_POINT);
1308             ok(hr == D3D_OK, "ID3DXRenderToEnvMap::Face returned %#x, expected %#x\n", hr, D3D_OK);
1309 
1310             hr = retrieve_device_state(device, &current_state);
1311             ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1312             compare_device_state(&current_state, &pre_state, FALSE);
1313             release_device_state(&current_state);
1314         }
1315 
1316         hr = ID3DXRenderToEnvMap_End(render, D3DX_FILTER_POINT);
1317         ok(hr == D3D_OK, "ID3DXRenderToEnvMap::End returned %#x, expected %#x\n", hr, D3D_OK);
1318 
1319         hr = retrieve_device_state(device, &current_state);
1320         ok(SUCCEEDED(hr), "Failed to retrieve device state\n");
1321         compare_device_state(&current_state, &pre_state, TRUE);
1322         release_device_state(&current_state);
1323 
1324         check_release((IUnknown *)render, 0);
1325     }
1326 
1327     release_device_state(&pre_state);
1328 
1329     check_release((IUnknown *)cube_texture, 0);
1330 }
1331 
1332 static void test_ID3DXRenderToEnvMap(IDirect3DDevice9 *device)
1333 {
1334     HRESULT hr;
1335     ID3DXRenderToEnvMap *render;
1336     IDirect3DSurface9 *depth_stencil_surface;
1337 
1338     hr = D3DXCreateRenderToEnvMap(device, 256, 0, D3DFMT_A8R8G8B8, FALSE, D3DFMT_UNKNOWN, &render);
1339     if (SUCCEEDED(hr))
1340     {
1341         ULONG ref_count;
1342         IDirect3DDevice9 *out_device;
1343 
1344         hr = ID3DXRenderToEnvMap_GetDesc(render, NULL);
1345         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::GetDesc returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1346 
1347         hr = ID3DXRenderToEnvMap_GetDevice(render, NULL);
1348         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::GetDevice returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1349 
1350         ref_count = get_ref((IUnknown *)device);
1351         hr = ID3DXRenderToEnvMap_GetDevice(render, &out_device);
1352         ok(hr == D3D_OK, "ID3DXRenderToEnvMap::GetDevice returned %#x, expected %#x\n", hr, D3D_OK);
1353         ok(out_device == device, "ID3DXRenderToEnvMap::GetDevice returned different device\n");
1354         check_release((IUnknown *)device, ref_count);
1355 
1356         hr = ID3DXRenderToEnvMap_End(render, D3DX_FILTER_NONE);
1357         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::End returned %#x, expected %#x\n", hr, D3D_OK);
1358 
1359         hr = ID3DXRenderToEnvMap_BeginCube(render, NULL);
1360         ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::BeginCube returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1361 
1362         hr = ID3DXRenderToEnvMap_BeginHemisphere(render, NULL, NULL);
1363         todo_wine ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::BeginHemisphere returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1364 
1365         hr = ID3DXRenderToEnvMap_BeginParabolic(render, NULL, NULL);
1366         todo_wine ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::BeginParabolic returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1367 
1368         hr = ID3DXRenderToEnvMap_BeginSphere(render, NULL);
1369         todo_wine ok(hr == D3DERR_INVALIDCALL, "ID3DXRenderToEnvMap::BeginSphere returned %#x, expected %#x\n", hr, D3DERR_INVALIDCALL);
1370 
1371         check_release((IUnknown *)render, 0);
1372     } else skip("Failed to create ID3DXRenderToEnvMap\n");
1373 
1374     /* make sure there is a depth stencil surface present */
1375     hr = IDirect3DDevice9_GetDepthStencilSurface(device, &depth_stencil_surface);
1376     if (SUCCEEDED(hr))
1377     {
1378         IDirect3DSurface9_Release(depth_stencil_surface);
1379         depth_stencil_surface = NULL;
1380     }
1381     else if (hr == D3DERR_NOTFOUND)
1382     {
1383         hr = IDirect3DDevice9_CreateDepthStencilSurface(device, 256, 256, D3DFMT_D24X8,
1384                 D3DMULTISAMPLE_NONE, 0, TRUE, &depth_stencil_surface, NULL);
1385         if (SUCCEEDED(hr)) IDirect3DDevice9_SetDepthStencilSurface(device, depth_stencil_surface);
1386     }
1387 
1388     if (FAILED(hr))
1389     {
1390         skip("Failed to create depth stencil surface\n");
1391         return;
1392     }
1393 
1394     test_ID3DXRenderToEnvMap_cube_map(device);
1395 
1396     if (depth_stencil_surface)
1397     {
1398         IDirect3DDevice9_SetDepthStencilSurface(device, NULL);
1399         IDirect3DSurface9_Release(depth_stencil_surface);
1400     }
1401 }
1402 
1403 START_TEST(core)
1404 {
1405     HWND wnd;
1406     IDirect3D9 *d3d;
1407     IDirect3DDevice9 *device;
1408     D3DPRESENT_PARAMETERS d3dpp;
1409     HRESULT hr;
1410 
1411     if (!(wnd = CreateWindowA("static", "d3dx9_test", WS_OVERLAPPEDWINDOW, 0, 0,
1412             640, 480, NULL, NULL, NULL, NULL)))
1413     {
1414         skip("Couldn't create application window\n");
1415         return;
1416     }
1417     if (!(d3d = Direct3DCreate9(D3D_SDK_VERSION)))
1418     {
1419         skip("Couldn't create IDirect3D9 object\n");
1420         DestroyWindow(wnd);
1421         return;
1422     }
1423 
1424     ZeroMemory(&d3dpp, sizeof(d3dpp));
1425     d3dpp.Windowed   = TRUE;
1426     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1427     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &device);
1428     if(FAILED(hr)) {
1429         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1430         IDirect3D9_Release(d3d);
1431         DestroyWindow(wnd);
1432         return;
1433     }
1434 
1435     test_ID3DXBuffer();
1436     test_ID3DXSprite(device);
1437     test_ID3DXFont(device);
1438     test_D3DXCreateRenderToSurface(device);
1439     test_ID3DXRenderToSurface(device);
1440     test_D3DXCreateRenderToEnvMap(device);
1441     test_ID3DXRenderToEnvMap(device);
1442 
1443     check_release((IUnknown*)device, 0);
1444     check_release((IUnknown*)d3d, 0);
1445     if (wnd) DestroyWindow(wnd);
1446 }
1447