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"
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/geometry/safe_integer_conversions.h"
17 #include "ui/gfx/rect.h"
18 #include "ui/gfx/scoped_canvas.h"
19 #include "ui/gfx/size_conversions.h"
20 #include "ui/gfx/skia_util.h"
21 #include "ui/gfx/transform.h"
24 #include "ui/gfx/canvas_skia_paint.h"
29 Canvas::Canvas(const Size
& size
, float image_scale
, bool is_opaque
)
30 : image_scale_(image_scale
),
32 Size pixel_size
= ToCeiledSize(ScaleSize(size
, image_scale
));
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_scalar
= SkFloatToScalar(image_scale
);
45 canvas_
->scale(scale_scalar
, scale_scalar
);
48 Canvas::Canvas(const ImageSkiaRep
& image_rep
, bool is_opaque
)
49 : image_scale_(image_rep
.scale()),
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_scalar
= SkFloatToScalar(image_scale_
);
56 canvas_
->scale(scale_scalar
, scale_scalar
);
57 DrawImageInt(ImageSkia(image_rep
), 0, 0);
62 owned_canvas_(skia::AdoptRef(skia::CreatePlatformCanvas(0, 0, false))),
63 canvas_(owned_canvas_
.get()) {
70 Canvas
* Canvas::CreateCanvasWithoutScaling(SkCanvas
* canvas
,
72 return new Canvas(canvas
, image_scale
);
75 void Canvas::RecreateBackingCanvas(const Size
& size
,
78 image_scale_
= image_scale
;
79 Size pixel_size
= ToFlooredSize(ScaleSize(size
, image_scale
));
80 owned_canvas_
= skia::AdoptRef(skia::CreatePlatformCanvas(pixel_size
.width(),
83 canvas_
= owned_canvas_
.get();
84 SkScalar scale_scalar
= SkFloatToScalar(image_scale
);
85 canvas_
->scale(scale_scalar
, scale_scalar
);
89 void Canvas::SizeStringInt(const base::string16
& text
,
90 const FontList
& font_list
,
95 float fractional_width
= static_cast<float>(*width
);
96 float factional_height
= static_cast<float>(*height
);
97 SizeStringFloat(text
, font_list
, &fractional_width
,
98 &factional_height
, line_height
, flags
);
99 *width
= ToCeiledInt(fractional_width
);
100 *height
= ToCeiledInt(factional_height
);
104 int Canvas::GetStringWidth(const base::string16
& text
,
105 const FontList
& font_list
) {
106 int width
= 0, height
= 0;
107 SizeStringInt(text
, font_list
, &width
, &height
, 0, NO_ELLIPSIS
);
112 float Canvas::GetStringWidthF(const base::string16
& text
,
113 const FontList
& font_list
) {
114 float width
= 0, height
= 0;
115 SizeStringFloat(text
, font_list
, &width
, &height
, 0, NO_ELLIPSIS
);
120 int Canvas::DefaultCanvasTextAlignment() {
121 return base::i18n::IsRTL() ? TEXT_ALIGN_RIGHT
: TEXT_ALIGN_LEFT
;
124 ImageSkiaRep
Canvas::ExtractImageRep() const {
125 // Make a bitmap to return, and a canvas to draw into it. We don't just want
126 // to call extractSubset or the copy constructor, since we want an actual copy
128 const SkISize size
= canvas_
->getDeviceSize();
130 result
.allocN32Pixels(size
.width(), size
.height());
132 canvas_
->readPixels(&result
, 0, 0);
133 return ImageSkiaRep(result
, image_scale_
);
136 void Canvas::DrawDashedRect(const Rect
& rect
, SkColor color
) {
139 // Create a 2D bitmap containing alternating on/off pixels - we do this
140 // so that you never get two pixels of the same color around the edges
141 // of the focus rect (this may mean that opposing edges of the rect may
142 // have a dot pattern out of phase to each other).
143 static SkColor last_color
;
144 static SkBitmap
* dots
= NULL
;
145 if (!dots
|| last_color
!= color
) {
152 dots
->allocN32Pixels(col_pixels
, row_pixels
);
153 dots
->eraseARGB(0, 0, 0, 0);
155 uint32_t* dot
= dots
->getAddr32(0, 0);
156 for (int i
= 0; i
< row_pixels
; i
++) {
157 for (int u
= 0; u
< col_pixels
; u
++) {
158 if ((u
% 2 + i
% 2) % 2 != 0) {
159 dot
[i
* row_pixels
+ u
] = color
;
165 // Make a shader for the bitmap with an origin of the box we'll draw. This
166 // shader is refcounted and will have an initial refcount of 1.
167 skia::RefPtr
<SkShader
> shader
= skia::AdoptRef(
168 SkShader::CreateBitmapShader(
169 *dots
, SkShader::kRepeat_TileMode
, SkShader::kRepeat_TileMode
));
170 // Assign the shader to the paint & release our reference. The paint will
171 // now own the shader and the shader will be destroyed when the paint goes
174 paint
.setShader(shader
.get());
176 DrawRect(Rect(rect
.x(), rect
.y(), rect
.width(), 1), paint
);
177 DrawRect(Rect(rect
.x(), rect
.y() + rect
.height() - 1, rect
.width(), 1),
179 DrawRect(Rect(rect
.x(), rect
.y(), 1, rect
.height()), paint
);
180 DrawRect(Rect(rect
.x() + rect
.width() - 1, rect
.y(), 1, rect
.height()),
184 void Canvas::Save() {
188 void Canvas::SaveLayerAlpha(uint8 alpha
) {
189 canvas_
->saveLayerAlpha(NULL
, alpha
);
192 void Canvas::SaveLayerAlpha(uint8 alpha
, const Rect
& layer_bounds
) {
193 SkRect
bounds(RectToSkRect(layer_bounds
));
194 canvas_
->saveLayerAlpha(&bounds
, alpha
);
197 void Canvas::Restore() {
201 void Canvas::ClipRect(const Rect
& rect
) {
202 canvas_
->clipRect(RectToSkRect(rect
));
205 void Canvas::ClipPath(const SkPath
& path
, bool do_anti_alias
) {
206 canvas_
->clipPath(path
, SkRegion::kIntersect_Op
, do_anti_alias
);
209 bool Canvas::IsClipEmpty() const {
210 return canvas_
->isClipEmpty();
213 bool Canvas::GetClipBounds(Rect
* bounds
) {
215 if (canvas_
->getClipBounds(&out
)) {
216 *bounds
= ToEnclosingRect(SkRectToRectF(out
));
219 *bounds
= gfx::Rect();
223 void Canvas::Translate(const Vector2d
& offset
) {
224 canvas_
->translate(SkIntToScalar(offset
.x()), SkIntToScalar(offset
.y()));
227 void Canvas::Scale(int x_scale
, int y_scale
) {
228 canvas_
->scale(SkIntToScalar(x_scale
), SkIntToScalar(y_scale
));
231 void Canvas::DrawColor(SkColor color
) {
232 DrawColor(color
, SkXfermode::kSrcOver_Mode
);
235 void Canvas::DrawColor(SkColor color
, SkXfermode::Mode mode
) {
236 canvas_
->drawColor(color
, mode
);
239 void Canvas::FillRect(const Rect
& rect
, SkColor color
) {
240 FillRect(rect
, color
, SkXfermode::kSrcOver_Mode
);
243 void Canvas::FillRect(const Rect
& rect
,
245 SkXfermode::Mode mode
) {
247 paint
.setColor(color
);
248 paint
.setStyle(SkPaint::kFill_Style
);
249 paint
.setXfermodeMode(mode
);
250 DrawRect(rect
, paint
);
253 void Canvas::DrawRect(const Rect
& rect
, SkColor color
) {
254 DrawRect(rect
, color
, SkXfermode::kSrcOver_Mode
);
257 void Canvas::DrawRect(const Rect
& rect
,
259 SkXfermode::Mode mode
) {
261 paint
.setColor(color
);
262 paint
.setStyle(SkPaint::kStroke_Style
);
263 // Set a stroke width of 0, which will put us down the stroke rect path. If
264 // we set a stroke width of 1, for example, this will internally create a
265 // path and fill it, which causes problems near the edge of the canvas.
266 paint
.setStrokeWidth(SkIntToScalar(0));
267 paint
.setXfermodeMode(mode
);
269 DrawRect(rect
, paint
);
272 void Canvas::DrawRect(const Rect
& rect
, const SkPaint
& paint
) {
273 canvas_
->drawIRect(RectToSkIRect(rect
), paint
);
276 void Canvas::DrawPoint(const Point
& p1
, const SkPaint
& paint
) {
277 canvas_
->drawPoint(SkIntToScalar(p1
.x()), SkIntToScalar(p1
.y()), paint
);
280 void Canvas::DrawLine(const Point
& p1
, const Point
& p2
, SkColor color
) {
282 paint
.setColor(color
);
283 paint
.setStrokeWidth(SkIntToScalar(1));
284 DrawLine(p1
, p2
, paint
);
287 void Canvas::DrawLine(const Point
& p1
, const Point
& p2
, const SkPaint
& paint
) {
288 canvas_
->drawLine(SkIntToScalar(p1
.x()), SkIntToScalar(p1
.y()),
289 SkIntToScalar(p2
.x()), SkIntToScalar(p2
.y()), paint
);
292 void Canvas::DrawCircle(const Point
& center_point
,
294 const SkPaint
& paint
) {
295 canvas_
->drawCircle(SkIntToScalar(center_point
.x()),
296 SkIntToScalar(center_point
.y()), SkIntToScalar(radius
), paint
);
299 void Canvas::DrawRoundRect(const Rect
& rect
,
301 const SkPaint
& paint
) {
302 canvas_
->drawRoundRect(RectToSkRect(rect
), SkIntToScalar(radius
),
303 SkIntToScalar(radius
), paint
);
306 void Canvas::DrawPath(const SkPath
& path
, const SkPaint
& paint
) {
307 canvas_
->drawPath(path
, paint
);
310 void Canvas::DrawFocusRect(const Rect
& rect
) {
311 DrawDashedRect(rect
, SK_ColorGRAY
);
314 void Canvas::DrawSolidFocusRect(const Rect
& rect
, SkColor color
) {
316 paint
.setColor(color
);
317 paint
.setStrokeWidth(SkIntToScalar(1));
318 // Note: We cannot use DrawRect since it would create a path and fill it which
319 // would cause problems near the edge of the canvas.
320 int x1
= std::min(rect
.x(), rect
.right());
321 int x2
= std::max(rect
.x(), rect
.right());
322 int y1
= std::min(rect
.y(), rect
.bottom());
323 int y2
= std::max(rect
.y(), rect
.bottom());
324 DrawLine(Point(x1
, y1
), Point(x2
, y1
), paint
);
325 DrawLine(Point(x1
, y2
), Point(x2
, y2
), paint
);
326 DrawLine(Point(x1
, y1
), Point(x1
, y2
), paint
);
327 DrawLine(Point(x2
, y1
), Point(x2
, y2
+ 1), paint
);
330 void Canvas::DrawImageInt(const ImageSkia
& image
, int x
, int y
) {
332 DrawImageInt(image
, x
, y
, paint
);
335 void Canvas::DrawImageInt(const ImageSkia
& image
, int x
, int y
, uint8 a
) {
338 DrawImageInt(image
, x
, y
, paint
);
341 void Canvas::DrawImageInt(const ImageSkia
& image
,
344 const SkPaint
& paint
) {
345 const ImageSkiaRep
& image_rep
= image
.GetRepresentation(image_scale_
);
346 if (image_rep
.is_null())
348 const SkBitmap
& bitmap
= image_rep
.sk_bitmap();
349 float bitmap_scale
= image_rep
.scale();
351 ScopedCanvas
scoper(this);
352 canvas_
->scale(SkFloatToScalar(1.0f
/ bitmap_scale
),
353 SkFloatToScalar(1.0f
/ bitmap_scale
));
354 canvas_
->drawBitmap(bitmap
,
355 SkFloatToScalar(x
* bitmap_scale
),
356 SkFloatToScalar(y
* bitmap_scale
),
360 void Canvas::DrawImageInt(const ImageSkia
& image
,
371 DrawImageInt(image
, src_x
, src_y
, src_w
, src_h
, dest_x
, dest_y
,
372 dest_w
, dest_h
, filter
, p
);
375 void Canvas::DrawImageInt(const ImageSkia
& image
,
385 const SkPaint
& paint
) {
386 DrawImageIntHelper(image
, src_x
, src_y
, src_w
, src_h
, dest_x
, dest_y
, dest_w
,
387 dest_h
, filter
, paint
, image_scale_
, false);
390 void Canvas::DrawImageIntInPixel(const ImageSkia
& image
,
400 const SkPaint
& paint
) {
401 // All values passed into this function are in pixels, i.e. no scaling needs
404 // 1. Get the matrix transform from the canvas.
405 // 2. Set the scale in the matrix to 1.0 while honoring the direction of the
406 // the scale (x/y). Example RTL layouts.
407 // 3. Round off the X and Y translation components in the matrix. This is to
408 // reduce floating point errors during rect transformation. This is needed
409 // for fractional scale factors like 1.25/1.5, etc.
410 // 4. Save the current state of the canvas.
411 // 5. Set the modified matrix in the canvas. This ensures that no scaling
412 // will be done for draw operations on the canvas.
413 // 6. Draw the image.
414 // 7. Restore the state of the canvas and the SkCanvas matrix stack.
415 SkMatrix matrix
= canvas_
->getTotalMatrix();
417 // Ensure that the direction of the x and y scales is preserved. This is
418 // important for RTL layouts.
419 matrix
.setScaleX(matrix
.getScaleX() > 0 ? 1.0f
: -1.0f
);
420 matrix
.setScaleY(matrix
.getScaleY() > 0 ? 1.0f
: -1.0f
);
422 // Floor so that we get consistent rounding.
423 matrix
.setTranslateX(SkScalarFloorToScalar(matrix
.getTranslateX()));
424 matrix
.setTranslateY(SkScalarFloorToScalar(matrix
.getTranslateY()));
426 ScopedCanvas
scoper(this);
428 canvas_
->setMatrix(matrix
);
430 DrawImageIntHelper(image
,
445 void Canvas::DrawImageInPath(const ImageSkia
& image
,
449 const SkPaint
& paint
) {
450 const ImageSkiaRep
& image_rep
= image
.GetRepresentation(image_scale_
);
451 if (image_rep
.is_null())
455 matrix
.setTranslate(SkIntToScalar(x
), SkIntToScalar(y
));
456 skia::RefPtr
<SkShader
> shader
= CreateImageRepShader(
458 SkShader::kRepeat_TileMode
,
462 p
.setShader(shader
.get());
463 canvas_
->drawPath(path
, p
);
466 void Canvas::DrawStringRect(const base::string16
& text
,
467 const FontList
& font_list
,
469 const Rect
& display_rect
) {
470 DrawStringRectWithFlags(text
, font_list
, color
, display_rect
,
471 DefaultCanvasTextAlignment());
474 void Canvas::DrawStringRectWithFlags(const base::string16
& text
,
475 const FontList
& font_list
,
477 const Rect
& display_rect
,
479 DrawStringRectWithShadows(text
, font_list
, color
, display_rect
, 0, flags
,
483 void Canvas::TileImageInt(const ImageSkia
& image
,
488 TileImageInt(image
, 0, 0, x
, y
, w
, h
);
491 void Canvas::TileImageInt(const ImageSkia
& image
,
498 TileImageInt(image
, src_x
, src_y
, 1.0f
, 1.0f
, dest_x
, dest_y
, w
, h
);
501 void Canvas::TileImageInt(const ImageSkia
& image
,
510 if (!IntersectsClipRectInt(dest_x
, dest_y
, w
, h
))
513 const ImageSkiaRep
& image_rep
= image
.GetRepresentation(image_scale_
);
514 if (image_rep
.is_null())
517 SkMatrix shader_scale
;
518 shader_scale
.setScale(SkFloatToScalar(tile_scale_x
),
519 SkFloatToScalar(tile_scale_y
));
520 shader_scale
.preTranslate(SkIntToScalar(-src_x
), SkIntToScalar(-src_y
));
521 shader_scale
.postTranslate(SkIntToScalar(dest_x
), SkIntToScalar(dest_y
));
523 skia::RefPtr
<SkShader
> shader
= CreateImageRepShader(
525 SkShader::kRepeat_TileMode
,
529 paint
.setShader(shader
.get());
530 paint
.setXfermodeMode(SkXfermode::kSrcOver_Mode
);
532 SkRect dest_rect
= { SkIntToScalar(dest_x
),
533 SkIntToScalar(dest_y
),
534 SkIntToScalar(dest_x
+ w
),
535 SkIntToScalar(dest_y
+ h
) };
536 canvas_
->drawRect(dest_rect
, paint
);
539 NativeDrawingContext
Canvas::BeginPlatformPaint() {
540 return skia::BeginPlatformPaint(canvas_
);
543 void Canvas::EndPlatformPaint() {
544 skia::EndPlatformPaint(canvas_
);
547 void Canvas::Transform(const gfx::Transform
& transform
) {
548 canvas_
->concat(transform
.matrix());
551 Canvas::Canvas(SkCanvas
* canvas
, float image_scale
)
552 : image_scale_(image_scale
),
558 bool Canvas::IntersectsClipRectInt(int x
, int y
, int w
, int h
) {
560 return canvas_
->getClipBounds(&clip
) &&
561 clip
.intersect(SkIntToScalar(x
), SkIntToScalar(y
), SkIntToScalar(x
+ w
),
562 SkIntToScalar(y
+ h
));
565 bool Canvas::IntersectsClipRect(const Rect
& rect
) {
566 return IntersectsClipRectInt(rect
.x(), rect
.y(),
567 rect
.width(), rect
.height());
570 void Canvas::DrawImageIntHelper(const ImageSkia
& image
,
580 const SkPaint
& paint
,
583 DLOG_ASSERT(src_x
+ src_w
< std::numeric_limits
<int16_t>::max() &&
584 src_y
+ src_h
< std::numeric_limits
<int16_t>::max());
585 if (src_w
<= 0 || src_h
<= 0) {
586 NOTREACHED() << "Attempting to draw bitmap from an empty rect!";
590 if (!IntersectsClipRectInt(dest_x
, dest_y
, dest_w
, dest_h
))
593 float user_scale_x
= static_cast<float>(dest_w
) / src_w
;
594 float user_scale_y
= static_cast<float>(dest_h
) / src_h
;
596 const ImageSkiaRep
& image_rep
= image
.GetRepresentation(image_scale
);
597 if (image_rep
.is_null())
600 SkRect dest_rect
= { SkIntToScalar(dest_x
),
601 SkIntToScalar(dest_y
),
602 SkIntToScalar(dest_x
+ dest_w
),
603 SkIntToScalar(dest_y
+ dest_h
) };
605 if (src_w
== dest_w
&& src_h
== dest_h
&&
606 user_scale_x
== 1.0f
&& user_scale_y
== 1.0f
&&
607 image_rep
.scale() == 1.0f
&& !pixel
) {
608 // Workaround for apparent bug in Skia that causes image to occasionally
610 SkIRect src_rect
= { src_x
, src_y
, src_x
+ src_w
, src_y
+ src_h
};
611 const SkBitmap
& bitmap
= image_rep
.sk_bitmap();
612 canvas_
->drawBitmapRect(bitmap
, &src_rect
, dest_rect
, &paint
);
616 // Make a bitmap shader that contains the bitmap we want to draw. This is
617 // basically what SkCanvas.drawBitmap does internally, but it gives us
618 // more control over quality and will use the mipmap in the source image if
619 // it has one, whereas drawBitmap won't.
620 SkMatrix shader_scale
;
621 shader_scale
.setScale(SkFloatToScalar(user_scale_x
),
622 SkFloatToScalar(user_scale_y
));
623 shader_scale
.preTranslate(SkIntToScalar(-src_x
), SkIntToScalar(-src_y
));
624 shader_scale
.postTranslate(SkIntToScalar(dest_x
), SkIntToScalar(dest_y
));
626 skia::RefPtr
<SkShader
> shader
= CreateImageRepShaderForScale(
628 SkShader::kRepeat_TileMode
,
630 pixel
? 1.0f
: image_rep
.scale());
632 // Set up our paint to use the shader & release our reference (now just owned
635 p
.setFilterLevel(filter
? SkPaint::kLow_FilterLevel
636 : SkPaint::kNone_FilterLevel
);
637 p
.setShader(shader
.get());
639 // The rect will be filled by the bitmap.
640 canvas_
->drawRect(dest_rect
, p
);