1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles, Brad Hughes
3 /*
4 * font_dwrite.cpp
5 *
6 */
7
8 #include "font_module.h"
9 #include "modules/osdmodule.h"
10 #include "modules/lib/osdlib.h"
11
12 #if defined(OSD_WINDOWS) || defined(OSD_UWP)
13
14 #include <windows.h>
15
16 #include <memory>
17
18 // Windows Imaging Components
19 #include <wincodec.h>
20
21 // Load InitGuid after WIC to work around missing format problem
22 #include <initguid.h>
23
24 // Direct2D
25 #include <d2d1_1.h>
26 #include <dwrite_1.h>
27
28 // WIC GUIDs
29 DEFINE_GUID(CLSID_WICImagingFactory, 0xcacaf262, 0x9370, 0x4615, 0xa1, 0x3b, 0x9f, 0x55, 0x39, 0xda, 0x4c, 0xa);
30 DEFINE_GUID(GUID_WICPixelFormat8bppAlpha, 0xe6cd0116, 0xeeba, 0x4161, 0xaa, 0x85, 0x27, 0xdd, 0x9f, 0xb3, 0xa8, 0x95);
31
32 #include <wrl/client.h>
33 #undef interface
34
35 #include "strconv.h"
36 #include "corestr.h"
37 #include "winutil.h"
38
39 using namespace Microsoft::WRL;
40
41 //-------------------------------------------------
42 // Some formulas and constants
43 //-------------------------------------------------
44
45 //#define DEFAULT_CELL_HEIGHT (200)
46 #define DEFAULT_EM_HEIGHT (166.5f)
47
48 // 1DIP = 1/96in vs 1pt = 1/72in (or 4 / 3)
49 static const float DIPS_PER_POINT = (4.0f / 3.0f);
50 static const float POINTS_PER_DIP = (3.0f / 4.0f);
51
52 //-------------------------------------------------
53 // Error handling macros
54 //-------------------------------------------------
55
56 // Macro to check for a failed HRESULT and if failed, return 0
57 #define HR_RET( CALL, ret ) do { \
58 result = CALL; \
59 if (FAILED(result)) { \
60 osd_printf_error(#CALL " failed with error 0x%X\n", (unsigned int)result); \
61 return ret; } \
62 } while (0)
63
64 #define HR_RETHR( CALL ) HR_RET(CALL, result)
65 #define HR_RET0( CALL ) HR_RET(CALL, 0)
66 #define HR_RET1( CALL ) HR_RET(CALL, 1)
67
68 // Debugging functions
69 #ifdef DWRITE_DEBUGGING
70
71 //-------------------------------------------------
72 // Save image to file
73 //-------------------------------------------------
74
SaveBitmap(IWICBitmap * bitmap,GUID pixelFormat,const WCHAR * filename)75 HRESULT SaveBitmap(IWICBitmap* bitmap, GUID pixelFormat, const WCHAR *filename)
76 {
77 HRESULT result = S_OK;
78 ComPtr<IWICStream> stream;
79 ComPtr<ID2D1Factory1> d2dfactory;
80 ComPtr<IDWriteFactory> dwriteFactory;
81 ComPtr<IWICImagingFactory> wicFactory;
82
83 OSD_DYNAMIC_API(dwrite, "dwrite.dll");
84 OSD_DYNAMIC_API(d2d1, "d2d1.dll");
85 OSD_DYNAMIC_API_FN(dwrite, HRESULT, WINAPI, DWriteCreateFactory, DWRITE_FACTORY_TYPE, REFIID, IUnknown **);
86 OSD_DYNAMIC_API_FN(d2d1, HRESULT, WINAPI, D2D1CreateFactory, D2D1_FACTORY_TYPE, REFIID, const D2D1_FACTORY_OPTIONS *, void **);
87
88 if (!OSD_DYNAMIC_API_TEST(D2D1CreateFactory) || !OSD_DYNAMIC_API_TEST(DWriteCreateFactory))
89 return ERROR_DLL_NOT_FOUND;
90
91 // Create a Direct2D factory
92 HR_RETHR(OSD_DYNAMIC_CALL(D2D1CreateFactory,
93 D2D1_FACTORY_TYPE_SINGLE_THREADED,
94 __uuidof(ID2D1Factory1),
95 nullptr,
96 reinterpret_cast<void**>(d2dfactory.GetAddressOf())));
97
98 // Initialize COM - ignore failure
99 CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
100
101 // Create a DirectWrite factory.
102 HR_RETHR(OSD_DYNAMIC_CALL(DWriteCreateFactory,
103 DWRITE_FACTORY_TYPE_SHARED,
104 __uuidof(IDWriteFactory),
105 reinterpret_cast<IUnknown **>(dwriteFactory.GetAddressOf())));
106
107 HR_RETHR(CoCreateInstance(
108 CLSID_WICImagingFactory,
109 nullptr,
110 CLSCTX_INPROC_SERVER,
111 __uuidof(IWICImagingFactory),
112 (void**)&wicFactory));
113
114 HR_RETHR(wicFactory->CreateStream(&stream));
115 HR_RETHR(stream->InitializeFromFilename(filename, GENERIC_WRITE));
116
117 ComPtr<IWICBitmapEncoder> encoder;
118 HR_RETHR(wicFactory->CreateEncoder(GUID_ContainerFormatBmp, nullptr, &encoder));
119 HR_RETHR(encoder->Initialize(stream.Get(), WICBitmapEncoderNoCache));
120
121 ComPtr<IWICBitmapFrameEncode> frameEncode;
122 HR_RETHR(encoder->CreateNewFrame(&frameEncode, nullptr));
123 HR_RETHR(frameEncode->Initialize(nullptr));
124
125 UINT width, height;
126 HR_RETHR(bitmap->GetSize(&width, &height));
127 HR_RETHR(frameEncode->SetSize(width, height));
128 HR_RETHR(frameEncode->SetPixelFormat(&pixelFormat));
129
130 HR_RETHR(frameEncode->WriteSource(bitmap, nullptr));
131
132 HR_RETHR(frameEncode->Commit());
133 HR_RETHR(encoder->Commit());
134
135 return S_OK;
136 }
137
SaveBitmap2(bitmap_argb32 & bitmap,const WCHAR * filename)138 HRESULT SaveBitmap2(bitmap_argb32 &bitmap, const WCHAR *filename)
139 {
140 HRESULT result;
141
142 // Convert the bitmap into a form we understand and save it
143 std::unique_ptr<uint32_t> pBitmap(new uint32_t[bitmap.width() * bitmap.height()]);
144 for (int y = 0; y < bitmap.height(); y++)
145 {
146 uint32_t* pRow = pBitmap.get() + (y * bitmap.width());
147 for (int x = 0; x < bitmap.width(); x++)
148 {
149 uint32_t pixel = bitmap.pix(y, x);
150 pRow[x] = (pixel == 0xFFFFFFFF) ? rgb_t(0xFF, 0x00, 0x00, 0x00) : rgb_t(0xFF, 0xFF, 0xFF, 0xFF);
151 }
152 }
153
154 ComPtr<IWICImagingFactory> wicFactory;
155 HR_RETHR(CoCreateInstance(
156 CLSID_WICImagingFactory,
157 nullptr,
158 CLSCTX_INPROC_SERVER,
159 __uuidof(IWICImagingFactory),
160 (void**)&wicFactory));
161
162 // Save bitmap
163 ComPtr<IWICBitmap> bmp2 = nullptr;
164 wicFactory->CreateBitmapFromMemory(
165 bitmap.width(),
166 bitmap.height(),
167 GUID_WICPixelFormat32bppRGBA,
168 bitmap.width() * sizeof(uint32_t),
169 bitmap.width() * bitmap.height() * sizeof(uint32_t),
170 (BYTE*)pBitmap.get(),
171 &bmp2);
172
173 SaveBitmap(bmp2.Get(), GUID_WICPixelFormat32bppRGBA, filename);
174
175 return S_OK;
176 }
177
178 #endif
179
180 //-------------------------------------------------
181 // FontDimension class - holds a font dimension
182 // and makes it availabile in different formats
183 //-------------------------------------------------
184
185 class FontDimension
186 {
187 private:
188 uint16_t m_designUnitsPerEm;
189 float m_emSizeInDip;
190 float m_designUnits;
191
192 public:
FontDimension(uint16_t designUnitsPerEm,float emSizeInDip,float designUnits)193 FontDimension(uint16_t designUnitsPerEm, float emSizeInDip, float designUnits)
194 {
195 m_designUnitsPerEm = designUnitsPerEm;
196 m_emSizeInDip = emSizeInDip;
197 m_designUnits = designUnits;
198 }
199
DesignUnitsPerEm() const200 uint16_t DesignUnitsPerEm() const
201 {
202 return m_designUnitsPerEm;
203 }
204
EmSizeInDip() const205 float EmSizeInDip() const
206 {
207 return m_emSizeInDip;
208 }
209
DesignUnits() const210 float DesignUnits() const
211 {
212 return m_designUnits;
213 }
214
Dips() const215 int Dips() const
216 {
217 return static_cast<int>(floor((m_designUnits * m_emSizeInDip) / m_designUnitsPerEm));
218 }
219
Points() const220 float Points() const
221 {
222 return Dips() * POINTS_PER_DIP;
223 }
224
operator -(const FontDimension & other) const225 FontDimension operator-(const FontDimension &other) const
226 {
227 if (m_designUnitsPerEm != other.m_designUnitsPerEm || m_emSizeInDip != other.m_emSizeInDip)
228 {
229 throw emu_fatalerror("Attempted subtraction of FontDimension with different scale.");
230 }
231
232 return FontDimension(m_designUnitsPerEm, m_emSizeInDip, m_designUnits - other.m_designUnits);
233 }
234
operator +(const FontDimension & other) const235 FontDimension operator+(const FontDimension &other) const
236 {
237 if (m_designUnitsPerEm != other.m_designUnitsPerEm || m_emSizeInDip != other.m_emSizeInDip)
238 {
239 throw emu_fatalerror("Attempted addition of FontDimension with different scale.");
240 }
241
242 return FontDimension(m_designUnitsPerEm, m_emSizeInDip, m_designUnits + other.m_designUnits);
243 }
244 };
245
246 //-------------------------------------------------
247 // FontABCWidths class - hold width-related
248 // font dimensions for a glyph
249 //-------------------------------------------------
250
251 class FontABCWidths
252 {
253 private:
254 FontDimension m_advanceWidth;
255 FontDimension m_a;
256 FontDimension m_c;
257
258 public:
FontABCWidths(const FontDimension & advanceWidth,const FontDimension & leftSideBearing,const FontDimension & rightsideBearing)259 FontABCWidths(const FontDimension &advanceWidth, const FontDimension &leftSideBearing, const FontDimension &rightsideBearing) :
260 m_advanceWidth(advanceWidth),
261 m_a(leftSideBearing),
262 m_c(rightsideBearing)
263 {
264 }
265
advanceWidth() const266 FontDimension advanceWidth() const { return m_advanceWidth; }
abcA() const267 FontDimension abcA() const { return m_a; }
268
269 // Relationship between advanceWidth and B is ADV = A + B + C so B = ADV - A - C
abcB() const270 FontDimension abcB() const { return advanceWidth() - abcA() - abcC(); }
abcC() const271 FontDimension abcC() const { return m_c; }
272 };
273
274 //-------------------------------------------------
275 // FontDimensionFactory class - simple way of
276 // creating font dimensions
277 //-------------------------------------------------
278
279 class FontDimensionFactory
280 {
281 private:
282 uint16_t m_designUnitsPerEm;
283 float m_emSizeInDip;
284
285 public:
EmSizeInDip() const286 float EmSizeInDip() const
287 {
288 return m_emSizeInDip;
289 }
290
FontDimensionFactory(uint16_t designUnitsPerEm,float emSizeInDip)291 FontDimensionFactory(uint16_t designUnitsPerEm, float emSizeInDip)
292 {
293 m_designUnitsPerEm = designUnitsPerEm;
294 m_emSizeInDip = emSizeInDip;
295 }
296
FromDip(float dip) const297 FontDimension FromDip(float dip) const
298 {
299 float sizeInDesignUnits = (dip / m_emSizeInDip) * m_designUnitsPerEm;
300 return FontDimension(m_designUnitsPerEm, m_emSizeInDip, sizeInDesignUnits);
301 }
302
FromDesignUnit(float designUnits) const303 FontDimension FromDesignUnit(float designUnits) const
304 {
305 return FontDimension(m_designUnitsPerEm, m_emSizeInDip, designUnits);
306 }
307
FromPoint(float pointSize) const308 FontDimension FromPoint(float pointSize) const
309 {
310 float sizeInDip = pointSize * (4.0f / 3.0f);
311 float sizeInDesignUnits = (sizeInDip / m_emSizeInDip) * m_designUnitsPerEm;
312 return FontDimension(m_designUnitsPerEm, m_emSizeInDip, sizeInDesignUnits);
313 }
314
CreateAbcWidths(float advanceWidth,float leftSideBearing,float rightSideBearing) const315 FontABCWidths CreateAbcWidths(float advanceWidth, float leftSideBearing, float rightSideBearing) const
316 {
317 return FontABCWidths(
318 FromDesignUnit(advanceWidth),
319 FromDesignUnit(leftSideBearing),
320 FromDesignUnit(rightSideBearing));
321 }
322 };
323
324 //-------------------------------------------------
325 // font_open - attempt to "open" a handle to the
326 // font with the given name
327 //-------------------------------------------------
328
329 class osd_font_dwrite : public osd_font
330 {
331 private:
332 ComPtr<ID2D1Factory> m_d2dfactory;
333 ComPtr<IDWriteFactory> m_dwriteFactory;
334 ComPtr<IWICImagingFactory> m_wicFactory;
335 ComPtr<IDWriteFont> m_font;
336 float m_fontEmHeightInDips;
337
338 public:
osd_font_dwrite(ComPtr<ID2D1Factory> d2dfactory,ComPtr<IDWriteFactory> dwriteFactory,ComPtr<IWICImagingFactory> wicFactory)339 osd_font_dwrite(ComPtr<ID2D1Factory> d2dfactory, ComPtr<IDWriteFactory> dwriteFactory, ComPtr<IWICImagingFactory> wicFactory)
340 : m_d2dfactory(d2dfactory), m_dwriteFactory(dwriteFactory), m_wicFactory(wicFactory), m_fontEmHeightInDips(0)
341 {
342 }
343
~osd_font_dwrite()344 virtual ~osd_font_dwrite() { osd_font_dwrite::close(); }
345
open(std::string const & font_path,std::string const & _name,int & height)346 virtual bool open(std::string const &font_path, std::string const &_name, int &height) override
347 {
348 if (m_d2dfactory == nullptr || m_dwriteFactory == nullptr || m_wicFactory == nullptr)
349 return false;
350
351 HRESULT result;
352
353 // accept qualifiers from the name
354 std::string name(_name);
355 if (name.compare("default") == 0)
356 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
357 name = "Tahoma";
358 #else
359 name = "Segoe UI";
360 #endif
361 bool bold = (strreplace(name, "[B]", "") + strreplace(name, "[b]", "") > 0);
362 bool italic = (strreplace(name, "[I]", "") + strreplace(name, "[i]", "") > 0);
363
364 // convert the face name
365 std::wstring familyName = osd::text::to_wstring(name.c_str());
366
367 // find the font
368 HR_RET0(find_font(
369 familyName,
370 bold ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL,
371 DWRITE_FONT_STRETCH_NORMAL,
372 italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL,
373 m_font.GetAddressOf()));
374
375 DWRITE_FONT_METRICS metrics;
376 m_font->GetMetrics(&metrics);
377
378 m_fontEmHeightInDips = DEFAULT_EM_HEIGHT;
379 height = static_cast<int>(round(m_fontEmHeightInDips * DIPS_PER_POINT));
380
381 return true;
382 }
383
384 //-------------------------------------------------
385 // font_close - release resources associated with
386 // a given OSD font
387 //-------------------------------------------------
388
close()389 virtual void close() override
390 {
391 m_font = nullptr;
392 m_fontEmHeightInDips = 0;
393 }
394
395 //-------------------------------------------------
396 // font_get_bitmap - allocate and populate a
397 // BITMAP_FORMAT_ARGB32 bitmap containing the
398 // pixel values rgb_t(0xff,0xff,0xff,0xff)
399 // or rgb_t(0x00,0xff,0xff,0xff) for each
400 // pixel of a black & white font
401 //-------------------------------------------------
402
get_bitmap(char32_t chnum,bitmap_argb32 & bitmap,std::int32_t & width,std::int32_t & xoffs,std::int32_t & yoffs)403 virtual bool get_bitmap(char32_t chnum, bitmap_argb32 &bitmap, std::int32_t &width, std::int32_t &xoffs, std::int32_t &yoffs) override
404 {
405 const int MEM_ALIGN_CONST = 31;
406 const int BITMAP_PAD = 50;
407
408 HRESULT result;
409 UINT cbData;
410 BYTE* pixels = nullptr;
411
412 ComPtr<ID2D1BitmapRenderTarget> target;
413 ComPtr<ID2D1SolidColorBrush> pWhiteBrush;
414 ComPtr<IWICBitmap> wicBitmap;
415 ComPtr<IWICBitmapLock> lock;
416
417 ComPtr<IDWriteFontFace> face;
418 HR_RET0(m_font->CreateFontFace(face.GetAddressOf()));
419
420 // get the GDI metrics
421 DWRITE_FONT_METRICS gdi_metrics;
422 HR_RET0(face->GetGdiCompatibleMetrics(
423 m_fontEmHeightInDips,
424 1.0f,
425 nullptr,
426 &gdi_metrics));
427
428 FontDimensionFactory fdf(gdi_metrics.designUnitsPerEm, m_fontEmHeightInDips);
429
430 uint32_t tempChar = chnum;
431 uint16_t glyphIndex;
432 HR_RET0(face->GetGlyphIndices(&tempChar, 1, &glyphIndex));
433
434 // get the width of this character
435 DWRITE_GLYPH_METRICS glyph_metrics = { 0 };
436 HR_RET0(face->GetGdiCompatibleGlyphMetrics(
437 m_fontEmHeightInDips,
438 1.0f,
439 nullptr,
440 FALSE,
441 &glyphIndex,
442 1,
443 &glyph_metrics));
444
445 // The height is the ascent added to the descent
446 // By definition, the Em is equal to Cell Height minus Internal Leading (topSide bearing).
447 //auto cellheight = fdf.FromDesignUnit(gdi_metrics.ascent + gdi_metrics.descent + gdi_metrics.);
448 auto ascent = fdf.FromDesignUnit(gdi_metrics.ascent);
449 auto descent = fdf.FromDesignUnit(gdi_metrics.descent);
450 auto charHeight = ascent + descent;
451
452 auto abc = fdf.CreateAbcWidths(
453 glyph_metrics.advanceWidth,
454 glyph_metrics.leftSideBearing,
455 glyph_metrics.rightSideBearing);
456
457 width = abc.abcA().Dips() + abc.abcB().Dips() + abc.abcC().Dips();
458
459 // determine desired bitmap size
460 int bmwidth = (BITMAP_PAD + abc.abcA().Dips() + abc.abcB().Dips() + abc.abcC().Dips() + BITMAP_PAD + MEM_ALIGN_CONST) & ~MEM_ALIGN_CONST;
461 int bmheight = BITMAP_PAD + charHeight.Dips() + BITMAP_PAD;
462
463 // GUID_WICPixelFormat8bppAlpha is 8 bits per pixel
464 const REFWICPixelFormatGUID source_bitmap_wic_format = GUID_WICPixelFormat8bppAlpha;
465 const DXGI_FORMAT source_bitmap_dxgi_format = DXGI_FORMAT_A8_UNORM;
466 const D2D1_ALPHA_MODE source_bitmap_d2d_alpha_mode = D2D1_ALPHA_MODE_STRAIGHT;
467
468 // describe the bitmap we want
469 HR_RET0(m_wicFactory->CreateBitmap(
470 bmwidth,
471 bmheight,
472 source_bitmap_wic_format,
473 WICBitmapCacheOnLoad,
474 wicBitmap.GetAddressOf()));
475
476 D2D1_RENDER_TARGET_PROPERTIES targetProps;
477 targetProps = D2D1::RenderTargetProperties();
478 targetProps.pixelFormat = D2D1::PixelFormat(source_bitmap_dxgi_format, source_bitmap_d2d_alpha_mode);
479
480 // create a DIB to render to
481 HR_RET0(this->m_d2dfactory->CreateWicBitmapRenderTarget(
482 wicBitmap.Get(),
483 &targetProps,
484 reinterpret_cast<ID2D1RenderTarget**>(target.GetAddressOf())));
485
486 target->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
487
488 // Create our brush
489 HR_RET0(target->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), pWhiteBrush.GetAddressOf()));
490
491 // Signal the start of the frame
492 target->BeginDraw();
493
494 // clear the bitmap
495 // In the alpha mask, it will look like 0x00 per pixel
496 target->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f));
497
498 // now draw the character
499 DWRITE_GLYPH_RUN run = { nullptr };
500 DWRITE_GLYPH_OFFSET offsets;
501 offsets.advanceOffset = 0;
502 offsets.ascenderOffset = 0;
503 float advanceWidth = abc.advanceWidth().Dips();
504
505 run.fontEmSize = m_fontEmHeightInDips;
506 run.fontFace = face.Get();
507 run.glyphCount = 1;
508 run.glyphIndices = &glyphIndex;
509 run.glyphAdvances = &advanceWidth;
510 run.glyphOffsets = &offsets;
511
512 auto baseline_origin = D2D1::Point2F(BITMAP_PAD + abc.abcA().Dips() + 1, BITMAP_PAD + ascent.Dips());
513 target->DrawGlyphRun(
514 baseline_origin,
515 &run,
516 pWhiteBrush.Get(),
517 DWRITE_MEASURING_MODE_GDI_CLASSIC);
518
519 HR_RET0(target->EndDraw());
520
521 #ifdef DWRITE_DEBUGGING
522 // Save to file for debugging
523 SaveBitmap(wicBitmap.Get(), GUID_WICPixelFormatBlackWhite, L"C:\\temp\\ddraw_step1.bmp");
524 #endif
525
526 // characters are expected to be full-height
527 rectangle actbounds;
528 actbounds.min_y = BITMAP_PAD;
529 actbounds.max_y = BITMAP_PAD + charHeight.Dips() - 1;
530
531 // Lock the bitmap and get the data pointer
532 WICRect rect = { 0, 0, bmwidth, bmheight };
533 HR_RET0(wicBitmap->Lock(&rect, WICBitmapLockRead, lock.GetAddressOf()));
534 HR_RET0(lock->GetDataPointer(&cbData, static_cast<BYTE**>(&pixels)));
535
536 // determine the actual left of the character
537 for (actbounds.min_x = 0; actbounds.min_x < bmwidth; actbounds.min_x++)
538 {
539 BYTE *offs = pixels + actbounds.min_x;
540 uint8_t summary = 0;
541 for (int y = 0; y < bmheight; y++)
542 summary |= offs[y * bmwidth];
543 if (summary != 0)
544 {
545 break;
546 }
547 }
548
549 // determine the actual right of the character
550 // Start from the right edge, and move in until we find a pixel
551 for (actbounds.max_x = bmwidth - 1; actbounds.max_x >= 0; actbounds.max_x--)
552 {
553 BYTE *offs = pixels + actbounds.max_x;
554 uint8_t summary = 0;
555
556 // Go through the entire column and build a summary
557 for (int y = 0; y < bmheight; y++)
558 summary |= offs[y * bmwidth];
559 if (summary != 0)
560 {
561 break;
562 }
563 }
564
565 // allocate a new bitmap
566 if (actbounds.max_x >= actbounds.min_x && actbounds.max_y >= actbounds.min_y)
567 {
568 bitmap.allocate(actbounds.max_x + 1 - actbounds.min_x, actbounds.max_y + 1 - actbounds.min_y);
569
570 // copy the bits into it
571 for (int y = 0; y < bitmap.height(); y++)
572 {
573 uint32_t *dstrow = &bitmap.pix(y);
574 uint8_t *srcrow = &pixels[(y + actbounds.min_y) * bmwidth];
575 for (int x = 0; x < bitmap.width(); x++)
576 {
577 int effx = x + actbounds.min_x;
578 dstrow[x] = rgb_t(srcrow[effx], 0xff, 0xff, 0xff);
579 }
580 }
581
582 // set the final offset values
583 xoffs = actbounds.min_x - (BITMAP_PAD + abc.abcA().Dips());
584 yoffs = actbounds.max_y - (BITMAP_PAD + ascent.Dips());
585
586 #ifdef DWRITE_DEBUGGING
587 SaveBitmap2(bitmap, L"C:\\temp\\dwrite_final.bmp");
588 #endif
589 }
590
591 BOOL success = bitmap.valid();
592
593 #ifdef DWRITE_DEBUGGING
594 osd_printf_debug(
595 "dwr: %s, c'%S' w%i x%i y%i asc%i dsc%i a%ib%ic%i\n",
596 success ? "Success" : "Error",
597 (WCHAR*)&chnum,
598 width,
599 xoffs,
600 yoffs,
601 ascent.Dips(),
602 descent.Dips(),
603 abc.abcA().Dips(),
604 abc.abcB().Dips(),
605 abc.abcC().Dips());
606 #endif
607 return success;
608 }
609
610 private:
611
612 //-------------------------------------------------
613 // find_font - finds a font, given attributes
614 //-------------------------------------------------
615
find_font(std::wstring familyName,DWRITE_FONT_WEIGHT weight,DWRITE_FONT_STRETCH stretch,DWRITE_FONT_STYLE style,IDWriteFont ** ppfont) const616 HRESULT find_font(std::wstring familyName, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STRETCH stretch, DWRITE_FONT_STYLE style, IDWriteFont ** ppfont) const
617 {
618 HRESULT result;
619
620 ComPtr<IDWriteFontCollection> fonts;
621 HR_RETHR(m_dwriteFactory->GetSystemFontCollection(fonts.GetAddressOf()));
622
623 UINT family_index; BOOL exists;
624 HR_RETHR(fonts->FindFamilyName(familyName.c_str(), &family_index, &exists));
625 if (!exists)
626 {
627 osd_printf_error("Font with family name %s does not exist.\n", osd::text::from_wstring(familyName));
628 return E_FAIL;
629 }
630
631 ComPtr<IDWriteFontFamily> family;
632 HR_RETHR(fonts->GetFontFamily(family_index, family.GetAddressOf()));
633
634 ComPtr<IDWriteFont> font;
635 HR_RETHR(family->GetFirstMatchingFont(weight, stretch, style, font.GetAddressOf()));
636
637 *ppfont = font.Detach();
638 return result;
639 }
640 };
641
642 //-------------------------------------------------
643 // font_dwrite - the DirectWrite font module
644 //-------------------------------------------------
645
646 class font_dwrite : public osd_module, public font_module
647 {
648 private:
649 OSD_DYNAMIC_API(dwrite, "dwrite.dll");
650 OSD_DYNAMIC_API(d2d1, "d2d1.dll");
651 OSD_DYNAMIC_API(locale, "kernel32.dll");
652 OSD_DYNAMIC_API_FN(dwrite, HRESULT, WINAPI, DWriteCreateFactory, DWRITE_FACTORY_TYPE, REFIID, IUnknown **);
653 OSD_DYNAMIC_API_FN(d2d1, HRESULT, WINAPI, D2D1CreateFactory, D2D1_FACTORY_TYPE, REFIID, const D2D1_FACTORY_OPTIONS *, void **);
654 OSD_DYNAMIC_API_FN(locale, int, WINAPI, GetUserDefaultLocaleName, LPWSTR, int);
655 ComPtr<ID2D1Factory> m_d2dfactory;
656 ComPtr<IDWriteFactory> m_dwriteFactory;
657 ComPtr<IWICImagingFactory> m_wicFactory;
658
659 public:
font_dwrite()660 font_dwrite() :
661 osd_module(OSD_FONT_PROVIDER, "dwrite"),
662 font_module(),
663 m_d2dfactory(nullptr),
664 m_dwriteFactory(nullptr),
665 m_wicFactory(nullptr)
666 {
667 }
668
probe()669 virtual bool probe() override
670 {
671 // This module is available if it can load the expected API Functions
672 if (!OSD_DYNAMIC_API_TEST(D2D1CreateFactory) || !OSD_DYNAMIC_API_TEST(DWriteCreateFactory))
673 return false;
674
675 return true;
676 }
677
init(const osd_options & options)678 virtual int init(const osd_options &options) override
679 {
680 HRESULT result;
681
682 osd_printf_verbose("FontProvider: Initializing DirectWrite\n");
683
684 // Make sure we can initialize our api functions
685 if (!OSD_DYNAMIC_API_TEST(D2D1CreateFactory) || !OSD_DYNAMIC_API_TEST(DWriteCreateFactory))
686 {
687 osd_printf_error("ERROR: FontProvider: Failed to load DirectWrite functions.\n");
688 return -1;
689 }
690
691 assert(OSD_DYNAMIC_API_TEST(GetUserDefaultLocaleName));
692
693 // Create a Direct2D factory.
694 HR_RET1(OSD_DYNAMIC_CALL(D2D1CreateFactory,
695 D2D1_FACTORY_TYPE_SINGLE_THREADED,
696 __uuidof(ID2D1Factory),
697 nullptr,
698 reinterpret_cast<void**>(this->m_d2dfactory.GetAddressOf())));
699
700 // Initialize COM
701 CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
702
703 // Create a DirectWrite factory.
704 HR_RET1(OSD_DYNAMIC_CALL(DWriteCreateFactory,
705 DWRITE_FACTORY_TYPE_SHARED,
706 __uuidof(IDWriteFactory),
707 reinterpret_cast<IUnknown **>(m_dwriteFactory.GetAddressOf())));
708
709 HR_RET1(CoCreateInstance(
710 CLSID_WICImagingFactory,
711 nullptr,
712 CLSCTX_INPROC_SERVER,
713 __uuidof(IWICImagingFactory),
714 static_cast<void**>(&m_wicFactory)));
715
716 osd_printf_verbose("FontProvider: DirectWrite initialized successfully.\n");
717 return 0;
718 }
719
font_alloc()720 virtual osd_font::ptr font_alloc() override
721 {
722 return std::make_unique<osd_font_dwrite>(m_d2dfactory, m_dwriteFactory, m_wicFactory);
723 }
724
get_font_families(std::string const & font_path,std::vector<std::pair<std::string,std::string>> & fontresult)725 virtual bool get_font_families(std::string const &font_path, std::vector<std::pair<std::string, std::string> > &fontresult) override
726 {
727 HRESULT result;
728 ComPtr<IDWriteFontFamily> family;
729 ComPtr<IDWriteLocalizedStrings> names;
730
731 // For now, we're just enumerating system fonts, if we want to support custom font
732 // collections, there's more work that neeeds to be done
733 ComPtr<IDWriteFontCollection> fonts;
734 HR_RET0(m_dwriteFactory->GetSystemFontCollection(fonts.GetAddressOf()));
735
736 int family_count = fonts->GetFontFamilyCount();
737 for (int i = 0; i < family_count; i++)
738 {
739 HR_RET0(fonts->GetFontFamily(i, family.ReleaseAndGetAddressOf()));
740
741 HR_RET0(family->GetFamilyNames(names.ReleaseAndGetAddressOf()));
742
743 std::unique_ptr<WCHAR[]> name = nullptr;
744 HR_RET0(get_localized_familyname(names, name));
745
746 std::string utf8_name = osd::text::from_wstring(name.get());
747 name.reset();
748
749 // Review: should the config name, be unlocalized?
750 // maybe the english name?
751 fontresult.emplace_back(make_pair(utf8_name, utf8_name));
752 }
753
754 std::stable_sort(fontresult.begin(), fontresult.end());
755 return true;
756 }
757
758 private:
get_family_for_locale(ComPtr<IDWriteLocalizedStrings> family_names,const std::wstring & locale,std::unique_ptr<WCHAR[]> & family_name) const759 HRESULT get_family_for_locale(ComPtr<IDWriteLocalizedStrings> family_names, const std::wstring &locale, std::unique_ptr<WCHAR[]> &family_name) const
760 {
761 HRESULT result;
762 uint32_t index;
763 BOOL exists = false;
764
765 result = family_names->FindLocaleName(locale.c_str(), &index, &exists);
766
767 // if the above find did not find a match, retry with US English
768 if (SUCCEEDED(result) && !exists)
769 family_names->FindLocaleName(L"en-us", &index, &exists);
770
771 // If the specified locale doesn't exist, select the first on the list.
772 if (!exists)
773 index = 0;
774
775 // Get the length and allocate our buffer
776 uint32_t name_length = 0;
777 HR_RETHR(family_names->GetStringLength(index, &name_length));
778 auto name_buffer = std::make_unique<WCHAR[]>(name_length + 1);
779
780 // Get the name
781 HR_RETHR(family_names->GetString(index, name_buffer.get(), name_length + 1));
782
783 family_name = std::move(name_buffer);
784 return S_OK;
785 }
786
get_localized_familyname(ComPtr<IDWriteLocalizedStrings> family_names,std::unique_ptr<WCHAR[]> & family_name)787 HRESULT get_localized_familyname(ComPtr<IDWriteLocalizedStrings> family_names, std::unique_ptr<WCHAR[]> &family_name)
788 {
789 std::wstring locale_name;
790
791 // Get the default locale for this user if possible.
792 // GetUserDefaultLocaleName doesn't exist on XP, so don't assume.
793 if (OSD_DYNAMIC_API_TEST(GetUserDefaultLocaleName))
794 {
795 wchar_t name_buffer[LOCALE_NAME_MAX_LENGTH];
796 int len = OSD_DYNAMIC_CALL(GetUserDefaultLocaleName, name_buffer, LOCALE_NAME_MAX_LENGTH);
797 if (len != 0)
798 locale_name = name_buffer;
799 }
800
801 // If the default locale is returned, find that locale name
802 if (!locale_name.empty())
803 return get_family_for_locale(family_names, locale_name, family_name);
804
805 // If locale can't be determined, fall back to US English
806 return get_family_for_locale(family_names, L"en-us", family_name);
807 }
808 };
809
810 #else
811 MODULE_NOT_SUPPORTED(font_dwrite, OSD_FONT_PROVIDER, "dwrite")
812 #endif
813
814 MODULE_DEFINITION(FONT_DWRITE, font_dwrite)
815