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/geometry/insets.h"
12 #include "ui/gfx/geometry/point.h"
13 #include "ui/gfx/geometry/point_conversions.h"
14 #include "ui/gfx/geometry/rect.h"
15 #include "ui/gfx/geometry/rect_conversions.h"
16 #include "ui/gfx/geometry/size.h"
17 #include "ui/gfx/geometry/size_conversions.h"
18 #include "ui/gfx/image/canvas_image_source.h"
19 #include "ui/gfx/image/image_skia.h"
20 #include "ui/gfx/image/image_skia_rep.h"
21 #include "ui/gfx/image/image_skia_source.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 ~BinaryImageSource() override
{}
59 // gfx::ImageSkiaSource overrides:
60 ImageSkiaRep
GetImageForScale(float scale
) override
{
61 ImageSkiaRep first_rep
= first_
.GetRepresentation(scale
);
62 ImageSkiaRep second_rep
= second_
.GetRepresentation(scale
);
63 if (first_rep
.pixel_size() != second_rep
.pixel_size()) {
64 DCHECK_NE(first_rep
.scale(), second_rep
.scale());
65 if (first_rep
.scale() == second_rep
.scale()) {
66 LOG(ERROR
) << "ImageSkiaRep size mismatch in " << source_name_
;
67 return GetErrorImageRep(first_rep
.scale(),first_rep
.pixel_size());
69 first_rep
= first_
.GetRepresentation(1.0f
);
70 second_rep
= second_
.GetRepresentation(1.0f
);
71 DCHECK_EQ(first_rep
.pixel_width(), second_rep
.pixel_width());
72 DCHECK_EQ(first_rep
.pixel_height(), second_rep
.pixel_height());
73 if (first_rep
.pixel_size() != second_rep
.pixel_size()) {
74 LOG(ERROR
) << "ImageSkiaRep size mismatch in " << source_name_
;
75 return GetErrorImageRep(first_rep
.scale(), first_rep
.pixel_size());
78 DCHECK_EQ(first_rep
.scale(), second_rep
.scale());
80 return CreateImageSkiaRep(first_rep
, second_rep
);
83 // Creates a final image from two ImageSkiaReps. The pixel size of
84 // the two images are guaranteed to be the same.
85 virtual ImageSkiaRep
CreateImageSkiaRep(
86 const ImageSkiaRep
& first_rep
,
87 const ImageSkiaRep
& second_rep
) const = 0;
90 const ImageSkia first_
;
91 const ImageSkia second_
;
92 // The name of a class that implements the BinaryImageSource.
93 // The subclass is responsible for managing the memory.
94 const char* source_name_
;
96 DISALLOW_COPY_AND_ASSIGN(BinaryImageSource
);
99 class BlendingImageSource
: public BinaryImageSource
{
101 BlendingImageSource(const ImageSkia
& first
,
102 const ImageSkia
& second
,
104 : BinaryImageSource(first
, second
, "BlendingImageSource"),
108 ~BlendingImageSource() override
{}
110 // BinaryImageSource overrides:
111 ImageSkiaRep
CreateImageSkiaRep(
112 const ImageSkiaRep
& first_rep
,
113 const ImageSkiaRep
& second_rep
) const override
{
114 SkBitmap blended
= SkBitmapOperations::CreateBlendedBitmap(
115 first_rep
.sk_bitmap(), second_rep
.sk_bitmap(), alpha_
);
116 return ImageSkiaRep(blended
, first_rep
.scale());
122 DISALLOW_COPY_AND_ASSIGN(BlendingImageSource
);
125 class SuperimposedImageSource
: public gfx::CanvasImageSource
{
127 SuperimposedImageSource(const ImageSkia
& first
,
128 const ImageSkia
& second
)
129 : gfx::CanvasImageSource(first
.size(), false /* is opaque */),
134 ~SuperimposedImageSource() override
{}
136 // gfx::CanvasImageSource override.
137 void Draw(Canvas
* canvas
) override
{
138 canvas
->DrawImageInt(first_
, 0, 0);
139 canvas
->DrawImageInt(second_
,
140 (first_
.width() - second_
.width()) / 2,
141 (first_
.height() - second_
.height()) / 2);
145 const ImageSkia first_
;
146 const ImageSkia second_
;
148 DISALLOW_COPY_AND_ASSIGN(SuperimposedImageSource
);
151 class TransparentImageSource
: public gfx::ImageSkiaSource
{
153 TransparentImageSource(const ImageSkia
& image
, double alpha
)
158 ~TransparentImageSource() override
{}
161 // gfx::ImageSkiaSource overrides:
162 ImageSkiaRep
GetImageForScale(float scale
) override
{
163 ImageSkiaRep image_rep
= image_
.GetRepresentation(scale
);
165 alpha
.allocN32Pixels(image_rep
.pixel_width(),
166 image_rep
.pixel_height());
167 alpha
.eraseColor(SkColorSetARGB(alpha_
* 255, 0, 0, 0));
169 SkBitmapOperations::CreateMaskedBitmap(image_rep
.sk_bitmap(), alpha
),
176 DISALLOW_COPY_AND_ASSIGN(TransparentImageSource
);
179 class MaskedImageSource
: public BinaryImageSource
{
181 MaskedImageSource(const ImageSkia
& rgb
, const ImageSkia
& alpha
)
182 : BinaryImageSource(rgb
, alpha
, "MaskedImageSource") {
185 ~MaskedImageSource() override
{}
187 // BinaryImageSource overrides:
188 ImageSkiaRep
CreateImageSkiaRep(
189 const ImageSkiaRep
& first_rep
,
190 const ImageSkiaRep
& second_rep
) const override
{
191 return ImageSkiaRep(SkBitmapOperations::CreateMaskedBitmap(
192 first_rep
.sk_bitmap(), second_rep
.sk_bitmap()),
197 DISALLOW_COPY_AND_ASSIGN(MaskedImageSource
);
200 class TiledImageSource
: public gfx::ImageSkiaSource
{
202 TiledImageSource(const ImageSkia
& source
,
203 int src_x
, int src_y
,
204 int dst_w
, int dst_h
)
212 ~TiledImageSource() override
{}
214 // gfx::ImageSkiaSource overrides:
215 ImageSkiaRep
GetImageForScale(float scale
) override
{
216 ImageSkiaRep source_rep
= source_
.GetRepresentation(scale
);
217 gfx::Rect bounds
= DIPToPixelBounds(gfx::Rect(src_x_
, src_y_
, dst_w_
,
218 dst_h_
), source_rep
.scale());
220 SkBitmapOperations::CreateTiledBitmap(
221 source_rep
.sk_bitmap(),
222 bounds
.x(), bounds
.y(), bounds
.width(), bounds
.height()),
227 const ImageSkia source_
;
233 DISALLOW_COPY_AND_ASSIGN(TiledImageSource
);
236 class HSLImageSource
: public gfx::ImageSkiaSource
{
238 HSLImageSource(const ImageSkia
& image
,
239 const color_utils::HSL
& hsl_shift
)
241 hsl_shift_(hsl_shift
) {
244 ~HSLImageSource() override
{}
246 // gfx::ImageSkiaSource overrides:
247 ImageSkiaRep
GetImageForScale(float scale
) override
{
248 ImageSkiaRep image_rep
= image_
.GetRepresentation(scale
);
249 return gfx::ImageSkiaRep(
250 SkBitmapOperations::CreateHSLShiftedBitmap(image_rep
.sk_bitmap(),
251 hsl_shift_
), image_rep
.scale());
255 const gfx::ImageSkia image_
;
256 const color_utils::HSL hsl_shift_
;
257 DISALLOW_COPY_AND_ASSIGN(HSLImageSource
);
260 // ImageSkiaSource which uses SkBitmapOperations::CreateButtonBackground
261 // to generate image reps for the target image. The image and mask can be
262 // diferent sizes (crbug.com/171725).
263 class ButtonImageSource
: public gfx::ImageSkiaSource
{
265 ButtonImageSource(SkColor color
,
266 const ImageSkia
& image
,
267 const ImageSkia
& mask
)
273 ~ButtonImageSource() override
{}
275 // gfx::ImageSkiaSource overrides:
276 ImageSkiaRep
GetImageForScale(float scale
) override
{
277 ImageSkiaRep image_rep
= image_
.GetRepresentation(scale
);
278 ImageSkiaRep mask_rep
= mask_
.GetRepresentation(scale
);
279 if (image_rep
.scale() != mask_rep
.scale()) {
280 image_rep
= image_
.GetRepresentation(1.0f
);
281 mask_rep
= mask_
.GetRepresentation(1.0f
);
283 return gfx::ImageSkiaRep(
284 SkBitmapOperations::CreateButtonBackground(color_
,
285 image_rep
.sk_bitmap(), mask_rep
.sk_bitmap()),
290 const SkColor color_
;
291 const ImageSkia image_
;
292 const ImageSkia mask_
;
294 DISALLOW_COPY_AND_ASSIGN(ButtonImageSource
);
297 // ImageSkiaSource which uses SkBitmap::extractSubset to generate image reps
298 // for the target image.
299 class ExtractSubsetImageSource
: public gfx::ImageSkiaSource
{
301 ExtractSubsetImageSource(const gfx::ImageSkia
& image
,
302 const gfx::Rect
& subset_bounds
)
304 subset_bounds_(subset_bounds
) {
307 ~ExtractSubsetImageSource() override
{}
309 // gfx::ImageSkiaSource overrides:
310 ImageSkiaRep
GetImageForScale(float scale
) override
{
311 ImageSkiaRep image_rep
= image_
.GetRepresentation(scale
);
312 SkIRect subset_bounds_in_pixel
= RectToSkIRect(
313 DIPToPixelBounds(subset_bounds_
, image_rep
.scale()));
315 bool success
= image_rep
.sk_bitmap().extractSubset(&dst
,
316 subset_bounds_in_pixel
);
318 return gfx::ImageSkiaRep(dst
, image_rep
.scale());
322 const gfx::ImageSkia image_
;
323 const gfx::Rect subset_bounds_
;
325 DISALLOW_COPY_AND_ASSIGN(ExtractSubsetImageSource
);
328 // ResizeSource resizes relevant image reps in |source| to |target_dip_size|
329 // for requested scale factors.
330 class ResizeSource
: public ImageSkiaSource
{
332 ResizeSource(const ImageSkia
& source
,
333 skia::ImageOperations::ResizeMethod method
,
334 const Size
& target_dip_size
)
336 resize_method_(method
),
337 target_dip_size_(target_dip_size
) {
339 ~ResizeSource() override
{}
341 // gfx::ImageSkiaSource overrides:
342 ImageSkiaRep
GetImageForScale(float scale
) override
{
343 const ImageSkiaRep
& image_rep
= source_
.GetRepresentation(scale
);
344 if (image_rep
.GetWidth() == target_dip_size_
.width() &&
345 image_rep
.GetHeight() == target_dip_size_
.height())
348 const Size target_pixel_size
= DIPToPixelSize(target_dip_size_
, scale
);
349 const SkBitmap resized
= skia::ImageOperations::Resize(
350 image_rep
.sk_bitmap(),
352 target_pixel_size
.width(),
353 target_pixel_size
.height());
354 return ImageSkiaRep(resized
, scale
);
358 const ImageSkia source_
;
359 skia::ImageOperations::ResizeMethod resize_method_
;
360 const Size target_dip_size_
;
362 DISALLOW_COPY_AND_ASSIGN(ResizeSource
);
365 // DropShadowSource generates image reps with drop shadow for image reps in
366 // |source| that represent requested scale factors.
367 class DropShadowSource
: public ImageSkiaSource
{
369 DropShadowSource(const ImageSkia
& source
,
370 const ShadowValues
& shadows_in_dip
)
372 shaodws_in_dip_(shadows_in_dip
) {
374 ~DropShadowSource() override
{}
376 // gfx::ImageSkiaSource overrides:
377 ImageSkiaRep
GetImageForScale(float scale
) override
{
378 const ImageSkiaRep
& image_rep
= source_
.GetRepresentation(scale
);
380 ShadowValues shadows_in_pixel
;
381 for (size_t i
= 0; i
< shaodws_in_dip_
.size(); ++i
)
382 shadows_in_pixel
.push_back(shaodws_in_dip_
[i
].Scale(scale
));
384 const SkBitmap shadow_bitmap
= SkBitmapOperations::CreateDropShadow(
385 image_rep
.sk_bitmap(),
387 return ImageSkiaRep(shadow_bitmap
, image_rep
.scale());
391 const ImageSkia source_
;
392 const ShadowValues shaodws_in_dip_
;
394 DISALLOW_COPY_AND_ASSIGN(DropShadowSource
);
397 // RotatedSource generates image reps that are rotations of those in
398 // |source| that represent requested scale factors.
399 class RotatedSource
: public ImageSkiaSource
{
401 RotatedSource(const ImageSkia
& source
,
402 SkBitmapOperations::RotationAmount rotation
)
404 rotation_(rotation
) {
406 ~RotatedSource() override
{}
408 // gfx::ImageSkiaSource overrides:
409 ImageSkiaRep
GetImageForScale(float scale
) override
{
410 const ImageSkiaRep
& image_rep
= source_
.GetRepresentation(scale
);
411 const SkBitmap rotated_bitmap
=
412 SkBitmapOperations::Rotate(image_rep
.sk_bitmap(), rotation_
);
413 return ImageSkiaRep(rotated_bitmap
, image_rep
.scale());
417 const ImageSkia source_
;
418 const SkBitmapOperations::RotationAmount rotation_
;
420 DISALLOW_COPY_AND_ASSIGN(RotatedSource
);
427 ImageSkia
ImageSkiaOperations::CreateBlendedImage(const ImageSkia
& first
,
428 const ImageSkia
& second
,
430 if (first
.isNull() || second
.isNull())
433 return ImageSkia(new BlendingImageSource(first
, second
, alpha
), first
.size());
437 ImageSkia
ImageSkiaOperations::CreateSuperimposedImage(
438 const ImageSkia
& first
,
439 const ImageSkia
& second
) {
440 if (first
.isNull() || second
.isNull())
443 return ImageSkia(new SuperimposedImageSource(first
, second
), first
.size());
447 ImageSkia
ImageSkiaOperations::CreateTransparentImage(const ImageSkia
& image
,
452 return ImageSkia(new TransparentImageSource(image
, alpha
), image
.size());
456 ImageSkia
ImageSkiaOperations::CreateMaskedImage(const ImageSkia
& rgb
,
457 const ImageSkia
& alpha
) {
458 if (rgb
.isNull() || alpha
.isNull())
461 return ImageSkia(new MaskedImageSource(rgb
, alpha
), rgb
.size());
465 ImageSkia
ImageSkiaOperations::CreateTiledImage(const ImageSkia
& source
,
466 int src_x
, int src_y
,
467 int dst_w
, int dst_h
) {
471 return ImageSkia(new TiledImageSource(source
, src_x
, src_y
, dst_w
, dst_h
),
472 gfx::Size(dst_w
, dst_h
));
476 ImageSkia
ImageSkiaOperations::CreateHSLShiftedImage(
477 const ImageSkia
& image
,
478 const color_utils::HSL
& hsl_shift
) {
482 return ImageSkia(new HSLImageSource(image
, hsl_shift
), image
.size());
486 ImageSkia
ImageSkiaOperations::CreateButtonBackground(SkColor color
,
487 const ImageSkia
& image
,
488 const ImageSkia
& mask
) {
489 if (image
.isNull() || mask
.isNull())
492 return ImageSkia(new ButtonImageSource(color
, image
, mask
), mask
.size());
496 ImageSkia
ImageSkiaOperations::ExtractSubset(const ImageSkia
& image
,
497 const Rect
& subset_bounds
) {
498 gfx::Rect clipped_bounds
=
499 gfx::IntersectRects(subset_bounds
, gfx::Rect(image
.size()));
500 if (image
.isNull() || clipped_bounds
.IsEmpty()) {
504 return ImageSkia(new ExtractSubsetImageSource(image
, clipped_bounds
),
505 clipped_bounds
.size());
509 ImageSkia
ImageSkiaOperations::CreateResizedImage(
510 const ImageSkia
& source
,
511 skia::ImageOperations::ResizeMethod method
,
512 const Size
& target_dip_size
) {
516 return ImageSkia(new ResizeSource(source
, method
, target_dip_size
),
521 ImageSkia
ImageSkiaOperations::CreateImageWithDropShadow(
522 const ImageSkia
& source
,
523 const ShadowValues
& shadows
) {
527 const gfx::Insets shadow_padding
= -gfx::ShadowValue::GetMargin(shadows
);
528 gfx::Size shadow_image_size
= source
.size();
529 shadow_image_size
.Enlarge(shadow_padding
.width(),
530 shadow_padding
.height());
531 return ImageSkia(new DropShadowSource(source
, shadows
), shadow_image_size
);
535 ImageSkia
ImageSkiaOperations::CreateRotatedImage(
536 const ImageSkia
& source
,
537 SkBitmapOperations::RotationAmount rotation
) {
541 return ImageSkia(new RotatedSource(source
, rotation
),
542 SkBitmapOperations::ROTATION_180_CW
== rotation
?
544 gfx::Size(source
.height(), source
.width()));