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.
4
5#include "ui/views/controls/image_view.h"
6
7#include "base/logging.h"
8#include "base/strings/utf_string_conversions.h"
9#include "third_party/skia/include/core/SkPaint.h"
10#include "ui/base/accessibility/accessible_view_state.h"
11#include "ui/gfx/canvas.h"
12#include "ui/gfx/insets.h"
13#include "ui/views/painter.h"
14
15namespace views {
16
17ImageView::ImageView()
18    : image_size_set_(false),
19      horiz_alignment_(CENTER),
20      vert_alignment_(CENTER),
21      interactive_(true),
22      last_paint_scale_(0.f),
23      last_painted_bitmap_pixels_(NULL),
24      focus_painter_(Painter::CreateDashedFocusPainter()) {
25}
26
27ImageView::~ImageView() {
28}
29
30void ImageView::SetImage(const gfx::ImageSkia& img) {
31  if (IsImageEqual(img))
32    return;
33
34  last_painted_bitmap_pixels_ = NULL;
35  gfx::Size pref_size(GetPreferredSize());
36  image_ = img;
37  if (pref_size != GetPreferredSize())
38    PreferredSizeChanged();
39  SchedulePaint();
40}
41
42void ImageView::SetImage(const gfx::ImageSkia* image_skia) {
43  if (image_skia) {
44    SetImage(*image_skia);
45  } else {
46    gfx::ImageSkia t;
47    SetImage(t);
48  }
49}
50
51const gfx::ImageSkia& ImageView::GetImage() {
52  return image_;
53}
54
55void ImageView::SetImageSize(const gfx::Size& image_size) {
56  image_size_set_ = true;
57  image_size_ = image_size;
58  PreferredSizeChanged();
59}
60
61bool ImageView::GetImageSize(gfx::Size* image_size) {
62  DCHECK(image_size);
63  if (image_size_set_)
64    *image_size = image_size_;
65  return image_size_set_;
66}
67
68gfx::Rect ImageView::GetImageBounds() const {
69  gfx::Size image_size(image_size_set_ ?
70    image_size_ : gfx::Size(image_.width(), image_.height()));
71  return gfx::Rect(ComputeImageOrigin(image_size), image_size);
72}
73
74void ImageView::ResetImageSize() {
75  image_size_set_ = false;
76}
77
78void ImageView::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
79  focus_painter_ = focus_painter.Pass();
80}
81
82gfx::Size ImageView::GetPreferredSize() {
83  gfx::Insets insets = GetInsets();
84  if (image_size_set_) {
85    gfx::Size image_size;
86    GetImageSize(&image_size);
87    image_size.Enlarge(insets.width(), insets.height());
88    return image_size;
89  }
90  return gfx::Size(image_.width() + insets.width(),
91                   image_.height() + insets.height());
92}
93
94bool ImageView::IsImageEqual(const gfx::ImageSkia& img) const {
95  // Even though we copy ImageSkia in SetImage() the backing store
96  // (ImageSkiaStorage) is not copied and may have changed since the last call
97  // to SetImage(). The expectation is that SetImage() with different pixels is
98  // treated as though the image changed. For this reason we compare not only
99  // the backing store but also the pixels of the last image we painted.
100  return image_.BackedBySameObjectAs(img) &&
101      last_paint_scale_ != 0.0f &&
102      last_painted_bitmap_pixels_ ==
103      img.GetRepresentation(last_paint_scale_).sk_bitmap().getPixels();
104}
105
106gfx::Point ImageView::ComputeImageOrigin(const gfx::Size& image_size) const {
107  gfx::Insets insets = GetInsets();
108
109  int x;
110  // In order to properly handle alignment of images in RTL locales, we need
111  // to flip the meaning of trailing and leading. For example, if the
112  // horizontal alignment is set to trailing, then we'll use left alignment for
113  // the image instead of right alignment if the UI layout is RTL.
114  Alignment actual_horiz_alignment = horiz_alignment_;
115  if (base::i18n::IsRTL() && (horiz_alignment_ != CENTER))
116    actual_horiz_alignment = (horiz_alignment_ == LEADING) ? TRAILING : LEADING;
117  switch (actual_horiz_alignment) {
118    case LEADING:  x = insets.left();                                 break;
119    case TRAILING: x = width() - insets.right() - image_size.width(); break;
120    case CENTER:   x = (width() - image_size.width()) / 2;            break;
121    default:       NOTREACHED(); x = 0;                               break;
122  }
123
124  int y;
125  switch (vert_alignment_) {
126    case LEADING:  y = insets.top();                                     break;
127    case TRAILING: y = height() - insets.bottom() - image_size.height(); break;
128    case CENTER:   y = (height() - image_size.height()) / 2;             break;
129    default:       NOTREACHED(); y = 0;                                  break;
130  }
131
132  return gfx::Point(x, y);
133}
134
135void ImageView::OnFocus() {
136  View::OnFocus();
137  if (focus_painter_.get())
138    SchedulePaint();
139}
140
141void ImageView::OnBlur() {
142  View::OnBlur();
143  if (focus_painter_.get())
144    SchedulePaint();
145}
146
147void ImageView::OnPaint(gfx::Canvas* canvas) {
148  View::OnPaint(canvas);
149  OnPaintImage(canvas);
150  Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
151}
152
153void ImageView::GetAccessibleState(ui::AccessibleViewState* state) {
154  state->role = ui::AccessibilityTypes::ROLE_GRAPHIC;
155  state->name = tooltip_text_;
156}
157
158void ImageView::SetHorizontalAlignment(Alignment ha) {
159  if (ha != horiz_alignment_) {
160    horiz_alignment_ = ha;
161    SchedulePaint();
162  }
163}
164
165ImageView::Alignment ImageView::GetHorizontalAlignment() const {
166  return horiz_alignment_;
167}
168
169void ImageView::SetVerticalAlignment(Alignment va) {
170  if (va != vert_alignment_) {
171    vert_alignment_ = va;
172    SchedulePaint();
173  }
174}
175
176ImageView::Alignment ImageView::GetVerticalAlignment() const {
177  return vert_alignment_;
178}
179
180void ImageView::SetTooltipText(const string16& tooltip) {
181  tooltip_text_ = tooltip;
182}
183
184string16 ImageView::GetTooltipText() const {
185  return tooltip_text_;
186}
187
188bool ImageView::GetTooltipText(const gfx::Point& p, string16* tooltip) const {
189  if (tooltip_text_.empty())
190    return false;
191
192  *tooltip = GetTooltipText();
193  return true;
194}
195
196bool ImageView::HitTestRect(const gfx::Rect& rect) const {
197  return interactive_ ? View::HitTestRect(rect) : false;
198}
199
200
201void ImageView::OnPaintImage(gfx::Canvas* canvas) {
202  last_paint_scale_ = canvas->image_scale();
203  last_painted_bitmap_pixels_ = NULL;
204
205  if (image_.isNull())
206    return;
207
208  gfx::Rect image_bounds(GetImageBounds());
209  if (image_bounds.IsEmpty())
210    return;
211
212  if (image_bounds.size() != gfx::Size(image_.width(), image_.height())) {
213    // Resize case
214    SkPaint paint;
215    paint.setFilterBitmap(true);
216    canvas->DrawImageInt(image_, 0, 0, image_.width(), image_.height(),
217        image_bounds.x(), image_bounds.y(), image_bounds.width(),
218        image_bounds.height(), true, paint);
219  } else {
220    canvas->DrawImageInt(image_, image_bounds.x(), image_bounds.y());
221  }
222  last_painted_bitmap_pixels_ =
223      image_.GetRepresentation(last_paint_scale_).sk_bitmap().getPixels();
224}
225
226}  // namespace views
227