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.
5 #include "ui/gfx/canvas.h"
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"
21 #include "ui/gfx/canvas_skia_paint.h"
26 Canvas::Canvas(const gfx::Size
& size
,
27 ui::ScaleFactor scale_factor
,
29 : scale_factor_(scale_factor
),
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(),
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.
41 owned_canvas_
->clear(SkColorSetARGB(0, 0, 0, 0));
44 SkScalar scale
= SkFloatToScalar(ui::GetScaleFactorScale(scale_factor
));
45 canvas_
->scale(scale
, scale
);
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(),
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);
61 : scale_factor_(ui::SCALE_FACTOR_100P
),
62 owned_canvas_(skia::AdoptRef(skia::CreatePlatformCanvas(0, 0, false))),
63 canvas_(owned_canvas_
.get()) {
70 Canvas
* Canvas::CreateCanvasWithoutScaling(SkCanvas
* canvas
,
71 ui::ScaleFactor scale_factor
) {
72 return new Canvas(canvas
, scale_factor
);
75 void Canvas::RecreateBackingCanvas(const gfx::Size
& size
,
76 ui::ScaleFactor scale_factor
,
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(),
84 canvas_
= owned_canvas_
.get();
85 SkScalar scale
= SkFloatToScalar(ui::GetScaleFactorScale(scale_factor_
));
86 canvas_
->scale(scale
, scale
);
90 int Canvas::GetStringWidth(const string16
& text
, const gfx::Font
& font
) {
91 int width
= 0, height
= 0;
92 Canvas::SizeStringInt(text
, font
, &width
, &height
, 0, NO_ELLIPSIS
);
97 int Canvas::DefaultCanvasTextAlignment() {
98 return base::i18n::IsRTL() ? TEXT_ALIGN_RIGHT
: TEXT_ALIGN_LEFT
;
101 gfx::ImageSkiaRep
Canvas::ExtractImageRep() const {
102 const SkBitmap
& device_bitmap
= canvas_
->getDevice()->accessBitmap(false);
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
108 device_bitmap
.copyTo(&result
, SkBitmap::kARGB_8888_Config
);
110 return gfx::ImageSkiaRep(result
, scale_factor_
);
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
) {
127 dots
->setConfig(SkBitmap::kARGB_8888_Config
, col_pixels
, row_pixels
);
129 dots
->eraseARGB(0, 0, 0, 0);
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
;
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
150 paint
.setShader(shader
.get());
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),
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()),
160 void Canvas::Save() {
164 void Canvas::SaveLayerAlpha(uint8 alpha
) {
165 canvas_
->saveLayerAlpha(NULL
, alpha
);
169 void Canvas::SaveLayerAlpha(uint8 alpha
, const gfx::Rect
& layer_bounds
) {
170 SkRect
bounds(gfx::RectToSkRect(layer_bounds
));
171 canvas_
->saveLayerAlpha(&bounds
, alpha
);
174 void Canvas::Restore() {
178 bool Canvas::ClipRect(const gfx::Rect
& rect
) {
179 return canvas_
->clipRect(gfx::RectToSkRect(rect
));
182 bool Canvas::ClipPath(const SkPath
& path
) {
183 return canvas_
->clipPath(path
);
186 bool Canvas::GetClipBounds(gfx::Rect
* bounds
) {
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
;
193 void Canvas::Translate(const gfx::Vector2d
& offset
) {
194 canvas_
->translate(SkIntToScalar(offset
.x()), SkIntToScalar(offset
.y()));
197 void Canvas::Scale(int x_scale
, int y_scale
) {
198 canvas_
->scale(SkIntToScalar(x_scale
), SkIntToScalar(y_scale
));
201 void Canvas::DrawColor(SkColor color
) {
202 DrawColor(color
, SkXfermode::kSrcOver_Mode
);
205 void Canvas::DrawColor(SkColor color
, SkXfermode::Mode mode
) {
206 canvas_
->drawColor(color
, mode
);
209 void Canvas::FillRect(const gfx::Rect
& rect
, SkColor color
) {
210 FillRect(rect
, color
, SkXfermode::kSrcOver_Mode
);
213 void Canvas::FillRect(const gfx::Rect
& rect
,
215 SkXfermode::Mode mode
) {
217 paint
.setColor(color
);
218 paint
.setStyle(SkPaint::kFill_Style
);
219 paint
.setXfermodeMode(mode
);
220 DrawRect(rect
, paint
);
223 void Canvas::DrawRect(const gfx::Rect
& rect
, SkColor color
) {
224 DrawRect(rect
, color
, SkXfermode::kSrcOver_Mode
);
227 void Canvas::DrawRect(const gfx::Rect
& rect
,
229 SkXfermode::Mode mode
) {
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
);
239 DrawRect(rect
, paint
);
242 void Canvas::DrawRect(const gfx::Rect
& rect
, const SkPaint
& paint
) {
243 canvas_
->drawIRect(RectToSkIRect(rect
), paint
);
246 void Canvas::DrawPoint(const gfx::Point
& p1
, const SkPaint
& paint
) {
247 canvas_
->drawPoint(SkIntToScalar(p1
.x()), SkIntToScalar(p1
.y()), paint
);
250 void Canvas::DrawLine(const gfx::Point
& p1
,
251 const gfx::Point
& p2
,
254 paint
.setColor(color
);
255 paint
.setStrokeWidth(SkIntToScalar(1));
256 DrawLine(p1
, p2
, paint
);
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
);
266 void Canvas::DrawCircle(const gfx::Point
& center_point
,
268 const SkPaint
& paint
) {
269 canvas_
->drawCircle(SkIntToScalar(center_point
.x()),
270 SkIntToScalar(center_point
.y()), SkIntToScalar(radius
), paint
);
273 void Canvas::DrawRoundRect(const gfx::Rect
& rect
,
275 const SkPaint
& paint
) {
276 canvas_
->drawRoundRect(RectToSkRect(rect
), SkIntToScalar(radius
),
277 SkIntToScalar(radius
), paint
);
280 void Canvas::DrawPath(const SkPath
& path
, const SkPaint
& paint
) {
281 canvas_
->drawPath(path
, paint
);
284 void Canvas::DrawFocusRect(const gfx::Rect
& rect
) {
285 DrawDashedRect(rect
, SK_ColorGRAY
);
288 void Canvas::DrawImageInt(const gfx::ImageSkia
& image
, int x
, int y
) {
290 DrawImageInt(image
, x
, y
, paint
);
293 void Canvas::DrawImageInt(const gfx::ImageSkia
& image
, int x
, int y
, uint8 a
) {
296 DrawImageInt(image
, x
, y
, paint
);
299 void Canvas::DrawImageInt(const gfx::ImageSkia
& image
,
301 const SkPaint
& paint
) {
302 const gfx::ImageSkiaRep
& image_rep
= GetImageRepToPaint(image
);
303 if (image_rep
.is_null())
305 const SkBitmap
& bitmap
= image_rep
.sk_bitmap();
306 float bitmap_scale
= image_rep
.GetScale();
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
),
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
,
323 DrawImageInt(image
, src_x
, src_y
, src_w
, src_h
, dest_x
, dest_y
,
324 dest_w
, dest_h
, filter
, p
);
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
,
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!";
339 if (!IntersectsClipRectInt(dest_x
, dest_y
, dest_w
, dest_h
))
342 float user_scale_x
= static_cast<float>(dest_w
) / src_w
;
343 float user_scale_y
= static_cast<float>(dest_h
) / src_h
;
345 const gfx::ImageSkiaRep
& image_rep
= GetImageRepToPaint(image
,
346 user_scale_x
, user_scale_y
);
347 if (image_rep
.is_null())
350 SkRect dest_rect
= { SkIntToScalar(dest_x
),
351 SkIntToScalar(dest_y
),
352 SkIntToScalar(dest_x
+ dest_w
),
353 SkIntToScalar(dest_y
+ dest_h
) };
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
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
);
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
));
376 skia::RefPtr
<SkShader
> shader
= gfx::CreateImageRepShader(
378 SkShader::kRepeat_TileMode
,
381 // Set up our paint to use the shader & release our reference (now just owned
384 p
.setFilterBitmap(filter
);
385 p
.setShader(shader
.get());
387 // The rect will be filled by the bitmap.
388 canvas_
->drawRect(dest_rect
, p
);
391 void Canvas::DrawImageInPath(const gfx::ImageSkia
& image
,
395 const SkPaint
& paint
) {
396 const gfx::ImageSkiaRep
& image_rep
= GetImageRepToPaint(image
);
397 if (image_rep
.is_null())
401 matrix
.setTranslate(SkIntToScalar(x
), SkIntToScalar(y
));
402 skia::RefPtr
<SkShader
> shader
= gfx::CreateImageRepShader(
404 SkShader::kRepeat_TileMode
,
408 p
.setShader(shader
.get());
409 canvas_
->drawPath(path
, p
);
412 void Canvas::DrawStringInt(const string16
& text
,
413 const gfx::Font
& font
,
415 int x
, int y
, int w
, int h
) {
416 DrawStringInt(text
, font
, color
, x
, y
, w
, h
, DefaultCanvasTextAlignment());
419 void Canvas::DrawStringInt(const string16
& text
,
420 const gfx::Font
& font
,
422 const gfx::Rect
& display_rect
) {
423 DrawStringInt(text
, font
, color
, display_rect
.x(), display_rect
.y(),
424 display_rect
.width(), display_rect
.height());
427 void Canvas::DrawStringInt(const string16
& text
,
428 const gfx::Font
& font
,
430 int x
, int y
, int w
, int h
,
432 DrawStringWithShadows(text
,
435 gfx::Rect(x
, y
, w
, h
),
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
);
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
);
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
))
459 const gfx::ImageSkiaRep
& image_rep
= GetImageRepToPaint(image
,
460 tile_scale_x
, tile_scale_y
);
461 if (image_rep
.is_null())
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
));
470 skia::RefPtr
<SkShader
> shader
= gfx::CreateImageRepShader(
472 SkShader::kRepeat_TileMode
,
476 paint
.setShader(shader
.get());
477 paint
.setXfermodeMode(SkXfermode::kSrcOver_Mode
);
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
);
486 gfx::NativeDrawingContext
Canvas::BeginPlatformPaint() {
487 return skia::BeginPlatformPaint(canvas_
);
490 void Canvas::EndPlatformPaint() {
491 skia::EndPlatformPaint(canvas_
);
494 void Canvas::Transform(const gfx::Transform
& transform
) {
495 canvas_
->concat(transform
.matrix());
498 Canvas::Canvas(SkCanvas
* canvas
, ui::ScaleFactor scale_factor
)
499 : scale_factor_(scale_factor
),
505 bool Canvas::IntersectsClipRectInt(int x
, int y
, int w
, int h
) {
507 return canvas_
->getClipBounds(&clip
) &&
508 clip
.intersect(SkIntToScalar(x
), SkIntToScalar(y
), SkIntToScalar(x
+ w
),
509 SkIntToScalar(y
+ h
));
512 bool Canvas::IntersectsClipRect(const gfx::Rect
& rect
) {
513 return IntersectsClipRectInt(rect
.x(), rect
.y(),
514 rect
.width(), rect
.height());
517 const gfx::ImageSkiaRep
& Canvas::GetImageRepToPaint(
518 const gfx::ImageSkia
& image
) const {
519 return GetImageRepToPaint(image
, 1.0f
, 1.0f
);
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_
);
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
;
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();