1 // Copyright 2014 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/nine_image_painter.h"
9 #include "third_party/skia/include/core/SkPaint.h"
10 #include "third_party/skia/include/core/SkRect.h"
11 #include "third_party/skia/include/core/SkScalar.h"
12 #include "ui/gfx/canvas.h"
13 #include "ui/gfx/geometry/insets.h"
14 #include "ui/gfx/geometry/rect.h"
15 #include "ui/gfx/geometry/rect_conversions.h"
16 #include "ui/gfx/geometry/safe_integer_conversions.h"
17 #include "ui/gfx/image/image_skia_operations.h"
18 #include "ui/gfx/scoped_canvas.h"
19 #include "ui/gfx/skia_util.h"
25 // The following functions calculate width and height of the image in pixels
26 // for the scale factor.
27 int ImageWidthInPixels(const ImageSkia
& i
, float scale
) {
30 ImageSkiaRep image_rep
= i
.GetRepresentation(scale
);
31 return image_rep
.pixel_width() * scale
/ image_rep
.scale();
34 int ImageHeightInPixels(const ImageSkia
& i
, float scale
) {
37 ImageSkiaRep image_rep
= i
.GetRepresentation(scale
);
38 return image_rep
.pixel_height() * scale
/ image_rep
.scale();
41 // Stretches the given image over the specified canvas area.
48 const SkPaint
& paint
) {
51 c
->DrawImageIntInPixel(i
, 0, 0, ImageWidthInPixels(i
, c
->image_scale()),
52 ImageHeightInPixels(i
, c
->image_scale()),
53 x
, y
, w
, h
, false, paint
);
58 NineImagePainter::NineImagePainter(const std::vector
<ImageSkia
>& images
) {
59 DCHECK_EQ(arraysize(images_
), images
.size());
60 for (size_t i
= 0; i
< arraysize(images_
); ++i
)
61 images_
[i
] = images
[i
];
64 NineImagePainter::NineImagePainter(const ImageSkia
& image
,
65 const Insets
& insets
) {
66 std::vector
<gfx::Rect
> regions
;
67 GetSubsetRegions(image
, insets
, ®ions
);
68 DCHECK_EQ(9u, regions
.size());
70 for (size_t i
= 0; i
< 9; ++i
)
71 images_
[i
] = ImageSkiaOperations::ExtractSubset(image
, regions
[i
]);
74 NineImagePainter::~NineImagePainter() {
77 bool NineImagePainter::IsEmpty() const {
78 return images_
[0].isNull();
81 Size
NineImagePainter::GetMinimumSize() const {
82 return IsEmpty() ? Size() : Size(
83 images_
[0].width() + images_
[1].width() + images_
[2].width(),
84 images_
[0].height() + images_
[3].height() + images_
[6].height());
87 void NineImagePainter::Paint(Canvas
* canvas
, const Rect
& bounds
) {
88 // When no alpha value is specified, use default value of 100% opacity.
89 Paint(canvas
, bounds
, std::numeric_limits
<uint8
>::max());
92 void NineImagePainter::Paint(Canvas
* canvas
,
98 ScopedCanvas
scoped_canvas(canvas
);
99 canvas
->Translate(bounds
.OffsetFromOrigin());
101 // Get the current transform from the canvas and apply it to the logical
102 // bounds passed in. This will give us the pixel bounds which can be used
103 // to draw the images at the correct locations.
104 // We should not scale the bounds by the canvas->image_scale() as that can be
105 // different from the real scale in the canvas transform.
106 SkRect bounds_in_pixels_f
;
107 if (!canvas
->sk_canvas()->getTotalMatrix().mapRect(
108 &bounds_in_pixels_f
, RectToSkRect(gfx::Rect(bounds
.size()))))
109 return; // Invalid transform.
111 SkIRect bounds_in_pixels
;
112 bounds_in_pixels_f
.dround(&bounds_in_pixels
);
114 SkMatrix matrix
= canvas
->sk_canvas()->getTotalMatrix();
115 matrix
.setTranslateX(SkIntToScalar(bounds_in_pixels
.x()));
116 matrix
.setTranslateY(SkIntToScalar(bounds_in_pixels
.y()));
117 canvas
->sk_canvas()->setMatrix(matrix
);
119 const int width_in_pixels
= bounds_in_pixels
.width();
120 const int height_in_pixels
= bounds_in_pixels
.height();
121 const float scale_x
= matrix
.getScaleX();
122 const float scale_y
= matrix
.getScaleY();
124 // In case the corners and edges don't all have the same width/height, we draw
125 // the center first, and extend it out in all directions to the edges of the
126 // images with the smallest widths/heights. This way there will be no
127 // unpainted areas, though some corners or edges might overlap the center.
128 int i0w
= ImageWidthInPixels(images_
[0], scale_x
);
129 int i2w
= ImageWidthInPixels(images_
[2], scale_x
);
130 int i3w
= ImageWidthInPixels(images_
[3], scale_x
);
131 int i5w
= ImageWidthInPixels(images_
[5], scale_x
);
132 int i6w
= ImageWidthInPixels(images_
[6], scale_x
);
133 int i8w
= ImageWidthInPixels(images_
[8], scale_x
);
135 int i4x
= std::min(std::min(i0w
, i3w
), i6w
);
136 int i4w
= width_in_pixels
- i4x
- std::min(std::min(i2w
, i5w
), i8w
);
138 int i0h
= ImageHeightInPixels(images_
[0], scale_y
);
139 int i1h
= ImageHeightInPixels(images_
[1], scale_y
);
140 int i2h
= ImageHeightInPixels(images_
[2], scale_y
);
141 int i6h
= ImageHeightInPixels(images_
[6], scale_y
);
142 int i7h
= ImageHeightInPixels(images_
[7], scale_y
);
143 int i8h
= ImageHeightInPixels(images_
[8], scale_y
);
145 int i4y
= std::min(std::min(i0h
, i1h
), i2h
);
146 int i4h
= height_in_pixels
- i4y
- std::min(std::min(i6h
, i7h
), i8h
);
149 paint
.setAlpha(alpha
);
151 Fill(canvas
, images_
[4], i4x
, i4y
, i4w
, i4h
, paint
);
153 Fill(canvas
, images_
[0], 0, 0, i0w
, i0h
, paint
);
155 Fill(canvas
, images_
[1], i0w
, 0, width_in_pixels
- i0w
- i2w
, i1h
, paint
);
157 Fill(canvas
, images_
[2], width_in_pixels
- i2w
, 0, i2w
, i2h
, paint
);
159 Fill(canvas
, images_
[3], 0, i0h
, i3w
, height_in_pixels
- i0h
- i6h
, paint
);
161 Fill(canvas
, images_
[5], width_in_pixels
- i5w
, i2h
, i5w
,
162 height_in_pixels
- i2h
- i8h
, paint
);
164 Fill(canvas
, images_
[6], 0, height_in_pixels
- i6h
, i6w
, i6h
, paint
);
166 Fill(canvas
, images_
[7], i6w
, height_in_pixels
- i7h
,
167 width_in_pixels
- i6w
- i8w
, i7h
, paint
);
169 Fill(canvas
, images_
[8], width_in_pixels
- i8w
, height_in_pixels
- i8h
, i8w
,
174 void NineImagePainter::GetSubsetRegions(const ImageSkia
& image
,
175 const Insets
& insets
,
176 std::vector
<Rect
>* regions
) {
177 DCHECK_GE(image
.width(), insets
.width());
178 DCHECK_GE(image
.height(), insets
.height());
180 std::vector
<Rect
> result(9);
183 0, insets
.left(), image
.width() - insets
.right(), image
.width()};
185 0, insets
.top(), image
.height() - insets
.bottom(), image
.height()};
187 for (size_t j
= 0; j
< 3; ++j
) {
188 for (size_t i
= 0; i
< 3; ++i
) {
189 result
[i
+ j
* 3] = Rect(x
[i
], y
[j
], x
[i
+ 1] - x
[i
], y
[j
+ 1] - y
[j
]);
192 result
.swap(*regions
);