Home | History | Annotate | Download | only in image
      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
    556