| 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 |