15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/nine_image_painter.h"
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include <limits>
823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "third_party/skia/include/core/SkPaint.h"
10f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "third_party/skia/include/core/SkRect.h"
11f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "third_party/skia/include/core/SkScalar.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/canvas.h"
13010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "ui/gfx/geometry/rect_conversions.h"
14010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "ui/gfx/geometry/safe_integer_conversions.h"
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/image/image_skia_operations.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/insets.h"
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/rect.h"
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/scoped_canvas.h"
19f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "ui/gfx/skia_util.h"
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace gfx {
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)namespace {
2423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
25010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// The following functions width and height of the image in pixels for the
26010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// scale factor in the Canvas.
27010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)int ImageWidthInPixels(const ImageSkia& i, Canvas* c) {
28010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  return i.GetRepresentation(c->image_scale()).pixel_width();
29010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
30010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
31010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)int ImageHeightInPixels(const ImageSkia& i, Canvas* c) {
32010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  return i.GetRepresentation(c->image_scale()).pixel_height();
33010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
34010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
3523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)// Stretches the given image over the specified canvas area.
3623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void Fill(Canvas* c,
3723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          const ImageSkia& i,
3823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          int x,
3923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          int y,
4023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          int w,
4123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          int h,
4223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          const SkPaint& paint) {
43010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  c->DrawImageIntInPixel(i, 0, 0, ImageWidthInPixels(i, c),
44010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                         ImageHeightInPixels(i, c), x, y, w, h, false, paint);
4523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
4623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
4723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}  // namespace
4823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)NineImagePainter::NineImagePainter(const std::vector<ImageSkia>& images) {
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_EQ(arraysize(images_), images.size());
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (size_t i = 0; i < arraysize(images_); ++i)
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    images_[i] = images[i];
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)NineImagePainter::NineImagePainter(const ImageSkia& image,
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                   const Insets& insets) {
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_GE(image.width(), insets.width());
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_GE(image.height(), insets.height());
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Extract subsets of the original image to match the |images_| format.
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const int x[] =
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      { 0, insets.left(), image.width() - insets.right(), image.width() };
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const int y[] =
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      { 0, insets.top(), image.height() - insets.bottom(), image.height() };
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (size_t j = 0; j < 3; ++j) {
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (size_t i = 0; i < 3; ++i) {
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      images_[i + j * 3] = ImageSkiaOperations::ExtractSubset(image,
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          Rect(x[i], y[j], x[i + 1] - x[i], y[j + 1] - y[j]));
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)NineImagePainter::~NineImagePainter() {
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool NineImagePainter::IsEmpty() const {
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return images_[0].isNull();
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)Size NineImagePainter::GetMinimumSize() const {
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return IsEmpty() ? Size() : Size(
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      images_[0].width() + images_[1].width() + images_[2].width(),
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      images_[0].height() + images_[3].height() + images_[6].height());
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void NineImagePainter::Paint(Canvas* canvas, const Rect& bounds) {
8823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  // When no alpha value is specified, use default value of 100% opacity.
8923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  Paint(canvas, bounds, std::numeric_limits<uint8>::max());
9023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
9123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
9223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void NineImagePainter::Paint(Canvas* canvas,
9323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                             const Rect& bounds,
9423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                             const uint8 alpha) {
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (IsEmpty())
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ScopedCanvas scoped_canvas(canvas);
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  canvas->Translate(bounds.OffsetFromOrigin());
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
10123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  SkPaint paint;
10223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  paint.setAlpha(alpha);
10323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
104f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Get the current transform from the canvas and apply it to the logical
105f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // bounds passed in. This will give us the pixel bounds which can be used
106f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // to draw the images at the correct locations.
107f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // We should not scale the bounds by the canvas->image_scale() as that can be
108f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // different from the real scale in the canvas transform.
109f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  SkMatrix matrix = canvas->sk_canvas()->getTotalMatrix();
110f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  SkRect scaled_rect;
111f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  matrix.mapRect(&scaled_rect, RectToSkRect(bounds));
112f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
113f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  int scaled_width = gfx::ToCeiledInt(SkScalarToFloat(scaled_rect.width()));
114f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  int scaled_height = gfx::ToCeiledInt(SkScalarToFloat(scaled_rect.height()));
115010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // In case the corners and edges don't all have the same width/height, we draw
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // the center first, and extend it out in all directions to the edges of the
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // images with the smallest widths/heights.  This way there will be no
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // unpainted areas, though some corners or edges might overlap the center.
120010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i0w = ImageWidthInPixels(images_[0], canvas);
121010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i2w = ImageWidthInPixels(images_[2], canvas);
122010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i3w = ImageWidthInPixels(images_[3], canvas);
123010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i5w = ImageWidthInPixels(images_[5], canvas);
124010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i6w = ImageWidthInPixels(images_[6], canvas);
125010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i8w = ImageWidthInPixels(images_[8], canvas);
126010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int i4x = std::min(std::min(i0w, i3w), i6w);
128f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  int i4w = scaled_width - i4x - std::min(std::min(i2w, i5w), i8w);
129010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
130010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i0h = ImageHeightInPixels(images_[0], canvas);
131010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i1h = ImageHeightInPixels(images_[1], canvas);
132010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i2h = ImageHeightInPixels(images_[2], canvas);
133010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i6h = ImageHeightInPixels(images_[6], canvas);
134010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i7h = ImageHeightInPixels(images_[7], canvas);
135010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int i8h = ImageHeightInPixels(images_[8], canvas);
136010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int i4y = std::min(std::min(i0h, i1h), i2h);
138f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  int i4h = scaled_height - i4y - std::min(std::min(i6h, i7h), i8h);
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!images_[4].isNull())
14023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    Fill(canvas, images_[4], i4x, i4y, i4w, i4h, paint);
141010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  canvas->DrawImageIntInPixel(images_[0], 0, 0, i0w, i0h,
142010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                              0, 0, i0w, i0h, false, paint);
143f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  Fill(canvas, images_[1], i0w, 0, scaled_width - i0w - i2w, i1h, paint);
144f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  canvas->DrawImageIntInPixel(images_[2], 0, 0, i2w, i2h, scaled_width - i2w,
145f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                              0, i2w, i2h, false, paint);
146f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  Fill(canvas, images_[3], 0, i0h, i3w, scaled_height - i0h - i6h, paint);
147f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  Fill(canvas, images_[5], scaled_width - i5w, i2h, i5w,
148f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)       scaled_height - i2h - i8h, paint);
149f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  canvas->DrawImageIntInPixel(images_[6], 0, 0, i6w, i6h, 0,
150f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                              scaled_height - i6h, i6w, i6h, false, paint);
151f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  Fill(canvas, images_[7], i6w, scaled_height - i7h, scaled_width - i6w - i8w,
152f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)       i7h, paint);
153f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  canvas->DrawImageIntInPixel(images_[8], 0, 0, i8w, i8h, scaled_width - i8w,
154f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                              scaled_height - i8h, i8w, i8h, false, paint);
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace gfx
158