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/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" 24 25 namespace gfx { 26 namespace { 27 28 gfx::Size DIPToPixelSize(gfx::Size dip_size, float scale) { 29 return ToCeiledSize(ScaleSize(dip_size, scale)); 30 } 31 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)); 35 } 36 37 // Returns an image rep for the ImageSkiaSource to return to visually indicate 38 // an error. 39 ImageSkiaRep GetErrorImageRep(float scale, const gfx::Size& pixel_size) { 40 SkBitmap bitmap; 41 bitmap.allocN32Pixels(pixel_size.width(), pixel_size.height()); 42 bitmap.eraseColor(SK_ColorRED); 43 return gfx::ImageSkiaRep(bitmap, scale); 44 } 45 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 { 49 protected: 50 BinaryImageSource(const ImageSkia& first, 51 const ImageSkia& second, 52 const char* source_name) 53 : first_(first), 54 second_(second), 55 source_name_(source_name) { 56 } 57 virtual ~BinaryImageSource() { 58 } 59 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()); 69 } 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()); 77 } 78 } else { 79 DCHECK_EQ(first_rep.scale(), second_rep.scale()); 80 } 81 return CreateImageSkiaRep(first_rep, second_rep); 82 } 83 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; 89 90 private: 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_; 96 97 DISALLOW_COPY_AND_ASSIGN(BinaryImageSource); 98 }; 99 100 class BlendingImageSource : public BinaryImageSource { 101 public: 102 BlendingImageSource(const ImageSkia& first, 103 const ImageSkia& second, 104 double alpha) 105 : BinaryImageSource(first, second, "BlendingImageSource"), 106 alpha_(alpha) { 107 } 108 109 virtual ~BlendingImageSource() { 110 } 111 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()); 119 } 120 121 private: 122 double alpha_; 123 124 DISALLOW_COPY_AND_ASSIGN(BlendingImageSource); 125 }; 126 127 class SuperimposedImageSource : public gfx::CanvasImageSource { 128 public: 129 SuperimposedImageSource(const ImageSkia& first, 130 const ImageSkia& second) 131 : gfx::CanvasImageSource(first.size(), false /* is opaque */), 132 first_(first), 133 second_(second) { 134 } 135 136 virtual ~SuperimposedImageSource() {} 137 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); 144 } 145 146 private: 147 const ImageSkia first_; 148 const ImageSkia second_; 149 150 DISALLOW_COPY_AND_ASSIGN(SuperimposedImageSource); 151 }; 152 153 class TransparentImageSource : public gfx::ImageSkiaSource { 154 public: 155 TransparentImageSource(const ImageSkia& image, double alpha) 156 : image_(image), 157 alpha_(alpha) { 158 } 159 160 virtual ~TransparentImageSource() {} 161 162 private: 163 // gfx::ImageSkiaSource overrides: 164 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 165 ImageSkiaRep image_rep = image_.GetRepresentation(scale); 166 SkBitmap alpha; 167 alpha.allocN32Pixels(image_rep.pixel_width(), 168 image_rep.pixel_height()); 169 alpha.eraseColor(SkColorSetARGB(alpha_ * 255, 0, 0, 0)); 170 return ImageSkiaRep( 171 SkBitmapOperations::CreateMaskedBitmap(image_rep.sk_bitmap(), alpha), 172 image_rep.scale()); 173 } 174 175 ImageSkia image_; 176 double alpha_; 177 178 DISALLOW_COPY_AND_ASSIGN(TransparentImageSource); 179 }; 180 181 class MaskedImageSource : public BinaryImageSource { 182 public: 183 MaskedImageSource(const ImageSkia& rgb, const ImageSkia& alpha) 184 : BinaryImageSource(rgb, alpha, "MaskedImageSource") { 185 } 186 187 virtual ~MaskedImageSource() { 188 } 189 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()), 196 first_rep.scale()); 197 } 198 199 private: 200 DISALLOW_COPY_AND_ASSIGN(MaskedImageSource); 201 }; 202 203 class TiledImageSource : public gfx::ImageSkiaSource { 204 public: 205 TiledImageSource(const ImageSkia& source, 206 int src_x, int src_y, 207 int dst_w, int dst_h) 208 : source_(source), 209 src_x_(src_x), 210 src_y_(src_y), 211 dst_w_(dst_w), 212 dst_h_(dst_h) { 213 } 214 215 virtual ~TiledImageSource() { 216 } 217 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()); 223 return ImageSkiaRep( 224 SkBitmapOperations::CreateTiledBitmap( 225 source_rep.sk_bitmap(), 226 bounds.x(), bounds.y(), bounds.width(), bounds.height()), 227 source_rep.scale()); 228 } 229 230 private: 231 const ImageSkia source_; 232 const int src_x_; 233 const int src_y_; 234 const int dst_w_; 235 const int dst_h_; 236 237 DISALLOW_COPY_AND_ASSIGN(TiledImageSource); 238 }; 239 240 class HSLImageSource : public gfx::ImageSkiaSource { 241 public: 242 HSLImageSource(const ImageSkia& image, 243 const color_utils::HSL& hsl_shift) 244 : image_(image), 245 hsl_shift_(hsl_shift) { 246 } 247 248 virtual ~HSLImageSource() { 249 } 250 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()); 257 } 258 259 private: 260 const gfx::ImageSkia image_; 261 const color_utils::HSL hsl_shift_; 262 DISALLOW_COPY_AND_ASSIGN(HSLImageSource); 263 }; 264 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 { 269 public: 270 ButtonImageSource(SkColor color, 271 const ImageSkia& image, 272 const ImageSkia& mask) 273 : color_(color), 274 image_(image), 275 mask_(mask) { 276 } 277 278 virtual ~ButtonImageSource() { 279 } 280 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); 288 } 289 return gfx::ImageSkiaRep( 290 SkBitmapOperations::CreateButtonBackground(color_, 291 image_rep.sk_bitmap(), mask_rep.sk_bitmap()), 292 image_rep.scale()); 293 } 294 295 private: 296 const SkColor color_; 297 const ImageSkia image_; 298 const ImageSkia mask_; 299 300 DISALLOW_COPY_AND_ASSIGN(ButtonImageSource); 301 }; 302 303 // ImageSkiaSource which uses SkBitmap::extractSubset to generate image reps 304 // for the target image. 305 class ExtractSubsetImageSource: public gfx::ImageSkiaSource { 306 public: 307 ExtractSubsetImageSource(const gfx::ImageSkia& image, 308 const gfx::Rect& subset_bounds) 309 : image_(image), 310 subset_bounds_(subset_bounds) { 311 } 312 313 virtual ~ExtractSubsetImageSource() { 314 } 315 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())); 321 SkBitmap dst; 322 bool success = image_rep.sk_bitmap().extractSubset(&dst, 323 subset_bounds_in_pixel); 324 DCHECK(success); 325 return gfx::ImageSkiaRep(dst, image_rep.scale()); 326 } 327 328 private: 329 const gfx::ImageSkia image_; 330 const gfx::Rect subset_bounds_; 331 332 DISALLOW_COPY_AND_ASSIGN(ExtractSubsetImageSource); 333 }; 334 335 // ResizeSource resizes relevant image reps in |source| to |target_dip_size| 336 // for requested scale factors. 337 class ResizeSource : public ImageSkiaSource { 338 public: 339 ResizeSource(const ImageSkia& source, 340 skia::ImageOperations::ResizeMethod method, 341 const Size& target_dip_size) 342 : source_(source), 343 resize_method_(method), 344 target_dip_size_(target_dip_size) { 345 } 346 virtual ~ResizeSource() {} 347 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()) 353 return image_rep; 354 355 const Size target_pixel_size = DIPToPixelSize(target_dip_size_, scale); 356 const SkBitmap resized = skia::ImageOperations::Resize( 357 image_rep.sk_bitmap(), 358 resize_method_, 359 target_pixel_size.width(), 360 target_pixel_size.height()); 361 return ImageSkiaRep(resized, scale); 362 } 363 364 private: 365 const ImageSkia source_; 366 skia::ImageOperations::ResizeMethod resize_method_; 367 const Size target_dip_size_; 368 369 DISALLOW_COPY_AND_ASSIGN(ResizeSource); 370 }; 371 372 // DropShadowSource generates image reps with drop shadow for image reps in 373 // |source| that represent requested scale factors. 374 class DropShadowSource : public ImageSkiaSource { 375 public: 376 DropShadowSource(const ImageSkia& source, 377 const ShadowValues& shadows_in_dip) 378 : source_(source), 379 shaodws_in_dip_(shadows_in_dip) { 380 } 381 virtual ~DropShadowSource() {} 382 383 // gfx::ImageSkiaSource overrides: 384 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 385 const ImageSkiaRep& image_rep = source_.GetRepresentation(scale); 386 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)); 390 391 const SkBitmap shadow_bitmap = SkBitmapOperations::CreateDropShadow( 392 image_rep.sk_bitmap(), 393 shadows_in_pixel); 394 return ImageSkiaRep(shadow_bitmap, image_rep.scale()); 395 } 396 397 private: 398 const ImageSkia source_; 399 const ShadowValues shaodws_in_dip_; 400 401 DISALLOW_COPY_AND_ASSIGN(DropShadowSource); 402 }; 403 404 // RotatedSource generates image reps that are rotations of those in 405 // |source| that represent requested scale factors. 406 class RotatedSource : public ImageSkiaSource { 407 public: 408 RotatedSource(const ImageSkia& source, 409 SkBitmapOperations::RotationAmount rotation) 410 : source_(source), 411 rotation_(rotation) { 412 } 413 virtual ~RotatedSource() {} 414 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()); 421 } 422 423 private: 424 const ImageSkia source_; 425 const SkBitmapOperations::RotationAmount rotation_; 426 427 DISALLOW_COPY_AND_ASSIGN(RotatedSource); 428 }; 429 430 431 } // namespace 432 433 // static 434 ImageSkia ImageSkiaOperations::CreateBlendedImage(const ImageSkia& first, 435 const ImageSkia& second, 436 double alpha) { 437 if (first.isNull() || second.isNull()) 438 return ImageSkia(); 439 440 return ImageSkia(new BlendingImageSource(first, second, alpha), first.size()); 441 } 442 443 // static 444 ImageSkia ImageSkiaOperations::CreateSuperimposedImage( 445 const ImageSkia& first, 446 const ImageSkia& second) { 447 if (first.isNull() || second.isNull()) 448 return ImageSkia(); 449 450 return ImageSkia(new SuperimposedImageSource(first, second), first.size()); 451 } 452 453 // static 454 ImageSkia ImageSkiaOperations::CreateTransparentImage(const ImageSkia& image, 455 double alpha) { 456 if (image.isNull()) 457 return ImageSkia(); 458 459 return ImageSkia(new TransparentImageSource(image, alpha), image.size()); 460 } 461 462 // static 463 ImageSkia ImageSkiaOperations::CreateMaskedImage(const ImageSkia& rgb, 464 const ImageSkia& alpha) { 465 if (rgb.isNull() || alpha.isNull()) 466 return ImageSkia(); 467 468 return ImageSkia(new MaskedImageSource(rgb, alpha), rgb.size()); 469 } 470 471 // static 472 ImageSkia ImageSkiaOperations::CreateTiledImage(const ImageSkia& source, 473 int src_x, int src_y, 474 int dst_w, int dst_h) { 475 if (source.isNull()) 476 return ImageSkia(); 477 478 return ImageSkia(new TiledImageSource(source, src_x, src_y, dst_w, dst_h), 479 gfx::Size(dst_w, dst_h)); 480 } 481 482 // static 483 ImageSkia ImageSkiaOperations::CreateHSLShiftedImage( 484 const ImageSkia& image, 485 const color_utils::HSL& hsl_shift) { 486 if (image.isNull()) 487 return ImageSkia(); 488 489 return ImageSkia(new HSLImageSource(image, hsl_shift), image.size()); 490 } 491 492 // static 493 ImageSkia ImageSkiaOperations::CreateButtonBackground(SkColor color, 494 const ImageSkia& image, 495 const ImageSkia& mask) { 496 if (image.isNull() || mask.isNull()) 497 return ImageSkia(); 498 499 return ImageSkia(new ButtonImageSource(color, image, mask), mask.size()); 500 } 501 502 // static 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()) { 508 return ImageSkia(); 509 } 510 511 return ImageSkia(new ExtractSubsetImageSource(image, clipped_bounds), 512 clipped_bounds.size()); 513 } 514 515 // static 516 ImageSkia ImageSkiaOperations::CreateResizedImage( 517 const ImageSkia& source, 518 skia::ImageOperations::ResizeMethod method, 519 const Size& target_dip_size) { 520 if (source.isNull()) 521 return ImageSkia(); 522 523 return ImageSkia(new ResizeSource(source, method, target_dip_size), 524 target_dip_size); 525 } 526 527 // static 528 ImageSkia ImageSkiaOperations::CreateImageWithDropShadow( 529 const ImageSkia& source, 530 const ShadowValues& shadows) { 531 if (source.isNull()) 532 return ImageSkia(); 533 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); 539 } 540 541 // static 542 ImageSkia ImageSkiaOperations::CreateRotatedImage( 543 const ImageSkia& source, 544 SkBitmapOperations::RotationAmount rotation) { 545 if (source.isNull()) 546 return ImageSkia(); 547 548 return ImageSkia(new RotatedSource(source, rotation), 549 SkBitmapOperations::ROTATION_180_CW == rotation ? 550 source.size() : 551 gfx::Size(source.height(), source.width())); 552 553 } 554 555 } // namespace gfx 556