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/base/layout.h" 11 #include "ui/gfx/canvas.h" 12 #include "ui/gfx/image/canvas_image_source.h" 13 #include "ui/gfx/image/image_skia.h" 14 #include "ui/gfx/image/image_skia_rep.h" 15 #include "ui/gfx/image/image_skia_source.h" 16 #include "ui/gfx/insets.h" 17 #include "ui/gfx/point.h" 18 #include "ui/gfx/point_conversions.h" 19 #include "ui/gfx/rect.h" 20 #include "ui/gfx/rect_conversions.h" 21 #include "ui/gfx/size.h" 22 #include "ui/gfx/size_conversions.h" 23 #include "ui/gfx/skbitmap_operations.h" 24 #include "ui/gfx/skia_util.h" 25 26 namespace gfx { 27 namespace { 28 29 gfx::Size DIPToPixelSize(gfx::Size dip_size, float scale) { 30 return ToCeiledSize(ScaleSize(dip_size, scale)); 31 } 32 33 gfx::Rect DIPToPixelBounds(gfx::Rect dip_bounds, float scale) { 34 return gfx::Rect(ToFlooredPoint(ScalePoint(dip_bounds.origin(), scale)), 35 DIPToPixelSize(dip_bounds.size(), scale)); 36 } 37 38 // Returns an image rep for the ImageSkiaSource to return to visually indicate 39 // an error. 40 ImageSkiaRep GetErrorImageRep(ui::ScaleFactor scale_factor, 41 const gfx::Size& pixel_size) { 42 SkBitmap bitmap; 43 bitmap.setConfig( 44 SkBitmap::kARGB_8888_Config, pixel_size.width(), pixel_size.height()); 45 bitmap.allocPixels(); 46 bitmap.eraseColor(SK_ColorRED); 47 return gfx::ImageSkiaRep(bitmap, scale_factor); 48 } 49 50 // A base image source class that creates an image from two source images. 51 // This class guarantees that two ImageSkiaReps have have the same pixel size. 52 class BinaryImageSource : public gfx::ImageSkiaSource { 53 protected: 54 BinaryImageSource(const ImageSkia& first, 55 const ImageSkia& second, 56 const char* source_name) 57 : first_(first), 58 second_(second), 59 source_name_(source_name) { 60 } 61 virtual ~BinaryImageSource() { 62 } 63 64 // gfx::ImageSkiaSource overrides: 65 virtual ImageSkiaRep GetImageForScale(ui::ScaleFactor scale_factor) OVERRIDE { 66 ImageSkiaRep first_rep = first_.GetRepresentation(scale_factor); 67 ImageSkiaRep second_rep = second_.GetRepresentation(scale_factor); 68 if (first_rep.pixel_size() != second_rep.pixel_size()) { 69 DCHECK_NE(first_rep.scale_factor(), second_rep.scale_factor()); 70 if (first_rep.scale_factor() == second_rep.scale_factor()) { 71 LOG(ERROR) << "ImageSkiaRep size mismatch in " << source_name_; 72 return GetErrorImageRep(first_rep.scale_factor(), 73 first_rep.pixel_size()); 74 } 75 first_rep = first_.GetRepresentation(ui::SCALE_FACTOR_100P); 76 second_rep = second_.GetRepresentation(ui::SCALE_FACTOR_100P); 77 DCHECK_EQ(first_rep.pixel_width(), second_rep.pixel_width()); 78 DCHECK_EQ(first_rep.pixel_height(), second_rep.pixel_height()); 79 if (first_rep.pixel_size() != second_rep.pixel_size()) { 80 LOG(ERROR) << "ImageSkiaRep size mismatch in " << source_name_; 81 return GetErrorImageRep(first_rep.scale_factor(), 82 first_rep.pixel_size()); 83 } 84 } else { 85 DCHECK_EQ(first_rep.scale_factor(), second_rep.scale_factor()); 86 } 87 return CreateImageSkiaRep(first_rep, second_rep); 88 } 89 90 // Creates a final image from two ImageSkiaReps. The pixel size of 91 // the two images are guaranteed to be the same. 92 virtual ImageSkiaRep CreateImageSkiaRep( 93 const ImageSkiaRep& first_rep, 94 const ImageSkiaRep& second_rep) const = 0; 95 96 private: 97 const ImageSkia first_; 98 const ImageSkia second_; 99 // The name of a class that implements the BinaryImageSource. 100 // The subclass is responsible for managing the memory. 101 const char* source_name_; 102 103 DISALLOW_COPY_AND_ASSIGN(BinaryImageSource); 104 }; 105 106 class BlendingImageSource : public BinaryImageSource { 107 public: 108 BlendingImageSource(const ImageSkia& first, 109 const ImageSkia& second, 110 double alpha) 111 : BinaryImageSource(first, second, "BlendingImageSource"), 112 alpha_(alpha) { 113 } 114 115 virtual ~BlendingImageSource() { 116 } 117 118 // BinaryImageSource overrides: 119 virtual ImageSkiaRep CreateImageSkiaRep( 120 const ImageSkiaRep& first_rep, 121 const ImageSkiaRep& second_rep) const OVERRIDE { 122 SkBitmap blended = SkBitmapOperations::CreateBlendedBitmap( 123 first_rep.sk_bitmap(), second_rep.sk_bitmap(), alpha_); 124 return ImageSkiaRep(blended, first_rep.scale_factor()); 125 } 126 127 private: 128 double alpha_; 129 130 DISALLOW_COPY_AND_ASSIGN(BlendingImageSource); 131 }; 132 133 class SuperimposedImageSource : public gfx::CanvasImageSource { 134 public: 135 SuperimposedImageSource(const ImageSkia& first, 136 const ImageSkia& second) 137 : gfx::CanvasImageSource(first.size(), false /* is opaque */), 138 first_(first), 139 second_(second) { 140 } 141 142 virtual ~SuperimposedImageSource() {} 143 144 // gfx::CanvasImageSource override. 145 virtual void Draw(Canvas* canvas) OVERRIDE { 146 canvas->DrawImageInt(first_, 0, 0); 147 canvas->DrawImageInt(second_, 148 (first_.width() - second_.width()) / 2, 149 (first_.height() - second_.height()) / 2); 150 } 151 152 private: 153 const ImageSkia first_; 154 const ImageSkia second_; 155 156 DISALLOW_COPY_AND_ASSIGN(SuperimposedImageSource); 157 }; 158 159 class TransparentImageSource : public gfx::ImageSkiaSource { 160 public: 161 TransparentImageSource(const ImageSkia& image, double alpha) 162 : image_(image), 163 alpha_(alpha) { 164 } 165 166 virtual ~TransparentImageSource() {} 167 168 private: 169 // gfx::ImageSkiaSource overrides: 170 virtual ImageSkiaRep GetImageForScale(ui::ScaleFactor scale_factor) OVERRIDE { 171 ImageSkiaRep image_rep = image_.GetRepresentation(scale_factor); 172 SkBitmap alpha; 173 alpha.setConfig(SkBitmap::kARGB_8888_Config, 174 image_rep.pixel_width(), 175 image_rep.pixel_height()); 176 alpha.allocPixels(); 177 alpha.eraseColor(SkColorSetARGB(alpha_ * 255, 0, 0, 0)); 178 return ImageSkiaRep( 179 SkBitmapOperations::CreateMaskedBitmap(image_rep.sk_bitmap(), alpha), 180 image_rep.scale_factor()); 181 } 182 183 ImageSkia image_; 184 double alpha_; 185 186 DISALLOW_COPY_AND_ASSIGN(TransparentImageSource); 187 }; 188 189 class MaskedImageSource : public BinaryImageSource { 190 public: 191 MaskedImageSource(const ImageSkia& rgb, const ImageSkia& alpha) 192 : BinaryImageSource(rgb, alpha, "MaskedImageSource") { 193 } 194 195 virtual ~MaskedImageSource() { 196 } 197 198 // BinaryImageSource overrides: 199 virtual ImageSkiaRep CreateImageSkiaRep( 200 const ImageSkiaRep& first_rep, 201 const ImageSkiaRep& second_rep) const OVERRIDE { 202 return ImageSkiaRep(SkBitmapOperations::CreateMaskedBitmap( 203 first_rep.sk_bitmap(), second_rep.sk_bitmap()), 204 first_rep.scale_factor()); 205 } 206 207 private: 208 DISALLOW_COPY_AND_ASSIGN(MaskedImageSource); 209 }; 210 211 class TiledImageSource : public gfx::ImageSkiaSource { 212 public: 213 TiledImageSource(const ImageSkia& source, 214 int src_x, int src_y, 215 int dst_w, int dst_h) 216 : source_(source), 217 src_x_(src_x), 218 src_y_(src_y), 219 dst_w_(dst_w), 220 dst_h_(dst_h) { 221 } 222 223 virtual ~TiledImageSource() { 224 } 225 226 // gfx::ImageSkiaSource overrides: 227 virtual ImageSkiaRep GetImageForScale(ui::ScaleFactor scale_factor) OVERRIDE { 228 ImageSkiaRep source_rep = source_.GetRepresentation(scale_factor); 229 float scale = ui::GetScaleFactorScale(source_rep.scale_factor()); 230 gfx::Rect bounds = DIPToPixelBounds(gfx::Rect(src_x_, src_y_, dst_w_, 231 dst_h_), scale); 232 return ImageSkiaRep( 233 SkBitmapOperations::CreateTiledBitmap( 234 source_rep.sk_bitmap(), 235 bounds.x(), bounds.y(), bounds.width(), bounds.height()), 236 source_rep.scale_factor()); 237 } 238 239 private: 240 const ImageSkia source_; 241 const int src_x_; 242 const int src_y_; 243 const int dst_w_; 244 const int dst_h_; 245 246 DISALLOW_COPY_AND_ASSIGN(TiledImageSource); 247 }; 248 249 class HSLImageSource : public gfx::ImageSkiaSource { 250 public: 251 HSLImageSource(const ImageSkia& image, 252 const color_utils::HSL& hsl_shift) 253 : image_(image), 254 hsl_shift_(hsl_shift) { 255 } 256 257 virtual ~HSLImageSource() { 258 } 259 260 // gfx::ImageSkiaSource overrides: 261 virtual ImageSkiaRep GetImageForScale(ui::ScaleFactor scale_factor) OVERRIDE { 262 ImageSkiaRep image_rep = image_.GetRepresentation(scale_factor); 263 return gfx::ImageSkiaRep( 264 SkBitmapOperations::CreateHSLShiftedBitmap(image_rep.sk_bitmap(), 265 hsl_shift_), image_rep.scale_factor()); 266 } 267 268 private: 269 const gfx::ImageSkia image_; 270 const color_utils::HSL hsl_shift_; 271 DISALLOW_COPY_AND_ASSIGN(HSLImageSource); 272 }; 273 274 // ImageSkiaSource which uses SkBitmapOperations::CreateButtonBackground 275 // to generate image reps for the target image. The image and mask can be 276 // diferent sizes (crbug.com/171725). 277 class ButtonImageSource: public gfx::ImageSkiaSource { 278 public: 279 ButtonImageSource(SkColor color, 280 const ImageSkia& image, 281 const ImageSkia& mask) 282 : color_(color), 283 image_(image), 284 mask_(mask) { 285 } 286 287 virtual ~ButtonImageSource() { 288 } 289 290 // gfx::ImageSkiaSource overrides: 291 virtual ImageSkiaRep GetImageForScale(ui::ScaleFactor scale_factor) OVERRIDE { 292 ImageSkiaRep image_rep = image_.GetRepresentation(scale_factor); 293 ImageSkiaRep mask_rep = mask_.GetRepresentation(scale_factor); 294 if (image_rep.scale_factor() != mask_rep.scale_factor()) { 295 image_rep = image_.GetRepresentation(ui::SCALE_FACTOR_100P); 296 mask_rep = mask_.GetRepresentation(ui::SCALE_FACTOR_100P); 297 } 298 return gfx::ImageSkiaRep( 299 SkBitmapOperations::CreateButtonBackground(color_, 300 image_rep.sk_bitmap(), mask_rep.sk_bitmap()), 301 image_rep.scale_factor()); 302 } 303 304 private: 305 const SkColor color_; 306 const ImageSkia image_; 307 const ImageSkia mask_; 308 309 DISALLOW_COPY_AND_ASSIGN(ButtonImageSource); 310 }; 311 312 // ImageSkiaSource which uses SkBitmap::extractSubset to generate image reps 313 // for the target image. 314 class ExtractSubsetImageSource: public gfx::ImageSkiaSource { 315 public: 316 ExtractSubsetImageSource(const gfx::ImageSkia& image, 317 const gfx::Rect& subset_bounds) 318 : image_(image), 319 subset_bounds_(subset_bounds) { 320 } 321 322 virtual ~ExtractSubsetImageSource() { 323 } 324 325 // gfx::ImageSkiaSource overrides: 326 virtual ImageSkiaRep GetImageForScale(ui::ScaleFactor scale_factor) OVERRIDE { 327 ImageSkiaRep image_rep = image_.GetRepresentation(scale_factor); 328 float scale_to_pixel = ui::GetScaleFactorScale(image_rep.scale_factor()); 329 SkIRect subset_bounds_in_pixel = RectToSkIRect( 330 DIPToPixelBounds(subset_bounds_, scale_to_pixel)); 331 SkBitmap dst; 332 bool success = image_rep.sk_bitmap().extractSubset(&dst, 333 subset_bounds_in_pixel); 334 DCHECK(success); 335 return gfx::ImageSkiaRep(dst, image_rep.scale_factor()); 336 } 337 338 private: 339 const gfx::ImageSkia image_; 340 const gfx::Rect subset_bounds_; 341 342 DISALLOW_COPY_AND_ASSIGN(ExtractSubsetImageSource); 343 }; 344 345 // ResizeSource resizes relevant image reps in |source| to |target_dip_size| 346 // for requested scale factors. 347 class ResizeSource : public ImageSkiaSource { 348 public: 349 ResizeSource(const ImageSkia& source, 350 skia::ImageOperations::ResizeMethod method, 351 const Size& target_dip_size) 352 : source_(source), 353 resize_method_(method), 354 target_dip_size_(target_dip_size) { 355 } 356 virtual ~ResizeSource() {} 357 358 // gfx::ImageSkiaSource overrides: 359 virtual ImageSkiaRep GetImageForScale(ui::ScaleFactor scale_factor) OVERRIDE { 360 const ImageSkiaRep& image_rep = source_.GetRepresentation(scale_factor); 361 if (image_rep.GetWidth() == target_dip_size_.width() && 362 image_rep.GetHeight() == target_dip_size_.height()) 363 return image_rep; 364 365 const float scale = ui::GetScaleFactorScale(scale_factor); 366 const Size target_pixel_size = DIPToPixelSize(target_dip_size_, scale); 367 const SkBitmap resized = skia::ImageOperations::Resize( 368 image_rep.sk_bitmap(), 369 resize_method_, 370 target_pixel_size.width(), 371 target_pixel_size.height()); 372 return ImageSkiaRep(resized, scale_factor); 373 } 374 375 private: 376 const ImageSkia source_; 377 skia::ImageOperations::ResizeMethod resize_method_; 378 const Size target_dip_size_; 379 380 DISALLOW_COPY_AND_ASSIGN(ResizeSource); 381 }; 382 383 // DropShadowSource generates image reps with drop shadow for image reps in 384 // |source| that represent requested scale factors. 385 class DropShadowSource : public ImageSkiaSource { 386 public: 387 DropShadowSource(const ImageSkia& source, 388 const ShadowValues& shadows_in_dip) 389 : source_(source), 390 shaodws_in_dip_(shadows_in_dip) { 391 } 392 virtual ~DropShadowSource() {} 393 394 // gfx::ImageSkiaSource overrides: 395 virtual ImageSkiaRep GetImageForScale(ui::ScaleFactor scale_factor) OVERRIDE { 396 const ImageSkiaRep& image_rep = source_.GetRepresentation(scale_factor); 397 398 const float scale = image_rep.GetScale(); 399 ShadowValues shadows_in_pixel; 400 for (size_t i = 0; i < shaodws_in_dip_.size(); ++i) 401 shadows_in_pixel.push_back(shaodws_in_dip_[i].Scale(scale)); 402 403 const SkBitmap shadow_bitmap = SkBitmapOperations::CreateDropShadow( 404 image_rep.sk_bitmap(), 405 shadows_in_pixel); 406 return ImageSkiaRep(shadow_bitmap, image_rep.scale_factor()); 407 } 408 409 private: 410 const ImageSkia source_; 411 const ShadowValues shaodws_in_dip_; 412 413 DISALLOW_COPY_AND_ASSIGN(DropShadowSource); 414 }; 415 416 // RotatedSource generates image reps that are rotations of those in 417 // |source| that represent requested scale factors. 418 class RotatedSource : public ImageSkiaSource { 419 public: 420 RotatedSource(const ImageSkia& source, 421 SkBitmapOperations::RotationAmount rotation) 422 : source_(source), 423 rotation_(rotation) { 424 } 425 virtual ~RotatedSource() {} 426 427 // gfx::ImageSkiaSource overrides: 428 virtual ImageSkiaRep GetImageForScale(ui::ScaleFactor scale_factor) OVERRIDE { 429 const ImageSkiaRep& image_rep = source_.GetRepresentation(scale_factor); 430 const SkBitmap rotated_bitmap = 431 SkBitmapOperations::Rotate(image_rep.sk_bitmap(), rotation_); 432 return ImageSkiaRep(rotated_bitmap, image_rep.scale_factor()); 433 } 434 435 private: 436 const ImageSkia source_; 437 const SkBitmapOperations::RotationAmount rotation_; 438 439 DISALLOW_COPY_AND_ASSIGN(RotatedSource); 440 }; 441 442 443 } // namespace 444 445 // static 446 ImageSkia ImageSkiaOperations::CreateBlendedImage(const ImageSkia& first, 447 const ImageSkia& second, 448 double alpha) { 449 if (first.isNull() || second.isNull()) 450 return ImageSkia(); 451 452 return ImageSkia(new BlendingImageSource(first, second, alpha), first.size()); 453 } 454 455 // static 456 ImageSkia ImageSkiaOperations::CreateSuperimposedImage( 457 const ImageSkia& first, 458 const ImageSkia& second) { 459 if (first.isNull() || second.isNull()) 460 return ImageSkia(); 461 462 return ImageSkia(new SuperimposedImageSource(first, second), first.size()); 463 } 464 465 // static 466 ImageSkia ImageSkiaOperations::CreateTransparentImage(const ImageSkia& image, 467 double alpha) { 468 if (image.isNull()) 469 return ImageSkia(); 470 471 return ImageSkia(new TransparentImageSource(image, alpha), image.size()); 472 } 473 474 // static 475 ImageSkia ImageSkiaOperations::CreateMaskedImage(const ImageSkia& rgb, 476 const ImageSkia& alpha) { 477 if (rgb.isNull() || alpha.isNull()) 478 return ImageSkia(); 479 480 return ImageSkia(new MaskedImageSource(rgb, alpha), rgb.size()); 481 } 482 483 // static 484 ImageSkia ImageSkiaOperations::CreateTiledImage(const ImageSkia& source, 485 int src_x, int src_y, 486 int dst_w, int dst_h) { 487 if (source.isNull()) 488 return ImageSkia(); 489 490 return ImageSkia(new TiledImageSource(source, src_x, src_y, dst_w, dst_h), 491 gfx::Size(dst_w, dst_h)); 492 } 493 494 // static 495 ImageSkia ImageSkiaOperations::CreateHSLShiftedImage( 496 const ImageSkia& image, 497 const color_utils::HSL& hsl_shift) { 498 if (image.isNull()) 499 return ImageSkia(); 500 501 return ImageSkia(new HSLImageSource(image, hsl_shift), image.size()); 502 } 503 504 // static 505 ImageSkia ImageSkiaOperations::CreateButtonBackground(SkColor color, 506 const ImageSkia& image, 507 const ImageSkia& mask) { 508 if (image.isNull() || mask.isNull()) 509 return ImageSkia(); 510 511 return ImageSkia(new ButtonImageSource(color, image, mask), mask.size()); 512 } 513 514 // static 515 ImageSkia ImageSkiaOperations::ExtractSubset(const ImageSkia& image, 516 const Rect& subset_bounds) { 517 gfx::Rect clipped_bounds = 518 gfx::IntersectRects(subset_bounds, gfx::Rect(image.size())); 519 if (image.isNull() || clipped_bounds.IsEmpty()) { 520 return ImageSkia(); 521 } 522 523 return ImageSkia(new ExtractSubsetImageSource(image, clipped_bounds), 524 clipped_bounds.size()); 525 } 526 527 // static 528 ImageSkia ImageSkiaOperations::CreateResizedImage( 529 const ImageSkia& source, 530 skia::ImageOperations::ResizeMethod method, 531 const Size& target_dip_size) { 532 if (source.isNull()) 533 return ImageSkia(); 534 535 return ImageSkia(new ResizeSource(source, method, target_dip_size), 536 target_dip_size); 537 } 538 539 // static 540 ImageSkia ImageSkiaOperations::CreateImageWithDropShadow( 541 const ImageSkia& source, 542 const ShadowValues& shadows) { 543 if (source.isNull()) 544 return ImageSkia(); 545 546 const gfx::Insets shadow_padding = -gfx::ShadowValue::GetMargin(shadows); 547 gfx::Size shadow_image_size = source.size(); 548 shadow_image_size.Enlarge(shadow_padding.width(), 549 shadow_padding.height()); 550 return ImageSkia(new DropShadowSource(source, shadows), shadow_image_size); 551 } 552 553 // static 554 ImageSkia ImageSkiaOperations::CreateRotatedImage( 555 const ImageSkia& source, 556 SkBitmapOperations::RotationAmount rotation) { 557 if (source.isNull()) 558 return ImageSkia(); 559 560 return ImageSkia(new RotatedSource(source, rotation), 561 SkBitmapOperations::ROTATION_180_CW == rotation ? 562 source.size() : 563 gfx::Size(source.height(), source.width())); 564 565 } 566 567 } // namespace gfx 568