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