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