// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/gfx/image/image_skia_operations.h" #include "base/command_line.h" #include "base/logging.h" #include "skia/ext/image_operations.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/canvas_image_source.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_rep.h" #include "ui/gfx/image/image_skia_source.h" #include "ui/gfx/insets.h" #include "ui/gfx/point.h" #include "ui/gfx/point_conversions.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/size.h" #include "ui/gfx/size_conversions.h" #include "ui/gfx/skbitmap_operations.h" #include "ui/gfx/skia_util.h" namespace gfx { namespace { gfx::Size DIPToPixelSize(gfx::Size dip_size, float scale) { return ToCeiledSize(ScaleSize(dip_size, scale)); } gfx::Rect DIPToPixelBounds(gfx::Rect dip_bounds, float scale) { return gfx::Rect(ToFlooredPoint(ScalePoint(dip_bounds.origin(), scale)), DIPToPixelSize(dip_bounds.size(), scale)); } // Returns an image rep for the ImageSkiaSource to return to visually indicate // an error. ImageSkiaRep GetErrorImageRep(float scale, const gfx::Size& pixel_size) { SkBitmap bitmap; bitmap.allocN32Pixels(pixel_size.width(), pixel_size.height()); bitmap.eraseColor(SK_ColorRED); return gfx::ImageSkiaRep(bitmap, scale); } // A base image source class that creates an image from two source images. // This class guarantees that two ImageSkiaReps have have the same pixel size. class BinaryImageSource : public gfx::ImageSkiaSource { protected: BinaryImageSource(const ImageSkia& first, const ImageSkia& second, const char* source_name) : first_(first), second_(second), source_name_(source_name) { } virtual ~BinaryImageSource() { } // gfx::ImageSkiaSource overrides: virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { ImageSkiaRep first_rep = first_.GetRepresentation(scale); ImageSkiaRep second_rep = second_.GetRepresentation(scale); if (first_rep.pixel_size() != second_rep.pixel_size()) { DCHECK_NE(first_rep.scale(), second_rep.scale()); if (first_rep.scale() == second_rep.scale()) { LOG(ERROR) << "ImageSkiaRep size mismatch in " << source_name_; return GetErrorImageRep(first_rep.scale(),first_rep.pixel_size()); } first_rep = first_.GetRepresentation(1.0f); second_rep = second_.GetRepresentation(1.0f); DCHECK_EQ(first_rep.pixel_width(), second_rep.pixel_width()); DCHECK_EQ(first_rep.pixel_height(), second_rep.pixel_height()); if (first_rep.pixel_size() != second_rep.pixel_size()) { LOG(ERROR) << "ImageSkiaRep size mismatch in " << source_name_; return GetErrorImageRep(first_rep.scale(), first_rep.pixel_size()); } } else { DCHECK_EQ(first_rep.scale(), second_rep.scale()); } return CreateImageSkiaRep(first_rep, second_rep); } // Creates a final image from two ImageSkiaReps. The pixel size of // the two images are guaranteed to be the same. virtual ImageSkiaRep CreateImageSkiaRep( const ImageSkiaRep& first_rep, const ImageSkiaRep& second_rep) const = 0; private: const ImageSkia first_; const ImageSkia second_; // The name of a class that implements the BinaryImageSource. // The subclass is responsible for managing the memory. const char* source_name_; DISALLOW_COPY_AND_ASSIGN(BinaryImageSource); }; class BlendingImageSource : public BinaryImageSource { public: BlendingImageSource(const ImageSkia& first, const ImageSkia& second, double alpha) : BinaryImageSource(first, second, "BlendingImageSource"), alpha_(alpha) { } virtual ~BlendingImageSource() { } // BinaryImageSource overrides: virtual ImageSkiaRep CreateImageSkiaRep( const ImageSkiaRep& first_rep, const ImageSkiaRep& second_rep) const OVERRIDE { SkBitmap blended = SkBitmapOperations::CreateBlendedBitmap( first_rep.sk_bitmap(), second_rep.sk_bitmap(), alpha_); return ImageSkiaRep(blended, first_rep.scale()); } private: double alpha_; DISALLOW_COPY_AND_ASSIGN(BlendingImageSource); }; class SuperimposedImageSource : public gfx::CanvasImageSource { public: SuperimposedImageSource(const ImageSkia& first, const ImageSkia& second) : gfx::CanvasImageSource(first.size(), false /* is opaque */), first_(first), second_(second) { } virtual ~SuperimposedImageSource() {} // gfx::CanvasImageSource override. virtual void Draw(Canvas* canvas) OVERRIDE { canvas->DrawImageInt(first_, 0, 0); canvas->DrawImageInt(second_, (first_.width() - second_.width()) / 2, (first_.height() - second_.height()) / 2); } private: const ImageSkia first_; const ImageSkia second_; DISALLOW_COPY_AND_ASSIGN(SuperimposedImageSource); }; class TransparentImageSource : public gfx::ImageSkiaSource { public: TransparentImageSource(const ImageSkia& image, double alpha) : image_(image), alpha_(alpha) { } virtual ~TransparentImageSource() {} private: // gfx::ImageSkiaSource overrides: virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { ImageSkiaRep image_rep = image_.GetRepresentation(scale); SkBitmap alpha; alpha.allocN32Pixels(image_rep.pixel_width(), image_rep.pixel_height()); alpha.eraseColor(SkColorSetARGB(alpha_ * 255, 0, 0, 0)); return ImageSkiaRep( SkBitmapOperations::CreateMaskedBitmap(image_rep.sk_bitmap(), alpha), image_rep.scale()); } ImageSkia image_; double alpha_; DISALLOW_COPY_AND_ASSIGN(TransparentImageSource); }; class MaskedImageSource : public BinaryImageSource { public: MaskedImageSource(const ImageSkia& rgb, const ImageSkia& alpha) : BinaryImageSource(rgb, alpha, "MaskedImageSource") { } virtual ~MaskedImageSource() { } // BinaryImageSource overrides: virtual ImageSkiaRep CreateImageSkiaRep( const ImageSkiaRep& first_rep, const ImageSkiaRep& second_rep) const OVERRIDE { return ImageSkiaRep(SkBitmapOperations::CreateMaskedBitmap( first_rep.sk_bitmap(), second_rep.sk_bitmap()), first_rep.scale()); } private: DISALLOW_COPY_AND_ASSIGN(MaskedImageSource); }; class TiledImageSource : public gfx::ImageSkiaSource { public: TiledImageSource(const ImageSkia& source, int src_x, int src_y, int dst_w, int dst_h) : source_(source), src_x_(src_x), src_y_(src_y), dst_w_(dst_w), dst_h_(dst_h) { } virtual ~TiledImageSource() { } // gfx::ImageSkiaSource overrides: virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { ImageSkiaRep source_rep = source_.GetRepresentation(scale); gfx::Rect bounds = DIPToPixelBounds(gfx::Rect(src_x_, src_y_, dst_w_, dst_h_), source_rep.scale()); return ImageSkiaRep( SkBitmapOperations::CreateTiledBitmap( source_rep.sk_bitmap(), bounds.x(), bounds.y(), bounds.width(), bounds.height()), source_rep.scale()); } private: const ImageSkia source_; const int src_x_; const int src_y_; const int dst_w_; const int dst_h_; DISALLOW_COPY_AND_ASSIGN(TiledImageSource); }; class HSLImageSource : public gfx::ImageSkiaSource { public: HSLImageSource(const ImageSkia& image, const color_utils::HSL& hsl_shift) : image_(image), hsl_shift_(hsl_shift) { } virtual ~HSLImageSource() { } // gfx::ImageSkiaSource overrides: virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { ImageSkiaRep image_rep = image_.GetRepresentation(scale); return gfx::ImageSkiaRep( SkBitmapOperations::CreateHSLShiftedBitmap(image_rep.sk_bitmap(), hsl_shift_), image_rep.scale()); } private: const gfx::ImageSkia image_; const color_utils::HSL hsl_shift_; DISALLOW_COPY_AND_ASSIGN(HSLImageSource); }; // ImageSkiaSource which uses SkBitmapOperations::CreateButtonBackground // to generate image reps for the target image. The image and mask can be // diferent sizes (crbug.com/171725). class ButtonImageSource: public gfx::ImageSkiaSource { public: ButtonImageSource(SkColor color, const ImageSkia& image, const ImageSkia& mask) : color_(color), image_(image), mask_(mask) { } virtual ~ButtonImageSource() { } // gfx::ImageSkiaSource overrides: virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { ImageSkiaRep image_rep = image_.GetRepresentation(scale); ImageSkiaRep mask_rep = mask_.GetRepresentation(scale); if (image_rep.scale() != mask_rep.scale()) { image_rep = image_.GetRepresentation(1.0f); mask_rep = mask_.GetRepresentation(1.0f); } return gfx::ImageSkiaRep( SkBitmapOperations::CreateButtonBackground(color_, image_rep.sk_bitmap(), mask_rep.sk_bitmap()), image_rep.scale()); } private: const SkColor color_; const ImageSkia image_; const ImageSkia mask_; DISALLOW_COPY_AND_ASSIGN(ButtonImageSource); }; // ImageSkiaSource which uses SkBitmap::extractSubset to generate image reps // for the target image. class ExtractSubsetImageSource: public gfx::ImageSkiaSource { public: ExtractSubsetImageSource(const gfx::ImageSkia& image, const gfx::Rect& subset_bounds) : image_(image), subset_bounds_(subset_bounds) { } virtual ~ExtractSubsetImageSource() { } // gfx::ImageSkiaSource overrides: virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { ImageSkiaRep image_rep = image_.GetRepresentation(scale); SkIRect subset_bounds_in_pixel = RectToSkIRect( DIPToPixelBounds(subset_bounds_, image_rep.scale())); SkBitmap dst; bool success = image_rep.sk_bitmap().extractSubset(&dst, subset_bounds_in_pixel); DCHECK(success); return gfx::ImageSkiaRep(dst, image_rep.scale()); } private: const gfx::ImageSkia image_; const gfx::Rect subset_bounds_; DISALLOW_COPY_AND_ASSIGN(ExtractSubsetImageSource); }; // ResizeSource resizes relevant image reps in |source| to |target_dip_size| // for requested scale factors. class ResizeSource : public ImageSkiaSource { public: ResizeSource(const ImageSkia& source, skia::ImageOperations::ResizeMethod method, const Size& target_dip_size) : source_(source), resize_method_(method), target_dip_size_(target_dip_size) { } virtual ~ResizeSource() {} // gfx::ImageSkiaSource overrides: virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { const ImageSkiaRep& image_rep = source_.GetRepresentation(scale); if (image_rep.GetWidth() == target_dip_size_.width() && image_rep.GetHeight() == target_dip_size_.height()) return image_rep; const Size target_pixel_size = DIPToPixelSize(target_dip_size_, scale); const SkBitmap resized = skia::ImageOperations::Resize( image_rep.sk_bitmap(), resize_method_, target_pixel_size.width(), target_pixel_size.height()); return ImageSkiaRep(resized, scale); } private: const ImageSkia source_; skia::ImageOperations::ResizeMethod resize_method_; const Size target_dip_size_; DISALLOW_COPY_AND_ASSIGN(ResizeSource); }; // DropShadowSource generates image reps with drop shadow for image reps in // |source| that represent requested scale factors. class DropShadowSource : public ImageSkiaSource { public: DropShadowSource(const ImageSkia& source, const ShadowValues& shadows_in_dip) : source_(source), shaodws_in_dip_(shadows_in_dip) { } virtual ~DropShadowSource() {} // gfx::ImageSkiaSource overrides: virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { const ImageSkiaRep& image_rep = source_.GetRepresentation(scale); ShadowValues shadows_in_pixel; for (size_t i = 0; i < shaodws_in_dip_.size(); ++i) shadows_in_pixel.push_back(shaodws_in_dip_[i].Scale(scale)); const SkBitmap shadow_bitmap = SkBitmapOperations::CreateDropShadow( image_rep.sk_bitmap(), shadows_in_pixel); return ImageSkiaRep(shadow_bitmap, image_rep.scale()); } private: const ImageSkia source_; const ShadowValues shaodws_in_dip_; DISALLOW_COPY_AND_ASSIGN(DropShadowSource); }; // RotatedSource generates image reps that are rotations of those in // |source| that represent requested scale factors. class RotatedSource : public ImageSkiaSource { public: RotatedSource(const ImageSkia& source, SkBitmapOperations::RotationAmount rotation) : source_(source), rotation_(rotation) { } virtual ~RotatedSource() {} // gfx::ImageSkiaSource overrides: virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { const ImageSkiaRep& image_rep = source_.GetRepresentation(scale); const SkBitmap rotated_bitmap = SkBitmapOperations::Rotate(image_rep.sk_bitmap(), rotation_); return ImageSkiaRep(rotated_bitmap, image_rep.scale()); } private: const ImageSkia source_; const SkBitmapOperations::RotationAmount rotation_; DISALLOW_COPY_AND_ASSIGN(RotatedSource); }; } // namespace // static ImageSkia ImageSkiaOperations::CreateBlendedImage(const ImageSkia& first, const ImageSkia& second, double alpha) { if (first.isNull() || second.isNull()) return ImageSkia(); return ImageSkia(new BlendingImageSource(first, second, alpha), first.size()); } // static ImageSkia ImageSkiaOperations::CreateSuperimposedImage( const ImageSkia& first, const ImageSkia& second) { if (first.isNull() || second.isNull()) return ImageSkia(); return ImageSkia(new SuperimposedImageSource(first, second), first.size()); } // static ImageSkia ImageSkiaOperations::CreateTransparentImage(const ImageSkia& image, double alpha) { if (image.isNull()) return ImageSkia(); return ImageSkia(new TransparentImageSource(image, alpha), image.size()); } // static ImageSkia ImageSkiaOperations::CreateMaskedImage(const ImageSkia& rgb, const ImageSkia& alpha) { if (rgb.isNull() || alpha.isNull()) return ImageSkia(); return ImageSkia(new MaskedImageSource(rgb, alpha), rgb.size()); } // static ImageSkia ImageSkiaOperations::CreateTiledImage(const ImageSkia& source, int src_x, int src_y, int dst_w, int dst_h) { if (source.isNull()) return ImageSkia(); return ImageSkia(new TiledImageSource(source, src_x, src_y, dst_w, dst_h), gfx::Size(dst_w, dst_h)); } // static ImageSkia ImageSkiaOperations::CreateHSLShiftedImage( const ImageSkia& image, const color_utils::HSL& hsl_shift) { if (image.isNull()) return ImageSkia(); return ImageSkia(new HSLImageSource(image, hsl_shift), image.size()); } // static ImageSkia ImageSkiaOperations::CreateButtonBackground(SkColor color, const ImageSkia& image, const ImageSkia& mask) { if (image.isNull() || mask.isNull()) return ImageSkia(); return ImageSkia(new ButtonImageSource(color, image, mask), mask.size()); } // static ImageSkia ImageSkiaOperations::ExtractSubset(const ImageSkia& image, const Rect& subset_bounds) { gfx::Rect clipped_bounds = gfx::IntersectRects(subset_bounds, gfx::Rect(image.size())); if (image.isNull() || clipped_bounds.IsEmpty()) { return ImageSkia(); } return ImageSkia(new ExtractSubsetImageSource(image, clipped_bounds), clipped_bounds.size()); } // static ImageSkia ImageSkiaOperations::CreateResizedImage( const ImageSkia& source, skia::ImageOperations::ResizeMethod method, const Size& target_dip_size) { if (source.isNull()) return ImageSkia(); return ImageSkia(new ResizeSource(source, method, target_dip_size), target_dip_size); } // static ImageSkia ImageSkiaOperations::CreateImageWithDropShadow( const ImageSkia& source, const ShadowValues& shadows) { if (source.isNull()) return ImageSkia(); const gfx::Insets shadow_padding = -gfx::ShadowValue::GetMargin(shadows); gfx::Size shadow_image_size = source.size(); shadow_image_size.Enlarge(shadow_padding.width(), shadow_padding.height()); return ImageSkia(new DropShadowSource(source, shadows), shadow_image_size); } // static ImageSkia ImageSkiaOperations::CreateRotatedImage( const ImageSkia& source, SkBitmapOperations::RotationAmount rotation) { if (source.isNull()) return ImageSkia(); return ImageSkia(new RotatedSource(source, rotation), SkBitmapOperations::ROTATION_180_CW == rotation ? source.size() : gfx::Size(source.height(), source.width())); } } // namespace gfx