• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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