Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / wm / core / image_grid.cc
blob700deb90362026d70d6127a94243c4abeac29458
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"
7 #include <algorithm>
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"
22 using std::max;
23 using std::min;
25 namespace wm {
26 namespace {
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
30 // center image.
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,
39 ui::Layer* layer,
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())
50 : gfx::Size();
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();
59 } // namespace
61 gfx::RectF ImageGrid::TestAPI::GetTransformedLayerBounds(
62 const ui::Layer& layer) {
63 gfx::RectF bounds = gfx::RectF(layer.bounds());
64 layer.transform().TransformRect(&bounds);
65 return bounds;
68 ImageGrid::ImageGrid()
69 : layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)),
70 top_image_height_(0),
71 bottom_image_height_(0),
72 left_image_width_(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, &center_layer_, &center_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);
100 SetImage(
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.
122 size_.SetSize(0, 0);
125 void ImageGrid::SetSize(const gfx::Size& size) {
126 if (size_ == size)
127 return;
129 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;
164 transform.Translate(
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;
183 transform.Translate(
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)) :
196 gfx::Rect(),
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,
206 right, top) :
207 gfx::Rect(),
208 top_right_layer_.get());
210 if (bottom_left_layer_.get()) {
211 gfx::Transform transform;
212 transform.Translate(
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,
218 left, bottom) :
219 gfx::Rect(),
220 bottom_left_layer_.get());
222 if (bottom_right_layer_.get()) {
223 gfx::Transform transform;
224 transform.Translate(
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,
232 right, bottom) :
233 gfx::Rect(),
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) {
251 SetSize(gfx::Size(
252 content_bounds.width() + left_image_width_ + right_image_width_,
253 content_bounds.height() + top_image_height_ +
254 bottom_image_height_));
255 layer_->SetBounds(
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,
263 ui::Layer* layer) {
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,
294 ImageType type) {
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());
303 layer_ptr->reset();
304 painter_ptr->reset();
306 // If we're not using an image, we're done.
307 if (!image)
308 return;
310 gfx::ImageSkia image_skia = image->AsImageSkia();
311 switch (type) {
312 case HORIZONTAL:
313 if (image_skia.width() < kMinimumSize) {
314 image_skia = gfx::ImageSkiaOperations::CreateResizedImage(
315 image_skia,
316 skia::ImageOperations::RESIZE_GOOD,
317 gfx::Size(kMinimumSize, image_skia.height()));
319 break;
320 case VERTICAL:
321 if (image_skia.height() < kMinimumSize) {
322 image_skia = gfx::ImageSkiaOperations::CreateResizedImage(
323 image_skia,
324 skia::ImageOperations::RESIZE_GOOD,
325 gfx::Size(image_skia.width(), kMinimumSize));
327 break;
328 case NONE:
329 break;
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());
345 } // namespace wm