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