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.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:
     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   }
     59   virtual ~BinaryImageSource() {
     60   }
     61 
     62   // gfx::ImageSkiaSource overrides:
     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:
    104   BlendingImageSource(const ImageSkia& first,
    105                       const ImageSkia& second,
    106                       double alpha)
    107       : BinaryImageSource(first, second, "BlendingImageSource"),
    108         alpha_(alpha) {
    109   }
    110 
    111   virtual ~BlendingImageSource() {
    112   }
    113 
    114   // BinaryImageSource overrides:
    115   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:
    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 
    138   virtual ~SuperimposedImageSource() {}
    139 
    140   // gfx::CanvasImageSource override.
    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:
    157   TransparentImageSource(const ImageSkia& image, double alpha)
    158       : image_(image),
    159         alpha_(alpha) {
    160   }
    161 
    162   virtual ~TransparentImageSource() {}
    163 
    164  private:
    165   // gfx::ImageSkiaSource overrides:
    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:
    187   MaskedImageSource(const ImageSkia& rgb, const ImageSkia& alpha)
    188       : BinaryImageSource(rgb, alpha, "MaskedImageSource") {
    189   }
    190 
    191   virtual ~MaskedImageSource() {
    192   }
    193 
    194   // BinaryImageSource overrides:
    195   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:
    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 
    219   virtual ~TiledImageSource() {
    220   }
    221 
    222   // gfx::ImageSkiaSource overrides:
    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:
    246   HSLImageSource(const ImageSkia& image,
    247                  const color_utils::HSL& hsl_shift)
    248       : image_(image),
    249         hsl_shift_(hsl_shift) {
    250   }
    251 
    252   virtual ~HSLImageSource() {
    253   }
    254 
    255   // gfx::ImageSkiaSource overrides:
    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:
    274   ButtonImageSource(SkColor color,
    275                     const ImageSkia& image,
    276                     const ImageSkia& mask)
    277       : color_(color),
    278         image_(image),
    279         mask_(mask) {
    280   }
    281 
    282   virtual ~ButtonImageSource() {
    283   }
    284 
    285   // gfx::ImageSkiaSource overrides:
    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:
    311   ExtractSubsetImageSource(const gfx::ImageSkia& image,
    312                            const gfx::Rect& subset_bounds)
    313       : image_(image),
    314         subset_bounds_(subset_bounds) {
    315   }
    316 
    317   virtual ~ExtractSubsetImageSource() {
    318   }
    319 
    320   // gfx::ImageSkiaSource overrides:
    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:
    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   }
    350   virtual ~ResizeSource() {}
    351 
    352   // gfx::ImageSkiaSource overrides:
    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:
    380   DropShadowSource(const ImageSkia& source,
    381                    const ShadowValues& shadows_in_dip)
    382       : source_(source),
    383         shaodws_in_dip_(shadows_in_dip) {
    384   }
    385   virtual ~DropShadowSource() {}
    386 
    387   // gfx::ImageSkiaSource overrides:
    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:
    412   RotatedSource(const ImageSkia& source,
    413                 SkBitmapOperations::RotationAmount rotation)
    414     : source_(source),
    415       rotation_(rotation) {
    416   }
    417   virtual ~RotatedSource() {}
    418 
    419   // gfx::ImageSkiaSource overrides:
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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