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 DCHECK_GE(image
.width(), insets
.width());
64 DCHECK_GE(image
.height(), insets
.height());
66 // Extract subsets of the original image to match the |images_| format.
68 { 0, insets
.left(), image
.width() - insets
.right(), image
.width() };
70 { 0, insets
.top(), image
.height() - insets
.bottom(), image
.height() };
72 for (size_t j
= 0; j
< 3; ++j
) {
73 for (size_t i
= 0; i
< 3; ++i
) {
74 images_
[i
+ j
* 3] = ImageSkiaOperations::ExtractSubset(image
,
75 Rect(x
[i
], y
[j
], x
[i
+ 1] - x
[i
], y
[j
+ 1] - y
[j
]));
80 NineImagePainter::~NineImagePainter() {
83 bool NineImagePainter::IsEmpty() const {
84 return images_
[0].isNull();
87 Size
NineImagePainter::GetMinimumSize() const {
88 return IsEmpty() ? Size() : Size(
89 images_
[0].width() + images_
[1].width() + images_
[2].width(),
90 images_
[0].height() + images_
[3].height() + images_
[6].height());
93 void NineImagePainter::Paint(Canvas
* canvas
, const Rect
& bounds
) {
94 // When no alpha value is specified, use default value of 100% opacity.
95 Paint(canvas
, bounds
, std::numeric_limits
<uint8
>::max());
98 void NineImagePainter::Paint(Canvas
* canvas
,
104 ScopedCanvas
scoped_canvas(canvas
);
105 canvas
->Translate(bounds
.OffsetFromOrigin());
107 // Get the current transform from the canvas and apply it to the logical
108 // bounds passed in. This will give us the pixel bounds which can be used
109 // to draw the images at the correct locations.
110 // We should not scale the bounds by the canvas->image_scale() as that can be
111 // different from the real scale in the canvas transform.
112 SkRect bounds_in_pixels_f
;
113 if (!canvas
->sk_canvas()->getTotalMatrix().mapRect(
114 &bounds_in_pixels_f
, RectToSkRect(gfx::Rect(bounds
.size()))))
115 return; // Invalid transform.
117 SkIRect bounds_in_pixels
;
118 bounds_in_pixels_f
.dround(&bounds_in_pixels
);
120 SkMatrix matrix
= canvas
->sk_canvas()->getTotalMatrix();
121 matrix
.setTranslateX(SkIntToScalar(bounds_in_pixels
.x()));
122 matrix
.setTranslateY(SkIntToScalar(bounds_in_pixels
.y()));
123 canvas
->sk_canvas()->setMatrix(matrix
);
125 const int width_in_pixels
= bounds_in_pixels
.width();
126 const int height_in_pixels
= bounds_in_pixels
.height();
128 // In case the corners and edges don't all have the same width/height, we draw
129 // the center first, and extend it out in all directions to the edges of the
130 // images with the smallest widths/heights. This way there will be no
131 // unpainted areas, though some corners or edges might overlap the center.
132 int i0w
= ImageWidthInPixels(images_
[0], canvas
);
133 int i2w
= ImageWidthInPixels(images_
[2], canvas
);
134 int i3w
= ImageWidthInPixels(images_
[3], canvas
);
135 int i5w
= ImageWidthInPixels(images_
[5], canvas
);
136 int i6w
= ImageWidthInPixels(images_
[6], canvas
);
137 int i8w
= ImageWidthInPixels(images_
[8], canvas
);
139 int i4x
= std::min(std::min(i0w
, i3w
), i6w
);
140 int i4w
= width_in_pixels
- i4x
- std::min(std::min(i2w
, i5w
), i8w
);
142 int i0h
= ImageHeightInPixels(images_
[0], canvas
);
143 int i1h
= ImageHeightInPixels(images_
[1], canvas
);
144 int i2h
= ImageHeightInPixels(images_
[2], canvas
);
145 int i6h
= ImageHeightInPixels(images_
[6], canvas
);
146 int i7h
= ImageHeightInPixels(images_
[7], canvas
);
147 int i8h
= ImageHeightInPixels(images_
[8], canvas
);
149 int i4y
= std::min(std::min(i0h
, i1h
), i2h
);
150 int i4h
= height_in_pixels
- i4y
- std::min(std::min(i6h
, i7h
), i8h
);
153 paint
.setAlpha(alpha
);
155 Fill(canvas
, images_
[4], i4x
, i4y
, i4w
, i4h
, paint
);
157 canvas
->DrawImageIntInPixel(images_
[0], 0, 0, i0w
, i0h
,
158 0, 0, i0w
, i0h
, false, paint
);
160 Fill(canvas
, images_
[1], i0w
, 0, width_in_pixels
- i0w
- i2w
, i1h
, paint
);
162 Fill(canvas
, images_
[2], width_in_pixels
- i2w
, 0, i2w
, i2h
, paint
);
164 Fill(canvas
, images_
[3], 0, i0h
, i3w
, height_in_pixels
- i0h
- i6h
, paint
);
166 Fill(canvas
, images_
[5], width_in_pixels
- i5w
, i2h
, i5w
,
167 height_in_pixels
- i2h
- i8h
, paint
);
169 Fill(canvas
, images_
[6], 0, height_in_pixels
- i6h
, i6w
, i6h
, paint
);
171 Fill(canvas
, images_
[7], i6w
, height_in_pixels
- i7h
,
172 width_in_pixels
- i6w
- i8w
, i7h
, paint
);
174 Fill(canvas
, images_
[8], width_in_pixels
- i8w
, height_in_pixels
- i8h
, i8w
,