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