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