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