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
25namespace gfx {
26
27Canvas::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
46Canvas::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
58Canvas::Canvas()
59    : image_scale_(1.0),
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                                           float image_scale) {
70  return new Canvas(canvas, image_scale);
71}
72
73void 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
87void 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
102int 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
110float 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
118int Canvas::DefaultCanvasTextAlignment() {
119  return base::i18n::IsRTL() ? TEXT_ALIGN_RIGHT : TEXT_ALIGN_LEFT;
120}
121
122ImageSkiaRep 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
134void 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
182void Canvas::Save() {
183  canvas_->save();
184}
185
186void Canvas::SaveLayerAlpha(uint8 alpha) {
187  canvas_->saveLayerAlpha(NULL, alpha);
188}
189
190void Canvas::SaveLayerAlpha(uint8 alpha, const Rect& layer_bounds) {
191  SkRect bounds(RectToSkRect(layer_bounds));
192  canvas_->saveLayerAlpha(&bounds, alpha);
193}
194
195void Canvas::Restore() {
196  canvas_->restore();
197}
198
199void Canvas::ClipRect(const Rect& rect) {
200  canvas_->clipRect(RectToSkRect(rect));
201}
202
203void Canvas::ClipPath(const SkPath& path, bool do_anti_alias) {
204  canvas_->clipPath(path, SkRegion::kIntersect_Op, do_anti_alias);
205}
206
207bool Canvas::IsClipEmpty() const {
208  return canvas_->isClipEmpty();
209}
210
211bool 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
221void Canvas::Translate(const Vector2d& offset) {
222  canvas_->translate(SkIntToScalar(offset.x()), SkIntToScalar(offset.y()));
223}
224
225void Canvas::Scale(int x_scale, int y_scale) {
226  canvas_->scale(SkIntToScalar(x_scale), SkIntToScalar(y_scale));
227}
228
229void Canvas::DrawColor(SkColor color) {
230  DrawColor(color, SkXfermode::kSrcOver_Mode);
231}
232
233void Canvas::DrawColor(SkColor color, SkXfermode::Mode mode) {
234  canvas_->drawColor(color, mode);
235}
236
237void Canvas::FillRect(const Rect& rect, SkColor color) {
238  FillRect(rect, color, SkXfermode::kSrcOver_Mode);
239}
240
241void 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
251void Canvas::DrawRect(const Rect& rect, SkColor color) {
252  DrawRect(rect, color, SkXfermode::kSrcOver_Mode);
253}
254
255void 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
270void Canvas::DrawRect(const Rect& rect, const SkPaint& paint) {
271  canvas_->drawIRect(RectToSkIRect(rect), paint);
272}
273
274void Canvas::DrawPoint(const Point& p1, const SkPaint& paint) {
275  canvas_->drawPoint(SkIntToScalar(p1.x()), SkIntToScalar(p1.y()), paint);
276}
277
278void 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
285void 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
290void 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
297void 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
304void Canvas::DrawPath(const SkPath& path, const SkPaint& paint) {
305  canvas_->drawPath(path, paint);
306}
307
308void Canvas::DrawFocusRect(const Rect& rect) {
309  DrawDashedRect(rect, SK_ColorGRAY);
310}
311
312void 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
328void Canvas::DrawImageInt(const ImageSkia& image, int x, int y) {
329  SkPaint paint;
330  DrawImageInt(image, x, y, paint);
331}
332
333void 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
339void 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
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  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
374void 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
389void 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
446void 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
467void 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
475void 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
484void 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
492void 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
502void 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
540NativeDrawingContext Canvas::BeginPlatformPaint() {
541  return skia::BeginPlatformPaint(canvas_);
542}
543
544void Canvas::EndPlatformPaint() {
545  skia::EndPlatformPaint(canvas_);
546}
547
548void Canvas::Transform(const gfx::Transform& transform) {
549  canvas_->concat(transform.matrix());
550}
551
552Canvas::Canvas(SkCanvas* canvas, float image_scale)
553    : image_scale_(image_scale),
554      owned_canvas_(),
555      canvas_(canvas) {
556  DCHECK(canvas);
557}
558
559bool 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
566bool Canvas::IntersectsClipRect(const Rect& rect) {
567  return IntersectsClipRectInt(rect.x(), rect.y(),
568                               rect.width(), rect.height());
569}
570
571void 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