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