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 width and height of the image in pixels for the
26 // scale factor in the Canvas.
27 int ImageWidthInPixels(const ImageSkia
& i
, Canvas
* c
) {
30 return i
.GetRepresentation(c
->image_scale()).pixel_width();
33 int ImageHeightInPixels(const ImageSkia
& i
, Canvas
* c
) {
36 return i
.GetRepresentation(c
->image_scale()).pixel_height();
39 // Stretches the given image over the specified canvas area.
46 const SkPaint
& paint
) {
49 c
->DrawImageIntInPixel(i
, 0, 0, ImageWidthInPixels(i
, c
),
50 ImageHeightInPixels(i
, c
), x
, y
, w
, h
, false, paint
);
55 NineImagePainter::NineImagePainter(const std::vector
<ImageSkia
>& images
) {
56 DCHECK_EQ(arraysize(images_
), images
.size());
57 for (size_t i
= 0; i
< arraysize(images_
); ++i
)
58 images_
[i
] = images
[i
];
61 NineImagePainter::NineImagePainter(const ImageSkia
& image
,
62 const Insets
& insets
) {
63 std::vector
<gfx::Rect
> regions
;
64 GetSubsetRegions(image
, insets
, ®ions
);
65 DCHECK_EQ(9u, regions
.size());
67 for (size_t i
= 0; i
< 9; ++i
)
68 images_
[i
] = ImageSkiaOperations::ExtractSubset(image
, regions
[i
]);
71 NineImagePainter::~NineImagePainter() {
74 bool NineImagePainter::IsEmpty() const {
75 return images_
[0].isNull();
78 Size
NineImagePainter::GetMinimumSize() const {
79 return IsEmpty() ? Size() : Size(
80 images_
[0].width() + images_
[1].width() + images_
[2].width(),
81 images_
[0].height() + images_
[3].height() + images_
[6].height());
84 void NineImagePainter::Paint(Canvas
* canvas
, const Rect
& bounds
) {
85 // When no alpha value is specified, use default value of 100% opacity.
86 Paint(canvas
, bounds
, std::numeric_limits
<uint8
>::max());
89 void NineImagePainter::Paint(Canvas
* canvas
,
95 ScopedCanvas
scoped_canvas(canvas
);
96 canvas
->Translate(bounds
.OffsetFromOrigin());
98 // Get the current transform from the canvas and apply it to the logical
99 // bounds passed in. This will give us the pixel bounds which can be used
100 // to draw the images at the correct locations.
101 // We should not scale the bounds by the canvas->image_scale() as that can be
102 // different from the real scale in the canvas transform.
103 SkRect bounds_in_pixels_f
;
104 if (!canvas
->sk_canvas()->getTotalMatrix().mapRect(
105 &bounds_in_pixels_f
, RectToSkRect(gfx::Rect(bounds
.size()))))
106 return; // Invalid transform.
108 SkIRect bounds_in_pixels
;
109 bounds_in_pixels_f
.dround(&bounds_in_pixels
);
111 SkMatrix matrix
= canvas
->sk_canvas()->getTotalMatrix();
112 matrix
.setTranslateX(SkIntToScalar(bounds_in_pixels
.x()));
113 matrix
.setTranslateY(SkIntToScalar(bounds_in_pixels
.y()));
114 canvas
->sk_canvas()->setMatrix(matrix
);
116 const int width_in_pixels
= bounds_in_pixels
.width();
117 const int height_in_pixels
= bounds_in_pixels
.height();
119 // In case the corners and edges don't all have the same width/height, we draw
120 // the center first, and extend it out in all directions to the edges of the
121 // images with the smallest widths/heights. This way there will be no
122 // unpainted areas, though some corners or edges might overlap the center.
123 int i0w
= ImageWidthInPixels(images_
[0], canvas
);
124 int i2w
= ImageWidthInPixels(images_
[2], canvas
);
125 int i3w
= ImageWidthInPixels(images_
[3], canvas
);
126 int i5w
= ImageWidthInPixels(images_
[5], canvas
);
127 int i6w
= ImageWidthInPixels(images_
[6], canvas
);
128 int i8w
= ImageWidthInPixels(images_
[8], canvas
);
130 int i4x
= std::min(std::min(i0w
, i3w
), i6w
);
131 int i4w
= width_in_pixels
- i4x
- std::min(std::min(i2w
, i5w
), i8w
);
133 int i0h
= ImageHeightInPixels(images_
[0], canvas
);
134 int i1h
= ImageHeightInPixels(images_
[1], canvas
);
135 int i2h
= ImageHeightInPixels(images_
[2], canvas
);
136 int i6h
= ImageHeightInPixels(images_
[6], canvas
);
137 int i7h
= ImageHeightInPixels(images_
[7], canvas
);
138 int i8h
= ImageHeightInPixels(images_
[8], canvas
);
140 int i4y
= std::min(std::min(i0h
, i1h
), i2h
);
141 int i4h
= height_in_pixels
- i4y
- std::min(std::min(i6h
, i7h
), i8h
);
144 paint
.setAlpha(alpha
);
146 Fill(canvas
, images_
[4], i4x
, i4y
, i4w
, i4h
, paint
);
148 canvas
->DrawImageIntInPixel(images_
[0], 0, 0, i0w
, i0h
,
149 0, 0, i0w
, i0h
, false, paint
);
151 Fill(canvas
, images_
[1], i0w
, 0, width_in_pixels
- i0w
- i2w
, i1h
, paint
);
153 Fill(canvas
, images_
[2], width_in_pixels
- i2w
, 0, i2w
, i2h
, paint
);
155 Fill(canvas
, images_
[3], 0, i0h
, i3w
, height_in_pixels
- i0h
- i6h
, paint
);
157 Fill(canvas
, images_
[5], width_in_pixels
- i5w
, i2h
, i5w
,
158 height_in_pixels
- i2h
- i8h
, paint
);
160 Fill(canvas
, images_
[6], 0, height_in_pixels
- i6h
, i6w
, i6h
, paint
);
162 Fill(canvas
, images_
[7], i6w
, height_in_pixels
- i7h
,
163 width_in_pixels
- i6w
- i8w
, i7h
, paint
);
165 Fill(canvas
, images_
[8], width_in_pixels
- i8w
, height_in_pixels
- i8h
, i8w
,
170 void NineImagePainter::GetSubsetRegions(const ImageSkia
& image
,
171 const Insets
& insets
,
172 std::vector
<Rect
>* regions
) {
173 DCHECK_GE(image
.width(), insets
.width());
174 DCHECK_GE(image
.height(), insets
.height());
176 std::vector
<Rect
> result(9);
179 0, insets
.left(), image
.width() - insets
.right(), image
.width()};
181 0, insets
.top(), image
.height() - insets
.bottom(), image
.height()};
183 for (size_t j
= 0; j
< 3; ++j
) {
184 for (size_t i
= 0; i
< 3; ++i
) {
185 result
[i
+ j
* 3] = Rect(x
[i
], y
[j
], x
[i
+ 1] - x
[i
], y
[j
+ 1] - y
[j
]);
188 result
.swap(*regions
);