Home | History | Annotate | Download | only in gfx
      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