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
) {
41 bitmap
.allocN32Pixels(pixel_size
.width(), pixel_size
.height());
42 bitmap
.eraseColor(SK_ColorRED
);
43 return gfx::ImageSkiaRep(bitmap
, scale
);
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
{
50 BinaryImageSource(const ImageSkia
& first
,
51 const ImageSkia
& second
,
52 const char* source_name
)
55 source_name_(source_name
) {
57 virtual ~BinaryImageSource() {
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());
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());
79 DCHECK_EQ(first_rep
.scale(), second_rep
.scale());
81 return CreateImageSkiaRep(first_rep
, second_rep
);
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;
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_
;
97 DISALLOW_COPY_AND_ASSIGN(BinaryImageSource
);
100 class BlendingImageSource
: public BinaryImageSource
{
102 BlendingImageSource(const ImageSkia
& first
,
103 const ImageSkia
& second
,
105 : BinaryImageSource(first
, second
, "BlendingImageSource"),
109 virtual ~BlendingImageSource() {
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());
124 DISALLOW_COPY_AND_ASSIGN(BlendingImageSource
);
127 class SuperimposedImageSource
: public gfx::CanvasImageSource
{
129 SuperimposedImageSource(const ImageSkia
& first
,
130 const ImageSkia
& second
)
131 : gfx::CanvasImageSource(first
.size(), false /* is opaque */),
136 virtual ~SuperimposedImageSource() {}
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);
147 const ImageSkia first_
;
148 const ImageSkia second_
;
150 DISALLOW_COPY_AND_ASSIGN(SuperimposedImageSource
);
153 class TransparentImageSource
: public gfx::ImageSkiaSource
{
155 TransparentImageSource(const ImageSkia
& image
, double alpha
)
160 virtual ~TransparentImageSource() {}
163 // gfx::ImageSkiaSource overrides:
164 virtual ImageSkiaRep
GetImageForScale(float scale
) OVERRIDE
{
165 ImageSkiaRep image_rep
= image_
.GetRepresentation(scale
);
167 alpha
.allocN32Pixels(image_rep
.pixel_width(),
168 image_rep
.pixel_height());
169 alpha
.eraseColor(SkColorSetARGB(alpha_
* 255, 0, 0, 0));
171 SkBitmapOperations::CreateMaskedBitmap(image_rep
.sk_bitmap(), alpha
),
178 DISALLOW_COPY_AND_ASSIGN(TransparentImageSource
);
181 class MaskedImageSource
: public BinaryImageSource
{
183 MaskedImageSource(const ImageSkia
& rgb
, const ImageSkia
& alpha
)
184 : BinaryImageSource(rgb
, alpha
, "MaskedImageSource") {
187 virtual ~MaskedImageSource() {
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()),
200 DISALLOW_COPY_AND_ASSIGN(MaskedImageSource
);
203 class TiledImageSource
: public gfx::ImageSkiaSource
{
205 TiledImageSource(const ImageSkia
& source
,
206 int src_x
, int src_y
,
207 int dst_w
, int dst_h
)
215 virtual ~TiledImageSource() {
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());
224 SkBitmapOperations::CreateTiledBitmap(
225 source_rep
.sk_bitmap(),
226 bounds
.x(), bounds
.y(), bounds
.width(), bounds
.height()),
231 const ImageSkia source_
;
237 DISALLOW_COPY_AND_ASSIGN(TiledImageSource
);
240 class HSLImageSource
: public gfx::ImageSkiaSource
{
242 HSLImageSource(const ImageSkia
& image
,
243 const color_utils::HSL
& hsl_shift
)
245 hsl_shift_(hsl_shift
) {
248 virtual ~HSLImageSource() {
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());
260 const gfx::ImageSkia image_
;
261 const color_utils::HSL hsl_shift_
;
262 DISALLOW_COPY_AND_ASSIGN(HSLImageSource
);
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
{
270 ButtonImageSource(SkColor color
,
271 const ImageSkia
& image
,
272 const ImageSkia
& mask
)
278 virtual ~ButtonImageSource() {
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
);
289 return gfx::ImageSkiaRep(
290 SkBitmapOperations::CreateButtonBackground(color_
,
291 image_rep
.sk_bitmap(), mask_rep
.sk_bitmap()),
296 const SkColor color_
;
297 const ImageSkia image_
;
298 const ImageSkia mask_
;
300 DISALLOW_COPY_AND_ASSIGN(ButtonImageSource
);
303 // ImageSkiaSource which uses SkBitmap::extractSubset to generate image reps
304 // for the target image.
305 class ExtractSubsetImageSource
: public gfx::ImageSkiaSource
{
307 ExtractSubsetImageSource(const gfx::ImageSkia
& image
,
308 const gfx::Rect
& subset_bounds
)
310 subset_bounds_(subset_bounds
) {
313 virtual ~ExtractSubsetImageSource() {
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()));
322 bool success
= image_rep
.sk_bitmap().extractSubset(&dst
,
323 subset_bounds_in_pixel
);
325 return gfx::ImageSkiaRep(dst
, image_rep
.scale());
329 const gfx::ImageSkia image_
;
330 const gfx::Rect subset_bounds_
;
332 DISALLOW_COPY_AND_ASSIGN(ExtractSubsetImageSource
);
335 // ResizeSource resizes relevant image reps in |source| to |target_dip_size|
336 // for requested scale factors.
337 class ResizeSource
: public ImageSkiaSource
{
339 ResizeSource(const ImageSkia
& source
,
340 skia::ImageOperations::ResizeMethod method
,
341 const Size
& target_dip_size
)
343 resize_method_(method
),
344 target_dip_size_(target_dip_size
) {
346 virtual ~ResizeSource() {}
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())
355 const Size target_pixel_size
= DIPToPixelSize(target_dip_size_
, scale
);
356 const SkBitmap resized
= skia::ImageOperations::Resize(
357 image_rep
.sk_bitmap(),
359 target_pixel_size
.width(),
360 target_pixel_size
.height());
361 return ImageSkiaRep(resized
, scale
);
365 const ImageSkia source_
;
366 skia::ImageOperations::ResizeMethod resize_method_
;
367 const Size target_dip_size_
;
369 DISALLOW_COPY_AND_ASSIGN(ResizeSource
);
372 // DropShadowSource generates image reps with drop shadow for image reps in
373 // |source| that represent requested scale factors.
374 class DropShadowSource
: public ImageSkiaSource
{
376 DropShadowSource(const ImageSkia
& source
,
377 const ShadowValues
& shadows_in_dip
)
379 shaodws_in_dip_(shadows_in_dip
) {
381 virtual ~DropShadowSource() {}
383 // gfx::ImageSkiaSource overrides:
384 virtual ImageSkiaRep
GetImageForScale(float scale
) OVERRIDE
{
385 const ImageSkiaRep
& image_rep
= source_
.GetRepresentation(scale
);
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
));
391 const SkBitmap shadow_bitmap
= SkBitmapOperations::CreateDropShadow(
392 image_rep
.sk_bitmap(),
394 return ImageSkiaRep(shadow_bitmap
, image_rep
.scale());
398 const ImageSkia source_
;
399 const ShadowValues shaodws_in_dip_
;
401 DISALLOW_COPY_AND_ASSIGN(DropShadowSource
);
404 // RotatedSource generates image reps that are rotations of those in
405 // |source| that represent requested scale factors.
406 class RotatedSource
: public ImageSkiaSource
{
408 RotatedSource(const ImageSkia
& source
,
409 SkBitmapOperations::RotationAmount rotation
)
411 rotation_(rotation
) {
413 virtual ~RotatedSource() {}
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());
424 const ImageSkia source_
;
425 const SkBitmapOperations::RotationAmount rotation_
;
427 DISALLOW_COPY_AND_ASSIGN(RotatedSource
);
434 ImageSkia
ImageSkiaOperations::CreateBlendedImage(const ImageSkia
& first
,
435 const ImageSkia
& second
,
437 if (first
.isNull() || second
.isNull())
440 return ImageSkia(new BlendingImageSource(first
, second
, alpha
), first
.size());
444 ImageSkia
ImageSkiaOperations::CreateSuperimposedImage(
445 const ImageSkia
& first
,
446 const ImageSkia
& second
) {
447 if (first
.isNull() || second
.isNull())
450 return ImageSkia(new SuperimposedImageSource(first
, second
), first
.size());
454 ImageSkia
ImageSkiaOperations::CreateTransparentImage(const ImageSkia
& image
,
459 return ImageSkia(new TransparentImageSource(image
, alpha
), image
.size());
463 ImageSkia
ImageSkiaOperations::CreateMaskedImage(const ImageSkia
& rgb
,
464 const ImageSkia
& alpha
) {
465 if (rgb
.isNull() || alpha
.isNull())
468 return ImageSkia(new MaskedImageSource(rgb
, alpha
), rgb
.size());
472 ImageSkia
ImageSkiaOperations::CreateTiledImage(const ImageSkia
& source
,
473 int src_x
, int src_y
,
474 int dst_w
, int dst_h
) {
478 return ImageSkia(new TiledImageSource(source
, src_x
, src_y
, dst_w
, dst_h
),
479 gfx::Size(dst_w
, dst_h
));
483 ImageSkia
ImageSkiaOperations::CreateHSLShiftedImage(
484 const ImageSkia
& image
,
485 const color_utils::HSL
& hsl_shift
) {
489 return ImageSkia(new HSLImageSource(image
, hsl_shift
), image
.size());
493 ImageSkia
ImageSkiaOperations::CreateButtonBackground(SkColor color
,
494 const ImageSkia
& image
,
495 const ImageSkia
& mask
) {
496 if (image
.isNull() || mask
.isNull())
499 return ImageSkia(new ButtonImageSource(color
, image
, mask
), mask
.size());
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()) {
511 return ImageSkia(new ExtractSubsetImageSource(image
, clipped_bounds
),
512 clipped_bounds
.size());
516 ImageSkia
ImageSkiaOperations::CreateResizedImage(
517 const ImageSkia
& source
,
518 skia::ImageOperations::ResizeMethod method
,
519 const Size
& target_dip_size
) {
523 return ImageSkia(new ResizeSource(source
, method
, target_dip_size
),
528 ImageSkia
ImageSkiaOperations::CreateImageWithDropShadow(
529 const ImageSkia
& source
,
530 const ShadowValues
& shadows
) {
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
);
542 ImageSkia
ImageSkiaOperations::CreateRotatedImage(
543 const ImageSkia
& source
,
544 SkBitmapOperations::RotationAmount rotation
) {
548 return ImageSkia(new RotatedSource(source
, rotation
),
549 SkBitmapOperations::ROTATION_180_CW
== rotation
?
551 gfx::Size(source
.height(), source
.width()));