OLD | NEW |
---|---|
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 #define _USE_MATH_DEFINES | 5 #define _USE_MATH_DEFINES |
6 #include <cmath> | 6 #include <cmath> |
7 #include <limits> | 7 #include <limits> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "skia/ext/image_operations.h" | 10 #include "skia/ext/image_operations.h" |
11 | 11 |
12 #include "base/gfx/rect.h" | 12 #include "base/gfx/rect.h" |
13 #include "base/gfx/size.h" | 13 #include "base/gfx/size.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/stack_container.h" | 15 #include "base/stack_container.h" |
16 #include "SkBitmap.h" | 16 #include "SkBitmap.h" |
17 #include "skia/ext/convolver.h" | 17 #include "skia/ext/convolver.h" |
18 #include "skia/include/SkColorPriv.h" | |
19 #include "skia/ext/skia_utils.h" | |
18 | 20 |
19 namespace skia { | 21 namespace skia { |
20 | 22 |
21 // TODO(brettw) remove this and put this file in the skia namespace. | 23 // TODO(brettw) remove this and put this file in the skia namespace. |
22 using namespace gfx; | 24 using namespace gfx; |
23 | 25 |
24 namespace { | 26 namespace { |
25 | 27 |
26 // Returns the ceiling/floor as an integer. | 28 // Returns the ceiling/floor as an integer. |
27 inline int CeilInt(float val) { | 29 inline int CeilInt(float val) { |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
256 | 258 |
257 // static | 259 // static |
258 SkBitmap ImageOperations::Resize(const SkBitmap& source, | 260 SkBitmap ImageOperations::Resize(const SkBitmap& source, |
259 ResizeMethod method, | 261 ResizeMethod method, |
260 int dest_width, int dest_height, | 262 int dest_width, int dest_height, |
261 const gfx::Rect& dest_subset) { | 263 const gfx::Rect& dest_subset) { |
262 DCHECK(gfx::Rect(dest_width, dest_height).Contains(dest_subset)) << | 264 DCHECK(gfx::Rect(dest_width, dest_height).Contains(dest_subset)) << |
263 "The supplied subset does not fall within the destination image."; | 265 "The supplied subset does not fall within the destination image."; |
264 | 266 |
265 // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just | 267 // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just |
266 // return empty | 268 // return empty. |
267 if (source.width() < 1 || source.height() < 1 || | 269 if (source.width() < 1 || source.height() < 1 || |
268 dest_width < 1 || dest_height < 1) | 270 dest_width < 1 || dest_height < 1) |
269 return SkBitmap(); | 271 return SkBitmap(); |
270 | 272 |
271 SkAutoLockPixels locker(source); | 273 SkAutoLockPixels locker(source); |
272 | 274 |
273 ResizeFilter filter(method, source.width(), source.height(), | 275 ResizeFilter filter(method, source.width(), source.height(), |
274 dest_width, dest_height, dest_subset); | 276 dest_width, dest_height, dest_subset); |
275 | 277 |
276 // Get a source bitmap encompassing this touched area. We construct the | 278 // Get a source bitmap encompassing this touched area. We construct the |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
308 double alpha) { | 310 double alpha) { |
309 DCHECK(alpha <= 1 && alpha >= 0); | 311 DCHECK(alpha <= 1 && alpha >= 0); |
310 DCHECK(first.width() == second.width()); | 312 DCHECK(first.width() == second.width()); |
311 DCHECK(first.height() == second.height()); | 313 DCHECK(first.height() == second.height()); |
312 DCHECK(first.bytesPerPixel() == second.bytesPerPixel()); | 314 DCHECK(first.bytesPerPixel() == second.bytesPerPixel()); |
313 DCHECK(first.config() == SkBitmap::kARGB_8888_Config); | 315 DCHECK(first.config() == SkBitmap::kARGB_8888_Config); |
314 | 316 |
315 // Optimize for case where we won't need to blend anything. | 317 // Optimize for case where we won't need to blend anything. |
316 static const double alpha_min = 1.0 / 255; | 318 static const double alpha_min = 1.0 / 255; |
317 static const double alpha_max = 254.0 / 255; | 319 static const double alpha_max = 254.0 / 255; |
318 if (alpha < alpha_min) { | 320 if (alpha < alpha_min) |
319 return first; | 321 return first; |
320 } else if (alpha > alpha_max) { | 322 else if (alpha > alpha_max) |
321 return second; | 323 return second; |
322 } | |
323 | 324 |
324 SkAutoLockPixels lock_first(first); | 325 SkAutoLockPixels lock_first(first); |
325 SkAutoLockPixels lock_second(second); | 326 SkAutoLockPixels lock_second(second); |
326 | 327 |
327 SkBitmap blended; | 328 SkBitmap blended; |
328 blended.setConfig(SkBitmap::kARGB_8888_Config, first.width(), | 329 blended.setConfig(SkBitmap::kARGB_8888_Config, first.width(), |
329 first.height(), 0); | 330 first.height(), 0); |
330 blended.allocPixels(); | 331 blended.allocPixels(); |
331 blended.eraseARGB(0, 0, 0, 0); | 332 blended.eraseARGB(0, 0, 0, 0); |
332 | 333 |
(...skipping 21 matching lines...) Expand all Loading... | |
354 SkColorGetB(first_pixel) * first_alpha + | 355 SkColorGetB(first_pixel) * first_alpha + |
355 SkColorGetB(second_pixel) * alpha); | 356 SkColorGetB(second_pixel) * alpha); |
356 | 357 |
357 dst_row[x] = SkColorSetARGB(a, r, g, b); | 358 dst_row[x] = SkColorSetARGB(a, r, g, b); |
358 } | 359 } |
359 } | 360 } |
360 | 361 |
361 return blended; | 362 return blended; |
362 } | 363 } |
363 | 364 |
365 // static | |
366 SkBitmap ImageOperations::CreateMaskedBitmap(const SkBitmap& rgb, | |
367 const SkBitmap& alpha) { | |
368 DCHECK(rgb.width() == alpha.width()); | |
369 DCHECK(rgb.height() == alpha.height()); | |
370 DCHECK(rgb.bytesPerPixel() == alpha.bytesPerPixel()); | |
371 DCHECK(rgb.config() == SkBitmap::kARGB_8888_Config); | |
372 DCHECK(alpha.config() == SkBitmap::kARGB_8888_Config); | |
373 | |
374 SkBitmap masked; | |
375 masked.setConfig(SkBitmap::kARGB_8888_Config, rgb.width(), rgb.height(), 0); | |
376 masked.allocPixels(); | |
377 masked.eraseARGB(0, 0, 0, 0); | |
378 | |
379 SkAutoLockPixels lock_rgb(rgb); | |
380 SkAutoLockPixels lock_alpha(alpha); | |
381 SkAutoLockPixels lock_masked(masked); | |
382 | |
383 for (int y = 0; y < rgb.height(); y++) { | |
384 uint32* rgb_row = rgb.getAddr32(0, y); | |
385 uint32* alpha_row = alpha.getAddr32(0, y); | |
386 uint32* dst_row = masked.getAddr32(0, y); | |
387 | |
388 for (int x = 0; x < rgb.width(); x++) { | |
389 uint32 alpha_pixel = alpha_row[x]; | |
390 uint32 rgb_pixel = rgb_row[x]; | |
391 | |
392 int alpha = SkColorGetA(alpha_pixel); | |
393 dst_row[x] = SkColorSetARGB(alpha, | |
394 SkAlphaMul(SkColorGetR(rgb_pixel), alpha), | |
395 SkAlphaMul(SkColorGetG(rgb_pixel), alpha), | |
396 SkAlphaMul(SkColorGetB(rgb_pixel), alpha)); | |
397 } | |
398 } | |
399 | |
400 return masked; | |
401 } | |
402 | |
403 SkBitmap ImageOperations::CreateBlurredBitmap(const SkBitmap& bitmap, | |
404 int blur_amount ) { | |
405 DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); | |
406 | |
407 // Blur factor (1 divided by how many pixels the blur takes place over). | |
408 double v = 1.0 / pow(static_cast<double>(blur_amount * 2 + 1), 2); | |
409 | |
410 SkBitmap blurred; | |
411 blurred.setConfig(SkBitmap::kARGB_8888_Config, bitmap.width(), | |
412 bitmap.height(), 0); | |
413 blurred.allocPixels(); | |
414 blurred.eraseARGB(0, 0, 0, 0); | |
415 | |
416 SkAutoLockPixels lock_bitmap(bitmap); | |
417 SkAutoLockPixels lock_blurred(blurred); | |
418 | |
419 // Loop through every pixel in the image. | |
420 for (int y = 0; y < bitmap.height(); y++) { // Skip top and bottom edges. | |
421 uint32* dst_row = blurred.getAddr32(0, y); | |
422 | |
423 for (int x = 0; x < bitmap.width(); x++) { // Skip left and right edges. | |
424 // Sums for this pixel. | |
425 double a = 0; | |
426 double r = 0; | |
427 double g = 0; | |
428 double b = 0; | |
429 | |
430 for (int ky = -blur_amount; ky <= blur_amount; ky++) { | |
431 for (int kx = -blur_amount; kx <= blur_amount; kx++) { | |
432 // Calculate the adjacent pixel for this kernel point. Blurs | |
433 // are wrapped. | |
434 int bx = (x + kx) % bitmap.width(); | |
435 while (bx < 0) | |
436 bx += bitmap.width(); | |
437 int by = (y + ky) % bitmap.height(); | |
438 while (by < 0) | |
439 by += bitmap.height(); | |
440 | |
441 uint32 src_pixel = bitmap.getAddr32(0, by)[bx]; | |
442 | |
443 a += v * static_cast<double>(SkColorGetA(src_pixel)); | |
444 r += v * static_cast<double>(SkColorGetR(src_pixel)); | |
445 g += v * static_cast<double>(SkColorGetG(src_pixel)); | |
446 b += v * static_cast<double>(SkColorGetB(src_pixel)); | |
447 } | |
448 } | |
449 | |
450 dst_row[x] = SkColorSetARGB( | |
451 static_cast<int>(a), | |
452 static_cast<int>(r), | |
453 static_cast<int>(g), | |
454 static_cast<int>(b)); | |
455 } | |
456 } | |
457 | |
458 return blurred; | |
459 } | |
460 | |
461 // static | |
462 SkBitmap ImageOperations::CreateHSLShiftedBitmap(const SkBitmap& bitmap, | |
463 float hsl_shift[3]) { | |
464 DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); | |
465 | |
466 SkBitmap shifted; | |
467 shifted.setConfig(SkBitmap::kARGB_8888_Config, bitmap.width(), | |
468 bitmap.height(), 0); | |
469 shifted.allocPixels(); | |
470 shifted.eraseARGB(0, 0, 0, 0); | |
471 shifted.setIsOpaque(false); | |
472 | |
473 SkAutoLockPixels lock_bitmap(bitmap); | |
474 SkAutoLockPixels lock_shifted(shifted); | |
475 | |
476 // Loop through the pixels of the original bitmap. | |
477 for (int y = 0; y < bitmap.height(); y++) { | |
478 SkColor* pixels = bitmap.getAddr32(0, y); | |
479 SkColor* tinted_pixels = shifted.getAddr32(0, y); | |
480 | |
481 for (int x = 0; x < bitmap.width(); x++) { | |
482 // Convert the color of this pixel to HSL. | |
483 SkColor color = pixels[x]; | |
484 int alpha = SkColorGetA(color); | |
485 if (alpha != 255) { | |
486 // We have to normalize the colors as they're pre-multiplied. | |
487 double r = SkColorGetR(color) / static_cast<double>(alpha); | |
488 double g = SkColorGetG(color) / static_cast<double>(alpha); | |
489 double b = SkColorGetB(color) / static_cast<double>(alpha); | |
490 color = SkColorSetARGB(255, | |
491 static_cast<int>(r * 255.0), | |
492 static_cast<int>(g * 255.0), | |
493 static_cast<int>(b * 255.0)); | |
494 } | |
495 | |
496 float pixel_hsl[3]; | |
497 SkColorToHSL(color, pixel_hsl); | |
498 | |
499 // Replace the hue with the tint's hue. | |
500 if (hsl_shift[0] >= 0) | |
501 pixel_hsl[0] = hsl_shift[0]; | |
502 | |
503 // Change the saturation. | |
504 if (hsl_shift[1] >= 0) { | |
505 if (hsl_shift[1] <= 0.5) { | |
506 pixel_hsl[1] *= hsl_shift[1] * 2.0; | |
507 } else { | |
508 pixel_hsl[1] = pixel_hsl[1] + (1.0 - pixel_hsl[1]) * | |
509 ((hsl_shift[1] - 0.5) * 2.0); | |
510 } | |
511 } | |
512 | |
513 // Change the lightness. | |
514 if (hsl_shift[2] >= 0) { | |
515 if (hsl_shift[2] <= 0.5) { | |
516 pixel_hsl[2] *= hsl_shift[2] * 2.0; | |
517 } else { | |
518 pixel_hsl[2] = pixel_hsl[2] + (1.0 - pixel_hsl[2]) * | |
519 ((hsl_shift[2] - 0.5) * 2.0); | |
520 } | |
521 } | |
522 | |
523 // Convert back to RGB. | |
524 tinted_pixels[x] = HSLToSKColor(alpha, pixel_hsl); | |
525 } | |
526 } | |
527 | |
528 return shifted; | |
529 } | |
530 | |
531 // static | |
532 SkBitmap ImageOperations::CreateTiledBitmap(const SkBitmap& source, | |
brettw
2009/04/28 02:49:43
FWIW this could have been done by making a tiled b
| |
533 int src_x, int src_y, | |
534 int dst_w, int dst_h) { | |
535 DCHECK(source.getConfig() == SkBitmap::kARGB_8888_Config); | |
536 | |
537 SkBitmap cropped; | |
538 cropped.setConfig(SkBitmap::kARGB_8888_Config, dst_w, dst_h, 0); | |
539 cropped.allocPixels(); | |
540 cropped.eraseARGB(0, 0, 0, 0); | |
541 | |
542 SkAutoLockPixels lock_source(source); | |
543 SkAutoLockPixels lock_cropped(cropped); | |
544 | |
545 // Loop through the pixels of the original bitmap. | |
546 for (int y = 0; y < dst_h; y++) { | |
547 int y_pix = (src_y + y) % source.height(); | |
548 while (y_pix < 0) | |
549 y_pix += source.height(); | |
550 | |
551 uint32* source_row = source.getAddr32(0, y_pix); | |
552 uint32* dst_row = cropped.getAddr32(0, y); | |
553 | |
554 for (int x = 0; x < dst_w; x++) { | |
555 int x_pix = (src_x + x) % source.width(); | |
556 while (x_pix < 0) | |
557 x_pix += source.width(); | |
558 | |
559 dst_row[x] = source_row[x_pix]; | |
560 } | |
561 } | |
562 | |
563 return cropped; | |
564 } | |
565 | |
364 } // namespace skia | 566 } // namespace skia |
365 | 567 |
OLD | NEW |