OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "skia/ext/vector_platform_device_cairo_linux.h" | |
6 | |
7 #include <cairo.h> | |
8 #include <cairo-ft.h> | |
9 | |
10 #include <ft2build.h> | |
11 #include FT_FREETYPE_H | |
12 | |
13 #include <map> | |
14 | |
15 #include "base/lazy_instance.h" | |
16 #include "base/logging.h" | |
17 #include "skia/ext/bitmap_platform_device.h" | |
18 #include "third_party/skia/include/core/SkFontHost.h" | |
19 #include "third_party/skia/include/core/SkStream.h" | |
20 #include "third_party/skia/include/core/SkTypeface.h" | |
21 | |
22 namespace { | |
23 | |
24 struct FontInfo { | |
25 SkStream* font_stream; | |
26 FT_Face ft_face; | |
27 cairo_font_face_t* cairo_face; | |
28 cairo_user_data_key_t data_key; | |
29 }; | |
30 | |
31 typedef std::map<uint32_t, FontInfo> MapFontId2FontInfo; | |
32 static base::LazyInstance<MapFontId2FontInfo> g_map_font_id_to_font_info( | |
33 base::LINKER_INITIALIZED); | |
34 | |
35 // Wrapper for FT_Library that handles initialization and cleanup, and allows | |
36 // us to use a singleton. | |
37 class FtLibrary { | |
38 public: | |
39 FtLibrary() : library_(NULL) { | |
40 FT_Error ft_error = FT_Init_FreeType(&library_); | |
41 if (ft_error) { | |
42 DLOG(ERROR) << "Cannot initialize FreeType library for " \ | |
43 << "VectorPlatformDeviceCairo."; | |
44 } | |
45 } | |
46 | |
47 ~FtLibrary() { | |
48 if (library_) { | |
49 FT_Error ft_error = FT_Done_FreeType(library_); | |
50 library_ = NULL; | |
51 DCHECK_EQ(ft_error, 0); | |
52 } | |
53 } | |
54 | |
55 FT_Library library() { return library_; } | |
56 | |
57 private: | |
58 FT_Library library_; | |
59 }; | |
60 static base::LazyInstance<FtLibrary> g_ft_library(base::LINKER_INITIALIZED); | |
61 | |
62 // Verify cairo surface after creation/modification. | |
63 bool IsContextValid(cairo_t* context) { | |
64 return cairo_status(context) == CAIRO_STATUS_SUCCESS; | |
65 } | |
66 | |
67 } // namespace | |
68 | |
69 namespace skia { | |
70 | |
71 // static | |
72 SkDevice* VectorPlatformDeviceCairo::CreateDevice(cairo_t* context, int width, | |
73 int height, bool isOpaque) { | |
74 // TODO(myhuang): Here we might also have similar issues as those on Windows | |
75 // (vector_canvas_win.cc, http://crbug.com/18382 & http://crbug.com/18383). | |
76 // Please note that is_opaque is true when we use this class for printing. | |
77 // Fallback to bitmap when context is NULL. | |
78 if (!isOpaque || NULL == context) { | |
79 return BitmapPlatformDevice::Create(width, height, isOpaque); | |
80 } | |
81 | |
82 SkASSERT(cairo_status(context) == CAIRO_STATUS_SUCCESS); | |
83 SkASSERT(width > 0); | |
84 SkASSERT(height > 0); | |
85 | |
86 SkBitmap bitmap; | |
87 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); | |
88 | |
89 return new VectorPlatformDeviceCairo(context, bitmap); | |
90 } | |
91 | |
92 VectorPlatformDeviceCairo::VectorPlatformDeviceCairo(PlatformSurface context, | |
93 const SkBitmap& bitmap) | |
94 : SkDevice(bitmap), | |
95 context_(context) { | |
96 SkASSERT(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); | |
97 | |
98 SetPlatformDevice(this, this); | |
99 | |
100 // Increase the reference count to keep the context alive. | |
101 cairo_reference(context_); | |
102 | |
103 transform_.reset(); | |
104 } | |
105 | |
106 VectorPlatformDeviceCairo::~VectorPlatformDeviceCairo() { | |
107 // Un-ref |context_| since we referenced it in the constructor. | |
108 cairo_destroy(context_); | |
109 } | |
110 | |
111 PlatformSurface VectorPlatformDeviceCairo::BeginPlatformPaint() { | |
112 return context_; | |
113 } | |
114 | |
115 void VectorPlatformDeviceCairo::DrawToNativeContext( | |
116 PlatformSurface surface, int x, int y, const PlatformRect* src_rect) { | |
117 // Should never be called on Linux. | |
118 SkASSERT(false); | |
119 } | |
120 | |
121 SkDevice* VectorPlatformDeviceCairo::onCreateCompatibleDevice( | |
122 SkBitmap::Config config, | |
123 int width, int height, | |
124 bool isOpaque, Usage) { | |
125 SkASSERT(config == SkBitmap::kARGB_8888_Config); | |
126 return CreateDevice(NULL, width, height, isOpaque); | |
127 } | |
128 | |
129 uint32_t VectorPlatformDeviceCairo::getDeviceCapabilities() { | |
130 return SkDevice::getDeviceCapabilities() | kVector_Capability; | |
131 } | |
132 | |
133 void VectorPlatformDeviceCairo::drawBitmap(const SkDraw& draw, | |
134 const SkBitmap& bitmap, | |
135 const SkIRect* srcRectOrNull, | |
136 const SkMatrix& matrix, | |
137 const SkPaint& paint) { | |
138 SkASSERT(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); | |
139 | |
140 // Load the temporary matrix. This is what will translate, rotate and resize | |
141 // the bitmap. | |
142 SkMatrix actual_transform(transform_); | |
143 actual_transform.preConcat(matrix); | |
144 LoadTransformToContext(actual_transform); | |
145 | |
146 InternalDrawBitmap(bitmap, 0, 0, paint); | |
147 | |
148 // Restore the original matrix. | |
149 LoadTransformToContext(transform_); | |
150 } | |
151 | |
152 void VectorPlatformDeviceCairo::drawDevice(const SkDraw& draw, | |
153 SkDevice* device, | |
154 int x, | |
155 int y, | |
156 const SkPaint& paint) { | |
157 SkASSERT(device); | |
158 | |
159 // TODO(myhuang): We may also have to consider http://b/1183870 . | |
160 drawSprite(draw, device->accessBitmap(false), x, y, paint); | |
161 } | |
162 | |
163 void VectorPlatformDeviceCairo::drawPaint(const SkDraw& draw, | |
164 const SkPaint& paint) { | |
165 // Bypass the current transformation matrix. | |
166 LoadIdentityTransformToContext(); | |
167 | |
168 // TODO(myhuang): Is there a better way to do this? | |
169 SkRect rect; | |
170 rect.fLeft = 0; | |
171 rect.fTop = 0; | |
172 rect.fRight = SkIntToScalar(width() + 1); | |
173 rect.fBottom = SkIntToScalar(height() + 1); | |
174 drawRect(draw, rect, paint); | |
175 | |
176 // Restore the original matrix. | |
177 LoadTransformToContext(transform_); | |
178 } | |
179 | |
180 void VectorPlatformDeviceCairo::drawPath(const SkDraw& draw, | |
181 const SkPath& path, | |
182 const SkPaint& paint, | |
183 const SkMatrix* prePathMatrix, | |
184 bool pathIsMutable) { | |
185 if (paint.getPathEffect()) { | |
186 // Apply the path effect forehand. | |
187 SkPath path_modified; | |
188 paint.getFillPath(path, &path_modified); | |
189 | |
190 // Removes the path effect from the temporary SkPaint object. | |
191 SkPaint paint_no_effet(paint); | |
192 SkSafeUnref(paint_no_effet.setPathEffect(NULL)); | |
193 | |
194 // Draw the calculated path. | |
195 drawPath(draw, path_modified, paint_no_effet); | |
196 return; | |
197 } | |
198 | |
199 // Setup paint color. | |
200 ApplyPaintColor(paint); | |
201 | |
202 SkPaint::Style style = paint.getStyle(); | |
203 // Setup fill style. | |
204 if (style & SkPaint::kFill_Style) { | |
205 ApplyFillStyle(path); | |
206 } | |
207 | |
208 // Setup stroke style. | |
209 if (style & SkPaint::kStroke_Style) { | |
210 ApplyStrokeStyle(paint); | |
211 } | |
212 | |
213 // Iterate path verbs. | |
214 // TODO(myhuang): Is there a better way to do this? | |
215 SkPoint current_points[4]; | |
216 SkPath::Iter iter(path, false); | |
217 for (SkPath::Verb verb = iter.next(current_points); | |
218 verb != SkPath::kDone_Verb; | |
219 verb = iter.next(current_points)) { | |
220 switch (verb) { | |
221 case SkPath::kMove_Verb: { // iter.next returns 1 point | |
222 cairo_move_to(context_, current_points[0].fX, current_points[0].fY); | |
223 } break; | |
224 | |
225 case SkPath::kLine_Verb: { // iter.next returns 2 points | |
226 cairo_line_to(context_, current_points[1].fX, current_points[1].fY); | |
227 } break; | |
228 | |
229 case SkPath::kQuad_Verb: { // iter.next returns 3 points | |
230 // Degree elevation (quadratic to cubic). | |
231 // c1 = (2 * p1 + p0) / 3 | |
232 // c2 = (2 * p1 + p2) / 3 | |
233 current_points[1].scale(2.); // p1 *= 2.0; | |
234 SkScalar c1_X = (current_points[1].fX + current_points[0].fX) / 3.; | |
235 SkScalar c1_Y = (current_points[1].fY + current_points[0].fY) / 3.; | |
236 SkScalar c2_X = (current_points[1].fX + current_points[2].fX) / 3.; | |
237 SkScalar c2_Y = (current_points[1].fY + current_points[2].fY) / 3.; | |
238 cairo_curve_to(context_, | |
239 c1_X, c1_Y, | |
240 c2_X, c2_Y, | |
241 current_points[2].fX, current_points[2].fY); | |
242 } break; | |
243 | |
244 case SkPath::kCubic_Verb: { // iter.next returns 4 points | |
245 cairo_curve_to(context_, | |
246 current_points[1].fX, current_points[1].fY, | |
247 current_points[2].fX, current_points[2].fY, | |
248 current_points[3].fX, current_points[3].fY); | |
249 } break; | |
250 | |
251 case SkPath::kClose_Verb: { // iter.next returns 1 point (the last pt). | |
252 cairo_close_path(context_); | |
253 } break; | |
254 | |
255 default: { | |
256 // Should not reach here! | |
257 SkASSERT(false); | |
258 } break; | |
259 } | |
260 } | |
261 | |
262 DoPaintStyle(paint); | |
263 } | |
264 | |
265 void VectorPlatformDeviceCairo::drawPoints(const SkDraw& draw, | |
266 SkCanvas::PointMode mode, | |
267 size_t count, | |
268 const SkPoint pts[], | |
269 const SkPaint& paint) { | |
270 SkASSERT(pts); | |
271 | |
272 if (!count) | |
273 return; | |
274 | |
275 // Setup paint color. | |
276 ApplyPaintColor(paint); | |
277 | |
278 // Setup stroke style. | |
279 ApplyStrokeStyle(paint); | |
280 | |
281 switch (mode) { | |
282 case SkCanvas::kPoints_PointMode: { | |
283 // There is a bug in Cairo that it won't draw anything when using some | |
284 // specific caps, e.g. SkPaint::kSquare_Cap. This is because Cairo does | |
285 // not have enough/ambiguous direction information. One possible work- | |
286 // around is to draw a really short line. | |
287 for (size_t i = 0; i < count; ++i) { | |
288 double x = pts[i].fX; | |
289 double y = pts[i].fY; | |
290 cairo_move_to(context_, x, y); | |
291 cairo_line_to(context_, x+.01, y); | |
292 } | |
293 } break; | |
294 | |
295 case SkCanvas::kLines_PointMode: { | |
296 if (count % 2) { | |
297 SkASSERT(false); | |
298 return; | |
299 } | |
300 | |
301 for (size_t i = 0; i < count >> 1; ++i) { | |
302 double x1 = pts[i << 1].fX; | |
303 double y1 = pts[i << 1].fY; | |
304 double x2 = pts[(i << 1) + 1].fX; | |
305 double y2 = pts[(i << 1) + 1].fY; | |
306 cairo_move_to(context_, x1, y1); | |
307 cairo_line_to(context_, x2, y2); | |
308 } | |
309 } break; | |
310 | |
311 case SkCanvas::kPolygon_PointMode: { | |
312 double x = pts[0].fX; | |
313 double y = pts[0].fY; | |
314 cairo_move_to(context_, x, y); | |
315 for (size_t i = 1; i < count; ++i) { | |
316 x = pts[i].fX; | |
317 y = pts[i].fY; | |
318 cairo_line_to(context_, x, y); | |
319 } | |
320 } break; | |
321 | |
322 default: | |
323 SkASSERT(false); | |
324 return; | |
325 } | |
326 cairo_stroke(context_); | |
327 } | |
328 | |
329 // TODO(myhuang): Embed fonts/texts into PDF surface. | |
330 // Please NOTE that len records text's length in byte, not uint16_t. | |
331 void VectorPlatformDeviceCairo::drawPosText(const SkDraw& draw, | |
332 const void* text, | |
333 size_t len, | |
334 const SkScalar pos[], | |
335 SkScalar constY, | |
336 int scalarsPerPos, | |
337 const SkPaint& paint) { | |
338 SkASSERT(text); | |
339 SkASSERT(pos); | |
340 SkASSERT(paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding); | |
341 // Each pos should contain either only x, or (x, y). | |
342 SkASSERT((scalarsPerPos == 1) || (scalarsPerPos == 2)); | |
343 | |
344 if (!len) | |
345 return; | |
346 | |
347 // Text color. | |
348 ApplyPaintColor(paint); | |
349 | |
350 const uint16_t* glyph_ids = static_cast<const uint16_t*>(text); | |
351 | |
352 // The style is either kFill_Style or kStroke_Style. | |
353 if (paint.getStyle() & SkPaint::kStroke_Style) { | |
354 ApplyStrokeStyle(paint); | |
355 | |
356 // Draw each glyph by its path. | |
357 for (size_t i = 0; i < len / sizeof(uint16_t); ++i) { | |
358 uint16_t glyph_id = glyph_ids[i]; | |
359 SkPath textPath; | |
360 paint.getTextPath(&glyph_id, | |
361 sizeof(uint16_t), | |
362 pos[i * scalarsPerPos], | |
363 (scalarsPerPos == 1) ? | |
364 constY : | |
365 pos[i * scalarsPerPos + 1], | |
366 &textPath); | |
367 drawPath(draw, textPath, paint); | |
368 } | |
369 } else { // kFill_Style. | |
370 // Selects correct font. | |
371 if (!SelectFontById(paint.getTypeface()->uniqueID())) { | |
372 SkASSERT(false); | |
373 return; | |
374 } | |
375 cairo_set_font_size(context_, paint.getTextSize()); | |
376 | |
377 // Draw glyphs. | |
378 for (size_t i = 0; i < len / sizeof(uint16_t); ++i) { | |
379 uint16_t glyph_id = glyph_ids[i]; | |
380 | |
381 cairo_glyph_t glyph; | |
382 glyph.index = glyph_id; | |
383 glyph.x = pos[i * scalarsPerPos]; | |
384 glyph.y = (scalarsPerPos == 1) ? constY : pos[i * scalarsPerPos + 1]; | |
385 | |
386 cairo_show_glyphs(context_, &glyph, 1); | |
387 } | |
388 } | |
389 } | |
390 | |
391 void VectorPlatformDeviceCairo::drawRect(const SkDraw& draw, | |
392 const SkRect& rect, | |
393 const SkPaint& paint) { | |
394 if (paint.getPathEffect()) { | |
395 // Draw a path instead. | |
396 SkPath path_orginal; | |
397 path_orginal.addRect(rect); | |
398 | |
399 // Apply the path effect to the rect. | |
400 SkPath path_modified; | |
401 paint.getFillPath(path_orginal, &path_modified); | |
402 | |
403 // Removes the path effect from the temporary SkPaint object. | |
404 SkPaint paint_no_effet(paint); | |
405 SkSafeUnref(paint_no_effet.setPathEffect(NULL)); | |
406 | |
407 // Draw the calculated path. | |
408 drawPath(draw, path_modified, paint_no_effet); | |
409 return; | |
410 } | |
411 | |
412 // Setup color. | |
413 ApplyPaintColor(paint); | |
414 | |
415 // Setup stroke style. | |
416 ApplyStrokeStyle(paint); | |
417 | |
418 // Draw rectangle. | |
419 cairo_rectangle(context_, | |
420 rect.fLeft, rect.fTop, | |
421 rect.fRight - rect.fLeft, rect.fBottom - rect.fTop); | |
422 | |
423 DoPaintStyle(paint); | |
424 } | |
425 | |
426 void VectorPlatformDeviceCairo::drawSprite(const SkDraw& draw, | |
427 const SkBitmap& bitmap, | |
428 int x, int y, | |
429 const SkPaint& paint) { | |
430 SkASSERT(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); | |
431 | |
432 LoadIdentityTransformToContext(); | |
433 | |
434 InternalDrawBitmap(bitmap, x, y, paint); | |
435 | |
436 // Restore the original matrix. | |
437 LoadTransformToContext(transform_); | |
438 } | |
439 | |
440 void VectorPlatformDeviceCairo::drawText(const SkDraw& draw, | |
441 const void* text, | |
442 size_t byteLength, | |
443 SkScalar x, | |
444 SkScalar y, | |
445 const SkPaint& paint) { | |
446 // This function isn't used in the code. Verify this assumption. | |
447 SkASSERT(false); | |
448 } | |
449 | |
450 | |
451 void VectorPlatformDeviceCairo::drawTextOnPath(const SkDraw& draw, | |
452 const void* text, | |
453 size_t len, | |
454 const SkPath& path, | |
455 const SkMatrix* matrix, | |
456 const SkPaint& paint) { | |
457 // This function isn't used in the code. Verify this assumption. | |
458 SkASSERT(false); | |
459 } | |
460 | |
461 void VectorPlatformDeviceCairo::drawVertices(const SkDraw& draw, | |
462 SkCanvas::VertexMode vmode, | |
463 int vertexCount, | |
464 const SkPoint vertices[], | |
465 const SkPoint texs[], | |
466 const SkColor colors[], | |
467 SkXfermode* xmode, | |
468 const uint16_t indices[], | |
469 int indexCount, | |
470 const SkPaint& paint) { | |
471 // This function isn't used in the code. Verify this assumption. | |
472 SkASSERT(false); | |
473 } | |
474 | |
475 void VectorPlatformDeviceCairo::setMatrixClip(const SkMatrix& transform, | |
476 const SkRegion& region, | |
477 const SkClipStack&) { | |
478 clip_region_ = region; | |
479 if (!clip_region_.isEmpty()) | |
480 LoadClipRegion(clip_region_); | |
481 | |
482 transform_ = transform; | |
483 LoadTransformToContext(transform_); | |
484 } | |
485 | |
486 void VectorPlatformDeviceCairo::ApplyPaintColor(const SkPaint& paint) { | |
487 SkColor color = paint.getColor(); | |
488 double a = static_cast<double>(SkColorGetA(color)) / 255.; | |
489 double r = static_cast<double>(SkColorGetR(color)) / 255.; | |
490 double g = static_cast<double>(SkColorGetG(color)) / 255.; | |
491 double b = static_cast<double>(SkColorGetB(color)) / 255.; | |
492 | |
493 cairo_set_source_rgba(context_, r, g, b, a); | |
494 } | |
495 | |
496 void VectorPlatformDeviceCairo::ApplyFillStyle(const SkPath& path) { | |
497 // Setup fill style. | |
498 // TODO(myhuang): Cairo does NOT support all skia fill rules!! | |
499 cairo_set_fill_rule(context_, | |
500 static_cast<cairo_fill_rule_t>(path.getFillType())); | |
501 } | |
502 | |
503 void VectorPlatformDeviceCairo::ApplyStrokeStyle(const SkPaint& paint) { | |
504 // Line width. | |
505 cairo_set_line_width(context_, paint.getStrokeWidth()); | |
506 | |
507 // Line join. | |
508 cairo_set_line_join(context_, | |
509 static_cast<cairo_line_join_t>(paint.getStrokeJoin())); | |
510 | |
511 // Line cap. | |
512 cairo_set_line_cap(context_, | |
513 static_cast<cairo_line_cap_t>(paint.getStrokeCap())); | |
514 } | |
515 | |
516 void VectorPlatformDeviceCairo::DoPaintStyle(const SkPaint& paint) { | |
517 SkPaint::Style style = paint.getStyle(); | |
518 | |
519 switch (style) { | |
520 case SkPaint::kFill_Style: { | |
521 cairo_fill(context_); | |
522 } break; | |
523 | |
524 case SkPaint::kStroke_Style: { | |
525 cairo_stroke(context_); | |
526 } break; | |
527 | |
528 case SkPaint::kStrokeAndFill_Style: { | |
529 cairo_fill_preserve(context_); | |
530 cairo_stroke(context_); | |
531 } break; | |
532 | |
533 default: | |
534 SkASSERT(false); | |
535 } | |
536 } | |
537 | |
538 void VectorPlatformDeviceCairo::InternalDrawBitmap(const SkBitmap& bitmap, | |
539 int x, int y, | |
540 const SkPaint& paint) { | |
541 SkASSERT(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); | |
542 | |
543 unsigned char alpha = paint.getAlpha(); | |
544 | |
545 if (alpha == 0) | |
546 return; | |
547 | |
548 int src_size_x = bitmap.width(); | |
549 int src_size_y = bitmap.height(); | |
550 | |
551 if (!src_size_x || !src_size_y) | |
552 return; | |
553 | |
554 SkAutoLockPixels image_lock(bitmap); | |
555 | |
556 cairo_surface_t* bitmap_surface = | |
557 cairo_image_surface_create_for_data( | |
558 reinterpret_cast<unsigned char*>(bitmap.getPixels()), | |
559 CAIRO_FORMAT_ARGB32, src_size_x, src_size_y, bitmap.rowBytes()); | |
560 | |
561 cairo_set_source_surface(context_, bitmap_surface, x, y); | |
562 cairo_paint_with_alpha(context_, static_cast<double>(alpha) / 255.); | |
563 | |
564 cairo_surface_destroy(bitmap_surface); | |
565 } | |
566 | |
567 void VectorPlatformDeviceCairo::LoadClipRegion(const SkRegion& clip) { | |
568 cairo_reset_clip(context_); | |
569 | |
570 LoadIdentityTransformToContext(); | |
571 | |
572 // TODO(myhuang): Support non-rect clips. | |
573 SkIRect bounding = clip.getBounds(); | |
574 cairo_rectangle(context_, bounding.fLeft, bounding.fTop, | |
575 bounding.fRight - bounding.fLeft, | |
576 bounding.fBottom - bounding.fTop); | |
577 cairo_clip(context_); | |
578 | |
579 // Restore the original matrix. | |
580 LoadTransformToContext(transform_); | |
581 } | |
582 | |
583 void VectorPlatformDeviceCairo::LoadIdentityTransformToContext() { | |
584 SkMatrix identity; | |
585 identity.reset(); | |
586 LoadTransformToContext(identity); | |
587 } | |
588 | |
589 void VectorPlatformDeviceCairo::LoadTransformToContext(const SkMatrix& matrix) { | |
590 cairo_matrix_t m; | |
591 m.xx = matrix[SkMatrix::kMScaleX]; | |
592 m.xy = matrix[SkMatrix::kMSkewX]; | |
593 m.x0 = matrix[SkMatrix::kMTransX]; | |
594 m.yx = matrix[SkMatrix::kMSkewY]; | |
595 m.yy = matrix[SkMatrix::kMScaleY]; | |
596 m.y0 = matrix[SkMatrix::kMTransY]; | |
597 cairo_set_matrix(context_, &m); | |
598 } | |
599 | |
600 bool VectorPlatformDeviceCairo::SelectFontById(uint32_t font_id) { | |
601 DCHECK(IsContextValid(context_)); | |
602 DCHECK(SkFontHost::ValidFontID(font_id)); | |
603 | |
604 FtLibrary* ft_library = g_ft_library.Pointer(); | |
605 if (!ft_library->library()) | |
606 return false; | |
607 | |
608 // Checks if we have a cache hit. | |
609 MapFontId2FontInfo* g_font_cache = g_map_font_id_to_font_info.Pointer(); | |
610 DCHECK(g_font_cache); | |
611 | |
612 MapFontId2FontInfo::iterator it = g_font_cache->find(font_id); | |
613 if (it != g_font_cache->end()) { | |
614 cairo_set_font_face(context_, it->second.cairo_face); | |
615 if (IsContextValid(context_)) { | |
616 return true; | |
617 } else { | |
618 NOTREACHED() << "Cannot set font face in Cairo!"; | |
619 return false; | |
620 } | |
621 } | |
622 | |
623 // Cache missed. We need to load and create the font. | |
624 FontInfo new_font_info = {0}; | |
625 new_font_info.font_stream = SkFontHost::OpenStream(font_id); | |
626 DCHECK(new_font_info.font_stream); | |
627 size_t stream_size = new_font_info.font_stream->getLength(); | |
628 DCHECK(stream_size) << "The Font stream has nothing!"; | |
629 | |
630 FT_Error ft_error = FT_New_Memory_Face( | |
631 ft_library->library(), | |
632 static_cast<FT_Byte*>( | |
633 const_cast<void*>(new_font_info.font_stream->getMemoryBase())), | |
634 stream_size, | |
635 0, | |
636 &new_font_info.ft_face); | |
637 | |
638 if (ft_error) { | |
639 new_font_info.font_stream->unref(); | |
640 DLOG(ERROR) << "Cannot create FT_Face!"; | |
641 SkASSERT(false); | |
642 return false; | |
643 } | |
644 | |
645 new_font_info.cairo_face = cairo_ft_font_face_create_for_ft_face( | |
646 new_font_info.ft_face, 0); | |
647 DCHECK(new_font_info.cairo_face) << "Cannot create font in Cairo!"; | |
648 | |
649 // Manage |new_font_info.ft_face|'s life by Cairo. | |
650 cairo_status_t status = cairo_font_face_set_user_data( | |
651 new_font_info.cairo_face, | |
652 &new_font_info.data_key, | |
653 new_font_info.ft_face, | |
654 reinterpret_cast<cairo_destroy_func_t>(FT_Done_Face)); | |
655 | |
656 if (status != CAIRO_STATUS_SUCCESS) { | |
657 DLOG(ERROR) << "Cannot set font's user data in Cairo!"; | |
658 cairo_font_face_destroy(new_font_info.cairo_face); | |
659 FT_Done_Face(new_font_info.ft_face); | |
660 new_font_info.font_stream->unref(); | |
661 SkASSERT(false); | |
662 return false; | |
663 } | |
664 | |
665 // Inserts |new_font_info| info |g_font_cache|. | |
666 (*g_font_cache)[font_id] = new_font_info; | |
667 | |
668 cairo_set_font_face(context_, new_font_info.cairo_face); | |
669 if (IsContextValid(context_)) { | |
670 return true; | |
671 } | |
672 | |
673 DLOG(ERROR) << "Connot set font face in Cairo!"; | |
674 return false; | |
675 } | |
676 | |
677 // static | |
678 void VectorPlatformDeviceCairo::ClearFontCache() { | |
679 MapFontId2FontInfo* g_font_cache = g_map_font_id_to_font_info.Pointer(); | |
680 DCHECK(g_font_cache); | |
681 | |
682 for (MapFontId2FontInfo::iterator it = g_font_cache->begin(); | |
683 it !=g_font_cache->end(); | |
684 ++it) { | |
685 DCHECK(it->second.cairo_face); | |
686 DCHECK(it->second.font_stream); | |
687 | |
688 cairo_font_face_destroy(it->second.cairo_face); | |
689 // |it->second.ft_face| is handled by Cairo. | |
690 it->second.font_stream->unref(); | |
691 } | |
692 g_font_cache->clear(); | |
693 } | |
694 | |
695 } // namespace skia | |
OLD | NEW |