OLD | NEW |
| (Empty) |
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 ~BinaryImageSource() override {} | |
58 | |
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()); | |
68 } | |
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()); | |
76 } | |
77 } else { | |
78 DCHECK_EQ(first_rep.scale(), second_rep.scale()); | |
79 } | |
80 return CreateImageSkiaRep(first_rep, second_rep); | |
81 } | |
82 | |
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; | |
88 | |
89 private: | |
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_; | |
95 | |
96 DISALLOW_COPY_AND_ASSIGN(BinaryImageSource); | |
97 }; | |
98 | |
99 class BlendingImageSource : public BinaryImageSource { | |
100 public: | |
101 BlendingImageSource(const ImageSkia& first, | |
102 const ImageSkia& second, | |
103 double alpha) | |
104 : BinaryImageSource(first, second, "BlendingImageSource"), | |
105 alpha_(alpha) { | |
106 } | |
107 | |
108 ~BlendingImageSource() override {} | |
109 | |
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()); | |
117 } | |
118 | |
119 private: | |
120 double alpha_; | |
121 | |
122 DISALLOW_COPY_AND_ASSIGN(BlendingImageSource); | |
123 }; | |
124 | |
125 class SuperimposedImageSource : public gfx::CanvasImageSource { | |
126 public: | |
127 SuperimposedImageSource(const ImageSkia& first, | |
128 const ImageSkia& second) | |
129 : gfx::CanvasImageSource(first.size(), false /* is opaque */), | |
130 first_(first), | |
131 second_(second) { | |
132 } | |
133 | |
134 ~SuperimposedImageSource() override {} | |
135 | |
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); | |
142 } | |
143 | |
144 private: | |
145 const ImageSkia first_; | |
146 const ImageSkia second_; | |
147 | |
148 DISALLOW_COPY_AND_ASSIGN(SuperimposedImageSource); | |
149 }; | |
150 | |
151 class TransparentImageSource : public gfx::ImageSkiaSource { | |
152 public: | |
153 TransparentImageSource(const ImageSkia& image, double alpha) | |
154 : image_(image), | |
155 alpha_(alpha) { | |
156 } | |
157 | |
158 ~TransparentImageSource() override {} | |
159 | |
160 private: | |
161 // gfx::ImageSkiaSource overrides: | |
162 ImageSkiaRep GetImageForScale(float scale) override { | |
163 ImageSkiaRep image_rep = image_.GetRepresentation(scale); | |
164 SkBitmap alpha; | |
165 alpha.allocN32Pixels(image_rep.pixel_width(), | |
166 image_rep.pixel_height()); | |
167 alpha.eraseColor(SkColorSetARGB(alpha_ * 255, 0, 0, 0)); | |
168 return ImageSkiaRep( | |
169 SkBitmapOperations::CreateMaskedBitmap(image_rep.sk_bitmap(), alpha), | |
170 image_rep.scale()); | |
171 } | |
172 | |
173 ImageSkia image_; | |
174 double alpha_; | |
175 | |
176 DISALLOW_COPY_AND_ASSIGN(TransparentImageSource); | |
177 }; | |
178 | |
179 class MaskedImageSource : public BinaryImageSource { | |
180 public: | |
181 MaskedImageSource(const ImageSkia& rgb, const ImageSkia& alpha) | |
182 : BinaryImageSource(rgb, alpha, "MaskedImageSource") { | |
183 } | |
184 | |
185 ~MaskedImageSource() override {} | |
186 | |
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()), | |
193 first_rep.scale()); | |
194 } | |
195 | |
196 private: | |
197 DISALLOW_COPY_AND_ASSIGN(MaskedImageSource); | |
198 }; | |
199 | |
200 class TiledImageSource : public gfx::ImageSkiaSource { | |
201 public: | |
202 TiledImageSource(const ImageSkia& source, | |
203 int src_x, int src_y, | |
204 int dst_w, int dst_h) | |
205 : source_(source), | |
206 src_x_(src_x), | |
207 src_y_(src_y), | |
208 dst_w_(dst_w), | |
209 dst_h_(dst_h) { | |
210 } | |
211 | |
212 ~TiledImageSource() override {} | |
213 | |
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()); | |
219 return ImageSkiaRep( | |
220 SkBitmapOperations::CreateTiledBitmap( | |
221 source_rep.sk_bitmap(), | |
222 bounds.x(), bounds.y(), bounds.width(), bounds.height()), | |
223 source_rep.scale()); | |
224 } | |
225 | |
226 private: | |
227 const ImageSkia source_; | |
228 const int src_x_; | |
229 const int src_y_; | |
230 const int dst_w_; | |
231 const int dst_h_; | |
232 | |
233 DISALLOW_COPY_AND_ASSIGN(TiledImageSource); | |
234 }; | |
235 | |
236 class HSLImageSource : public gfx::ImageSkiaSource { | |
237 public: | |
238 HSLImageSource(const ImageSkia& image, | |
239 const color_utils::HSL& hsl_shift) | |
240 : image_(image), | |
241 hsl_shift_(hsl_shift) { | |
242 } | |
243 | |
244 ~HSLImageSource() override {} | |
245 | |
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()); | |
252 } | |
253 | |
254 private: | |
255 const gfx::ImageSkia image_; | |
256 const color_utils::HSL hsl_shift_; | |
257 DISALLOW_COPY_AND_ASSIGN(HSLImageSource); | |
258 }; | |
259 | |
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 { | |
264 public: | |
265 ButtonImageSource(SkColor color, | |
266 const ImageSkia& image, | |
267 const ImageSkia& mask) | |
268 : color_(color), | |
269 image_(image), | |
270 mask_(mask) { | |
271 } | |
272 | |
273 ~ButtonImageSource() override {} | |
274 | |
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); | |
282 } | |
283 return gfx::ImageSkiaRep( | |
284 SkBitmapOperations::CreateButtonBackground(color_, | |
285 image_rep.sk_bitmap(), mask_rep.sk_bitmap()), | |
286 image_rep.scale()); | |
287 } | |
288 | |
289 private: | |
290 const SkColor color_; | |
291 const ImageSkia image_; | |
292 const ImageSkia mask_; | |
293 | |
294 DISALLOW_COPY_AND_ASSIGN(ButtonImageSource); | |
295 }; | |
296 | |
297 // ImageSkiaSource which uses SkBitmap::extractSubset to generate image reps | |
298 // for the target image. | |
299 class ExtractSubsetImageSource: public gfx::ImageSkiaSource { | |
300 public: | |
301 ExtractSubsetImageSource(const gfx::ImageSkia& image, | |
302 const gfx::Rect& subset_bounds) | |
303 : image_(image), | |
304 subset_bounds_(subset_bounds) { | |
305 } | |
306 | |
307 ~ExtractSubsetImageSource() override {} | |
308 | |
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())); | |
314 SkBitmap dst; | |
315 bool success = image_rep.sk_bitmap().extractSubset(&dst, | |
316 subset_bounds_in_pixel); | |
317 DCHECK(success); | |
318 return gfx::ImageSkiaRep(dst, image_rep.scale()); | |
319 } | |
320 | |
321 private: | |
322 const gfx::ImageSkia image_; | |
323 const gfx::Rect subset_bounds_; | |
324 | |
325 DISALLOW_COPY_AND_ASSIGN(ExtractSubsetImageSource); | |
326 }; | |
327 | |
328 // ResizeSource resizes relevant image reps in |source| to |target_dip_size| | |
329 // for requested scale factors. | |
330 class ResizeSource : public ImageSkiaSource { | |
331 public: | |
332 ResizeSource(const ImageSkia& source, | |
333 skia::ImageOperations::ResizeMethod method, | |
334 const Size& target_dip_size) | |
335 : source_(source), | |
336 resize_method_(method), | |
337 target_dip_size_(target_dip_size) { | |
338 } | |
339 ~ResizeSource() override {} | |
340 | |
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()) | |
346 return image_rep; | |
347 | |
348 const Size target_pixel_size = DIPToPixelSize(target_dip_size_, scale); | |
349 const SkBitmap resized = skia::ImageOperations::Resize( | |
350 image_rep.sk_bitmap(), | |
351 resize_method_, | |
352 target_pixel_size.width(), | |
353 target_pixel_size.height()); | |
354 return ImageSkiaRep(resized, scale); | |
355 } | |
356 | |
357 private: | |
358 const ImageSkia source_; | |
359 skia::ImageOperations::ResizeMethod resize_method_; | |
360 const Size target_dip_size_; | |
361 | |
362 DISALLOW_COPY_AND_ASSIGN(ResizeSource); | |
363 }; | |
364 | |
365 // DropShadowSource generates image reps with drop shadow for image reps in | |
366 // |source| that represent requested scale factors. | |
367 class DropShadowSource : public ImageSkiaSource { | |
368 public: | |
369 DropShadowSource(const ImageSkia& source, | |
370 const ShadowValues& shadows_in_dip) | |
371 : source_(source), | |
372 shaodws_in_dip_(shadows_in_dip) { | |
373 } | |
374 ~DropShadowSource() override {} | |
375 | |
376 // gfx::ImageSkiaSource overrides: | |
377 ImageSkiaRep GetImageForScale(float scale) override { | |
378 const ImageSkiaRep& image_rep = source_.GetRepresentation(scale); | |
379 | |
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)); | |
383 | |
384 const SkBitmap shadow_bitmap = SkBitmapOperations::CreateDropShadow( | |
385 image_rep.sk_bitmap(), | |
386 shadows_in_pixel); | |
387 return ImageSkiaRep(shadow_bitmap, image_rep.scale()); | |
388 } | |
389 | |
390 private: | |
391 const ImageSkia source_; | |
392 const ShadowValues shaodws_in_dip_; | |
393 | |
394 DISALLOW_COPY_AND_ASSIGN(DropShadowSource); | |
395 }; | |
396 | |
397 // RotatedSource generates image reps that are rotations of those in | |
398 // |source| that represent requested scale factors. | |
399 class RotatedSource : public ImageSkiaSource { | |
400 public: | |
401 RotatedSource(const ImageSkia& source, | |
402 SkBitmapOperations::RotationAmount rotation) | |
403 : source_(source), | |
404 rotation_(rotation) { | |
405 } | |
406 ~RotatedSource() override {} | |
407 | |
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()); | |
414 } | |
415 | |
416 private: | |
417 const ImageSkia source_; | |
418 const SkBitmapOperations::RotationAmount rotation_; | |
419 | |
420 DISALLOW_COPY_AND_ASSIGN(RotatedSource); | |
421 }; | |
422 | |
423 | |
424 } // namespace | |
425 | |
426 // static | |
427 ImageSkia ImageSkiaOperations::CreateBlendedImage(const ImageSkia& first, | |
428 const ImageSkia& second, | |
429 double alpha) { | |
430 if (first.isNull() || second.isNull()) | |
431 return ImageSkia(); | |
432 | |
433 return ImageSkia(new BlendingImageSource(first, second, alpha), first.size()); | |
434 } | |
435 | |
436 // static | |
437 ImageSkia ImageSkiaOperations::CreateSuperimposedImage( | |
438 const ImageSkia& first, | |
439 const ImageSkia& second) { | |
440 if (first.isNull() || second.isNull()) | |
441 return ImageSkia(); | |
442 | |
443 return ImageSkia(new SuperimposedImageSource(first, second), first.size()); | |
444 } | |
445 | |
446 // static | |
447 ImageSkia ImageSkiaOperations::CreateTransparentImage(const ImageSkia& image, | |
448 double alpha) { | |
449 if (image.isNull()) | |
450 return ImageSkia(); | |
451 | |
452 return ImageSkia(new TransparentImageSource(image, alpha), image.size()); | |
453 } | |
454 | |
455 // static | |
456 ImageSkia ImageSkiaOperations::CreateMaskedImage(const ImageSkia& rgb, | |
457 const ImageSkia& alpha) { | |
458 if (rgb.isNull() || alpha.isNull()) | |
459 return ImageSkia(); | |
460 | |
461 return ImageSkia(new MaskedImageSource(rgb, alpha), rgb.size()); | |
462 } | |
463 | |
464 // static | |
465 ImageSkia ImageSkiaOperations::CreateTiledImage(const ImageSkia& source, | |
466 int src_x, int src_y, | |
467 int dst_w, int dst_h) { | |
468 if (source.isNull()) | |
469 return ImageSkia(); | |
470 | |
471 return ImageSkia(new TiledImageSource(source, src_x, src_y, dst_w, dst_h), | |
472 gfx::Size(dst_w, dst_h)); | |
473 } | |
474 | |
475 // static | |
476 ImageSkia ImageSkiaOperations::CreateHSLShiftedImage( | |
477 const ImageSkia& image, | |
478 const color_utils::HSL& hsl_shift) { | |
479 if (image.isNull()) | |
480 return ImageSkia(); | |
481 | |
482 return ImageSkia(new HSLImageSource(image, hsl_shift), image.size()); | |
483 } | |
484 | |
485 // static | |
486 ImageSkia ImageSkiaOperations::CreateButtonBackground(SkColor color, | |
487 const ImageSkia& image, | |
488 const ImageSkia& mask) { | |
489 if (image.isNull() || mask.isNull()) | |
490 return ImageSkia(); | |
491 | |
492 return ImageSkia(new ButtonImageSource(color, image, mask), mask.size()); | |
493 } | |
494 | |
495 // static | |
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()) { | |
501 return ImageSkia(); | |
502 } | |
503 | |
504 return ImageSkia(new ExtractSubsetImageSource(image, clipped_bounds), | |
505 clipped_bounds.size()); | |
506 } | |
507 | |
508 // static | |
509 ImageSkia ImageSkiaOperations::CreateResizedImage( | |
510 const ImageSkia& source, | |
511 skia::ImageOperations::ResizeMethod method, | |
512 const Size& target_dip_size) { | |
513 if (source.isNull()) | |
514 return ImageSkia(); | |
515 | |
516 return ImageSkia(new ResizeSource(source, method, target_dip_size), | |
517 target_dip_size); | |
518 } | |
519 | |
520 // static | |
521 ImageSkia ImageSkiaOperations::CreateImageWithDropShadow( | |
522 const ImageSkia& source, | |
523 const ShadowValues& shadows) { | |
524 if (source.isNull()) | |
525 return ImageSkia(); | |
526 | |
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); | |
532 } | |
533 | |
534 // static | |
535 ImageSkia ImageSkiaOperations::CreateRotatedImage( | |
536 const ImageSkia& source, | |
537 SkBitmapOperations::RotationAmount rotation) { | |
538 if (source.isNull()) | |
539 return ImageSkia(); | |
540 | |
541 return ImageSkia(new RotatedSource(source, rotation), | |
542 SkBitmapOperations::ROTATION_180_CW == rotation ? | |
543 source.size() : | |
544 gfx::Size(source.height(), source.width())); | |
545 | |
546 } | |
547 | |
548 } // namespace gfx | |
OLD | NEW |