xref: /reactos/dll/directx/wine/d3dx9_36/font.c (revision 51fd824e)
1 /*
2  * Copyright (C) 2008 Tony Wasserka
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  */
19 
20 #include "config.h"
21 #include "wine/port.h"
22 
23 #include "d3dx9_private.h"
24 #include "wine/unicode.h"
25 
26 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
27 
28 struct d3dx_font
29 {
30     ID3DXFont ID3DXFont_iface;
31     LONG ref;
32 
33     IDirect3DDevice9 *device;
34     D3DXFONT_DESCW desc;
35 
36     HDC hdc;
37     HFONT hfont;
38 
39     UINT tex_width;
40     UINT tex_height;
41     IDirect3DTexture9 *texture;
42     HBITMAP bitmap;
43     BYTE *bits;
44 };
45 
46 /* Returns the smallest power of 2 which is greater than or equal to num */
47 static UINT make_pow2(UINT num)
48 {
49     UINT result = 1;
50 
51     /* In the unlikely event somebody passes a large value, make sure we don't enter an infinite loop */
52     if (num >= 0x80000000)
53         return 0x80000000;
54 
55     while (result < num)
56         result <<= 1;
57 
58     return result;
59 }
60 
61 static inline struct d3dx_font *impl_from_ID3DXFont(ID3DXFont *iface)
62 {
63     return CONTAINING_RECORD(iface, struct d3dx_font, ID3DXFont_iface);
64 }
65 
66 static HRESULT WINAPI ID3DXFontImpl_QueryInterface(ID3DXFont *iface, REFIID riid, void **out)
67 {
68     TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
69 
70     if (IsEqualGUID(riid, &IID_ID3DXFont)
71             || IsEqualGUID(riid, &IID_IUnknown))
72     {
73         IUnknown_AddRef(iface);
74         *out = iface;
75         return S_OK;
76     }
77 
78     WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
79 
80     *out = NULL;
81     return E_NOINTERFACE;
82 }
83 
84 static ULONG WINAPI ID3DXFontImpl_AddRef(ID3DXFont *iface)
85 {
86     struct d3dx_font *This = impl_from_ID3DXFont(iface);
87     ULONG ref = InterlockedIncrement(&This->ref);
88 
89     TRACE("%p increasing refcount to %u\n", iface, ref);
90 
91     return ref;
92 }
93 
94 static ULONG WINAPI ID3DXFontImpl_Release(ID3DXFont *iface)
95 {
96     struct d3dx_font *This = impl_from_ID3DXFont(iface);
97     ULONG ref = InterlockedDecrement(&This->ref);
98 
99     TRACE("%p decreasing refcount to %u\n", iface, ref);
100 
101     if (!ref)
102     {
103         if (This->texture)
104         {
105             IDirect3DTexture9_Release(This->texture);
106             DeleteObject(This->bitmap);
107         }
108         DeleteObject(This->hfont);
109         DeleteDC(This->hdc);
110         IDirect3DDevice9_Release(This->device);
111         HeapFree(GetProcessHeap(), 0, This);
112     }
113     return ref;
114 }
115 
116 static HRESULT WINAPI ID3DXFontImpl_GetDevice(ID3DXFont *iface, IDirect3DDevice9 **device)
117 {
118     struct d3dx_font *This = impl_from_ID3DXFont(iface);
119 
120     TRACE("iface %p, device %p\n", iface, device);
121 
122     if( !device ) return D3DERR_INVALIDCALL;
123     *device = This->device;
124     IDirect3DDevice9_AddRef(This->device);
125 
126     return D3D_OK;
127 }
128 
129 static HRESULT WINAPI ID3DXFontImpl_GetDescA(ID3DXFont *iface, D3DXFONT_DESCA *desc)
130 {
131     struct d3dx_font *This = impl_from_ID3DXFont(iface);
132 
133     TRACE("iface %p, desc %p\n", iface, desc);
134 
135     if( !desc ) return D3DERR_INVALIDCALL;
136     memcpy(desc, &This->desc, FIELD_OFFSET(D3DXFONT_DESCA, FaceName));
137     WideCharToMultiByte(CP_ACP, 0, This->desc.FaceName, -1, desc->FaceName, sizeof(desc->FaceName) / sizeof(CHAR), NULL, NULL);
138 
139     return D3D_OK;
140 }
141 
142 static HRESULT WINAPI ID3DXFontImpl_GetDescW(ID3DXFont *iface, D3DXFONT_DESCW *desc)
143 {
144     struct d3dx_font *This = impl_from_ID3DXFont(iface);
145 
146     TRACE("iface %p, desc %p\n", iface, desc);
147 
148     if( !desc ) return D3DERR_INVALIDCALL;
149     *desc = This->desc;
150 
151     return D3D_OK;
152 }
153 
154 static BOOL WINAPI ID3DXFontImpl_GetTextMetricsA(ID3DXFont *iface, TEXTMETRICA *metrics)
155 {
156     struct d3dx_font *This = impl_from_ID3DXFont(iface);
157     TRACE("iface %p, metrics %p\n", iface, metrics);
158     return GetTextMetricsA(This->hdc, metrics);
159 }
160 
161 static BOOL WINAPI ID3DXFontImpl_GetTextMetricsW(ID3DXFont *iface, TEXTMETRICW *metrics)
162 {
163     struct d3dx_font *This = impl_from_ID3DXFont(iface);
164     TRACE("iface %p, metrics %p\n", iface, metrics);
165     return GetTextMetricsW(This->hdc, metrics);
166 }
167 
168 static HDC WINAPI ID3DXFontImpl_GetDC(ID3DXFont *iface)
169 {
170     struct d3dx_font *This = impl_from_ID3DXFont(iface);
171     TRACE("iface %p\n", iface);
172     return This->hdc;
173 }
174 
175 static HRESULT WINAPI ID3DXFontImpl_GetGlyphData(ID3DXFont *iface, UINT glyph,
176         IDirect3DTexture9 **texture, RECT *blackbox, POINT *cellinc)
177 {
178     FIXME("iface %p, glyph %#x, texture %p, blackbox %p, cellinc %p stub!\n",
179             iface, glyph, texture, blackbox, cellinc);
180     return E_NOTIMPL;
181 }
182 
183 static HRESULT WINAPI ID3DXFontImpl_PreloadCharacters(ID3DXFont *iface, UINT first, UINT last)
184 {
185     FIXME("iface %p, first %u, last %u stub!\n", iface, first, last);
186     return S_OK;
187 }
188 
189 static HRESULT WINAPI ID3DXFontImpl_PreloadGlyphs(ID3DXFont *iface, UINT first, UINT last)
190 {
191     FIXME("iface %p, first %u, last %u stub!\n", iface, first, last);
192     return E_NOTIMPL;
193 }
194 
195 static HRESULT WINAPI ID3DXFontImpl_PreloadTextA(ID3DXFont *iface, const char *string, INT count)
196 {
197     FIXME("iface %p, string %s, count %d stub!\n", iface, debugstr_a(string), count);
198     return E_NOTIMPL;
199 }
200 
201 static HRESULT WINAPI ID3DXFontImpl_PreloadTextW(ID3DXFont *iface, const WCHAR *string, INT count)
202 {
203     FIXME("iface %p, string %s, count %d stub!\n", iface, debugstr_w(string), count);
204     return E_NOTIMPL;
205 }
206 
207 static INT WINAPI ID3DXFontImpl_DrawTextA(ID3DXFont *iface, ID3DXSprite *sprite,
208         const char *string, INT count, RECT *rect, DWORD format, D3DCOLOR color)
209 {
210     LPWSTR stringW;
211     INT countW, ret = 0;
212 
213     TRACE("iface %p, sprite %p, string %s, count %d, rect %s, format %#x, color 0x%08x\n",
214             iface,  sprite, debugstr_a(string), count, wine_dbgstr_rect(rect), format, color);
215 
216     if (!string || count == 0)
217         return 0;
218 
219     if (count < 0)
220        count = -1;
221 
222     countW = MultiByteToWideChar(CP_ACP, 0, string, count, NULL, 0);
223     stringW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR));
224     if (stringW)
225     {
226         MultiByteToWideChar(CP_ACP, 0, string, count, stringW, countW);
227         ret = ID3DXFont_DrawTextW(iface, sprite, stringW, countW, rect, format, color);
228         HeapFree(GetProcessHeap(), 0, stringW);
229     }
230 
231     return ret;
232 }
233 
234 static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
235         const WCHAR *string, INT count, RECT *rect, DWORD format, D3DCOLOR color)
236 {
237     struct d3dx_font *This = impl_from_ID3DXFont(iface);
238     RECT calc_rect;
239     INT height;
240 
241     TRACE("iface %p, sprite %p, string %s, count %d, rect %s, format %#x, color 0x%08x\n",
242             iface,  sprite, debugstr_w(string), count, wine_dbgstr_rect(rect), format, color);
243 
244     if (!string || count == 0)
245         return 0;
246 
247     if (count < 0)
248        count = lstrlenW(string);
249 
250     /* Strip terminating NULL characters */
251     while (count > 0 && !string[count-1])
252         count--;
253 
254     if (rect)
255         calc_rect = *rect;
256 
257     height = DrawTextW(This->hdc, string, count, &calc_rect, format | DT_CALCRECT);
258 
259     if (format & DT_CALCRECT)
260     {
261         if (rect)
262             *rect = calc_rect;
263         return height;
264     }
265 
266     if (format & DT_CENTER)
267     {
268         UINT new_width = calc_rect.right - calc_rect.left;
269         calc_rect.left = (rect->right + rect->left - new_width) / 2;
270         calc_rect.right = calc_rect.left + new_width;
271     }
272 
273     if (height && (calc_rect.left < calc_rect.right))
274     {
275         D3DLOCKED_RECT locked_rect;
276         D3DXVECTOR3 position;
277         UINT text_width, text_height;
278         RECT text_rect;
279         ID3DXSprite *target = sprite;
280         HRESULT hr;
281         int i, j;
282 
283         /* Get rect position and dimensions */
284         position.x = calc_rect.left;
285         position.y = calc_rect.top;
286         position.z = 0;
287         text_width = calc_rect.right - calc_rect.left;
288         text_height = calc_rect.bottom - calc_rect.top;
289         text_rect.left = 0;
290         text_rect.top = 0;
291         text_rect.right = text_width;
292         text_rect.bottom = text_height;
293 
294         /* We need to flush as it seems all draws in the begin/end sequence use only the latest updated texture */
295         if (sprite)
296             ID3DXSprite_Flush(sprite);
297 
298         /* Extend texture and DIB section to contain text */
299         if ((text_width > This->tex_width) || (text_height > This->tex_height))
300         {
301             BITMAPINFOHEADER header;
302 
303             if (text_width > This->tex_width)
304                 This->tex_width = make_pow2(text_width);
305             if (text_height > This->tex_height)
306                 This->tex_height = make_pow2(text_height);
307 
308             if (This->texture)
309             {
310                 IDirect3DTexture9_Release(This->texture);
311                 DeleteObject(This->bitmap);
312             }
313 
314             hr = D3DXCreateTexture(This->device, This->tex_width, This->tex_height, 1, 0,
315                                    D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &This->texture);
316             if (FAILED(hr))
317             {
318                 This->texture = NULL;
319                 return 0;
320             }
321 
322             header.biSize = sizeof(header);
323             header.biWidth = This->tex_width;
324             header.biHeight = -This->tex_height;
325             header.biPlanes = 1;
326             header.biBitCount = 32;
327             header.biCompression = BI_RGB;
328             header.biSizeImage = sizeof(DWORD) * This->tex_width * This->tex_height;
329             header.biXPelsPerMeter = 0;
330             header.biYPelsPerMeter = 0;
331             header.biClrUsed = 0;
332             header.biClrImportant = 0;
333 
334             This->bitmap = CreateDIBSection(This->hdc, (const BITMAPINFO*)&header,
335                                             DIB_RGB_COLORS, (void**)&This->bits, NULL, 0);
336             if (!This->bitmap)
337             {
338                 IDirect3DTexture9_Release(This->texture);
339                 This->texture = NULL;
340                 return 0;
341             }
342 
343             SelectObject(This->hdc, This->bitmap);
344         }
345 
346         if (FAILED(IDirect3DTexture9_LockRect(This->texture, 0, &locked_rect, &text_rect, D3DLOCK_DISCARD)))
347             return 0;
348 
349         /* Clear rect */
350         for (i = 0; i < text_height; i++)
351             memset(This->bits + i * This->tex_width * sizeof(DWORD), 0,
352                    text_width * sizeof(DWORD));
353 
354         DrawTextW(This->hdc, string, count, &text_rect, format);
355 
356         /* All RGB components are equal so take one as alpha and set RGB
357          * color to white, so it can be modulated with color parameter */
358         for (i = 0; i < text_height; i++)
359         {
360             DWORD *src = (DWORD *)This->bits + i * This->tex_width;
361             DWORD *dst = (DWORD *)((BYTE *)locked_rect.pBits + i * locked_rect.Pitch);
362             for (j = 0; j < text_width; j++)
363             {
364                 *dst++ = (*src++ << 24) | 0xFFFFFF;
365             }
366         }
367 
368         IDirect3DTexture9_UnlockRect(This->texture, 0);
369 
370         if (!sprite)
371         {
372             hr = D3DXCreateSprite(This->device, &target);
373             if (FAILED(hr))
374                  return 0;
375             ID3DXSprite_Begin(target, 0);
376         }
377 
378         hr = target->lpVtbl->Draw(target, This->texture, &text_rect, NULL, &position, color);
379 
380         if (!sprite)
381         {
382             ID3DXSprite_End(target);
383             ID3DXSprite_Release(target);
384         }
385 
386         if (FAILED(hr))
387             return 0;
388     }
389 
390     return height;
391 }
392 
393 static HRESULT WINAPI ID3DXFontImpl_OnLostDevice(ID3DXFont *iface)
394 {
395     FIXME("iface %p stub!\n", iface);
396     return D3D_OK;
397 }
398 
399 static HRESULT WINAPI ID3DXFontImpl_OnResetDevice(ID3DXFont *iface)
400 {
401     FIXME("iface %p stub\n", iface);
402     return D3D_OK;
403 }
404 
405 static const ID3DXFontVtbl D3DXFont_Vtbl =
406 {
407     /*** IUnknown methods ***/
408     ID3DXFontImpl_QueryInterface,
409     ID3DXFontImpl_AddRef,
410     ID3DXFontImpl_Release,
411     /*** ID3DXFont methods ***/
412     ID3DXFontImpl_GetDevice,
413     ID3DXFontImpl_GetDescA,
414     ID3DXFontImpl_GetDescW,
415     ID3DXFontImpl_GetTextMetricsA,
416     ID3DXFontImpl_GetTextMetricsW,
417     ID3DXFontImpl_GetDC,
418     ID3DXFontImpl_GetGlyphData,
419     ID3DXFontImpl_PreloadCharacters,
420     ID3DXFontImpl_PreloadGlyphs,
421     ID3DXFontImpl_PreloadTextA,
422     ID3DXFontImpl_PreloadTextW,
423     ID3DXFontImpl_DrawTextA,
424     ID3DXFontImpl_DrawTextW,
425     ID3DXFontImpl_OnLostDevice,
426     ID3DXFontImpl_OnResetDevice
427 };
428 
429 HRESULT WINAPI D3DXCreateFontA(struct IDirect3DDevice9 *device, INT height, UINT width,
430         UINT weight, UINT miplevels, BOOL italic, DWORD charset, DWORD precision, DWORD quality,
431         DWORD pitchandfamily, const char *facename, struct ID3DXFont **font)
432 {
433     D3DXFONT_DESCA desc;
434 
435     if( !device || !font ) return D3DERR_INVALIDCALL;
436 
437     desc.Height=height;
438     desc.Width=width;
439     desc.Weight=weight;
440     desc.MipLevels=miplevels;
441     desc.Italic=italic;
442     desc.CharSet=charset;
443     desc.OutputPrecision=precision;
444     desc.Quality=quality;
445     desc.PitchAndFamily=pitchandfamily;
446     if(facename != NULL) lstrcpyA(desc.FaceName, facename);
447     else desc.FaceName[0] = '\0';
448 
449     return D3DXCreateFontIndirectA(device, &desc, font);
450 }
451 
452 HRESULT WINAPI D3DXCreateFontW(IDirect3DDevice9 *device, INT height, UINT width, UINT weight, UINT miplevels, BOOL italic, DWORD charset,
453                                DWORD precision, DWORD quality, DWORD pitchandfamily, const WCHAR *facename, ID3DXFont **font)
454 {
455     D3DXFONT_DESCW desc;
456 
457     if( !device || !font ) return D3DERR_INVALIDCALL;
458 
459     desc.Height=height;
460     desc.Width=width;
461     desc.Weight=weight;
462     desc.MipLevels=miplevels;
463     desc.Italic=italic;
464     desc.CharSet=charset;
465     desc.OutputPrecision=precision;
466     desc.Quality=quality;
467     desc.PitchAndFamily=pitchandfamily;
468     if(facename != NULL) strcpyW(desc.FaceName, facename);
469     else desc.FaceName[0] = '\0';
470 
471     return D3DXCreateFontIndirectW(device, &desc, font);
472 }
473 
474 /***********************************************************************
475  *           D3DXCreateFontIndirectA    (D3DX9_36.@)
476  */
477 HRESULT WINAPI D3DXCreateFontIndirectA(IDirect3DDevice9 *device, const D3DXFONT_DESCA *desc, ID3DXFont **font)
478 {
479     D3DXFONT_DESCW widedesc;
480 
481     if( !device || !desc || !font ) return D3DERR_INVALIDCALL;
482 
483     /* Copy everything but the last structure member. This requires the
484        two D3DXFONT_DESC structures to be equal until the FaceName member */
485     memcpy(&widedesc, desc, FIELD_OFFSET(D3DXFONT_DESCA, FaceName));
486     MultiByteToWideChar(CP_ACP, 0, desc->FaceName, -1,
487                         widedesc.FaceName, sizeof(widedesc.FaceName)/sizeof(WCHAR));
488     return D3DXCreateFontIndirectW(device, &widedesc, font);
489 }
490 
491 /***********************************************************************
492  *           D3DXCreateFontIndirectW    (D3DX9_36.@)
493  */
494 HRESULT WINAPI D3DXCreateFontIndirectW(IDirect3DDevice9 *device, const D3DXFONT_DESCW *desc, ID3DXFont **font)
495 {
496     D3DDEVICE_CREATION_PARAMETERS cpars;
497     D3DDISPLAYMODE mode;
498     struct d3dx_font *object;
499     IDirect3D9 *d3d;
500     HRESULT hr;
501 
502     TRACE("(%p, %p, %p)\n", device, desc, font);
503 
504     if (!device || !desc || !font) return D3DERR_INVALIDCALL;
505 
506     TRACE("desc: %d %d %d %d %d %d %d %d %d %s\n", desc->Height, desc->Width, desc->Weight, desc->MipLevels, desc->Italic,
507             desc->CharSet, desc->OutputPrecision, desc->Quality, desc->PitchAndFamily, debugstr_w(desc->FaceName));
508 
509     /* The device MUST support D3DFMT_A8R8G8B8 */
510     IDirect3DDevice9_GetDirect3D(device, &d3d);
511     IDirect3DDevice9_GetCreationParameters(device, &cpars);
512     IDirect3DDevice9_GetDisplayMode(device, 0, &mode);
513     hr = IDirect3D9_CheckDeviceFormat(d3d, cpars.AdapterOrdinal, cpars.DeviceType, mode.Format, 0, D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8);
514     if (FAILED(hr))
515     {
516         IDirect3D9_Release(d3d);
517         return D3DXERR_INVALIDDATA;
518     }
519     IDirect3D9_Release(d3d);
520 
521     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct d3dx_font));
522     if (!object)
523     {
524         *font = NULL;
525         return E_OUTOFMEMORY;
526     }
527     object->ID3DXFont_iface.lpVtbl = &D3DXFont_Vtbl;
528     object->ref = 1;
529     object->device = device;
530     object->desc = *desc;
531 
532     object->hdc = CreateCompatibleDC(NULL);
533     if (!object->hdc)
534     {
535         HeapFree(GetProcessHeap(), 0, object);
536         return D3DXERR_INVALIDDATA;
537     }
538 
539     object->hfont = CreateFontW(desc->Height, desc->Width, 0, 0, desc->Weight, desc->Italic, FALSE, FALSE, desc->CharSet,
540                                 desc->OutputPrecision, CLIP_DEFAULT_PRECIS, desc->Quality, desc->PitchAndFamily, desc->FaceName);
541     if (!object->hfont)
542     {
543         DeleteDC(object->hdc);
544         HeapFree(GetProcessHeap(), 0, object);
545         return D3DXERR_INVALIDDATA;
546     }
547     SelectObject(object->hdc, object->hfont);
548     SetTextColor(object->hdc, 0x00ffffff);
549     SetBkColor(object->hdc, 0x00000000);
550 
551     IDirect3DDevice9_AddRef(device);
552     *font = &object->ID3DXFont_iface;
553 
554     return D3D_OK;
555 }
556