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/compositor/paint_recorder.h"
13 #include "ui/gfx/canvas.h"
14 #include "ui/gfx/geometry/rect.h"
15 #include "ui/gfx/geometry/rect_conversions.h"
16 #include "ui/gfx/geometry/size.h"
17 #include "ui/gfx/geometry/size_conversions.h"
18 #include "ui/gfx/image/image.h"
19 #include "ui/gfx/image/image_skia_operations.h"
20 #include "ui/gfx/transform.h"
28 // Sets the scaling for the transform applied to a layer. The left, top,
29 // right and bottom layers are stretched to the height or width of the
32 void ScaleWidth(gfx::Size center
, ui::Layer
* layer
, gfx::Transform
& transform
) {
33 float layer_width
= layer
->bounds().width() * layer
->device_scale_factor();
34 float scale
= static_cast<float>(center
.width()) / layer_width
;
35 transform
.Scale(scale
, 1.0);
38 void ScaleHeight(gfx::Size center
,
40 gfx::Transform
& transform
) {
41 float layer_height
= layer
->bounds().height() * layer
->device_scale_factor();
42 float scale
= static_cast<float>(center
.height()) / layer_height
;
43 transform
.Scale(1.0, scale
);
46 // Returns the dimensions of |image| if non-NULL or gfx::Size(0, 0) otherwise.
47 gfx::Size
GetImageSize(const gfx::Image
* image
) {
48 return image
? gfx::Size(image
->ToImageSkia()->width(),
49 image
->ToImageSkia()->height())
53 // Returns true if |layer|'s bounds don't fit within |size|.
54 bool LayerExceedsSize(const ui::Layer
* layer
, const gfx::Size
& size
) {
55 return layer
->bounds().width() > size
.width() ||
56 layer
->bounds().height() > size
.height();
61 gfx::RectF
ImageGrid::TestAPI::GetTransformedLayerBounds(
62 const ui::Layer
& layer
) {
63 gfx::RectF bounds
= layer
.bounds();
64 layer
.transform().TransformRect(&bounds
);
68 ImageGrid::ImageGrid()
69 : layer_(new ui::Layer(ui::LAYER_NOT_DRAWN
)),
71 bottom_image_height_(0),
73 right_image_width_(0),
74 base_top_row_height_(0),
75 base_bottom_row_height_(0),
76 base_left_column_width_(0),
77 base_right_column_width_(0) {
80 ImageGrid::~ImageGrid() {
83 void ImageGrid::SetImages(const gfx::Image
* top_left_image
,
84 const gfx::Image
* top_image
,
85 const gfx::Image
* top_right_image
,
86 const gfx::Image
* left_image
,
87 const gfx::Image
* center_image
,
88 const gfx::Image
* right_image
,
89 const gfx::Image
* bottom_left_image
,
90 const gfx::Image
* bottom_image
,
91 const gfx::Image
* bottom_right_image
) {
92 SetImage(top_left_image
, &top_left_layer_
, &top_left_painter_
, NONE
);
93 SetImage(top_image
, &top_layer_
, &top_painter_
, HORIZONTAL
);
94 SetImage(top_right_image
, &top_right_layer_
, &top_right_painter_
, NONE
);
95 SetImage(left_image
, &left_layer_
, &left_painter_
, VERTICAL
);
96 SetImage(center_image
, ¢er_layer_
, ¢er_painter_
, NONE
);
97 SetImage(right_image
, &right_layer_
, &right_painter_
, VERTICAL
);
98 SetImage(bottom_left_image
, &bottom_left_layer_
, &bottom_left_painter_
, NONE
);
99 SetImage(bottom_image
, &bottom_layer_
, &bottom_painter_
, HORIZONTAL
);
101 bottom_right_image
, &bottom_right_layer_
, &bottom_right_painter_
, NONE
);
103 top_image_height_
= GetImageSize(top_image
).height();
104 bottom_image_height_
= GetImageSize(bottom_image
).height();
105 left_image_width_
= GetImageSize(left_image
).width();
106 right_image_width_
= GetImageSize(right_image
).width();
108 base_top_row_height_
= max(GetImageSize(top_left_image
).height(),
109 max(GetImageSize(top_image
).height(),
110 GetImageSize(top_right_image
).height()));
111 base_bottom_row_height_
= max(GetImageSize(bottom_left_image
).height(),
112 max(GetImageSize(bottom_image
).height(),
113 GetImageSize(bottom_right_image
).height()));
114 base_left_column_width_
= max(GetImageSize(top_left_image
).width(),
115 max(GetImageSize(left_image
).width(),
116 GetImageSize(bottom_left_image
).width()));
117 base_right_column_width_
= max(GetImageSize(top_right_image
).width(),
118 max(GetImageSize(right_image
).width(),
119 GetImageSize(bottom_right_image
).width()));
121 // Invalidate previous |size_| so calls to SetSize() will recompute it.
125 void ImageGrid::SetSize(const gfx::Size
& size
) {
131 gfx::Rect updated_bounds
= layer_
->bounds();
132 updated_bounds
.set_size(size
);
133 layer_
->SetBounds(updated_bounds
);
135 // Calculate the available amount of space for corner images on all sides of
136 // the grid. If the images don't fit, we need to clip them.
137 const int left
= min(base_left_column_width_
, size_
.width() / 2);
138 const int right
= min(base_right_column_width_
, size_
.width() - left
);
139 const int top
= min(base_top_row_height_
, size_
.height() / 2);
140 const int bottom
= min(base_bottom_row_height_
, size_
.height() - top
);
142 // The remaining space goes to the center image.
143 int center_width
= std::max(size
.width() - left
- right
, 0);
144 int center_height
= std::max(size
.height() - top
- bottom
, 0);
146 // At non-integer scale factors, the ratio of dimensions in DIP is not
147 // necessarily the same as the ratio in physical pixels due to rounding. Set
148 // the transform on each of the layers based on dimensions in pixels.
149 gfx::Size center_size_in_pixels
= gfx::ToFlooredSize(gfx::ScaleSize(
150 gfx::Size(center_width
, center_height
), layer_
->device_scale_factor()));
152 if (top_layer_
.get()) {
153 if (center_width
> 0) {
154 gfx::Transform transform
;
155 transform
.Translate(left
, 0);
156 ScaleWidth(center_size_in_pixels
, top_layer_
.get(), transform
);
157 top_layer_
->SetTransform(transform
);
159 top_layer_
->SetVisible(center_width
> 0);
161 if (bottom_layer_
.get()) {
162 if (center_width
> 0) {
163 gfx::Transform transform
;
165 left
, size
.height() - bottom_layer_
->bounds().height());
166 ScaleWidth(center_size_in_pixels
, bottom_layer_
.get(), transform
);
167 bottom_layer_
->SetTransform(transform
);
169 bottom_layer_
->SetVisible(center_width
> 0);
171 if (left_layer_
.get()) {
172 if (center_height
> 0) {
173 gfx::Transform transform
;
174 transform
.Translate(0, top
);
175 ScaleHeight(center_size_in_pixels
, left_layer_
.get(), transform
);
176 left_layer_
->SetTransform(transform
);
178 left_layer_
->SetVisible(center_height
> 0);
180 if (right_layer_
.get()) {
181 if (center_height
> 0) {
182 gfx::Transform transform
;
184 size
.width() - right_layer_
->bounds().width(), top
);
185 ScaleHeight(center_size_in_pixels
, right_layer_
.get(), transform
);
186 right_layer_
->SetTransform(transform
);
188 right_layer_
->SetVisible(center_height
> 0);
191 if (top_left_layer_
.get()) {
192 // No transformation needed; it should be at (0, 0) and unscaled.
193 top_left_painter_
->SetClipRect(
194 LayerExceedsSize(top_left_layer_
.get(), gfx::Size(left
, top
)) ?
195 gfx::Rect(gfx::Rect(0, 0, left
, top
)) :
197 top_left_layer_
.get());
199 if (top_right_layer_
.get()) {
200 gfx::Transform transform
;
201 transform
.Translate(size
.width() - top_right_layer_
->bounds().width(), 0.0);
202 top_right_layer_
->SetTransform(transform
);
203 top_right_painter_
->SetClipRect(
204 LayerExceedsSize(top_right_layer_
.get(), gfx::Size(right
, top
)) ?
205 gfx::Rect(top_right_layer_
->bounds().width() - right
, 0,
208 top_right_layer_
.get());
210 if (bottom_left_layer_
.get()) {
211 gfx::Transform transform
;
213 0.0, size
.height() - bottom_left_layer_
->bounds().height());
214 bottom_left_layer_
->SetTransform(transform
);
215 bottom_left_painter_
->SetClipRect(
216 LayerExceedsSize(bottom_left_layer_
.get(), gfx::Size(left
, bottom
)) ?
217 gfx::Rect(0, bottom_left_layer_
->bounds().height() - bottom
,
220 bottom_left_layer_
.get());
222 if (bottom_right_layer_
.get()) {
223 gfx::Transform transform
;
225 size
.width() - bottom_right_layer_
->bounds().width(),
226 size
.height() - bottom_right_layer_
->bounds().height());
227 bottom_right_layer_
->SetTransform(transform
);
228 bottom_right_painter_
->SetClipRect(
229 LayerExceedsSize(bottom_right_layer_
.get(), gfx::Size(right
, bottom
)) ?
230 gfx::Rect(bottom_right_layer_
->bounds().width() - right
,
231 bottom_right_layer_
->bounds().height() - bottom
,
234 bottom_right_layer_
.get());
237 if (center_layer_
.get()) {
238 if (center_width
> 0 && center_height
> 0) {
239 gfx::Transform transform
;
240 transform
.Translate(left
, top
);
241 transform
.Scale(center_width
/ center_layer_
->bounds().width(),
242 center_height
/ center_layer_
->bounds().height());
243 center_layer_
->SetTransform(transform
);
245 center_layer_
->SetVisible(center_width
> 0 && center_height
> 0);
249 void ImageGrid::SetContentBounds(const gfx::Rect
& content_bounds
) {
252 content_bounds
.width() + left_image_width_
+ right_image_width_
,
253 content_bounds
.height() + top_image_height_
+
254 bottom_image_height_
));
256 gfx::Rect(content_bounds
.x() - left_image_width_
,
257 content_bounds
.y() - top_image_height_
,
258 layer_
->bounds().width(),
259 layer_
->bounds().height()));
262 void ImageGrid::ImagePainter::SetClipRect(const gfx::Rect
& clip_rect
,
264 if (clip_rect
!= clip_rect_
) {
265 clip_rect_
= clip_rect
;
266 layer
->SchedulePaint(layer
->bounds());
270 void ImageGrid::ImagePainter::OnPaintLayer(const ui::PaintContext
& context
) {
271 gfx::Size
bounding_size(clip_rect_
.right(), clip_rect_
.bottom());
272 ui::PaintRecorder
recorder(context
, bounding_size
);
273 if (!clip_rect_
.IsEmpty())
274 recorder
.canvas()->ClipRect(clip_rect_
);
275 recorder
.canvas()->DrawImageInt(image_
, 0, 0);
278 void ImageGrid::ImagePainter::OnDelegatedFrameDamage(
279 const gfx::Rect
& damage_rect_in_dip
) {
282 void ImageGrid::ImagePainter::OnDeviceScaleFactorChanged(
283 float device_scale_factor
) {
284 // Redrawing will take care of scale factor change.
287 base::Closure
ImageGrid::ImagePainter::PrepareForLayerBoundsChange() {
288 return base::Closure();
291 void ImageGrid::SetImage(const gfx::Image
* image
,
292 scoped_ptr
<ui::Layer
>* layer_ptr
,
293 scoped_ptr
<ImagePainter
>* painter_ptr
,
295 // Minimum width (for HORIZONTAL) or height (for VERTICAL) of the
296 // |image| so that layers are scaled property if the device scale
297 // factor is non integral.
298 const int kMinimumSize
= 20;
300 // Clean out old layers and painters.
301 if (layer_ptr
->get())
302 layer_
->Remove(layer_ptr
->get());
304 painter_ptr
->reset();
306 // If we're not using an image, we're done.
310 gfx::ImageSkia image_skia
= image
->AsImageSkia();
313 if (image_skia
.width() < kMinimumSize
) {
314 image_skia
= gfx::ImageSkiaOperations::CreateResizedImage(
316 skia::ImageOperations::RESIZE_GOOD
,
317 gfx::Size(kMinimumSize
, image_skia
.height()));
321 if (image_skia
.height() < kMinimumSize
) {
322 image_skia
= gfx::ImageSkiaOperations::CreateResizedImage(
324 skia::ImageOperations::RESIZE_GOOD
,
325 gfx::Size(image_skia
.width(), kMinimumSize
));
332 // Set up the new layer and painter.
333 layer_ptr
->reset(new ui::Layer(ui::LAYER_TEXTURED
));
335 const gfx::Size size
= image_skia
.size();
336 layer_ptr
->get()->SetBounds(gfx::Rect(0, 0, size
.width(), size
.height()));
338 painter_ptr
->reset(new ImagePainter(image_skia
));
339 layer_ptr
->get()->set_delegate(painter_ptr
->get());
340 layer_ptr
->get()->SetFillsBoundsOpaquely(false);
341 layer_ptr
->get()->SetVisible(true);
342 layer_
->Add(layer_ptr
->get());