xref: /reactos/dll/directx/wine/d3dx9_36/font.c (revision d7f9f9f6)
1 #ifdef __REACTOS__
2 #include "precomp.h"
3 #else
4 /*
5  * Copyright (C) 2008 Tony Wasserka
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  */
22 
23 
24 #include "d3dx9_private.h"
25 
26 #include "usp10.h"
27 #endif /* __REACTOS__ */
28 
29 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
30 
31 struct d3dx_glyph
32 {
33     unsigned int id;
34     RECT black_box;
35     POINT cell_inc;
36     IDirect3DTexture9 *texture;
37 
38     struct wine_rb_entry entry;
39 };
40 
41 struct d3dx_font
42 {
43     ID3DXFont ID3DXFont_iface;
44     LONG ref;
45 
46     IDirect3DDevice9 *device;
47     D3DXFONT_DESCW desc;
48     TEXTMETRICW metrics;
49 
50     HDC hdc;
51     HFONT hfont;
52 
53     struct wine_rb_tree glyph_tree;
54 
55     IDirect3DTexture9 **textures;
56     unsigned int texture_count, texture_pos;
57 
58     unsigned int texture_size, glyph_size, glyphs_per_texture;
59 };
60 
glyph_rb_compare(const void * key,const struct wine_rb_entry * entry)61 static int glyph_rb_compare(const void *key, const struct wine_rb_entry *entry)
62 {
63     struct d3dx_glyph *glyph = WINE_RB_ENTRY_VALUE(entry, struct d3dx_glyph, entry);
64     unsigned int id = (UINT_PTR)key;
65 
66     return id - glyph->id;
67 }
68 
glyph_rb_free(struct wine_rb_entry * entry,void * context)69 static void glyph_rb_free(struct wine_rb_entry *entry, void *context)
70 {
71     struct d3dx_glyph *glyph = WINE_RB_ENTRY_VALUE(entry, struct d3dx_glyph, entry);
72 
73     heap_free(glyph);
74 }
75 
impl_from_ID3DXFont(ID3DXFont * iface)76 static inline struct d3dx_font *impl_from_ID3DXFont(ID3DXFont *iface)
77 {
78     return CONTAINING_RECORD(iface, struct d3dx_font, ID3DXFont_iface);
79 }
80 
ID3DXFontImpl_QueryInterface(ID3DXFont * iface,REFIID riid,void ** out)81 static HRESULT WINAPI ID3DXFontImpl_QueryInterface(ID3DXFont *iface, REFIID riid, void **out)
82 {
83     TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
84 
85     if (IsEqualGUID(riid, &IID_ID3DXFont)
86             || IsEqualGUID(riid, &IID_IUnknown))
87     {
88         IUnknown_AddRef(iface);
89         *out = iface;
90         return S_OK;
91     }
92 
93     WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
94 
95     *out = NULL;
96     return E_NOINTERFACE;
97 }
98 
ID3DXFontImpl_AddRef(ID3DXFont * iface)99 static ULONG WINAPI ID3DXFontImpl_AddRef(ID3DXFont *iface)
100 {
101     struct d3dx_font *font = impl_from_ID3DXFont(iface);
102     ULONG ref = InterlockedIncrement(&font->ref);
103 
104     TRACE("%p increasing refcount to %u\n", iface, ref);
105     return ref;
106 }
107 
ID3DXFontImpl_Release(ID3DXFont * iface)108 static ULONG WINAPI ID3DXFontImpl_Release(ID3DXFont *iface)
109 {
110     struct d3dx_font *font = impl_from_ID3DXFont(iface);
111     ULONG ref = InterlockedDecrement(&font->ref);
112     unsigned int i;
113 
114     TRACE("%p decreasing refcount to %u\n", iface, ref);
115 
116     if (!ref)
117     {
118         for (i = 0; i < font->texture_count; ++i)
119             IDirect3DTexture9_Release(font->textures[i]);
120 
121         heap_free(font->textures);
122 
123         wine_rb_destroy(&font->glyph_tree, glyph_rb_free, NULL);
124 
125         DeleteObject(font->hfont);
126         DeleteDC(font->hdc);
127         IDirect3DDevice9_Release(font->device);
128         heap_free(font);
129     }
130     return ref;
131 }
132 
ID3DXFontImpl_GetDevice(ID3DXFont * iface,IDirect3DDevice9 ** device)133 static HRESULT WINAPI ID3DXFontImpl_GetDevice(ID3DXFont *iface, IDirect3DDevice9 **device)
134 {
135     struct d3dx_font *font = impl_from_ID3DXFont(iface);
136 
137     TRACE("iface %p, device %p\n", iface, device);
138 
139     if( !device ) return D3DERR_INVALIDCALL;
140     *device = font->device;
141     IDirect3DDevice9_AddRef(font->device);
142 
143     return D3D_OK;
144 }
145 
ID3DXFontImpl_GetDescA(ID3DXFont * iface,D3DXFONT_DESCA * desc)146 static HRESULT WINAPI ID3DXFontImpl_GetDescA(ID3DXFont *iface, D3DXFONT_DESCA *desc)
147 {
148     struct d3dx_font *font = impl_from_ID3DXFont(iface);
149 
150     TRACE("iface %p, desc %p\n", iface, desc);
151 
152     if( !desc ) return D3DERR_INVALIDCALL;
153     memcpy(desc, &font->desc, FIELD_OFFSET(D3DXFONT_DESCA, FaceName));
154     WideCharToMultiByte(CP_ACP, 0, font->desc.FaceName, -1, desc->FaceName, ARRAY_SIZE(desc->FaceName), NULL, NULL);
155 
156     return D3D_OK;
157 }
158 
ID3DXFontImpl_GetDescW(ID3DXFont * iface,D3DXFONT_DESCW * desc)159 static HRESULT WINAPI ID3DXFontImpl_GetDescW(ID3DXFont *iface, D3DXFONT_DESCW *desc)
160 {
161     struct d3dx_font *font = impl_from_ID3DXFont(iface);
162 
163     TRACE("iface %p, desc %p\n", iface, desc);
164 
165     if( !desc ) return D3DERR_INVALIDCALL;
166     *desc = font->desc;
167 
168     return D3D_OK;
169 }
170 
ID3DXFontImpl_GetTextMetricsA(ID3DXFont * iface,TEXTMETRICA * metrics)171 static BOOL WINAPI ID3DXFontImpl_GetTextMetricsA(ID3DXFont *iface, TEXTMETRICA *metrics)
172 {
173     struct d3dx_font *font = impl_from_ID3DXFont(iface);
174     TRACE("iface %p, metrics %p\n", iface, metrics);
175     return GetTextMetricsA(font->hdc, metrics);
176 }
177 
ID3DXFontImpl_GetTextMetricsW(ID3DXFont * iface,TEXTMETRICW * metrics)178 static BOOL WINAPI ID3DXFontImpl_GetTextMetricsW(ID3DXFont *iface, TEXTMETRICW *metrics)
179 {
180     struct d3dx_font *font = impl_from_ID3DXFont(iface);
181     TRACE("iface %p, metrics %p\n", iface, metrics);
182     return GetTextMetricsW(font->hdc, metrics);
183 }
184 
ID3DXFontImpl_GetDC(ID3DXFont * iface)185 static HDC WINAPI ID3DXFontImpl_GetDC(ID3DXFont *iface)
186 {
187     struct d3dx_font *font = impl_from_ID3DXFont(iface);
188     TRACE("iface %p\n", iface);
189     return font->hdc;
190 }
191 
ID3DXFontImpl_GetGlyphData(ID3DXFont * iface,UINT glyph,IDirect3DTexture9 ** texture,RECT * black_box,POINT * cell_inc)192 static HRESULT WINAPI ID3DXFontImpl_GetGlyphData(ID3DXFont *iface, UINT glyph,
193         IDirect3DTexture9 **texture, RECT *black_box, POINT *cell_inc)
194 {
195     struct d3dx_font *font = impl_from_ID3DXFont(iface);
196     struct wine_rb_entry *entry;
197     HRESULT hr;
198 
199     TRACE("iface %p, glyph %#x, texture %p, black_box %p, cell_inc %p.\n",
200           iface, glyph, texture, black_box, cell_inc);
201 
202     hr = ID3DXFont_PreloadGlyphs(iface, glyph, glyph);
203     if (FAILED(hr))
204         return hr;
205 
206     entry = wine_rb_get(&font->glyph_tree, ULongToPtr(glyph));
207     if (entry)
208     {
209         struct d3dx_glyph *current_glyph = WINE_RB_ENTRY_VALUE(entry, struct d3dx_glyph, entry);
210 
211         if (cell_inc)
212             *cell_inc = current_glyph->cell_inc;
213         if (black_box)
214             *black_box = current_glyph->black_box;
215         if (texture)
216         {
217             *texture = current_glyph->texture;
218             if (*texture)
219                 IDirect3DTexture9_AddRef(current_glyph->texture);
220         }
221         return D3D_OK;
222     }
223 
224     return D3DXERR_INVALIDDATA;
225 }
226 
ID3DXFontImpl_PreloadCharacters(ID3DXFont * iface,UINT first,UINT last)227 static HRESULT WINAPI ID3DXFontImpl_PreloadCharacters(ID3DXFont *iface, UINT first, UINT last)
228 {
229     struct d3dx_font *font = impl_from_ID3DXFont(iface);
230     unsigned int i, count, start, end;
231     WORD *indices;
232     WCHAR *chars;
233 
234     TRACE("iface %p, first %u, last %u.\n", iface, first, last);
235 
236     if (last < first)
237         return D3D_OK;
238 
239     count = last - first + 1;
240     indices = heap_alloc(count * sizeof(*indices));
241     if (!indices)
242         return E_OUTOFMEMORY;
243 
244     chars = heap_alloc(count * sizeof(*chars));
245     if (!chars)
246     {
247         heap_free(indices);
248         return E_OUTOFMEMORY;
249     }
250 
251     for (i = 0; i < count; ++i)
252         chars[i] = first + i;
253 
254     GetGlyphIndicesW(font->hdc, chars, count, indices, 0);
255 
256     start = end = indices[0];
257     for (i = 1; i < count; ++i)
258     {
259         if (indices[i] == end + 1)
260         {
261             end = indices[i];
262             continue;
263         }
264         ID3DXFont_PreloadGlyphs(iface, start, end);
265         start = end = indices[i];
266     }
267     ID3DXFont_PreloadGlyphs(iface, start, end);
268 
269     heap_free(chars);
270     heap_free(indices);
271 
272     return D3D_OK;
273 }
274 
morton_decode(uint32_t x)275 static uint32_t morton_decode(uint32_t x)
276 {
277     x &= 0x55555555;
278     x = (x ^ (x >> 1)) & 0x33333333;
279     x = (x ^ (x >> 2)) & 0x0f0f0f0f;
280     x = (x ^ (x >> 4)) & 0x00ff00ff;
281     x = (x ^ (x >> 8)) & 0x0000ffff;
282     return x;
283 }
284 
285 /* The glyphs are stored in a grid. Cell sizes vary between different font
286  * sizes.
287  *
288  * The grid is filled in Morton order:
289  *  1   2   5   6  17  18  21  22
290  *  3   4   7   8  19  20  23  24
291  *  9  10  13  14  25  26  29  30
292  * 11  12  15  16  27  28  31  32
293  * 33  34 ...
294  * ...
295  *
296  * i.e. we try to fill one small square, then three equal-sized squares so
297  * that we get one big square, etc.
298  *
299  * The glyphs are positioned around their baseline, which is located at y
300  * position glyph_size * i + tmAscent. Concerning the x position, the glyphs
301  * are centered around glyph_size * (i + 0.5). */
ID3DXFontImpl_PreloadGlyphs(ID3DXFont * iface,UINT first,UINT last)302 static HRESULT WINAPI ID3DXFontImpl_PreloadGlyphs(ID3DXFont *iface, UINT first, UINT last)
303 {
304     static const MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
305     struct d3dx_font *font = impl_from_ID3DXFont(iface);
306     IDirect3DTexture9 *current_texture = NULL;
307     unsigned int size, stride, glyph, x, y;
308     struct d3dx_glyph *current_glyph;
309     D3DLOCKED_RECT lockrect;
310     GLYPHMETRICS metrics;
311     BOOL mapped = FALSE;
312     DWORD *pixel_data;
313     BYTE *buffer;
314     HRESULT hr;
315 
316     TRACE("iface %p, first %u, last %u.\n", iface, first, last);
317 
318     if (last < first)
319         return D3D_OK;
320 
321     if (font->texture_count)
322         current_texture = font->textures[font->texture_count - 1];
323 
324     for (glyph = first; glyph <= last; ++glyph)
325     {
326         if (wine_rb_get(&font->glyph_tree, ULongToPtr(glyph)))
327             continue;
328 
329         current_glyph = heap_alloc(sizeof(*current_glyph));
330         if (!current_glyph)
331         {
332             if (mapped)
333                 IDirect3DTexture9_UnlockRect(current_texture, 0);
334             return E_OUTOFMEMORY;
335         }
336 
337         current_glyph->id = glyph;
338         current_glyph->texture = NULL;
339         wine_rb_put(&font->glyph_tree, ULongToPtr(current_glyph->id), &current_glyph->entry);
340 
341         size = GetGlyphOutlineW(font->hdc, glyph, GGO_GLYPH_INDEX | GGO_GRAY8_BITMAP, &metrics, 0, NULL, &mat);
342         if (size == GDI_ERROR)
343         {
344             WARN("GetGlyphOutlineW failed.\n");
345             continue;
346         }
347         if (!size)
348             continue;
349 
350         buffer = heap_alloc(size);
351         if (!buffer)
352         {
353             if (mapped)
354                 IDirect3DTexture9_UnlockRect(current_texture, 0);
355             return E_OUTOFMEMORY;
356         }
357 
358         GetGlyphOutlineW(font->hdc, glyph, GGO_GLYPH_INDEX | GGO_GRAY8_BITMAP, &metrics, size, buffer, &mat);
359 
360         if (font->texture_pos == font->glyphs_per_texture)
361         {
362             unsigned int new_texture_count = font->texture_count + 1;
363             IDirect3DTexture9 **new_textures;
364 
365             if (mapped)
366                 IDirect3DTexture9_UnlockRect(current_texture, 0);
367             mapped = FALSE;
368             new_textures = heap_realloc(font->textures, new_texture_count * sizeof(*new_textures));
369             if (!new_textures)
370             {
371                 heap_free(buffer);
372                 return E_OUTOFMEMORY;
373             }
374             font->textures = new_textures;
375 
376             if (FAILED(hr = IDirect3DDevice9_CreateTexture(font->device, font->texture_size,
377                     font->texture_size, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
378                     &font->textures[font->texture_count], NULL)))
379             {
380                 heap_free(buffer);
381                 return hr;
382             }
383 
384             current_texture = font->textures[font->texture_count++];
385             font->texture_pos = 0;
386         }
387 
388         if (!mapped)
389         {
390             if (FAILED(hr = IDirect3DTexture9_LockRect(current_texture, 0, &lockrect, NULL, 0)))
391             {
392                 heap_free(buffer);
393                 return hr;
394             }
395             mapped = TRUE;
396         }
397 
398         x = morton_decode(font->texture_pos) * font->glyph_size;
399         y = morton_decode(font->texture_pos >> 1) * font->glyph_size;
400 
401         current_glyph->black_box.left = x - metrics.gmptGlyphOrigin.x + font->glyph_size / 2
402                 - metrics.gmBlackBoxX / 2;
403         current_glyph->black_box.top = y - metrics.gmptGlyphOrigin.y + font->metrics.tmAscent + 1;
404         current_glyph->black_box.right = current_glyph->black_box.left + metrics.gmBlackBoxX;
405         current_glyph->black_box.bottom = current_glyph->black_box.top + metrics.gmBlackBoxY;
406         current_glyph->cell_inc.x = metrics.gmptGlyphOrigin.x - 1;
407         current_glyph->cell_inc.y = font->metrics.tmAscent - metrics.gmptGlyphOrigin.y - 1;
408         current_glyph->texture = current_texture;
409 
410         pixel_data = lockrect.pBits;
411         stride = (metrics.gmBlackBoxX + 3) & ~3;
412         for (y = 0; y < metrics.gmBlackBoxY; ++y)
413             for (x = 0; x < metrics.gmBlackBoxX; ++x)
414                 pixel_data[(current_glyph->black_box.top + y) * lockrect.Pitch / 4
415                         + current_glyph->black_box.left + x] =
416                         (buffer[y * stride + x] * 255 / 64 << 24) | 0x00ffffffu;
417 
418         heap_free(buffer);
419         ++font->texture_pos;
420     }
421     if (mapped)
422         IDirect3DTexture9_UnlockRect(current_texture, 0);
423 
424     return D3D_OK;
425 }
426 
ID3DXFontImpl_PreloadTextA(ID3DXFont * iface,const char * string,INT count)427 static HRESULT WINAPI ID3DXFontImpl_PreloadTextA(ID3DXFont *iface, const char *string, INT count)
428 {
429     WCHAR *wstr;
430     HRESULT hr;
431     int countW;
432 
433     TRACE("iface %p, string %s, count %d.\n", iface, debugstr_an(string, count), count);
434 
435     if (!string && !count)
436         return D3D_OK;
437 
438     if (!string)
439         return D3DERR_INVALIDCALL;
440 
441     countW = MultiByteToWideChar(CP_ACP, 0, string, count < 0 ? -1 : count, NULL, 0);
442 
443     wstr = heap_alloc(countW * sizeof(*wstr));
444     if (!wstr)
445         return E_OUTOFMEMORY;
446 
447     MultiByteToWideChar(CP_ACP, 0, string, count < 0 ? -1 : count, wstr, countW);
448 
449     hr = ID3DXFont_PreloadTextW(iface, wstr, count < 0 ? countW - 1 : countW);
450 
451     heap_free(wstr);
452 
453     return hr;
454 }
455 
ID3DXFontImpl_PreloadTextW(ID3DXFont * iface,const WCHAR * string,INT count)456 static HRESULT WINAPI ID3DXFontImpl_PreloadTextW(ID3DXFont *iface, const WCHAR *string, INT count)
457 {
458     struct d3dx_font *font = impl_from_ID3DXFont(iface);
459     WORD *indices;
460     int i;
461 
462     TRACE("iface %p, string %s, count %d.\n", iface, debugstr_wn(string, count), count);
463 
464     if (!string && !count)
465         return D3D_OK;
466 
467     if (!string)
468         return D3DERR_INVALIDCALL;
469 
470     if (count < 0)
471         count = lstrlenW(string);
472 
473     indices = heap_alloc(count * sizeof(*indices));
474     if (!indices)
475         return E_OUTOFMEMORY;
476 
477     GetGlyphIndicesW(font->hdc, string, count, indices, 0);
478 
479     for (i = 0; i < count; ++i)
480         ID3DXFont_PreloadGlyphs(iface, indices[i], indices[i]);
481 
482     heap_free(indices);
483 
484     return D3D_OK;
485 }
486 
ID3DXFontImpl_DrawTextA(ID3DXFont * iface,ID3DXSprite * sprite,const char * string,INT count,RECT * rect,DWORD format,D3DCOLOR color)487 static INT WINAPI ID3DXFontImpl_DrawTextA(ID3DXFont *iface, ID3DXSprite *sprite,
488         const char *string, INT count, RECT *rect, DWORD format, D3DCOLOR color)
489 {
490     int ret, countW;
491     WCHAR *wstr;
492 
493     TRACE("iface %p, sprite %p, string %s, count %d, rect %s, format %#x, color 0x%08x.\n",
494           iface,  sprite, debugstr_an(string, count), count, wine_dbgstr_rect(rect), format, color);
495 
496     if (!string || !count)
497         return 0;
498 
499     countW = MultiByteToWideChar(CP_ACP, 0, string, count < 0 ? -1 : count, NULL, 0);
500 
501     if (!countW)
502         return 0;
503 
504     wstr = heap_alloc_zero(countW * sizeof(*wstr));
505     if (!wstr)
506         return 0;
507 
508     MultiByteToWideChar(CP_ACP, 0, string, count < 0 ? -1 : count, wstr, countW);
509 
510     ret = ID3DXFont_DrawTextW(iface, sprite, wstr, count < 0 ? countW - 1 : countW,
511                               rect, format, color);
512 
513     heap_free(wstr);
514 
515     return ret;
516 }
517 
word_break(HDC hdc,const WCHAR * str,unsigned int * str_len,unsigned int chars_fit,unsigned int * chars_used,SIZE * size)518 static void word_break(HDC hdc, const WCHAR *str, unsigned int *str_len,
519         unsigned int chars_fit, unsigned int *chars_used, SIZE *size)
520 {
521     SCRIPT_LOGATTR *sla;
522     SCRIPT_ANALYSIS sa;
523     unsigned int i;
524 
525     *chars_used = 0;
526 
527     sla = heap_alloc(*str_len * sizeof(*sla));
528     if (!sla)
529         return;
530 
531     memset(&sa, 0, sizeof(sa));
532     sa.eScript = SCRIPT_UNDEFINED;
533 
534     ScriptBreak(str, *str_len, &sa, sla);
535 
536     /* Work back from the last character that did fit to a place where we can break */
537     i = chars_fit;
538     while (i > 0 && !sla[i].fSoftBreak) /* chars_fit < *str_len so this is valid */
539         --i;
540 
541     /* If the there is no word that fits put in all characters that do fit */
542     if (!sla[i].fSoftBreak)
543         i = chars_fit;
544 
545     *chars_used = i;
546     if (sla[i].fWhiteSpace)
547         ++(*chars_used);
548 
549     /* Remove extra spaces */
550     while (i > 0 && sla[i-1].fWhiteSpace)
551         --i;
552     *str_len = i;
553 
554     /* Remeasure the string */
555     GetTextExtentExPointW(hdc, str, *str_len, 0, NULL, NULL, size);
556     heap_free(sla);
557 }
558 
read_line(HDC hdc,const WCHAR * str,unsigned int * count,WCHAR * dest,unsigned int * dest_len,int width,DWORD format,SIZE * size)559 static const WCHAR *read_line(HDC hdc, const WCHAR *str, unsigned int *count,
560         WCHAR *dest, unsigned int *dest_len, int width, DWORD format, SIZE *size)
561 {
562     unsigned int orig_count = *count;
563     unsigned int i = 0;
564     int num_fit;
565 
566     *dest_len = 0;
567     while (*count && (str[i] != '\n' || (format & DT_SINGLELINE)))
568     {
569         --(*count);
570         if (str[i] != '\r' && str[i] != '\n')
571             dest[(*dest_len)++] = str[i];
572         ++i;
573     }
574 
575     num_fit = 0;
576     GetTextExtentExPointW(hdc, dest, *dest_len, width, &num_fit, NULL, size);
577 
578     if (num_fit < *dest_len && (format & DT_WORDBREAK))
579     {
580         unsigned int chars_used;
581 
582         word_break(hdc, dest, dest_len, num_fit, &chars_used, size);
583         *count = orig_count - chars_used;
584         i = chars_used;
585     }
586 
587     if (*count && str[i] == '\n')
588     {
589         --(*count);
590         ++i;
591     }
592 
593     if (*count)
594         return str + i;
595     return NULL;
596 }
597 
compute_rect(struct d3dx_font * font,const WCHAR * string,unsigned int count,WCHAR * line,RECT * rect,DWORD format)598 static int compute_rect(struct d3dx_font *font, const WCHAR *string, unsigned int count,
599         WCHAR *line, RECT *rect, DWORD format)
600 {
601     int y, lh, width, top = rect->top;
602     int max_width = 0;
603     SIZE size;
604 
605     y = rect->top;
606     lh = font->metrics.tmHeight;
607     width = rect->right - rect->left;
608 
609     while (string)
610     {
611         unsigned int line_len;
612 
613         string = read_line(font->hdc, string, &count, line, &line_len, width, format, &size);
614 
615         if (size.cx > max_width)
616             max_width = size.cx;
617 
618         y += lh;
619         if (!(format & DT_NOCLIP) && (y > rect->bottom))
620             break;
621     }
622 
623     if (format & DT_CENTER)
624     {
625         rect->left += (rect->right - rect->left - max_width) / 2;
626         rect->right = rect->left + max_width;
627     }
628     else if (format & DT_RIGHT)
629     {
630         rect->left = rect->right - max_width;
631     }
632     else
633     {
634         rect->right = rect->left + max_width;
635     }
636 
637     if (format & DT_VCENTER)
638     {
639         rect->top += (rect->bottom - y) / 2;
640         rect->bottom = rect->top + y - top;
641     }
642     else if (format & DT_BOTTOM)
643     {
644         rect->top += rect->bottom - y;
645     }
646     else
647     {
648         rect->bottom = y;
649     }
650 
651     return rect->bottom - top;
652 }
653 
ID3DXFontImpl_DrawTextW(ID3DXFont * iface,ID3DXSprite * sprite,const WCHAR * string,INT in_count,RECT * rect,DWORD format,D3DCOLOR color)654 static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
655         const WCHAR *string, INT in_count, RECT *rect, DWORD format, D3DCOLOR color)
656 {
657     struct d3dx_font *font = impl_from_ID3DXFont(iface);
658     int lh, x, y, width, top, ret = 0;
659     ID3DXSprite *target = sprite;
660     unsigned int count;
661     RECT r = {0};
662     WCHAR *line;
663     SIZE size;
664 
665     TRACE("iface %p, sprite %p, string %s, in_count %d, rect %s, format %#x, color 0x%08x.\n",
666           iface,  sprite, debugstr_wn(string, in_count), in_count, wine_dbgstr_rect(rect), format, color);
667 
668     if (!string)
669         return 0;
670 
671     count = in_count < 0 ? lstrlenW(string) : in_count;
672 
673     if (!count)
674         return 0;
675 
676     if (format & DT_CALCRECT)
677         format |= DT_NOCLIP;
678 
679     if (format & DT_SINGLELINE)
680         format &= ~DT_WORDBREAK;
681 
682     line = heap_alloc(count * sizeof(*line));
683     if (!line)
684         return 0;
685 
686     if (!rect || format & (DT_CALCRECT | DT_VCENTER | DT_BOTTOM))
687     {
688         if (!rect)
689         {
690             rect = &r;
691             format |= DT_NOCLIP;
692         }
693         else if (!(format & DT_CALCRECT))
694         {
695             r = *rect;
696             rect = &r;
697         }
698 
699         top = rect->top;
700 
701         ret = compute_rect(font, string, count, line, rect, format);
702 
703         if (format & DT_CALCRECT)
704             goto cleanup;
705     }
706     else
707     {
708         top = rect->top;
709     }
710 
711     y = rect->top;
712     lh = font->metrics.tmHeight;
713     width = rect->right - rect->left;
714 
715     if (!sprite)
716     {
717         D3DXCreateSprite(font->device, &target);
718         ID3DXSprite_Begin(target, 0);
719     }
720 
721     while (string)
722     {
723         unsigned int line_len, i;
724         GCP_RESULTSW results;
725 
726         string = read_line(font->hdc, string, &count, line, &line_len, width, format, &size);
727 
728         if (format & DT_CENTER)
729             x = (rect->left + rect->right - size.cx) / 2;
730         else if (format & DT_RIGHT)
731             x = rect->right - size.cx;
732         else
733             x = rect->left;
734 
735         memset(&results, 0, sizeof(results));
736         results.nGlyphs = line_len;
737 
738         results.lpCaretPos = heap_alloc(line_len * sizeof(*results.lpCaretPos));
739         if (!results.lpCaretPos)
740             goto cleanup;
741 
742         results.lpGlyphs = heap_alloc(line_len * sizeof(*results.lpGlyphs));
743         if (!results.lpGlyphs)
744         {
745             heap_free(results.lpCaretPos);
746             goto cleanup;
747         }
748 
749         GetCharacterPlacementW(font->hdc, line, line_len, 0, &results, 0);
750 
751         for (i = 0; i < results.nGlyphs; ++i)
752         {
753             IDirect3DTexture9 *texture;
754             D3DXVECTOR3 pos;
755             POINT cell_inc;
756             RECT black_box;
757 
758             ID3DXFont_GetGlyphData(iface, results.lpGlyphs[i], &texture, &black_box, &cell_inc);
759 
760             if (!texture)
761                 continue;
762 
763             pos.x = cell_inc.x + x + results.lpCaretPos[i];
764             pos.y = cell_inc.y + y;
765             pos.z = 0;
766 
767             if (!(format & DT_NOCLIP))
768             {
769                 if (pos.x > rect->right)
770                 {
771                     IDirect3DTexture9_Release(texture);
772                     continue;
773                 }
774 
775                 if (pos.x + black_box.right - black_box.left > rect->right)
776                     black_box.right = black_box.left + rect->right - pos.x;
777 
778                 if (pos.y + black_box.bottom - black_box.top > rect->bottom)
779                     black_box.bottom = black_box.top + rect->bottom - pos.y;
780             }
781 
782             ID3DXSprite_Draw(target, texture, &black_box, NULL, &pos, color);
783             IDirect3DTexture9_Release(texture);
784         }
785 
786         heap_free(results.lpCaretPos);
787         heap_free(results.lpGlyphs);
788 
789         y += lh;
790         if (!(DT_NOCLIP & format) && (y > rect->bottom))
791             break;
792     }
793 
794     ret = y - top;
795 
796 cleanup:
797     if (target != sprite)
798     {
799         ID3DXSprite_End(target);
800         ID3DXSprite_Release(target);
801     }
802 
803     heap_free(line);
804 
805     return ret;
806 }
807 
ID3DXFontImpl_OnLostDevice(ID3DXFont * iface)808 static HRESULT WINAPI ID3DXFontImpl_OnLostDevice(ID3DXFont *iface)
809 {
810     FIXME("iface %p stub!\n", iface);
811     return D3D_OK;
812 }
813 
ID3DXFontImpl_OnResetDevice(ID3DXFont * iface)814 static HRESULT WINAPI ID3DXFontImpl_OnResetDevice(ID3DXFont *iface)
815 {
816     FIXME("iface %p stub\n", iface);
817     return D3D_OK;
818 }
819 
820 static const ID3DXFontVtbl D3DXFont_Vtbl =
821 {
822     /*** IUnknown methods ***/
823     ID3DXFontImpl_QueryInterface,
824     ID3DXFontImpl_AddRef,
825     ID3DXFontImpl_Release,
826     /*** ID3DXFont methods ***/
827     ID3DXFontImpl_GetDevice,
828     ID3DXFontImpl_GetDescA,
829     ID3DXFontImpl_GetDescW,
830     ID3DXFontImpl_GetTextMetricsA,
831     ID3DXFontImpl_GetTextMetricsW,
832     ID3DXFontImpl_GetDC,
833     ID3DXFontImpl_GetGlyphData,
834     ID3DXFontImpl_PreloadCharacters,
835     ID3DXFontImpl_PreloadGlyphs,
836     ID3DXFontImpl_PreloadTextA,
837     ID3DXFontImpl_PreloadTextW,
838     ID3DXFontImpl_DrawTextA,
839     ID3DXFontImpl_DrawTextW,
840     ID3DXFontImpl_OnLostDevice,
841     ID3DXFontImpl_OnResetDevice
842 };
843 
D3DXCreateFontA(struct IDirect3DDevice9 * device,INT height,UINT width,UINT weight,UINT miplevels,BOOL italic,DWORD charset,DWORD precision,DWORD quality,DWORD pitchandfamily,const char * facename,struct ID3DXFont ** font)844 HRESULT WINAPI D3DXCreateFontA(struct IDirect3DDevice9 *device, INT height, UINT width,
845         UINT weight, UINT miplevels, BOOL italic, DWORD charset, DWORD precision, DWORD quality,
846         DWORD pitchandfamily, const char *facename, struct ID3DXFont **font)
847 {
848     D3DXFONT_DESCA desc;
849 
850     if( !device || !font ) return D3DERR_INVALIDCALL;
851 
852     desc.Height=height;
853     desc.Width=width;
854     desc.Weight=weight;
855     desc.MipLevels=miplevels;
856     desc.Italic=italic;
857     desc.CharSet=charset;
858     desc.OutputPrecision=precision;
859     desc.Quality=quality;
860     desc.PitchAndFamily=pitchandfamily;
861     if(facename != NULL) lstrcpyA(desc.FaceName, facename);
862     else desc.FaceName[0] = '\0';
863 
864     return D3DXCreateFontIndirectA(device, &desc, font);
865 }
866 
D3DXCreateFontW(IDirect3DDevice9 * device,INT height,UINT width,UINT weight,UINT miplevels,BOOL italic,DWORD charset,DWORD precision,DWORD quality,DWORD pitchandfamily,const WCHAR * facename,ID3DXFont ** font)867 HRESULT WINAPI D3DXCreateFontW(IDirect3DDevice9 *device, INT height, UINT width, UINT weight, UINT miplevels, BOOL italic, DWORD charset,
868                                DWORD precision, DWORD quality, DWORD pitchandfamily, const WCHAR *facename, ID3DXFont **font)
869 {
870     D3DXFONT_DESCW desc;
871 
872     if( !device || !font ) return D3DERR_INVALIDCALL;
873 
874     desc.Height=height;
875     desc.Width=width;
876     desc.Weight=weight;
877     desc.MipLevels=miplevels;
878     desc.Italic=italic;
879     desc.CharSet=charset;
880     desc.OutputPrecision=precision;
881     desc.Quality=quality;
882     desc.PitchAndFamily=pitchandfamily;
883     if(facename != NULL) lstrcpyW(desc.FaceName, facename);
884     else desc.FaceName[0] = '\0';
885 
886     return D3DXCreateFontIndirectW(device, &desc, font);
887 }
888 
889 /***********************************************************************
890  *           D3DXCreateFontIndirectA    (D3DX9_36.@)
891  */
D3DXCreateFontIndirectA(IDirect3DDevice9 * device,const D3DXFONT_DESCA * desc,ID3DXFont ** font)892 HRESULT WINAPI D3DXCreateFontIndirectA(IDirect3DDevice9 *device, const D3DXFONT_DESCA *desc, ID3DXFont **font)
893 {
894     D3DXFONT_DESCW widedesc;
895 
896     if( !device || !desc || !font ) return D3DERR_INVALIDCALL;
897 
898     /* Copy everything but the last structure member. This requires the
899        two D3DXFONT_DESC structures to be equal until the FaceName member */
900     memcpy(&widedesc, desc, FIELD_OFFSET(D3DXFONT_DESCA, FaceName));
901     MultiByteToWideChar(CP_ACP, 0, desc->FaceName, -1, widedesc.FaceName, ARRAY_SIZE(widedesc.FaceName));
902     return D3DXCreateFontIndirectW(device, &widedesc, font);
903 }
904 
905 /***********************************************************************
906  *           D3DXCreateFontIndirectW    (D3DX9_36.@)
907  */
D3DXCreateFontIndirectW(IDirect3DDevice9 * device,const D3DXFONT_DESCW * desc,ID3DXFont ** font)908 HRESULT WINAPI D3DXCreateFontIndirectW(IDirect3DDevice9 *device, const D3DXFONT_DESCW *desc, ID3DXFont **font)
909 {
910     D3DDEVICE_CREATION_PARAMETERS cpars;
911     struct d3dx_font *object;
912     D3DDISPLAYMODE mode;
913     IDirect3D9 *d3d;
914     HRESULT hr;
915 
916     TRACE("(%p, %p, %p)\n", device, desc, font);
917 
918     if( !device || !desc || !font ) return D3DERR_INVALIDCALL;
919 
920     /* the device MUST support D3DFMT_A8R8G8B8 */
921     IDirect3DDevice9_GetDirect3D(device, &d3d);
922     IDirect3DDevice9_GetCreationParameters(device, &cpars);
923     IDirect3DDevice9_GetDisplayMode(device, 0, &mode);
924     hr = IDirect3D9_CheckDeviceFormat(d3d, cpars.AdapterOrdinal, cpars.DeviceType, mode.Format, 0, D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8);
925     if (FAILED(hr))
926     {
927         IDirect3D9_Release(d3d);
928         return D3DXERR_INVALIDDATA;
929     }
930     IDirect3D9_Release(d3d);
931 
932     object = heap_alloc_zero(sizeof(*object));
933     if (!object)
934     {
935         *font = NULL;
936         return E_OUTOFMEMORY;
937     }
938     object->ID3DXFont_iface.lpVtbl = &D3DXFont_Vtbl;
939     object->ref = 1;
940     object->device = device;
941     object->desc = *desc;
942 
943     object->hdc = CreateCompatibleDC(NULL);
944     if (!object->hdc)
945     {
946         heap_free(object);
947         return D3DXERR_INVALIDDATA;
948     }
949 
950     object->hfont = CreateFontW(desc->Height, desc->Width, 0, 0, desc->Weight, desc->Italic, FALSE, FALSE, desc->CharSet,
951                                 desc->OutputPrecision, CLIP_DEFAULT_PRECIS, desc->Quality, desc->PitchAndFamily, desc->FaceName);
952     if (!object->hfont)
953     {
954         DeleteDC(object->hdc);
955         heap_free(object);
956         return D3DXERR_INVALIDDATA;
957     }
958     SelectObject(object->hdc, object->hfont);
959 
960     wine_rb_init(&object->glyph_tree, glyph_rb_compare);
961 
962     if (!GetTextMetricsW(object->hdc, &object->metrics))
963     {
964         DeleteObject(object->hfont);
965         DeleteDC(object->hdc);
966         heap_free(object);
967         return D3DXERR_INVALIDDATA;
968     }
969 
970     object->glyph_size = make_pow2(object->metrics.tmHeight);
971 
972     object->texture_size = object->glyph_size;
973     if (object->glyph_size < 256)
974         object->texture_size = min(256, object->texture_size * 16);
975 
976     object->glyphs_per_texture = object->texture_size * object->texture_size
977             / (object->glyph_size * object->glyph_size);
978     object->texture_pos = object->glyphs_per_texture;
979 
980     IDirect3DDevice9_AddRef(device);
981     *font = &object->ID3DXFont_iface;
982 
983     return D3D_OK;
984 }
985