1 // Copyright (c) 2012 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 "ui/gfx/canvas.h" 6 7 #include <cmath> 8 #include <limits> 9 10 #include "base/i18n/rtl.h" 11 #include "base/logging.h" 12 #include "third_party/skia/include/core/SkBitmap.h" 13 #include "third_party/skia/include/effects/SkGradientShader.h" 14 #include "ui/gfx/font_list.h" 15 #include "ui/gfx/geometry/rect_conversions.h" 16 #include "ui/gfx/rect.h" 17 #include "ui/gfx/size_conversions.h" 18 #include "ui/gfx/skia_util.h" 19 #include "ui/gfx/transform.h" 20 21 #if defined(OS_WIN) 22 #include "ui/gfx/canvas_skia_paint.h" 23 #endif 24 25 namespace gfx { 26 27 Canvas::Canvas(const Size& size, float image_scale, bool is_opaque) 28 : image_scale_(image_scale), 29 canvas_(NULL) { 30 Size pixel_size = ToCeiledSize(ScaleSize(size, image_scale)); 31 owned_canvas_ = skia::AdoptRef(skia::CreatePlatformCanvas(pixel_size.width(), 32 pixel_size.height(), 33 is_opaque)); 34 canvas_ = owned_canvas_.get(); 35 #if defined(OS_WIN) || defined(OS_MACOSX) 36 // skia::PlatformCanvas instances are initialized to 0 by Cairo on Linux, but 37 // uninitialized on Win and Mac. 38 if (!is_opaque) 39 owned_canvas_->clear(SkColorSetARGB(0, 0, 0, 0)); 40 #endif 41 42 SkScalar scale_scalar = SkFloatToScalar(image_scale); 43 canvas_->scale(scale_scalar, scale_scalar); 44 } 45 46 Canvas::Canvas(const ImageSkiaRep& image_rep, bool is_opaque) 47 : image_scale_(image_rep.scale()), 48 owned_canvas_(skia::AdoptRef( 49 skia::CreatePlatformCanvas(image_rep.pixel_width(), 50 image_rep.pixel_height(), 51 is_opaque))), 52 canvas_(owned_canvas_.get()) { 53 SkScalar scale_scalar = SkFloatToScalar(image_scale_); 54 canvas_->scale(scale_scalar, scale_scalar); 55 DrawImageInt(ImageSkia(image_rep), 0, 0); 56 } 57 58 Canvas::Canvas() 59 : image_scale_(1.0), 60 owned_canvas_(skia::AdoptRef(skia::CreatePlatformCanvas(0, 0, false))), 61 canvas_(owned_canvas_.get()) { 62 } 63 64 Canvas::~Canvas() { 65 } 66 67 // static 68 Canvas* Canvas::CreateCanvasWithoutScaling(SkCanvas* canvas, 69 float image_scale) { 70 return new Canvas(canvas, image_scale); 71 } 72 73 void Canvas::RecreateBackingCanvas(const Size& size, 74 float image_scale, 75 bool is_opaque) { 76 image_scale_ = image_scale; 77 Size pixel_size = ToFlooredSize(ScaleSize(size, image_scale)); 78 owned_canvas_ = skia::AdoptRef(skia::CreatePlatformCanvas(pixel_size.width(), 79 pixel_size.height(), 80 is_opaque)); 81 canvas_ = owned_canvas_.get(); 82 SkScalar scale_scalar = SkFloatToScalar(image_scale); 83 canvas_->scale(scale_scalar, scale_scalar); 84 } 85 86 // static 87 void Canvas::SizeStringInt(const base::string16& text, 88 const FontList& font_list, 89 int* width, 90 int* height, 91 int line_height, 92 int flags) { 93 float fractional_width = *width; 94 float factional_height = *height; 95 SizeStringFloat(text, font_list, &fractional_width, 96 &factional_height, line_height, flags); 97 *width = std::ceil(fractional_width); 98 *height = std::ceil(factional_height); 99 } 100 101 // static 102 int Canvas::GetStringWidth(const base::string16& text, 103 const FontList& font_list) { 104 int width = 0, height = 0; 105 SizeStringInt(text, font_list, &width, &height, 0, NO_ELLIPSIS); 106 return width; 107 } 108 109 // static 110 float Canvas::GetStringWidthF(const base::string16& text, 111 const FontList& font_list) { 112 float width = 0, height = 0; 113 SizeStringFloat(text, font_list, &width, &height, 0, NO_ELLIPSIS); 114 return width; 115 } 116 117 // static 118 int Canvas::DefaultCanvasTextAlignment() { 119 return base::i18n::IsRTL() ? TEXT_ALIGN_RIGHT : TEXT_ALIGN_LEFT; 120 } 121 122 ImageSkiaRep Canvas::ExtractImageRep() const { 123 // Make a bitmap to return, and a canvas to draw into it. We don't just want 124 // to call extractSubset or the copy constructor, since we want an actual copy 125 // of the bitmap. 126 const SkISize size = canvas_->getDeviceSize(); 127 SkBitmap result; 128 result.allocN32Pixels(size.width(), size.height()); 129 130 canvas_->readPixels(&result, 0, 0); 131 return ImageSkiaRep(result, image_scale_); 132 } 133 134 void Canvas::DrawDashedRect(const Rect& rect, SkColor color) { 135 if (rect.IsEmpty()) 136 return; 137 // Create a 2D bitmap containing alternating on/off pixels - we do this 138 // so that you never get two pixels of the same color around the edges 139 // of the focus rect (this may mean that opposing edges of the rect may 140 // have a dot pattern out of phase to each other). 141 static SkColor last_color; 142 static SkBitmap* dots = NULL; 143 if (!dots || last_color != color) { 144 int col_pixels = 32; 145 int row_pixels = 32; 146 147 delete dots; 148 last_color = color; 149 dots = new SkBitmap; 150 dots->allocN32Pixels(col_pixels, row_pixels); 151 dots->eraseARGB(0, 0, 0, 0); 152 153 uint32_t* dot = dots->getAddr32(0, 0); 154 for (int i = 0; i < row_pixels; i++) { 155 for (int u = 0; u < col_pixels; u++) { 156 if ((u % 2 + i % 2) % 2 != 0) { 157 dot[i * row_pixels + u] = color; 158 } 159 } 160 } 161 } 162 163 // Make a shader for the bitmap with an origin of the box we'll draw. This 164 // shader is refcounted and will have an initial refcount of 1. 165 skia::RefPtr<SkShader> shader = skia::AdoptRef( 166 SkShader::CreateBitmapShader( 167 *dots, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); 168 // Assign the shader to the paint & release our reference. The paint will 169 // now own the shader and the shader will be destroyed when the paint goes 170 // out of scope. 171 SkPaint paint; 172 paint.setShader(shader.get()); 173 174 DrawRect(Rect(rect.x(), rect.y(), rect.width(), 1), paint); 175 DrawRect(Rect(rect.x(), rect.y() + rect.height() - 1, rect.width(), 1), 176 paint); 177 DrawRect(Rect(rect.x(), rect.y(), 1, rect.height()), paint); 178 DrawRect(Rect(rect.x() + rect.width() - 1, rect.y(), 1, rect.height()), 179 paint); 180 } 181 182 void Canvas::Save() { 183 canvas_->save(); 184 } 185 186 void Canvas::SaveLayerAlpha(uint8 alpha) { 187 canvas_->saveLayerAlpha(NULL, alpha); 188 } 189 190 void Canvas::SaveLayerAlpha(uint8 alpha, const Rect& layer_bounds) { 191 SkRect bounds(RectToSkRect(layer_bounds)); 192 canvas_->saveLayerAlpha(&bounds, alpha); 193 } 194 195 void Canvas::Restore() { 196 canvas_->restore(); 197 } 198 199 void Canvas::ClipRect(const Rect& rect) { 200 canvas_->clipRect(RectToSkRect(rect)); 201 } 202 203 void Canvas::ClipPath(const SkPath& path, bool do_anti_alias) { 204 canvas_->clipPath(path, SkRegion::kIntersect_Op, do_anti_alias); 205 } 206 207 bool Canvas::IsClipEmpty() const { 208 return canvas_->isClipEmpty(); 209 } 210 211 bool Canvas::GetClipBounds(Rect* bounds) { 212 SkRect out; 213 if (canvas_->getClipBounds(&out)) { 214 *bounds = ToEnclosingRect(SkRectToRectF(out)); 215 return true; 216 } 217 *bounds = gfx::Rect(); 218 return false; 219 } 220 221 void Canvas::Translate(const Vector2d& offset) { 222 canvas_->translate(SkIntToScalar(offset.x()), SkIntToScalar(offset.y())); 223 } 224 225 void Canvas::Scale(int x_scale, int y_scale) { 226 canvas_->scale(SkIntToScalar(x_scale), SkIntToScalar(y_scale)); 227 } 228 229 void Canvas::DrawColor(SkColor color) { 230 DrawColor(color, SkXfermode::kSrcOver_Mode); 231 } 232 233 void Canvas::DrawColor(SkColor color, SkXfermode::Mode mode) { 234 canvas_->drawColor(color, mode); 235 } 236 237 void Canvas::FillRect(const Rect& rect, SkColor color) { 238 FillRect(rect, color, SkXfermode::kSrcOver_Mode); 239 } 240 241 void Canvas::FillRect(const Rect& rect, 242 SkColor color, 243 SkXfermode::Mode mode) { 244 SkPaint paint; 245 paint.setColor(color); 246 paint.setStyle(SkPaint::kFill_Style); 247 paint.setXfermodeMode(mode); 248 DrawRect(rect, paint); 249 } 250 251 void Canvas::DrawRect(const Rect& rect, SkColor color) { 252 DrawRect(rect, color, SkXfermode::kSrcOver_Mode); 253 } 254 255 void Canvas::DrawRect(const Rect& rect, 256 SkColor color, 257 SkXfermode::Mode mode) { 258 SkPaint paint; 259 paint.setColor(color); 260 paint.setStyle(SkPaint::kStroke_Style); 261 // Set a stroke width of 0, which will put us down the stroke rect path. If 262 // we set a stroke width of 1, for example, this will internally create a 263 // path and fill it, which causes problems near the edge of the canvas. 264 paint.setStrokeWidth(SkIntToScalar(0)); 265 paint.setXfermodeMode(mode); 266 267 DrawRect(rect, paint); 268 } 269 270 void Canvas::DrawRect(const Rect& rect, const SkPaint& paint) { 271 canvas_->drawIRect(RectToSkIRect(rect), paint); 272 } 273 274 void Canvas::DrawPoint(const Point& p1, const SkPaint& paint) { 275 canvas_->drawPoint(SkIntToScalar(p1.x()), SkIntToScalar(p1.y()), paint); 276 } 277 278 void Canvas::DrawLine(const Point& p1, const Point& p2, SkColor color) { 279 SkPaint paint; 280 paint.setColor(color); 281 paint.setStrokeWidth(SkIntToScalar(1)); 282 DrawLine(p1, p2, paint); 283 } 284 285 void Canvas::DrawLine(const Point& p1, const Point& p2, const SkPaint& paint) { 286 canvas_->drawLine(SkIntToScalar(p1.x()), SkIntToScalar(p1.y()), 287 SkIntToScalar(p2.x()), SkIntToScalar(p2.y()), paint); 288 } 289 290 void Canvas::DrawCircle(const Point& center_point, 291 int radius, 292 const SkPaint& paint) { 293 canvas_->drawCircle(SkIntToScalar(center_point.x()), 294 SkIntToScalar(center_point.y()), SkIntToScalar(radius), paint); 295 } 296 297 void Canvas::DrawRoundRect(const Rect& rect, 298 int radius, 299 const SkPaint& paint) { 300 canvas_->drawRoundRect(RectToSkRect(rect), SkIntToScalar(radius), 301 SkIntToScalar(radius), paint); 302 } 303 304 void Canvas::DrawPath(const SkPath& path, const SkPaint& paint) { 305 canvas_->drawPath(path, paint); 306 } 307 308 void Canvas::DrawFocusRect(const Rect& rect) { 309 DrawDashedRect(rect, SK_ColorGRAY); 310 } 311 312 void Canvas::DrawSolidFocusRect(const Rect& rect, SkColor color) { 313 SkPaint paint; 314 paint.setColor(color); 315 paint.setStrokeWidth(SkIntToScalar(1)); 316 // Note: We cannot use DrawRect since it would create a path and fill it which 317 // would cause problems near the edge of the canvas. 318 int x1 = std::min(rect.x(), rect.right()); 319 int x2 = std::max(rect.x(), rect.right()); 320 int y1 = std::min(rect.y(), rect.bottom()); 321 int y2 = std::max(rect.y(), rect.bottom()); 322 DrawLine(Point(x1, y1), Point(x2, y1), paint); 323 DrawLine(Point(x1, y2), Point(x2, y2), paint); 324 DrawLine(Point(x1, y1), Point(x1, y2), paint); 325 DrawLine(Point(x2, y1), Point(x2, y2 + 1), paint); 326 } 327 328 void Canvas::DrawImageInt(const ImageSkia& image, int x, int y) { 329 SkPaint paint; 330 DrawImageInt(image, x, y, paint); 331 } 332 333 void Canvas::DrawImageInt(const ImageSkia& image, int x, int y, uint8 a) { 334 SkPaint paint; 335 paint.setAlpha(a); 336 DrawImageInt(image, x, y, paint); 337 } 338 339 void Canvas::DrawImageInt(const ImageSkia& image, 340 int x, 341 int y, 342 const SkPaint& paint) { 343 const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale_); 344 if (image_rep.is_null()) 345 return; 346 const SkBitmap& bitmap = image_rep.sk_bitmap(); 347 float bitmap_scale = image_rep.scale(); 348 349 canvas_->save(); 350 canvas_->scale(SkFloatToScalar(1.0f / bitmap_scale), 351 SkFloatToScalar(1.0f / bitmap_scale)); 352 canvas_->drawBitmap(bitmap, 353 SkFloatToScalar(x * bitmap_scale), 354 SkFloatToScalar(y * bitmap_scale), 355 &paint); 356 canvas_->restore(); 357 } 358 359 void Canvas::DrawImageInt(const ImageSkia& image, 360 int src_x, 361 int src_y, 362 int src_w, 363 int src_h, 364 int dest_x, 365 int dest_y, 366 int dest_w, 367 int dest_h, 368 bool filter) { 369 SkPaint p; 370 DrawImageInt(image, src_x, src_y, src_w, src_h, dest_x, dest_y, 371 dest_w, dest_h, filter, p); 372 } 373 374 void Canvas::DrawImageInt(const ImageSkia& image, 375 int src_x, 376 int src_y, 377 int src_w, 378 int src_h, 379 int dest_x, 380 int dest_y, 381 int dest_w, 382 int dest_h, 383 bool filter, 384 const SkPaint& paint) { 385 DrawImageIntHelper(image, src_x, src_y, src_w, src_h, dest_x, dest_y, dest_w, 386 dest_h, filter, paint, image_scale_, false); 387 } 388 389 void Canvas::DrawImageIntInPixel(const ImageSkia& image, 390 int src_x, 391 int src_y, 392 int src_w, 393 int src_h, 394 int dest_x, 395 int dest_y, 396 int dest_w, 397 int dest_h, 398 bool filter, 399 const SkPaint& paint) { 400 // All values passed into this function are in pixels, i.e. no scaling needs 401 // be done. 402 // Logic as below:- 403 // 1. Get the matrix transform from the canvas. 404 // 2. Set the scale in the matrix to 1.0 while honoring the direction of the 405 // the scale (x/y). Example RTL layouts. 406 // 3. Round off the X and Y translation components in the matrix. This is to 407 // reduce floating point errors during rect transformation. This is needed 408 // for fractional scale factors like 1.25/1.5, etc. 409 // 4. Save the current state of the canvas. 410 // 5. Set the modified matrix in the canvas. This ensures that no scaling 411 // will be done for draw operations on the canvas. 412 // 6. Draw the image. 413 // 7. Restore the state of the canvas and the SkCanvas matrix stack. 414 SkMatrix matrix = canvas_->getTotalMatrix(); 415 416 // Ensure that the direction of the x and y scales is preserved. This is 417 // important for RTL layouts. 418 matrix.getScaleX() > 0 ? matrix.setScaleX(1.0f) : matrix.setScaleX(-1.0f); 419 matrix.getScaleY() > 0 ? matrix.setScaleY(1.0f) : matrix.setScaleY(-1.0f); 420 421 matrix.setTranslateX(SkScalarRoundToInt(matrix.getTranslateX())); 422 matrix.setTranslateY(SkScalarRoundToInt(matrix.getTranslateY())); 423 424 Save(); 425 426 canvas_->setMatrix(matrix); 427 428 DrawImageIntHelper(image, 429 src_x, 430 src_y, 431 src_w, 432 src_h, 433 dest_x, 434 dest_y, 435 dest_w, 436 dest_h, 437 filter, 438 paint, 439 image_scale_, 440 true); 441 442 // Restore the state of the canvas. 443 Restore(); 444 } 445 446 void Canvas::DrawImageInPath(const ImageSkia& image, 447 int x, 448 int y, 449 const SkPath& path, 450 const SkPaint& paint) { 451 const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale_); 452 if (image_rep.is_null()) 453 return; 454 455 SkMatrix matrix; 456 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); 457 skia::RefPtr<SkShader> shader = CreateImageRepShader( 458 image_rep, 459 SkShader::kRepeat_TileMode, 460 matrix); 461 462 SkPaint p(paint); 463 p.setShader(shader.get()); 464 canvas_->drawPath(path, p); 465 } 466 467 void Canvas::DrawStringRect(const base::string16& text, 468 const FontList& font_list, 469 SkColor color, 470 const Rect& display_rect) { 471 DrawStringRectWithFlags(text, font_list, color, display_rect, 472 DefaultCanvasTextAlignment()); 473 } 474 475 void Canvas::DrawStringRectWithFlags(const base::string16& text, 476 const FontList& font_list, 477 SkColor color, 478 const Rect& display_rect, 479 int flags) { 480 DrawStringRectWithShadows(text, font_list, color, display_rect, 0, flags, 481 ShadowValues()); 482 } 483 484 void Canvas::TileImageInt(const ImageSkia& image, 485 int x, 486 int y, 487 int w, 488 int h) { 489 TileImageInt(image, 0, 0, x, y, w, h); 490 } 491 492 void Canvas::TileImageInt(const ImageSkia& image, 493 int src_x, 494 int src_y, 495 int dest_x, 496 int dest_y, 497 int w, 498 int h) { 499 TileImageInt(image, src_x, src_y, 1.0f, 1.0f, dest_x, dest_y, w, h); 500 } 501 502 void Canvas::TileImageInt(const ImageSkia& image, 503 int src_x, 504 int src_y, 505 float tile_scale_x, 506 float tile_scale_y, 507 int dest_x, 508 int dest_y, 509 int w, 510 int h) { 511 if (!IntersectsClipRectInt(dest_x, dest_y, w, h)) 512 return; 513 514 const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale_); 515 if (image_rep.is_null()) 516 return; 517 518 SkMatrix shader_scale; 519 shader_scale.setScale(SkFloatToScalar(tile_scale_x), 520 SkFloatToScalar(tile_scale_y)); 521 shader_scale.preTranslate(SkIntToScalar(-src_x), SkIntToScalar(-src_y)); 522 shader_scale.postTranslate(SkIntToScalar(dest_x), SkIntToScalar(dest_y)); 523 524 skia::RefPtr<SkShader> shader = CreateImageRepShader( 525 image_rep, 526 SkShader::kRepeat_TileMode, 527 shader_scale); 528 529 SkPaint paint; 530 paint.setShader(shader.get()); 531 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); 532 533 SkRect dest_rect = { SkIntToScalar(dest_x), 534 SkIntToScalar(dest_y), 535 SkIntToScalar(dest_x + w), 536 SkIntToScalar(dest_y + h) }; 537 canvas_->drawRect(dest_rect, paint); 538 } 539 540 NativeDrawingContext Canvas::BeginPlatformPaint() { 541 return skia::BeginPlatformPaint(canvas_); 542 } 543 544 void Canvas::EndPlatformPaint() { 545 skia::EndPlatformPaint(canvas_); 546 } 547 548 void Canvas::Transform(const gfx::Transform& transform) { 549 canvas_->concat(transform.matrix()); 550 } 551 552 Canvas::Canvas(SkCanvas* canvas, float image_scale) 553 : image_scale_(image_scale), 554 owned_canvas_(), 555 canvas_(canvas) { 556 DCHECK(canvas); 557 } 558 559 bool Canvas::IntersectsClipRectInt(int x, int y, int w, int h) { 560 SkRect clip; 561 return canvas_->getClipBounds(&clip) && 562 clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w), 563 SkIntToScalar(y + h)); 564 } 565 566 bool Canvas::IntersectsClipRect(const Rect& rect) { 567 return IntersectsClipRectInt(rect.x(), rect.y(), 568 rect.width(), rect.height()); 569 } 570 571 void Canvas::DrawImageIntHelper(const ImageSkia& image, 572 int src_x, 573 int src_y, 574 int src_w, 575 int src_h, 576 int dest_x, 577 int dest_y, 578 int dest_w, 579 int dest_h, 580 bool filter, 581 const SkPaint& paint, 582 float image_scale, 583 bool pixel) { 584 DLOG_ASSERT(src_x + src_w < std::numeric_limits<int16_t>::max() && 585 src_y + src_h < std::numeric_limits<int16_t>::max()); 586 if (src_w <= 0 || src_h <= 0) { 587 NOTREACHED() << "Attempting to draw bitmap from an empty rect!"; 588 return; 589 } 590 591 if (!IntersectsClipRectInt(dest_x, dest_y, dest_w, dest_h)) 592 return; 593 594 float user_scale_x = static_cast<float>(dest_w) / src_w; 595 float user_scale_y = static_cast<float>(dest_h) / src_h; 596 597 const ImageSkiaRep& image_rep = image.GetRepresentation(image_scale); 598 if (image_rep.is_null()) 599 return; 600 601 SkRect dest_rect = { SkIntToScalar(dest_x), 602 SkIntToScalar(dest_y), 603 SkIntToScalar(dest_x + dest_w), 604 SkIntToScalar(dest_y + dest_h) }; 605 606 if (src_w == dest_w && src_h == dest_h && 607 user_scale_x == 1.0f && user_scale_y == 1.0f && 608 image_rep.scale() == 1.0f && !pixel) { 609 // Workaround for apparent bug in Skia that causes image to occasionally 610 // shift. 611 SkIRect src_rect = { src_x, src_y, src_x + src_w, src_y + src_h }; 612 const SkBitmap& bitmap = image_rep.sk_bitmap(); 613 canvas_->drawBitmapRect(bitmap, &src_rect, dest_rect, &paint); 614 return; 615 } 616 617 // Make a bitmap shader that contains the bitmap we want to draw. This is 618 // basically what SkCanvas.drawBitmap does internally, but it gives us 619 // more control over quality and will use the mipmap in the source image if 620 // it has one, whereas drawBitmap won't. 621 SkMatrix shader_scale; 622 shader_scale.setScale(SkFloatToScalar(user_scale_x), 623 SkFloatToScalar(user_scale_y)); 624 shader_scale.preTranslate(SkIntToScalar(-src_x), SkIntToScalar(-src_y)); 625 shader_scale.postTranslate(SkIntToScalar(dest_x), SkIntToScalar(dest_y)); 626 627 skia::RefPtr<SkShader> shader = CreateImageRepShaderForScale( 628 image_rep, 629 SkShader::kRepeat_TileMode, 630 shader_scale, 631 pixel ? 1.0f : image_rep.scale()); 632 633 // Set up our paint to use the shader & release our reference (now just owned 634 // by the paint). 635 SkPaint p(paint); 636 p.setFilterLevel(filter ? SkPaint::kLow_FilterLevel 637 : SkPaint::kNone_FilterLevel); 638 p.setShader(shader.get()); 639 640 // The rect will be filled by the bitmap. 641 canvas_->drawRect(dest_rect, p); 642 } 643 644 } // namespace gfx 645