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.
5 #include "ui/gfx/image/image_skia_operations.h"
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"
28 gfx::Size
DIPToPixelSize(gfx::Size dip_size
, float scale
) {
29 return ToCeiledSize(ScaleSize(dip_size
, scale
));
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
));
37 // Returns an image rep for the ImageSkiaSource to return to visually indicate
39 ImageSkiaRep
GetErrorImageRep(float scale
, const gfx::Size
& pixel_size
) {
42 SkBitmap::kARGB_8888_Config
, pixel_size
.width(), pixel_size
.height());
44 bitmap
.eraseColor(SK_ColorRED
);
45 return gfx::ImageSkiaRep(bitmap
, scale
);
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
{
52 BinaryImageSource(const ImageSkia
& first
,
53 const ImageSkia
& second
,
54 const char* source_name
)
57 source_name_(source_name
) {
59 virtual ~BinaryImageSource() {
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());
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());
81 DCHECK_EQ(first_rep
.scale(), second_rep
.scale());
83 return CreateImageSkiaRep(first_rep
, second_rep
);
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;
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_
;
99 DISALLOW_COPY_AND_ASSIGN(BinaryImageSource
);
102 class BlendingImageSource
: public BinaryImageSource
{
104 BlendingImageSource(const ImageSkia
& first
,
105 const ImageSkia
& second
,
107 : BinaryImageSource(first
, second
, "BlendingImageSource"),
111 virtual ~BlendingImageSource() {
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());
126 DISALLOW_COPY_AND_ASSIGN(BlendingImageSource
);
129 class SuperimposedImageSource
: public gfx::CanvasImageSource
{
131 SuperimposedImageSource(const ImageSkia
& first
,
132 const ImageSkia
& second
)
133 : gfx::CanvasImageSource(first
.size(), false /* is opaque */),
138 virtual ~SuperimposedImageSource() {}
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);
149 const ImageSkia first_
;
150 const ImageSkia second_
;
152 DISALLOW_COPY_AND_ASSIGN(SuperimposedImageSource
);
155 class TransparentImageSource
: public gfx::ImageSkiaSource
{
157 TransparentImageSource(const ImageSkia
& image
, double alpha
)
162 virtual ~TransparentImageSource() {}
165 // gfx::ImageSkiaSource overrides:
166 virtual ImageSkiaRep
GetImageForScale(float scale
) OVERRIDE
{
167 ImageSkiaRep image_rep
= image_
.GetRepresentation(scale
);
169 alpha
.setConfig(SkBitmap::kARGB_8888_Config
,
170 image_rep
.pixel_width(),
171 image_rep
.pixel_height());
173 alpha
.eraseColor(SkColorSetARGB(alpha_
* 255, 0, 0, 0));
175 SkBitmapOperations::CreateMaskedBitmap(image_rep
.sk_bitmap(), alpha
),
182 DISALLOW_COPY_AND_ASSIGN(TransparentImageSource
);
185 class MaskedImageSource
: public BinaryImageSource
{
187 MaskedImageSource(const ImageSkia
& rgb
, const ImageSkia
& alpha
)
188 : BinaryImageSource(rgb
, alpha
, "MaskedImageSource") {
191 virtual ~MaskedImageSource() {
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()),
204 DISALLOW_COPY_AND_ASSIGN(MaskedImageSource
);
207 class TiledImageSource
: public gfx::ImageSkiaSource
{
209 TiledImageSource(const ImageSkia
& source
,
210 int src_x
, int src_y
,
211 int dst_w
, int dst_h
)
219 virtual ~TiledImageSource() {
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());
228 SkBitmapOperations::CreateTiledBitmap(
229 source_rep
.sk_bitmap(),
230 bounds
.x(), bounds
.y(), bounds
.width(), bounds
.height()),
235 const ImageSkia source_
;
241 DISALLOW_COPY_AND_ASSIGN(TiledImageSource
);
244 class HSLImageSource
: public gfx::ImageSkiaSource
{
246 HSLImageSource(const ImageSkia
& image
,
247 const color_utils::HSL
& hsl_shift
)
249 hsl_shift_(hsl_shift
) {
252 virtual ~HSLImageSource() {
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());
264 const gfx::ImageSkia image_
;
265 const color_utils::HSL hsl_shift_
;
266 DISALLOW_COPY_AND_ASSIGN(HSLImageSource
);
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
{
274 ButtonImageSource(SkColor color
,
275 const ImageSkia
& image
,
276 const ImageSkia
& mask
)
282 virtual ~ButtonImageSource() {
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
);
293 return gfx::ImageSkiaRep(
294 SkBitmapOperations::CreateButtonBackground(color_
,
295 image_rep
.sk_bitmap(), mask_rep
.sk_bitmap()),
300 const SkColor color_
;
301 const ImageSkia image_
;
302 const ImageSkia mask_
;
304 DISALLOW_COPY_AND_ASSIGN(ButtonImageSource
);
307 // ImageSkiaSource which uses SkBitmap::extractSubset to generate image reps
308 // for the target image.
309 class ExtractSubsetImageSource
: public gfx::ImageSkiaSource
{
311 ExtractSubsetImageSource(const gfx::ImageSkia
& image
,
312 const gfx::Rect
& subset_bounds
)
314 subset_bounds_(subset_bounds
) {
317 virtual ~ExtractSubsetImageSource() {
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()));
326 bool success
= image_rep
.sk_bitmap().extractSubset(&dst
,
327 subset_bounds_in_pixel
);
329 return gfx::ImageSkiaRep(dst
, image_rep
.scale());
333 const gfx::ImageSkia image_
;
334 const gfx::Rect subset_bounds_
;
336 DISALLOW_COPY_AND_ASSIGN(ExtractSubsetImageSource
);
339 // ResizeSource resizes relevant image reps in |source| to |target_dip_size|
340 // for requested scale factors.
341 class ResizeSource
: public ImageSkiaSource
{
343 ResizeSource(const ImageSkia
& source
,
344 skia::ImageOperations::ResizeMethod method
,
345 const Size
& target_dip_size
)
347 resize_method_(method
),
348 target_dip_size_(target_dip_size
) {
350 virtual ~ResizeSource() {}
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())
359 const Size target_pixel_size
= DIPToPixelSize(target_dip_size_
, scale
);
360 const SkBitmap resized
= skia::ImageOperations::Resize(
361 image_rep
.sk_bitmap(),
363 target_pixel_size
.width(),
364 target_pixel_size
.height());
365 return ImageSkiaRep(resized
, scale
);
369 const ImageSkia source_
;
370 skia::ImageOperations::ResizeMethod resize_method_
;
371 const Size target_dip_size_
;
373 DISALLOW_COPY_AND_ASSIGN(ResizeSource
);
376 // DropShadowSource generates image reps with drop shadow for image reps in
377 // |source| that represent requested scale factors.
378 class DropShadowSource
: public ImageSkiaSource
{
380 DropShadowSource(const ImageSkia
& source
,
381 const ShadowValues
& shadows_in_dip
)
383 shaodws_in_dip_(shadows_in_dip
) {
385 virtual ~DropShadowSource() {}
387 // gfx::ImageSkiaSource overrides:
388 virtual ImageSkiaRep
GetImageForScale(float scale
) OVERRIDE
{
389 const ImageSkiaRep
& image_rep
= source_
.GetRepresentation(scale
);
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
));
395 const SkBitmap shadow_bitmap
= SkBitmapOperations::CreateDropShadow(
396 image_rep
.sk_bitmap(),
398 return ImageSkiaRep(shadow_bitmap
, image_rep
.scale());
402 const ImageSkia source_
;
403 const ShadowValues shaodws_in_dip_
;
405 DISALLOW_COPY_AND_ASSIGN(DropShadowSource
);
408 // RotatedSource generates image reps that are rotations of those in
409 // |source| that represent requested scale factors.
410 class RotatedSource
: public ImageSkiaSource
{
412 RotatedSource(const ImageSkia
& source
,
413 SkBitmapOperations::RotationAmount rotation
)
415 rotation_(rotation
) {
417 virtual ~RotatedSource() {}
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());
428 const ImageSkia source_
;
429 const SkBitmapOperations::RotationAmount rotation_
;
431 DISALLOW_COPY_AND_ASSIGN(RotatedSource
);
438 ImageSkia
ImageSkiaOperations::CreateBlendedImage(const ImageSkia
& first
,
439 const ImageSkia
& second
,
441 if (first
.isNull() || second
.isNull())
444 return ImageSkia(new BlendingImageSource(first
, second
, alpha
), first
.size());
448 ImageSkia
ImageSkiaOperations::CreateSuperimposedImage(
449 const ImageSkia
& first
,
450 const ImageSkia
& second
) {
451 if (first
.isNull() || second
.isNull())
454 return ImageSkia(new SuperimposedImageSource(first
, second
), first
.size());
458 ImageSkia
ImageSkiaOperations::CreateTransparentImage(const ImageSkia
& image
,
463 return ImageSkia(new TransparentImageSource(image
, alpha
), image
.size());
467 ImageSkia
ImageSkiaOperations::CreateMaskedImage(const ImageSkia
& rgb
,
468 const ImageSkia
& alpha
) {
469 if (rgb
.isNull() || alpha
.isNull())
472 return ImageSkia(new MaskedImageSource(rgb
, alpha
), rgb
.size());
476 ImageSkia
ImageSkiaOperations::CreateTiledImage(const ImageSkia
& source
,
477 int src_x
, int src_y
,
478 int dst_w
, int dst_h
) {
482 return ImageSkia(new TiledImageSource(source
, src_x
, src_y
, dst_w
, dst_h
),
483 gfx::Size(dst_w
, dst_h
));
487 ImageSkia
ImageSkiaOperations::CreateHSLShiftedImage(
488 const ImageSkia
& image
,
489 const color_utils::HSL
& hsl_shift
) {
493 return ImageSkia(new HSLImageSource(image
, hsl_shift
), image
.size());
497 ImageSkia
ImageSkiaOperations::CreateButtonBackground(SkColor color
,
498 const ImageSkia
& image
,
499 const ImageSkia
& mask
) {
500 if (image
.isNull() || mask
.isNull())
503 return ImageSkia(new ButtonImageSource(color
, image
, mask
), mask
.size());
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()) {
515 return ImageSkia(new ExtractSubsetImageSource(image
, clipped_bounds
),
516 clipped_bounds
.size());
520 ImageSkia
ImageSkiaOperations::CreateResizedImage(
521 const ImageSkia
& source
,
522 skia::ImageOperations::ResizeMethod method
,
523 const Size
& target_dip_size
) {
527 return ImageSkia(new ResizeSource(source
, method
, target_dip_size
),
532 ImageSkia
ImageSkiaOperations::CreateImageWithDropShadow(
533 const ImageSkia
& source
,
534 const ShadowValues
& shadows
) {
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
);
546 ImageSkia
ImageSkiaOperations::CreateRotatedImage(
547 const ImageSkia
& source
,
548 SkBitmapOperations::RotationAmount rotation
) {
552 return ImageSkia(new RotatedSource(source
, rotation
),
553 SkBitmapOperations::ROTATION_180_CW
== rotation
?
555 gfx::Size(source
.height(), source
.width()));