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.
5 #include "ui/wm/core/image_grid.h"
9 #include "third_party/skia/include/core/SkColor.h"
10 #include "third_party/skia/include/core/SkXfermode.h"
11 #include "ui/compositor/dip_util.h"
12 #include "ui/gfx/canvas.h"
13 #include "ui/gfx/geometry/rect.h"
14 #include "ui/gfx/geometry/rect_conversions.h"
15 #include "ui/gfx/geometry/size.h"
16 #include "ui/gfx/geometry/size_conversions.h"
17 #include "ui/gfx/image/image.h"
18 #include "ui/gfx/image/image_skia_operations.h"
19 #include "ui/gfx/transform.h"
27 // Sets the scaling for the transform applied to a layer. The left, top,
28 // right and bottom layers are stretched to the height or width of the
31 void ScaleWidth(gfx::Size center
, ui::Layer
* layer
, gfx::Transform
& transform
) {
32 float layer_width
= layer
->bounds().width() * layer
->device_scale_factor();
33 float scale
= static_cast<float>(center
.width()) / layer_width
;
34 transform
.Scale(scale
, 1.0);
37 void ScaleHeight(gfx::Size center
,
39 gfx::Transform
& transform
) {
40 float layer_height
= layer
->bounds().height() * layer
->device_scale_factor();
41 float scale
= static_cast<float>(center
.height()) / layer_height
;
42 transform
.Scale(1.0, scale
);
45 // Returns the dimensions of |image| if non-NULL or gfx::Size(0, 0) otherwise.
46 gfx::Size
GetImageSize(const gfx::Image
* image
) {
47 return image
? gfx::Size(image
->ToImageSkia()->width(),
48 image
->ToImageSkia()->height())
52 // Returns true if |layer|'s bounds don't fit within |size|.
53 bool LayerExceedsSize(const ui::Layer
* layer
, const gfx::Size
& size
) {
54 return layer
->bounds().width() > size
.width() ||
55 layer
->bounds().height() > size
.height();
60 gfx::RectF
ImageGrid::TestAPI::GetTransformedLayerBounds(
61 const ui::Layer
& layer
) {
62 gfx::RectF bounds
= layer
.bounds();
63 layer
.transform().TransformRect(&bounds
);
67 ImageGrid::ImageGrid()
68 : layer_(new ui::Layer(ui::LAYER_NOT_DRAWN
)),
70 bottom_image_height_(0),
72 right_image_width_(0),
73 base_top_row_height_(0),
74 base_bottom_row_height_(0),
75 base_left_column_width_(0),
76 base_right_column_width_(0) {
79 ImageGrid::~ImageGrid() {
82 void ImageGrid::SetImages(const gfx::Image
* top_left_image
,
83 const gfx::Image
* top_image
,
84 const gfx::Image
* top_right_image
,
85 const gfx::Image
* left_image
,
86 const gfx::Image
* center_image
,
87 const gfx::Image
* right_image
,
88 const gfx::Image
* bottom_left_image
,
89 const gfx::Image
* bottom_image
,
90 const gfx::Image
* bottom_right_image
) {
91 SetImage(top_left_image
, &top_left_layer_
, &top_left_painter_
, NONE
);
92 SetImage(top_image
, &top_layer_
, &top_painter_
, HORIZONTAL
);
93 SetImage(top_right_image
, &top_right_layer_
, &top_right_painter_
, NONE
);
94 SetImage(left_image
, &left_layer_
, &left_painter_
, VERTICAL
);
95 SetImage(center_image
, ¢er_layer_
, ¢er_painter_
, NONE
);
96 SetImage(right_image
, &right_layer_
, &right_painter_
, VERTICAL
);
97 SetImage(bottom_left_image
, &bottom_left_layer_
, &bottom_left_painter_
, NONE
);
98 SetImage(bottom_image
, &bottom_layer_
, &bottom_painter_
, HORIZONTAL
);
100 bottom_right_image
, &bottom_right_layer_
, &bottom_right_painter_
, NONE
);
102 top_image_height_
= GetImageSize(top_image
).height();
103 bottom_image_height_
= GetImageSize(bottom_image
).height();
104 left_image_width_
= GetImageSize(left_image
).width();
105 right_image_width_
= GetImageSize(right_image
).width();
107 base_top_row_height_
= max(GetImageSize(top_left_image
).height(),
108 max(GetImageSize(top_image
).height(),
109 GetImageSize(top_right_image
).height()));
110 base_bottom_row_height_
= max(GetImageSize(bottom_left_image
).height(),
111 max(GetImageSize(bottom_image
).height(),
112 GetImageSize(bottom_right_image
).height()));
113 base_left_column_width_
= max(GetImageSize(top_left_image
).width(),
114 max(GetImageSize(left_image
).width(),
115 GetImageSize(bottom_left_image
).width()));
116 base_right_column_width_
= max(GetImageSize(top_right_image
).width(),
117 max(GetImageSize(right_image
).width(),
118 GetImageSize(bottom_right_image
).width()));
120 // Invalidate previous |size_| so calls to SetSize() will recompute it.
124 void ImageGrid::SetSize(const gfx::Size
& size
) {
130 gfx::Rect updated_bounds
= layer_
->bounds();
131 updated_bounds
.set_size(size
);
132 layer_
->SetBounds(updated_bounds
);
134 // Calculate the available amount of space for corner images on all sides of
135 // the grid. If the images don't fit, we need to clip them.
136 const int left
= min(base_left_column_width_
, size_
.width() / 2);
137 const int right
= min(base_right_column_width_
, size_
.width() - left
);
138 const int top
= min(base_top_row_height_
, size_
.height() / 2);
139 const int bottom
= min(base_bottom_row_height_
, size_
.height() - top
);
141 // The remaining space goes to the center image.
142 int center_width
= std::max(size
.width() - left
- right
, 0);
143 int center_height
= std::max(size
.height() - top
- bottom
, 0);
145 // At non-integer scale factors, the ratio of dimensions in DIP is not
146 // necessarily the same as the ratio in physical pixels due to rounding. Set
147 // the transform on each of the layers based on dimensions in pixels.
148 gfx::Size center_size_in_pixels
= gfx::ToFlooredSize(gfx::ScaleSize(
149 gfx::Size(center_width
, center_height
), layer_
->device_scale_factor()));
151 if (top_layer_
.get()) {
152 if (center_width
> 0) {
153 gfx::Transform transform
;
154 transform
.Translate(left
, 0);
155 ScaleWidth(center_size_in_pixels
, top_layer_
.get(), transform
);
156 top_layer_
->SetTransform(transform
);
158 top_layer_
->SetVisible(center_width
> 0);
160 if (bottom_layer_
.get()) {
161 if (center_width
> 0) {
162 gfx::Transform transform
;
164 left
, size
.height() - bottom_layer_
->bounds().height());
165 ScaleWidth(center_size_in_pixels
, bottom_layer_
.get(), transform
);
166 bottom_layer_
->SetTransform(transform
);
168 bottom_layer_
->SetVisible(center_width
> 0);
170 if (left_layer_
.get()) {
171 if (center_height
> 0) {
172 gfx::Transform transform
;
173 transform
.Translate(0, top
);
174 ScaleHeight(center_size_in_pixels
, left_layer_
.get(), transform
);
175 left_layer_
->SetTransform(transform
);
177 left_layer_
->SetVisible(center_height
> 0);
179 if (right_layer_
.get()) {
180 if (center_height
> 0) {
181 gfx::Transform transform
;
183 size
.width() - right_layer_
->bounds().width(), top
);
184 ScaleHeight(center_size_in_pixels
, right_layer_
.get(), transform
);
185 right_layer_
->SetTransform(transform
);
187 right_layer_
->SetVisible(center_height
> 0);
190 if (top_left_layer_
.get()) {
191 // No transformation needed; it should be at (0, 0) and unscaled.
192 top_left_painter_
->SetClipRect(
193 LayerExceedsSize(top_left_layer_
.get(), gfx::Size(left
, top
)) ?
194 gfx::Rect(gfx::Rect(0, 0, left
, top
)) :
196 top_left_layer_
.get());
198 if (top_right_layer_
.get()) {
199 gfx::Transform transform
;
200 transform
.Translate(size
.width() - top_right_layer_
->bounds().width(), 0.0);
201 top_right_layer_
->SetTransform(transform
);
202 top_right_painter_
->SetClipRect(
203 LayerExceedsSize(top_right_layer_
.get(), gfx::Size(right
, top
)) ?
204 gfx::Rect(top_right_layer_
->bounds().width() - right
, 0,
207 top_right_layer_
.get());
209 if (bottom_left_layer_
.get()) {
210 gfx::Transform transform
;
212 0.0, size
.height() - bottom_left_layer_
->bounds().height());
213 bottom_left_layer_
->SetTransform(transform
);
214 bottom_left_painter_
->SetClipRect(
215 LayerExceedsSize(bottom_left_layer_
.get(), gfx::Size(left
, bottom
)) ?
216 gfx::Rect(0, bottom_left_layer_
->bounds().height() - bottom
,
219 bottom_left_layer_
.get());
221 if (bottom_right_layer_
.get()) {
222 gfx::Transform transform
;
224 size
.width() - bottom_right_layer_
->bounds().width(),
225 size
.height() - bottom_right_layer_
->bounds().height());
226 bottom_right_layer_
->SetTransform(transform
);
227 bottom_right_painter_
->SetClipRect(
228 LayerExceedsSize(bottom_right_layer_
.get(), gfx::Size(right
, bottom
)) ?
229 gfx::Rect(bottom_right_layer_
->bounds().width() - right
,
230 bottom_right_layer_
->bounds().height() - bottom
,
233 bottom_right_layer_
.get());
236 if (center_layer_
.get()) {
237 if (center_width
> 0 && center_height
> 0) {
238 gfx::Transform transform
;
239 transform
.Translate(left
, top
);
240 transform
.Scale(center_width
/ center_layer_
->bounds().width(),
241 center_height
/ center_layer_
->bounds().height());
242 center_layer_
->SetTransform(transform
);
244 center_layer_
->SetVisible(center_width
> 0 && center_height
> 0);
248 void ImageGrid::SetContentBounds(const gfx::Rect
& content_bounds
) {
251 content_bounds
.width() + left_image_width_
+ right_image_width_
,
252 content_bounds
.height() + top_image_height_
+
253 bottom_image_height_
));
255 gfx::Rect(content_bounds
.x() - left_image_width_
,
256 content_bounds
.y() - top_image_height_
,
257 layer_
->bounds().width(),
258 layer_
->bounds().height()));
261 void ImageGrid::ImagePainter::SetClipRect(const gfx::Rect
& clip_rect
,
263 if (clip_rect
!= clip_rect_
) {
264 clip_rect_
= clip_rect
;
265 layer
->SchedulePaint(layer
->bounds());
269 void ImageGrid::ImagePainter::OnPaintLayer(gfx::Canvas
* canvas
) {
270 if (!clip_rect_
.IsEmpty())
271 canvas
->ClipRect(clip_rect_
);
272 canvas
->DrawImageInt(image_
, 0, 0);
275 void ImageGrid::ImagePainter::OnDelegatedFrameDamage(
276 const gfx::Rect
& damage_rect_in_dip
) {
279 void ImageGrid::ImagePainter::OnDeviceScaleFactorChanged(
280 float device_scale_factor
) {
281 // Redrawing will take care of scale factor change.
284 base::Closure
ImageGrid::ImagePainter::PrepareForLayerBoundsChange() {
285 return base::Closure();
288 void ImageGrid::SetImage(const gfx::Image
* image
,
289 scoped_ptr
<ui::Layer
>* layer_ptr
,
290 scoped_ptr
<ImagePainter
>* painter_ptr
,
292 // Minimum width (for HORIZONTAL) or height (for VERTICAL) of the
293 // |image| so that layers are scaled property if the device scale
294 // factor is non integral.
295 const int kMinimumSize
= 20;
297 // Clean out old layers and painters.
298 if (layer_ptr
->get())
299 layer_
->Remove(layer_ptr
->get());
301 painter_ptr
->reset();
303 // If we're not using an image, we're done.
307 gfx::ImageSkia image_skia
= image
->AsImageSkia();
310 if (image_skia
.width() < kMinimumSize
) {
311 image_skia
= gfx::ImageSkiaOperations::CreateResizedImage(
313 skia::ImageOperations::RESIZE_GOOD
,
314 gfx::Size(kMinimumSize
, image_skia
.height()));
318 if (image_skia
.height() < kMinimumSize
) {
319 image_skia
= gfx::ImageSkiaOperations::CreateResizedImage(
321 skia::ImageOperations::RESIZE_GOOD
,
322 gfx::Size(image_skia
.width(), kMinimumSize
));
329 // Set up the new layer and painter.
330 layer_ptr
->reset(new ui::Layer(ui::LAYER_TEXTURED
));
332 const gfx::Size size
= image_skia
.size();
333 layer_ptr
->get()->SetBounds(gfx::Rect(0, 0, size
.width(), size
.height()));
335 painter_ptr
->reset(new ImagePainter(image_skia
));
336 layer_ptr
->get()->set_delegate(painter_ptr
->get());
337 layer_ptr
->get()->SetFillsBoundsOpaquely(false);
338 layer_ptr
->get()->SetVisible(true);
339 layer_
->Add(layer_ptr
->get());