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.setConfig( 42 SkBitmap::kARGB_8888_Config, pixel_size.width(), pixel_size.height()); 43 bitmap.allocPixels(); 44 bitmap.eraseColor(SK_ColorRED); 45 return gfx::ImageSkiaRep(bitmap, scale); 46 } 47 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 { 51 protected: 52 BinaryImageSource(const ImageSkia& first, 53 const ImageSkia& second, 54 const char* source_name) 55 : first_(first), 56 second_(second), 57 source_name_(source_name) { 58 } 59 virtual ~BinaryImageSource() { 60 } 61 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()); 71 } 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()); 79 } 80 } else { 81 DCHECK_EQ(first_rep.scale(), second_rep.scale()); 82 } 83 return CreateImageSkiaRep(first_rep, second_rep); 84 } 85 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; 91 92 private: 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_; 98 99 DISALLOW_COPY_AND_ASSIGN(BinaryImageSource); 100 }; 101 102 class BlendingImageSource : public BinaryImageSource { 103 public: 104 BlendingImageSource(const ImageSkia& first, 105 const ImageSkia& second, 106 double alpha) 107 : BinaryImageSource(first, second, "BlendingImageSource"), 108 alpha_(alpha) { 109 } 110 111 virtual ~BlendingImageSource() { 112 } 113 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()); 121 } 122 123 private: 124 double alpha_; 125 126 DISALLOW_COPY_AND_ASSIGN(BlendingImageSource); 127 }; 128 129 class SuperimposedImageSource : public gfx::CanvasImageSource { 130 public: 131 SuperimposedImageSource(const ImageSkia& first, 132 const ImageSkia& second) 133 : gfx::CanvasImageSource(first.size(), false /* is opaque */), 134 first_(first), 135 second_(second) { 136 } 137 138 virtual ~SuperimposedImageSource() {} 139 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); 146 } 147 148 private: 149 const ImageSkia first_; 150 const ImageSkia second_; 151 152 DISALLOW_COPY_AND_ASSIGN(SuperimposedImageSource); 153 }; 154 155 class TransparentImageSource : public gfx::ImageSkiaSource { 156 public: 157 TransparentImageSource(const ImageSkia& image, double alpha) 158 : image_(image), 159 alpha_(alpha) { 160 } 161 162 virtual ~TransparentImageSource() {} 163 164 private: 165 // gfx::ImageSkiaSource overrides: 166 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 167 ImageSkiaRep image_rep = image_.GetRepresentation(scale); 168 SkBitmap alpha; 169 alpha.setConfig(SkBitmap::kARGB_8888_Config, 170 image_rep.pixel_width(), 171 image_rep.pixel_height()); 172 alpha.allocPixels(); 173 alpha.eraseColor(SkColorSetARGB(alpha_ * 255, 0, 0, 0)); 174 return ImageSkiaRep( 175 SkBitmapOperations::CreateMaskedBitmap(image_rep.sk_bitmap(), alpha), 176 image_rep.scale()); 177 } 178 179 ImageSkia image_; 180 double alpha_; 181 182 DISALLOW_COPY_AND_ASSIGN(TransparentImageSource); 183 }; 184 185 class MaskedImageSource : public BinaryImageSource { 186 public: 187 MaskedImageSource(const ImageSkia& rgb, const ImageSkia& alpha) 188 : BinaryImageSource(rgb, alpha, "MaskedImageSource") { 189 } 190 191 virtual ~MaskedImageSource() { 192 } 193 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()), 200 first_rep.scale()); 201 } 202 203 private: 204 DISALLOW_COPY_AND_ASSIGN(MaskedImageSource); 205 }; 206 207 class TiledImageSource : public gfx::ImageSkiaSource { 208 public: 209 TiledImageSource(const ImageSkia& source, 210 int src_x, int src_y, 211 int dst_w, int dst_h) 212 : source_(source), 213 src_x_(src_x), 214 src_y_(src_y), 215 dst_w_(dst_w), 216 dst_h_(dst_h) { 217 } 218 219 virtual ~TiledImageSource() { 220 } 221 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()); 227 return ImageSkiaRep( 228 SkBitmapOperations::CreateTiledBitmap( 229 source_rep.sk_bitmap(), 230 bounds.x(), bounds.y(), bounds.width(), bounds.height()), 231 source_rep.scale()); 232 } 233 234 private: 235 const ImageSkia source_; 236 const int src_x_; 237 const int src_y_; 238 const int dst_w_; 239 const int dst_h_; 240 241 DISALLOW_COPY_AND_ASSIGN(TiledImageSource); 242 }; 243 244 class HSLImageSource : public gfx::ImageSkiaSource { 245 public: 246 HSLImageSource(const ImageSkia& image, 247 const color_utils::HSL& hsl_shift) 248 : image_(image), 249 hsl_shift_(hsl_shift) { 250 } 251 252 virtual ~HSLImageSource() { 253 } 254 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()); 261 } 262 263 private: 264 const gfx::ImageSkia image_; 265 const color_utils::HSL hsl_shift_; 266 DISALLOW_COPY_AND_ASSIGN(HSLImageSource); 267 }; 268 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 { 273 public: 274 ButtonImageSource(SkColor color, 275 const ImageSkia& image, 276 const ImageSkia& mask) 277 : color_(color), 278 image_(image), 279 mask_(mask) { 280 } 281 282 virtual ~ButtonImageSource() { 283 } 284 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); 292 } 293 return gfx::ImageSkiaRep( 294 SkBitmapOperations::CreateButtonBackground(color_, 295 image_rep.sk_bitmap(), mask_rep.sk_bitmap()), 296 image_rep.scale()); 297 } 298 299 private: 300 const SkColor color_; 301 const ImageSkia image_; 302 const ImageSkia mask_; 303 304 DISALLOW_COPY_AND_ASSIGN(ButtonImageSource); 305 }; 306 307 // ImageSkiaSource which uses SkBitmap::extractSubset to generate image reps 308 // for the target image. 309 class ExtractSubsetImageSource: public gfx::ImageSkiaSource { 310 public: 311 ExtractSubsetImageSource(const gfx::ImageSkia& image, 312 const gfx::Rect& subset_bounds) 313 : image_(image), 314 subset_bounds_(subset_bounds) { 315 } 316 317 virtual ~ExtractSubsetImageSource() { 318 } 319 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())); 325 SkBitmap dst; 326 bool success = image_rep.sk_bitmap().extractSubset(&dst, 327 subset_bounds_in_pixel); 328 DCHECK(success); 329 return gfx::ImageSkiaRep(dst, image_rep.scale()); 330 } 331 332 private: 333 const gfx::ImageSkia image_; 334 const gfx::Rect subset_bounds_; 335 336 DISALLOW_COPY_AND_ASSIGN(ExtractSubsetImageSource); 337 }; 338 339 // ResizeSource resizes relevant image reps in |source| to |target_dip_size| 340 // for requested scale factors. 341 class ResizeSource : public ImageSkiaSource { 342 public: 343 ResizeSource(const ImageSkia& source, 344 skia::ImageOperations::ResizeMethod method, 345 const Size& target_dip_size) 346 : source_(source), 347 resize_method_(method), 348 target_dip_size_(target_dip_size) { 349 } 350 virtual ~ResizeSource() {} 351 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()) 357 return image_rep; 358 359 const Size target_pixel_size = DIPToPixelSize(target_dip_size_, scale); 360 const SkBitmap resized = skia::ImageOperations::Resize( 361 image_rep.sk_bitmap(), 362 resize_method_, 363 target_pixel_size.width(), 364 target_pixel_size.height()); 365 return ImageSkiaRep(resized, scale); 366 } 367 368 private: 369 const ImageSkia source_; 370 skia::ImageOperations::ResizeMethod resize_method_; 371 const Size target_dip_size_; 372 373 DISALLOW_COPY_AND_ASSIGN(ResizeSource); 374 }; 375 376 // DropShadowSource generates image reps with drop shadow for image reps in 377 // |source| that represent requested scale factors. 378 class DropShadowSource : public ImageSkiaSource { 379 public: 380 DropShadowSource(const ImageSkia& source, 381 const ShadowValues& shadows_in_dip) 382 : source_(source), 383 shaodws_in_dip_(shadows_in_dip) { 384 } 385 virtual ~DropShadowSource() {} 386 387 // gfx::ImageSkiaSource overrides: 388 virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 389 const ImageSkiaRep& image_rep = source_.GetRepresentation(scale); 390 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)); 394 395 const SkBitmap shadow_bitmap = SkBitmapOperations::CreateDropShadow( 396 image_rep.sk_bitmap(), 397 shadows_in_pixel); 398 return ImageSkiaRep(shadow_bitmap, image_rep.scale()); 399 } 400 401 private: 402 const ImageSkia source_; 403 const ShadowValues shaodws_in_dip_; 404 405 DISALLOW_COPY_AND_ASSIGN(DropShadowSource); 406 }; 407 408 // RotatedSource generates image reps that are rotations of those in 409 // |source| that represent requested scale factors. 410 class RotatedSource : public ImageSkiaSource { 411 public: 412 RotatedSource(const ImageSkia& source, 413 SkBitmapOperations::RotationAmount rotation) 414 : source_(source), 415 rotation_(rotation) { 416 } 417 virtual ~RotatedSource() {} 418 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()); 425 } 426 427 private: 428 const ImageSkia source_; 429 const SkBitmapOperations::RotationAmount rotation_; 430 431 DISALLOW_COPY_AND_ASSIGN(RotatedSource); 432 }; 433 434 435 } // namespace 436 437 // static 438 ImageSkia ImageSkiaOperations::CreateBlendedImage(const ImageSkia& first, 439 const ImageSkia& second, 440 double alpha) { 441 if (first.isNull() || second.isNull()) 442 return ImageSkia(); 443 444 return ImageSkia(new BlendingImageSource(first, second, alpha), first.size()); 445 } 446 447 // static 448 ImageSkia ImageSkiaOperations::CreateSuperimposedImage( 449 const ImageSkia& first, 450 const ImageSkia& second) { 451 if (first.isNull() || second.isNull()) 452 return ImageSkia(); 453 454 return ImageSkia(new SuperimposedImageSource(first, second), first.size()); 455 } 456 457 // static 458 ImageSkia ImageSkiaOperations::CreateTransparentImage(const ImageSkia& image, 459 double alpha) { 460 if (image.isNull()) 461 return ImageSkia(); 462 463 return ImageSkia(new TransparentImageSource(image, alpha), image.size()); 464 } 465 466 // static 467 ImageSkia ImageSkiaOperations::CreateMaskedImage(const ImageSkia& rgb, 468 const ImageSkia& alpha) { 469 if (rgb.isNull() || alpha.isNull()) 470 return ImageSkia(); 471 472 return ImageSkia(new MaskedImageSource(rgb, alpha), rgb.size()); 473 } 474 475 // static 476 ImageSkia ImageSkiaOperations::CreateTiledImage(const ImageSkia& source, 477 int src_x, int src_y, 478 int dst_w, int dst_h) { 479 if (source.isNull()) 480 return ImageSkia(); 481 482 return ImageSkia(new TiledImageSource(source, src_x, src_y, dst_w, dst_h), 483 gfx::Size(dst_w, dst_h)); 484 } 485 486 // static 487 ImageSkia ImageSkiaOperations::CreateHSLShiftedImage( 488 const ImageSkia& image, 489 const color_utils::HSL& hsl_shift) { 490 if (image.isNull()) 491 return ImageSkia(); 492 493 return ImageSkia(new HSLImageSource(image, hsl_shift), image.size()); 494 } 495 496 // static 497 ImageSkia ImageSkiaOperations::CreateButtonBackground(SkColor color, 498 const ImageSkia& image, 499 const ImageSkia& mask) { 500 if (image.isNull() || mask.isNull()) 501 return ImageSkia(); 502 503 return ImageSkia(new ButtonImageSource(color, image, mask), mask.size()); 504 } 505 506 // static 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()) { 512 return ImageSkia(); 513 } 514 515 return ImageSkia(new ExtractSubsetImageSource(image, clipped_bounds), 516 clipped_bounds.size()); 517 } 518 519 // static 520 ImageSkia ImageSkiaOperations::CreateResizedImage( 521 const ImageSkia& source, 522 skia::ImageOperations::ResizeMethod method, 523 const Size& target_dip_size) { 524 if (source.isNull()) 525 return ImageSkia(); 526 527 return ImageSkia(new ResizeSource(source, method, target_dip_size), 528 target_dip_size); 529 } 530 531 // static 532 ImageSkia ImageSkiaOperations::CreateImageWithDropShadow( 533 const ImageSkia& source, 534 const ShadowValues& shadows) { 535 if (source.isNull()) 536 return ImageSkia(); 537 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); 543 } 544 545 // static 546 ImageSkia ImageSkiaOperations::CreateRotatedImage( 547 const ImageSkia& source, 548 SkBitmapOperations::RotationAmount rotation) { 549 if (source.isNull()) 550 return ImageSkia(); 551 552 return ImageSkia(new RotatedSource(source, rotation), 553 SkBitmapOperations::ROTATION_180_CW == rotation ? 554 source.size() : 555 gfx::Size(source.height(), source.width())); 556 557 } 558 559 } // namespace gfx 560