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