Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(219)

Side by Side Diff: content/common/font_warmup_win.cc

Issue 1327673002: Added GDI font emulation support for Flash. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@warmup_direct_write
Patch Set: Changed comment and used common check Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/common/font_warmup_win.h" 5 #include "content/common/font_warmup_win.h"
6 6
7 #include <dwrite.h> 7 #include <dwrite.h>
8 #include <list>
8 9
9 #include "base/debug/alias.h" 10 #include "base/debug/alias.h"
11 #include "base/files/file_path.h"
10 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/numerics/safe_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/synchronization/lock.h"
17 #include "base/sys_byteorder.h"
11 #include "base/win/iat_patch_function.h" 18 #include "base/win/iat_patch_function.h"
12 #include "base/win/windows_version.h" 19 #include "base/win/windows_version.h"
13 #include "content/public/common/dwrite_font_platform_win.h" 20 #include "content/public/common/dwrite_font_platform_win.h"
21 #include "skia/ext/refptr.h"
14 #include "third_party/WebKit/public/web/win/WebFontRendering.h" 22 #include "third_party/WebKit/public/web/win/WebFontRendering.h"
15 #include "third_party/skia/include/core/SkPaint.h" 23 #include "third_party/skia/include/core/SkPaint.h"
16 #include "third_party/skia/include/ports/SkFontMgr.h" 24 #include "third_party/skia/include/ports/SkFontMgr.h"
17 #include "third_party/skia/include/ports/SkTypeface_win.h" 25 #include "third_party/skia/include/ports/SkTypeface_win.h"
18 26
19 namespace content { 27 namespace content {
20 28
21 namespace { 29 namespace {
22 30
23 SkFontMgr* g_warmup_fontmgr = nullptr; 31 SkFontMgr* g_warmup_fontmgr = nullptr;
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 183
176 void PatchDWriteFactory(IDWriteFactory* factory) { 184 void PatchDWriteFactory(IDWriteFactory* factory) {
177 const unsigned int kGetSystemFontCollectionVTableIndex = 3; 185 const unsigned int kGetSystemFontCollectionVTableIndex = 3;
178 186
179 PROC* vtable = *reinterpret_cast<PROC**>(factory); 187 PROC* vtable = *reinterpret_cast<PROC**>(factory);
180 PROC* function_ptr = &vtable[kGetSystemFontCollectionVTableIndex]; 188 PROC* function_ptr = &vtable[kGetSystemFontCollectionVTableIndex];
181 void* stub_function = &StubFontCollection; 189 void* stub_function = &StubFontCollection;
182 base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC)); 190 base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC));
183 } 191 }
184 192
193 // Magic values for the fake GDI objects.
194 const uint32_t kFakeDCMagic = 'fkdc';
195 const uint32_t kFakeFontMagic = 'fkft';
196
197 // Class to fake out a DC or a Font object. Maintains a reference to a
198 // SkTypeFace to emulate the simple operation of a DC and Font. We can't
199 // be sure that this won't be used in a multi-threaded environment so we
200 // need to ensure a lock is taken before updating the static table of
201 // issued objects.
202 class FakeGdiObject : public base::RefCountedThreadSafe<FakeGdiObject> {
203 public:
204 // Find a corresponding fake GDI object and verify its magic value.
205 // The returned object is either nullptr or the new object.
206 static scoped_refptr<FakeGdiObject> Validate(void* obj, uint32_t magic) {
207 if (obj) {
208 base::AutoLock scoped_lock(objects_lock_);
209 uintptr_t handle = reinterpret_cast<uintptr_t>(obj);
210 for (const auto& currobj : objects_) {
211 if ((currobj->magic_ == magic) && (handle == currobj->handle_))
212 return currobj;
213 }
214 }
215
216 return nullptr;
217 }
218
219 static scoped_refptr<FakeGdiObject> Create(uint32_t magic) {
220 base::AutoLock scoped_lock(objects_lock_);
221 scoped_refptr<FakeGdiObject> object(new FakeGdiObject(magic));
222 objects_.push_back(object);
223 return object;
224 }
225
226 static bool DeleteObject(void* obj, uint32_t magic) {
227 uintptr_t handle = reinterpret_cast<uintptr_t>(obj);
228 base::AutoLock scoped_lock(objects_lock_);
229 for (auto i = objects_.begin(); i != objects_.end(); ++i) {
230 const auto& fakeobj = *i;
231 if ((fakeobj->magic_ == magic) && (fakeobj->handle_ == handle)) {
232 objects_.erase(i);
233 return true;
234 }
235 }
236 return false;
237 }
238
239 void SetTypeface(const skia::RefPtr<SkTypeface>& typeface) {
240 typeface_ = typeface;
241 }
242
243 skia::RefPtr<SkTypeface> GetTypeface() { return typeface_; }
244
245 void* GetHandle() { return reinterpret_cast<void*>(handle_); }
246
247 private:
248 uint32_t magic_;
scottmg 2015/09/17 23:24:24 it would pack better on x64 to have magic_ lower d
forshaw 2015/09/18 00:00:28 I could move it, as this class (presumably) has a
249 uintptr_t handle_;
250 skia::RefPtr<SkTypeface> typeface_;
251 static uintptr_t curr_handle_;
scottmg 2015/09/17 23:24:25 separate statics from members
forshaw 2015/09/18 00:00:28 Acknowledged.
252 static std::list<scoped_refptr<FakeGdiObject>> objects_;
scottmg 2015/09/17 23:24:24 is a map from handle->FakeGdiObject not sensible f
forshaw 2015/09/18 00:00:28 If I can just use map directly then I guess it sho
253 static base::Lock objects_lock_;
254 FakeGdiObject(uint32_t magic) : magic_(magic), handle_(++curr_handle_) {}
255 ~FakeGdiObject() {}
256 friend class base::RefCountedThreadSafe<FakeGdiObject>;
scottmg 2015/09/17 23:24:24 move friend to first after private:
forshaw 2015/09/18 00:00:28 Acknowledged.
257
258 DISALLOW_COPY_AND_ASSIGN(FakeGdiObject);
259 };
260
261 uintptr_t FakeGdiObject::curr_handle_ = 0;
262 std::list<scoped_refptr<FakeGdiObject>> FakeGdiObject::objects_;
263 base::Lock FakeGdiObject::objects_lock_;
264
265 skia::RefPtr<SkTypeface> GetTypefaceFromLOGFONT(const LOGFONTW* logfont) {
266 CHECK(g_warmup_fontmgr);
267 int weight = logfont->lfWeight;
268 if (weight == FW_DONTCARE)
269 weight = SkFontStyle::kNormal_Weight;
270
271 SkFontStyle style(weight, logfont->lfWidth,
272 logfont->lfItalic ? SkFontStyle::kItalic_Slant
273 : SkFontStyle::kUpright_Slant);
274
275 std::string family_name = base::WideToUTF8(logfont->lfFaceName);
276 return skia::AdoptRef(
277 g_warmup_fontmgr->matchFamilyStyle(family_name.c_str(), style));
278 }
279
280 HDC WINAPI CreateCompatibleDCPatch(HDC hdc) {
281 scoped_refptr<FakeGdiObject> ret = FakeGdiObject::Create(kFakeDCMagic);
282 return static_cast<HDC>(ret->GetHandle());
283 }
284
285 HFONT WINAPI CreateFontIndirectWPatch(const LOGFONTW* lplf) {
286 if (!lplf)
287 return nullptr;
288
289 skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(lplf);
290 if (!typeface)
291 return nullptr;
292
293 scoped_refptr<FakeGdiObject> ret = FakeGdiObject::Create(kFakeFontMagic);
294 ret->SetTypeface(typeface);
295
296 return static_cast<HFONT>(ret->GetHandle());
297 }
298
299 BOOL WINAPI DeleteDCPatch(HDC hdc) {
300 if (hdc)
301 return FakeGdiObject::DeleteObject(hdc, kFakeDCMagic);
302 return FALSE;
303 }
304
305 BOOL WINAPI DeleteObjectPatch(HGDIOBJ hObject) {
306 if (hObject)
307 return FakeGdiObject::DeleteObject(hObject, kFakeFontMagic);
308 return FALSE;
309 }
310
311 int WINAPI EnumFontFamiliesExWPatch(HDC hdc,
312 LPLOGFONTW lpLogfont,
scottmg 2015/09/17 23:24:24 i know they're copied from the declarations, but n
forshaw 2015/09/18 00:00:28 Acknowledged.
313 FONTENUMPROCW lpEnumFontFamExProc,
314 LPARAM lParam,
315 DWORD dwFlags) {
316 scoped_refptr<FakeGdiObject> dcobj =
317 FakeGdiObject::Validate(hdc, kFakeDCMagic);
318 if (!dcobj) {
319 LOG(ERROR) << "Invalid DC Object";
320 return 1;
321 }
322 if (!lpLogfont || !lpEnumFontFamExProc)
323 return 1;
324
325 skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(lpLogfont);
326 if (!typeface)
327 return 1;
328
329 ENUMLOGFONTEXDVW enum_log_font = {0};
330 enum_log_font.elfEnumLogfontEx.elfLogFont = *lpLogfont;
331 // TODO: Fill in the rest of the text metric structure. Not yet needed for
332 // Flash support but might be in the future.
333 NEWTEXTMETRICEXW text_metric = {0};
334 text_metric.ntmTm.ntmFlags = NTM_PS_OPENTYPE;
335
336 return lpEnumFontFamExProc(&enum_log_font.elfEnumLogfontEx.elfLogFont,
337 reinterpret_cast<TEXTMETRIC*>(&text_metric),
338 TRUETYPE_FONTTYPE, lParam);
339 }
340
341 DWORD WINAPI GetFontDataPatch(HDC hdc,
342 DWORD dwTable,
343 DWORD dwOffset,
344 LPVOID lpvBuffer,
345 DWORD cbData) {
346 scoped_refptr<FakeGdiObject> dcobj =
347 FakeGdiObject::Validate(hdc, kFakeDCMagic);
348 if (!dcobj) {
349 LOG(ERROR) << "Invalid DC Object";
350 return GDI_ERROR;
351 }
352
353 skia::RefPtr<SkTypeface> typeface = dcobj->GetTypeface();
354 if (!typeface)
355 return GDI_ERROR;
356
357 if (cbData > INT32_MAX)
358 return GDI_ERROR;
359
360 // |getTableData| handles lpvBuffer being nullptr. However if it is nullptr
361 // then set the size to INT32_MAX otherwise |getTableData| will return the
362 // minimum value between the table entry size and the size passed in. The
363 // common Windows idiom is to pass 0 as cbSize when passing nullptr, which
palmer 2015/09/17 21:12:52 Nit: Don't forget to use |...| to distinguish iden
forshaw 2015/09/18 00:00:28 Will do.
364 // would in this case result in |getTableData| returning 0 which isn't the
365 // correct answer for emulating GDI. The dwTable tag must also have its byte
366 // order swapped to counter the byte order swap which occurs in the method.
367 size_t length = typeface->getTableData(
368 base::ByteSwap(base::strict_cast<uint32>(dwTable)), dwOffset,
369 lpvBuffer ? cbData : INT32_MAX, lpvBuffer);
370 // We can't distinguish between an empty table and an error.
371 if (length == 0)
372 return GDI_ERROR;
373
374 return base::checked_cast<DWORD>(length);
375 }
376
377 HGDIOBJ WINAPI SelectObjectPatch(HDC hdc, HGDIOBJ hgdiobj) {
scottmg 2015/09/17 23:24:24 is it never possible for these patches to get call
forshaw 2015/09/18 00:00:28 These patches should only be enabled if the win32k
378 scoped_refptr<FakeGdiObject> dcobj =
379 FakeGdiObject::Validate(hdc, kFakeDCMagic);
380 if (!dcobj) {
381 LOG(ERROR) << "Invalid DC Object";
382 return nullptr;
383 }
384
385 scoped_refptr<FakeGdiObject> fontobj =
386 FakeGdiObject::Validate(hgdiobj, kFakeFontMagic);
387 if (!fontobj) {
388 LOG(ERROR) << "Invalid Font Object";
389 return nullptr;
390 }
391
392 // Construct a new fake font object to handle the old font if there's one.
393 scoped_refptr<FakeGdiObject> newfontobj;
394 skia::RefPtr<SkTypeface> oldfont = dcobj->GetTypeface();
395 if (oldfont) {
396 newfontobj = FakeGdiObject::Create(kFakeFontMagic);
397 newfontobj->SetTypeface(oldfont);
398 }
399 dcobj->SetTypeface(fontobj->GetTypeface());
400
401 if (newfontobj)
402 return static_cast<HGDIOBJ>(newfontobj->GetHandle());
403 return nullptr;
404 }
405
406 void DoSingleGdiPatch(base::win::IATPatchFunction& patch,
407 const base::FilePath& path,
408 const char* function_name,
409 void* new_function) {
410 DWORD error = patch.Patch(path.value().c_str(), "gdi32.dll", function_name,
411 new_function);
412 if (error != 0)
413 LOG(WARNING) << "Failed patching " << function_name << " " << error;
414 }
415
416 class GdiFontPatchDataImpl : public content::GdiFontPatchData {
417 public:
418 GdiFontPatchDataImpl(const base::FilePath& path);
419
420 private:
421 base::win::IATPatchFunction create_compatible_dc_patch_;
422 base::win::IATPatchFunction create_font_indirect_patch_;
423 base::win::IATPatchFunction create_delete_dc_patch_;
424 base::win::IATPatchFunction create_delete_object_patch_;
425 base::win::IATPatchFunction create_enum_font_families_patch_;
426 base::win::IATPatchFunction create_get_font_data_patch_;
427 base::win::IATPatchFunction create_select_object_patch_;
428 };
429
430 GdiFontPatchDataImpl::GdiFontPatchDataImpl(const base::FilePath& path) {
431 DoSingleGdiPatch(create_compatible_dc_patch_, path, "CreateCompatibleDC",
432 CreateCompatibleDCPatch);
433 DoSingleGdiPatch(create_font_indirect_patch_, path, "CreateFontIndirectW",
434 CreateFontIndirectWPatch);
435 DoSingleGdiPatch(create_delete_dc_patch_, path, "DeleteDC", DeleteDCPatch);
436 DoSingleGdiPatch(create_delete_object_patch_, path, "DeleteObject",
437 DeleteObjectPatch);
438 DoSingleGdiPatch(create_enum_font_families_patch_, path,
439 "EnumFontFamiliesExW", EnumFontFamiliesExWPatch);
440 DoSingleGdiPatch(create_get_font_data_patch_, path, "GetFontData",
441 GetFontDataPatch);
442 DoSingleGdiPatch(create_select_object_patch_, path, "SelectObject",
443 SelectObjectPatch);
444 }
445
185 } // namespace 446 } // namespace
186 447
187 void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) { 448 void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) {
188 SkPaint paint_warmup; 449 SkPaint paint_warmup;
189 paint_warmup.setTypeface(typeface); 450 paint_warmup.setTypeface(typeface);
190 wchar_t glyph = L'S'; 451 wchar_t glyph = L'S';
191 paint_warmup.measureText(&glyph, 2); 452 paint_warmup.measureText(&glyph, 2);
192 } 453 }
193 454
194 SkFontMgr* GetPreSandboxWarmupFontMgr() { 455 SkFontMgr* GetPreSandboxWarmupFontMgr() {
195 if (!g_warmup_fontmgr) { 456 if (!g_warmup_fontmgr) {
196 IDWriteFactory* factory; 457 IDWriteFactory* factory;
197 CreateDirectWriteFactory(&factory); 458 CreateDirectWriteFactory(&factory);
198 459
199 GetCustomFontCollection(factory); 460 GetCustomFontCollection(factory);
200 461
201 PatchDWriteFactory(factory); 462 PatchDWriteFactory(factory);
202 463
203 blink::WebFontRendering::setDirectWriteFactory(factory); 464 blink::WebFontRendering::setDirectWriteFactory(factory);
204 g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory); 465 g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory);
205 } 466 }
206 return g_warmup_fontmgr; 467 return g_warmup_fontmgr;
207 } 468 }
208 469
470 GdiFontPatchData* PatchGdiFontEnumeration(const base::FilePath& path) {
471 // We assume the fontmgr is already warmed up before calling this.
472 DCHECK(g_warmup_fontmgr);
473 return new GdiFontPatchDataImpl(path);
474 }
475
476 void SetPreSandboxWarmupFontMgr(SkFontMgr* fontmgr) {
477 g_warmup_fontmgr = fontmgr;
478 }
479
209 } // namespace content 480 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698