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/gfx/image/image_skia_operations.h"
6
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "skia/ext/image_operations.h"
10 #include "ui/gfx/canvas.h"
11 #include "ui/gfx/image/canvas_image_source.h"
12 #include "ui/gfx/image/image_skia.h"
13 #include "ui/gfx/image/image_skia_rep.h"
14 #include "ui/gfx/image/image_skia_source.h"
15 #include "ui/gfx/insets.h"
16 #include "ui/gfx/point.h"
17 #include "ui/gfx/point_conversions.h"
18 #include "ui/gfx/rect.h"
19 #include "ui/gfx/rect_conversions.h"
20 #include "ui/gfx/size.h"
21 #include "ui/gfx/size_conversions.h"
22 #include "ui/gfx/skbitmap_operations.h"
23 #include "ui/gfx/skia_util.h"
24
25 namespace gfx {
26 namespace {
27
DIPToPixelSize(gfx::Size dip_size,float scale)28 gfx::Size DIPToPixelSize(gfx::Size dip_size, float scale) {
29 return ToCeiledSize(ScaleSize(dip_size, scale));
30 }
31
DIPToPixelBounds(gfx::Rect dip_bounds,float scale)32 gfx::Rect DIPToPixelBounds(gfx::Rect dip_bounds, float scale) {
33 return gfx::Rect(ToFlooredPoint(ScalePoint(dip_bounds.origin(), scale)),
34 DIPToPixelSize(dip_bounds.size(), scale));
35 }
36
37 // Returns an image rep for the ImageSkiaSource to return to visually indicate
38 // an error.
GetErrorImageRep(float scale,const gfx::Size & pixel_size)39 ImageSkiaRep GetErrorImageRep(float scale, const gfx::Size& pixel_size) {
40 SkBitmap bitmap;
41 bitmap.setConfig(
42 SkBitmap::kARGB_8888_Config, pixel_size.width(), pixel_size.height());
43 bitmap.allocPixels();
44 bitmap.eraseColor(SK_ColorRED);
45 return gfx::ImageSkiaRep(bitmap, scale);
46 }
47
48 // A base image source class that creates an image from two source images.
49 // This class guarantees that two ImageSkiaReps have have the same pixel size.
50 class BinaryImageSource : public gfx::ImageSkiaSource {
51 protected:
BinaryImageSource(const ImageSkia & first,const ImageSkia & second,const char * source_name)52 BinaryImageSource(const ImageSkia& first,
53 const ImageSkia& second,
54 const char* source_name)
55 : first_(first),
56 second_(second),
57 source_name_(source_name) {
58 }
~BinaryImageSource()59 virtual ~BinaryImageSource() {
60 }
61
62 // gfx::ImageSkiaSource overrides:
GetImageForScale(float scale)63 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
64 ImageSkiaRep first_rep = first_.GetRepresentation(scale);
65 ImageSkiaRep second_rep = second_.GetRepresentation(scale);
66 if (first_rep.pixel_size() != second_rep.pixel_size()) {
67 DCHECK_NE(first_rep.scale(), second_rep.scale());
68 if (first_rep.scale() == second_rep.scale()) {
69 LOG(ERROR) << "ImageSkiaRep size mismatch in " << source_name_;
70 return GetErrorImageRep(first_rep.scale(),first_rep.pixel_size());
71 }
72 first_rep = first_.GetRepresentation(1.0f);
73 second_rep = second_.GetRepresentation(1.0f);
74 DCHECK_EQ(first_rep.pixel_width(), second_rep.pixel_width());
75 DCHECK_EQ(first_rep.pixel_height(), second_rep.pixel_height());
76 if (first_rep.pixel_size() != second_rep.pixel_size()) {
77 LOG(ERROR) << "ImageSkiaRep size mismatch in " << source_name_;
78 return GetErrorImageRep(first_rep.scale(), first_rep.pixel_size());
79 }
80 } else {
81 DCHECK_EQ(first_rep.scale(), second_rep.scale());
82 }
83 return CreateImageSkiaRep(first_rep, second_rep);
84 }
85
86 // Creates a final image from two ImageSkiaReps. The pixel size of
87 // the two images are guaranteed to be the same.
88 virtual ImageSkiaRep CreateImageSkiaRep(
89 const ImageSkiaRep& first_rep,
90 const ImageSkiaRep& second_rep) const = 0;
91
92 private:
93 const ImageSkia first_;
94 const ImageSkia second_;
95 // The name of a class that implements the BinaryImageSource.
96 // The subclass is responsible for managing the memory.
97 const char* source_name_;
98
99 DISALLOW_COPY_AND_ASSIGN(BinaryImageSource);
100 };
101
102 class BlendingImageSource : public BinaryImageSource {
103 public:
BlendingImageSource(const ImageSkia & first,const ImageSkia & second,double alpha)104 BlendingImageSource(const ImageSkia& first,
105 const ImageSkia& second,
106 double alpha)
107 : BinaryImageSource(first, second, "BlendingImageSource"),
108 alpha_(alpha) {
109 }
110
~BlendingImageSource()111 virtual ~BlendingImageSource() {
112 }
113
114 // BinaryImageSource overrides:
CreateImageSkiaRep(const ImageSkiaRep & first_rep,const ImageSkiaRep & second_rep) const115 virtual ImageSkiaRep CreateImageSkiaRep(
116 const ImageSkiaRep& first_rep,
117 const ImageSkiaRep& second_rep) const OVERRIDE {
118 SkBitmap blended = SkBitmapOperations::CreateBlendedBitmap(
119 first_rep.sk_bitmap(), second_rep.sk_bitmap(), alpha_);
120 return ImageSkiaRep(blended, first_rep.scale());
121 }
122
123 private:
124 double alpha_;
125
126 DISALLOW_COPY_AND_ASSIGN(BlendingImageSource);
127 };
128
129 class SuperimposedImageSource : public gfx::CanvasImageSource {
130 public:
SuperimposedImageSource(const ImageSkia & first,const ImageSkia & second)131 SuperimposedImageSource(const ImageSkia& first,
132 const ImageSkia& second)
133 : gfx::CanvasImageSource(first.size(), false /* is opaque */),
134 first_(first),
135 second_(second) {
136 }
137
~SuperimposedImageSource()138 virtual ~SuperimposedImageSource() {}
139
140 // gfx::CanvasImageSource override.
Draw(Canvas * canvas)141 virtual void Draw(Canvas* canvas) OVERRIDE {
142 canvas->DrawImageInt(first_, 0, 0);
143 canvas->DrawImageInt(second_,
144 (first_.width() - second_.width()) / 2,
145 (first_.height() - second_.height()) / 2);
146 }
147
148 private:
149 const ImageSkia first_;
150 const ImageSkia second_;
151
152 DISALLOW_COPY_AND_ASSIGN(SuperimposedImageSource);
153 };
154
155 class TransparentImageSource : public gfx::ImageSkiaSource {
156 public:
TransparentImageSource(const ImageSkia & image,double alpha)157 TransparentImageSource(const ImageSkia& image, double alpha)
158 : image_(image),
159 alpha_(alpha) {
160 }
161
~TransparentImageSource()162 virtual ~TransparentImageSource() {}
163
164 private:
165 // gfx::ImageSkiaSource overrides:
GetImageForScale(float scale)166 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
167 ImageSkiaRep image_rep = image_.GetRepresentation(scale);
168 SkBitmap alpha;
169 alpha.setConfig(SkBitmap::kARGB_8888_Config,
170 image_rep.pixel_width(),
171 image_rep.pixel_height());
172 alpha.allocPixels();
173 alpha.eraseColor(SkColorSetARGB(alpha_ * 255, 0, 0, 0));
174 return ImageSkiaRep(
175 SkBitmapOperations::CreateMaskedBitmap(image_rep.sk_bitmap(), alpha),
176 image_rep.scale());
177 }
178
179 ImageSkia image_;
180 double alpha_;
181
182 DISALLOW_COPY_AND_ASSIGN(TransparentImageSource);
183 };
184
185 class MaskedImageSource : public BinaryImageSource {
186 public:
MaskedImageSource(const ImageSkia & rgb,const ImageSkia & alpha)187 MaskedImageSource(const ImageSkia& rgb, const ImageSkia& alpha)
188 : BinaryImageSource(rgb, alpha, "MaskedImageSource") {
189 }
190
~MaskedImageSource()191 virtual ~MaskedImageSource() {
192 }
193
194 // BinaryImageSource overrides:
CreateImageSkiaRep(const ImageSkiaRep & first_rep,const ImageSkiaRep & second_rep) const195 virtual ImageSkiaRep CreateImageSkiaRep(
196 const ImageSkiaRep& first_rep,
197 const ImageSkiaRep& second_rep) const OVERRIDE {
198 return ImageSkiaRep(SkBitmapOperations::CreateMaskedBitmap(
199 first_rep.sk_bitmap(), second_rep.sk_bitmap()),
200 first_rep.scale());
201 }
202
203 private:
204 DISALLOW_COPY_AND_ASSIGN(MaskedImageSource);
205 };
206
207 class TiledImageSource : public gfx::ImageSkiaSource {
208 public:
TiledImageSource(const ImageSkia & source,int src_x,int src_y,int dst_w,int dst_h)209 TiledImageSource(const ImageSkia& source,
210 int src_x, int src_y,
211 int dst_w, int dst_h)
212 : source_(source),
213 src_x_(src_x),
214 src_y_(src_y),
215 dst_w_(dst_w),
216 dst_h_(dst_h) {
217 }
218
~TiledImageSource()219 virtual ~TiledImageSource() {
220 }
221
222 // gfx::ImageSkiaSource overrides:
GetImageForScale(float scale)223 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
224 ImageSkiaRep source_rep = source_.GetRepresentation(scale);
225 gfx::Rect bounds = DIPToPixelBounds(gfx::Rect(src_x_, src_y_, dst_w_,
226 dst_h_), source_rep.scale());
227 return ImageSkiaRep(
228 SkBitmapOperations::CreateTiledBitmap(
229 source_rep.sk_bitmap(),
230 bounds.x(), bounds.y(), bounds.width(), bounds.height()),
231 source_rep.scale());
232 }
233
234 private:
235 const ImageSkia source_;
236 const int src_x_;
237 const int src_y_;
238 const int dst_w_;
239 const int dst_h_;
240
241 DISALLOW_COPY_AND_ASSIGN(TiledImageSource);
242 };
243
244 class HSLImageSource : public gfx::ImageSkiaSource {
245 public:
HSLImageSource(const ImageSkia & image,const color_utils::HSL & hsl_shift)246 HSLImageSource(const ImageSkia& image,
247 const color_utils::HSL& hsl_shift)
248 : image_(image),
249 hsl_shift_(hsl_shift) {
250 }
251
~HSLImageSource()252 virtual ~HSLImageSource() {
253 }
254
255 // gfx::ImageSkiaSource overrides:
GetImageForScale(float scale)256 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
257 ImageSkiaRep image_rep = image_.GetRepresentation(scale);
258 return gfx::ImageSkiaRep(
259 SkBitmapOperations::CreateHSLShiftedBitmap(image_rep.sk_bitmap(),
260 hsl_shift_), image_rep.scale());
261 }
262
263 private:
264 const gfx::ImageSkia image_;
265 const color_utils::HSL hsl_shift_;
266 DISALLOW_COPY_AND_ASSIGN(HSLImageSource);
267 };
268
269 // ImageSkiaSource which uses SkBitmapOperations::CreateButtonBackground
270 // to generate image reps for the target image. The image and mask can be
271 // diferent sizes (crbug.com/171725).
272 class ButtonImageSource: public gfx::ImageSkiaSource {
273 public:
ButtonImageSource(SkColor color,const ImageSkia & image,const ImageSkia & mask)274 ButtonImageSource(SkColor color,
275 const ImageSkia& image,
276 const ImageSkia& mask)
277 : color_(color),
278 image_(image),
279 mask_(mask) {
280 }
281
~ButtonImageSource()282 virtual ~ButtonImageSource() {
283 }
284
285 // gfx::ImageSkiaSource overrides:
GetImageForScale(float scale)286 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
287 ImageSkiaRep image_rep = image_.GetRepresentation(scale);
288 ImageSkiaRep mask_rep = mask_.GetRepresentation(scale);
289 if (image_rep.scale() != mask_rep.scale()) {
290 image_rep = image_.GetRepresentation(1.0f);
291 mask_rep = mask_.GetRepresentation(1.0f);
292 }
293 return gfx::ImageSkiaRep(
294 SkBitmapOperations::CreateButtonBackground(color_,
295 image_rep.sk_bitmap(), mask_rep.sk_bitmap()),
296 image_rep.scale());
297 }
298
299 private:
300 const SkColor color_;
301 const ImageSkia image_;
302 const ImageSkia mask_;
303
304 DISALLOW_COPY_AND_ASSIGN(ButtonImageSource);
305 };
306
307 // ImageSkiaSource which uses SkBitmap::extractSubset to generate image reps
308 // for the target image.
309 class ExtractSubsetImageSource: public gfx::ImageSkiaSource {
310 public:
ExtractSubsetImageSource(const gfx::ImageSkia & image,const gfx::Rect & subset_bounds)311 ExtractSubsetImageSource(const gfx::ImageSkia& image,
312 const gfx::Rect& subset_bounds)
313 : image_(image),
314 subset_bounds_(subset_bounds) {
315 }
316
~ExtractSubsetImageSource()317 virtual ~ExtractSubsetImageSource() {
318 }
319
320 // gfx::ImageSkiaSource overrides:
GetImageForScale(float scale)321 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
322 ImageSkiaRep image_rep = image_.GetRepresentation(scale);
323 SkIRect subset_bounds_in_pixel = RectToSkIRect(
324 DIPToPixelBounds(subset_bounds_, image_rep.scale()));
325 SkBitmap dst;
326 bool success = image_rep.sk_bitmap().extractSubset(&dst,
327 subset_bounds_in_pixel);
328 DCHECK(success);
329 return gfx::ImageSkiaRep(dst, image_rep.scale());
330 }
331
332 private:
333 const gfx::ImageSkia image_;
334 const gfx::Rect subset_bounds_;
335
336 DISALLOW_COPY_AND_ASSIGN(ExtractSubsetImageSource);
337 };
338
339 // ResizeSource resizes relevant image reps in |source| to |target_dip_size|
340 // for requested scale factors.
341 class ResizeSource : public ImageSkiaSource {
342 public:
ResizeSource(const ImageSkia & source,skia::ImageOperations::ResizeMethod method,const Size & target_dip_size)343 ResizeSource(const ImageSkia& source,
344 skia::ImageOperations::ResizeMethod method,
345 const Size& target_dip_size)
346 : source_(source),
347 resize_method_(method),
348 target_dip_size_(target_dip_size) {
349 }
~ResizeSource()350 virtual ~ResizeSource() {}
351
352 // gfx::ImageSkiaSource overrides:
GetImageForScale(float scale)353 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
354 const ImageSkiaRep& image_rep = source_.GetRepresentation(scale);
355 if (image_rep.GetWidth() == target_dip_size_.width() &&
356 image_rep.GetHeight() == target_dip_size_.height())
357 return image_rep;
358
359 const Size target_pixel_size = DIPToPixelSize(target_dip_size_, scale);
360 const SkBitmap resized = skia::ImageOperations::Resize(
361 image_rep.sk_bitmap(),
362 resize_method_,
363 target_pixel_size.width(),
364 target_pixel_size.height());
365 return ImageSkiaRep(resized, scale);
366 }
367
368 private:
369 const ImageSkia source_;
370 skia::ImageOperations::ResizeMethod resize_method_;
371 const Size target_dip_size_;
372
373 DISALLOW_COPY_AND_ASSIGN(ResizeSource);
374 };
375
376 // DropShadowSource generates image reps with drop shadow for image reps in
377 // |source| that represent requested scale factors.
378 class DropShadowSource : public ImageSkiaSource {
379 public:
DropShadowSource(const ImageSkia & source,const ShadowValues & shadows_in_dip)380 DropShadowSource(const ImageSkia& source,
381 const ShadowValues& shadows_in_dip)
382 : source_(source),
383 shaodws_in_dip_(shadows_in_dip) {
384 }
~DropShadowSource()385 virtual ~DropShadowSource() {}
386
387 // gfx::ImageSkiaSource overrides:
GetImageForScale(float scale)388 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
389 const ImageSkiaRep& image_rep = source_.GetRepresentation(scale);
390
391 ShadowValues shadows_in_pixel;
392 for (size_t i = 0; i < shaodws_in_dip_.size(); ++i)
393 shadows_in_pixel.push_back(shaodws_in_dip_[i].Scale(scale));
394
395 const SkBitmap shadow_bitmap = SkBitmapOperations::CreateDropShadow(
396 image_rep.sk_bitmap(),
397 shadows_in_pixel);
398 return ImageSkiaRep(shadow_bitmap, image_rep.scale());
399 }
400
401 private:
402 const ImageSkia source_;
403 const ShadowValues shaodws_in_dip_;
404
405 DISALLOW_COPY_AND_ASSIGN(DropShadowSource);
406 };
407
408 // RotatedSource generates image reps that are rotations of those in
409 // |source| that represent requested scale factors.
410 class RotatedSource : public ImageSkiaSource {
411 public:
RotatedSource(const ImageSkia & source,SkBitmapOperations::RotationAmount rotation)412 RotatedSource(const ImageSkia& source,
413 SkBitmapOperations::RotationAmount rotation)
414 : source_(source),
415 rotation_(rotation) {
416 }
~RotatedSource()417 virtual ~RotatedSource() {}
418
419 // gfx::ImageSkiaSource overrides:
GetImageForScale(float scale)420 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
421 const ImageSkiaRep& image_rep = source_.GetRepresentation(scale);
422 const SkBitmap rotated_bitmap =
423 SkBitmapOperations::Rotate(image_rep.sk_bitmap(), rotation_);
424 return ImageSkiaRep(rotated_bitmap, image_rep.scale());
425 }
426
427 private:
428 const ImageSkia source_;
429 const SkBitmapOperations::RotationAmount rotation_;
430
431 DISALLOW_COPY_AND_ASSIGN(RotatedSource);
432 };
433
434
435 } // namespace
436
437 // static
CreateBlendedImage(const ImageSkia & first,const ImageSkia & second,double alpha)438 ImageSkia ImageSkiaOperations::CreateBlendedImage(const ImageSkia& first,
439 const ImageSkia& second,
440 double alpha) {
441 if (first.isNull() || second.isNull())
442 return ImageSkia();
443
444 return ImageSkia(new BlendingImageSource(first, second, alpha), first.size());
445 }
446
447 // static
CreateSuperimposedImage(const ImageSkia & first,const ImageSkia & second)448 ImageSkia ImageSkiaOperations::CreateSuperimposedImage(
449 const ImageSkia& first,
450 const ImageSkia& second) {
451 if (first.isNull() || second.isNull())
452 return ImageSkia();
453
454 return ImageSkia(new SuperimposedImageSource(first, second), first.size());
455 }
456
457 // static
CreateTransparentImage(const ImageSkia & image,double alpha)458 ImageSkia ImageSkiaOperations::CreateTransparentImage(const ImageSkia& image,
459 double alpha) {
460 if (image.isNull())
461 return ImageSkia();
462
463 return ImageSkia(new TransparentImageSource(image, alpha), image.size());
464 }
465
466 // static
CreateMaskedImage(const ImageSkia & rgb,const ImageSkia & alpha)467 ImageSkia ImageSkiaOperations::CreateMaskedImage(const ImageSkia& rgb,
468 const ImageSkia& alpha) {
469 if (rgb.isNull() || alpha.isNull())
470 return ImageSkia();
471
472 return ImageSkia(new MaskedImageSource(rgb, alpha), rgb.size());
473 }
474
475 // static
CreateTiledImage(const ImageSkia & source,int src_x,int src_y,int dst_w,int dst_h)476 ImageSkia ImageSkiaOperations::CreateTiledImage(const ImageSkia& source,
477 int src_x, int src_y,
478 int dst_w, int dst_h) {
479 if (source.isNull())
480 return ImageSkia();
481
482 return ImageSkia(new TiledImageSource(source, src_x, src_y, dst_w, dst_h),
483 gfx::Size(dst_w, dst_h));
484 }
485
486 // static
CreateHSLShiftedImage(const ImageSkia & image,const color_utils::HSL & hsl_shift)487 ImageSkia ImageSkiaOperations::CreateHSLShiftedImage(
488 const ImageSkia& image,
489 const color_utils::HSL& hsl_shift) {
490 if (image.isNull())
491 return ImageSkia();
492
493 return ImageSkia(new HSLImageSource(image, hsl_shift), image.size());
494 }
495
496 // static
CreateButtonBackground(SkColor color,const ImageSkia & image,const ImageSkia & mask)497 ImageSkia ImageSkiaOperations::CreateButtonBackground(SkColor color,
498 const ImageSkia& image,
499 const ImageSkia& mask) {
500 if (image.isNull() || mask.isNull())
501 return ImageSkia();
502
503 return ImageSkia(new ButtonImageSource(color, image, mask), mask.size());
504 }
505
506 // static
ExtractSubset(const ImageSkia & image,const Rect & subset_bounds)507 ImageSkia ImageSkiaOperations::ExtractSubset(const ImageSkia& image,
508 const Rect& subset_bounds) {
509 gfx::Rect clipped_bounds =
510 gfx::IntersectRects(subset_bounds, gfx::Rect(image.size()));
511 if (image.isNull() || clipped_bounds.IsEmpty()) {
512 return ImageSkia();
513 }
514
515 return ImageSkia(new ExtractSubsetImageSource(image, clipped_bounds),
516 clipped_bounds.size());
517 }
518
519 // static
CreateResizedImage(const ImageSkia & source,skia::ImageOperations::ResizeMethod method,const Size & target_dip_size)520 ImageSkia ImageSkiaOperations::CreateResizedImage(
521 const ImageSkia& source,
522 skia::ImageOperations::ResizeMethod method,
523 const Size& target_dip_size) {
524 if (source.isNull())
525 return ImageSkia();
526
527 return ImageSkia(new ResizeSource(source, method, target_dip_size),
528 target_dip_size);
529 }
530
531 // static
CreateImageWithDropShadow(const ImageSkia & source,const ShadowValues & shadows)532 ImageSkia ImageSkiaOperations::CreateImageWithDropShadow(
533 const ImageSkia& source,
534 const ShadowValues& shadows) {
535 if (source.isNull())
536 return ImageSkia();
537
538 const gfx::Insets shadow_padding = -gfx::ShadowValue::GetMargin(shadows);
539 gfx::Size shadow_image_size = source.size();
540 shadow_image_size.Enlarge(shadow_padding.width(),
541 shadow_padding.height());
542 return ImageSkia(new DropShadowSource(source, shadows), shadow_image_size);
543 }
544
545 // static
CreateRotatedImage(const ImageSkia & source,SkBitmapOperations::RotationAmount rotation)546 ImageSkia ImageSkiaOperations::CreateRotatedImage(
547 const ImageSkia& source,
548 SkBitmapOperations::RotationAmount rotation) {
549 if (source.isNull())
550 return ImageSkia();
551
552 return ImageSkia(new RotatedSource(source, rotation),
553 SkBitmapOperations::ROTATION_180_CW == rotation ?
554 source.size() :
555 gfx::Size(source.height(), source.width()));
556
557 }
558
559 } // namespace gfx
560