diff options
author | pkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-23 20:40:52 +0000 |
---|---|---|
committer | pkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-23 20:40:52 +0000 |
commit | 0819b02efb758f7b3f6e5661a18fa7908720cac5 (patch) | |
tree | 7e0cce071d63804f06451d08c4a26e747a28ecc2 /skia | |
parent | b5685797f80769c3db0c3c3a1c6bc117b2029b71 (diff) | |
download | chromium_src-0819b02efb758f7b3f6e5661a18fa7908720cac5.zip chromium_src-0819b02efb758f7b3f6e5661a18fa7908720cac5.tar.gz chromium_src-0819b02efb758f7b3f6e5661a18fa7908720cac5.tar.bz2 |
Move functions from skia/ext to app/gfx where possible: most of skia_utils.* and image_operations.* can be moved because they are not used by WebKit code.
This also fixes the spelling of "Convolusion" to "Convolution" and updates some copyrights.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/207059
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@26975 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'skia')
-rw-r--r-- | skia/ext/convolver.cc | 52 | ||||
-rw-r--r-- | skia/ext/convolver.h | 16 | ||||
-rw-r--r-- | skia/ext/convolver_unittest.cc | 18 | ||||
-rw-r--r-- | skia/ext/image_operations.cc | 407 | ||||
-rw-r--r-- | skia/ext/image_operations.h | 73 | ||||
-rw-r--r-- | skia/ext/image_operations_unittest.cc | 380 | ||||
-rw-r--r-- | skia/ext/skia_utils.cc | 148 | ||||
-rw-r--r-- | skia/ext/skia_utils.h | 34 | ||||
-rw-r--r-- | skia/ext/skia_utils_unittest.cc | 42 |
9 files changed, 81 insertions, 1089 deletions
diff --git a/skia/ext/convolver.cc b/skia/ext/convolver.cc index 9fe9d3c..ec0c014 100644 --- a/skia/ext/convolver.cc +++ b/skia/ext/convolver.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -112,14 +112,14 @@ class CircularRowBuffer { // |src_data| and continues for the num_values() of the filter. template<bool has_alpha> void ConvolveHorizontally(const unsigned char* src_data, - const ConvolusionFilter1D& filter, + const ConvolutionFilter1D& filter, unsigned char* out_row) { // Loop over each pixel on this row in the output image. int num_values = filter.num_values(); for (int out_x = 0; out_x < num_values; out_x++) { // Get the filter that determines the current output pixel. int filter_offset, filter_length; - const ConvolusionFilter1D::Fixed* filter_values = + const ConvolutionFilter1D::Fixed* filter_values = filter.FilterForValue(out_x, &filter_offset, &filter_length); // Compute the first pixel in this row that the filter affects. It will @@ -129,7 +129,7 @@ void ConvolveHorizontally(const unsigned char* src_data, // Apply the filter to the row to get the destination pixel in |accum|. int accum[4] = {0}; for (int filter_x = 0; filter_x < filter_length; filter_x++) { - ConvolusionFilter1D::Fixed cur_filter = filter_values[filter_x]; + ConvolutionFilter1D::Fixed cur_filter = filter_values[filter_x]; accum[0] += cur_filter * row_to_filter[filter_x * 4 + 0]; accum[1] += cur_filter * row_to_filter[filter_x * 4 + 1]; accum[2] += cur_filter * row_to_filter[filter_x * 4 + 2]; @@ -139,11 +139,11 @@ void ConvolveHorizontally(const unsigned char* src_data, // Bring this value back in range. All of the filter scaling factors // are in fixed point with kShiftBits bits of fractional part. - accum[0] >>= ConvolusionFilter1D::kShiftBits; - accum[1] >>= ConvolusionFilter1D::kShiftBits; - accum[2] >>= ConvolusionFilter1D::kShiftBits; + accum[0] >>= ConvolutionFilter1D::kShiftBits; + accum[1] >>= ConvolutionFilter1D::kShiftBits; + accum[2] >>= ConvolutionFilter1D::kShiftBits; if (has_alpha) - accum[3] >>= ConvolusionFilter1D::kShiftBits; + accum[3] >>= ConvolutionFilter1D::kShiftBits; // Store the new pixel. out_row[out_x * 4 + 0] = ClampTo8(accum[0]); @@ -154,19 +154,19 @@ void ConvolveHorizontally(const unsigned char* src_data, } } -// Does vertical convolusion to produce one output row. The filter values and +// Does vertical convolution to produce one output row. The filter values and // length are given in the first two parameters. These are applied to each // of the rows pointed to in the |source_data_rows| array, with each row // being |pixel_width| wide. // // The output must have room for |pixel_width * 4| bytes. template<bool has_alpha> -void ConvolveVertically(const ConvolusionFilter1D::Fixed* filter_values, +void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values, int filter_length, unsigned char* const* source_data_rows, int pixel_width, unsigned char* out_row) { - // We go through each column in the output and do a vertical convolusion, + // We go through each column in the output and do a vertical convolution, // generating one output pixel each time. for (int out_x = 0; out_x < pixel_width; out_x++) { // Compute the number of bytes over in each row that the current column @@ -176,7 +176,7 @@ void ConvolveVertically(const ConvolusionFilter1D::Fixed* filter_values, // Apply the filter to one column of pixels. int accum[4] = {0}; for (int filter_y = 0; filter_y < filter_length; filter_y++) { - ConvolusionFilter1D::Fixed cur_filter = filter_values[filter_y]; + ConvolutionFilter1D::Fixed cur_filter = filter_values[filter_y]; accum[0] += cur_filter * source_data_rows[filter_y][byte_offset + 0]; accum[1] += cur_filter * source_data_rows[filter_y][byte_offset + 1]; accum[2] += cur_filter * source_data_rows[filter_y][byte_offset + 2]; @@ -186,11 +186,11 @@ void ConvolveVertically(const ConvolusionFilter1D::Fixed* filter_values, // Bring this value back in range. All of the filter scaling factors // are in fixed point with kShiftBits bits of precision. - accum[0] >>= ConvolusionFilter1D::kShiftBits; - accum[1] >>= ConvolusionFilter1D::kShiftBits; - accum[2] >>= ConvolusionFilter1D::kShiftBits; + accum[0] >>= ConvolutionFilter1D::kShiftBits; + accum[1] >>= ConvolutionFilter1D::kShiftBits; + accum[2] >>= ConvolutionFilter1D::kShiftBits; if (has_alpha) - accum[3] >>= ConvolusionFilter1D::kShiftBits; + accum[3] >>= ConvolutionFilter1D::kShiftBits; // Store the new pixel. out_row[byte_offset + 0] = ClampTo8(accum[0]); @@ -221,9 +221,9 @@ void ConvolveVertically(const ConvolusionFilter1D::Fixed* filter_values, } // namespace -// ConvolusionFilter1D --------------------------------------------------------- +// ConvolutionFilter1D --------------------------------------------------------- -void ConvolusionFilter1D::AddFilter(int filter_offset, +void ConvolutionFilter1D::AddFilter(int filter_offset, const float* filter_values, int filter_length) { FilterInstance instance; @@ -239,7 +239,7 @@ void ConvolusionFilter1D::AddFilter(int filter_offset, max_filter_ = std::max(max_filter_, filter_length); } -void ConvolusionFilter1D::AddFilter(int filter_offset, +void ConvolutionFilter1D::AddFilter(int filter_offset, const Fixed* filter_values, int filter_length) { FilterInstance instance; @@ -260,8 +260,8 @@ void ConvolusionFilter1D::AddFilter(int filter_offset, void BGRAConvolve2D(const unsigned char* source_data, int source_byte_row_stride, bool source_has_alpha, - const ConvolusionFilter1D& filter_x, - const ConvolusionFilter1D& filter_y, + const ConvolutionFilter1D& filter_x, + const ConvolutionFilter1D& filter_y, unsigned char* output) { int max_y_filter_size = filter_y.max_filter(); @@ -269,22 +269,22 @@ void BGRAConvolve2D(const unsigned char* source_data, // convolved row for. If the filter doesn't start at the beginning of the // image (this is the case when we are only resizing a subset), then we // don't want to generate any output rows before that. Compute the starting - // row for convolusion as the first pixel for the first vertical filter. + // row for convolution as the first pixel for the first vertical filter. int filter_offset, filter_length; - const ConvolusionFilter1D::Fixed* filter_values = + const ConvolutionFilter1D::Fixed* filter_values = filter_y.FilterForValue(0, &filter_offset, &filter_length); int next_x_row = filter_offset; - // We loop over each row in the input doing a horizontal convolusion. This + // We loop over each row in the input doing a horizontal convolution. This // will result in a horizontally convolved image. We write the results into - // a circular buffer of convolved rows and do vertical convolusion as rows + // a circular buffer of convolved rows and do vertical convolution as rows // are available. This prevents us from having to store the entire // intermediate image and helps cache coherency. CircularRowBuffer row_buffer(filter_x.num_values(), max_y_filter_size, filter_offset); // Loop over every possible output row, processing just enough horizontal - // convolusions to run each subsequent vertical convolusion. + // convolutions to run each subsequent vertical convolution. int output_row_byte_width = filter_x.num_values() * 4; int num_output_rows = filter_y.num_values(); for (int out_y = 0; out_y < num_output_rows; out_y++) { diff --git a/skia/ext/convolver.h b/skia/ext/convolver.h index 91c7ccb..1f38282 100644 --- a/skia/ext/convolver.h +++ b/skia/ext/convolver.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -18,18 +18,18 @@ namespace skia { // object for the filter values contributing to it. You build up the filter // list by calling AddFilter for each output pixel (in order). // -// We do 2-dimensional convolusion by first convolving each row by one -// ConvolusionFilter1D, then convolving each column by another one. +// We do 2-dimensional convolution by first convolving each row by one +// ConvolutionFilter1D, then convolving each column by another one. // // Entries are stored in fixed point, shifted left by kShiftBits. -class ConvolusionFilter1D { +class ConvolutionFilter1D { public: // The number of bits that fixed point values are shifted by. enum { kShiftBits = 14 }; typedef short Fixed; - ConvolusionFilter1D() : max_filter_(0) { + ConvolutionFilter1D() : max_filter_(0) { } // Convert between floating point and our fixed point representation. @@ -106,7 +106,7 @@ class ConvolusionFilter1D { int max_filter_; }; -// Does a two-dimensional convolusion on the given source image. +// Does a two-dimensional convolution on the given source image. // // It is assumed the source pixel offsets referenced in the input filters // reference only valid pixels, so the source image size is not required. Each @@ -127,8 +127,8 @@ class ConvolusionFilter1D { void BGRAConvolve2D(const unsigned char* source_data, int source_byte_row_stride, bool source_has_alpha, - const ConvolusionFilter1D& xfilter, - const ConvolusionFilter1D& yfilter, + const ConvolutionFilter1D& xfilter, + const ConvolutionFilter1D& yfilter, unsigned char* output); } // namespace skia diff --git a/skia/ext/convolver_unittest.cc b/skia/ext/convolver_unittest.cc index 10db76c..a25de6e 100644 --- a/skia/ext/convolver_unittest.cc +++ b/skia/ext/convolver_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,7 +14,7 @@ namespace skia { namespace { // Fills the given filter with impulse functions for the range 0->num_entries. -void FillImpulseFilter(int num_entries, ConvolusionFilter1D* filter) { +void FillImpulseFilter(int num_entries, ConvolutionFilter1D* filter) { float one = 1.0f; for (int i = 0; i < num_entries; i++) filter->AddFilter(i, &one, 1); @@ -22,13 +22,13 @@ void FillImpulseFilter(int num_entries, ConvolusionFilter1D* filter) { // Filters the given input with the impulse function, and verifies that it // does not change. -void TestImpulseConvolusion(const unsigned char* data, int width, int height) { +void TestImpulseConvolution(const unsigned char* data, int width, int height) { int byte_count = width * height * 4; - ConvolusionFilter1D filter_x; + ConvolutionFilter1D filter_x; FillImpulseFilter(width, &filter_x); - ConvolusionFilter1D filter_y; + ConvolutionFilter1D filter_y; FillImpulseFilter(height, &filter_y); std::vector<unsigned char> output; @@ -41,7 +41,7 @@ void TestImpulseConvolusion(const unsigned char* data, int width, int height) { // Fills the destination filter with a box filter averaging every two pixels // to produce the output. -void FillBoxFilter(int size, ConvolusionFilter1D* filter) { +void FillBoxFilter(int size, ConvolutionFilter1D* filter) { const float box[2] = { 0.5, 0.5 }; for (int i = 0; i < size; i++) filter->AddFilter(i * 2, box, 2); @@ -68,7 +68,7 @@ TEST(Convolver, Impulse) { input_ptr[(y * width + x) * 4 + channel] = 0xff; // Always set the alpha channel or it will attempt to "fix" it for us. input_ptr[(y * width + x) * 4 + 3] = 0xff; - TestImpulseConvolusion(input_ptr, width, height); + TestImpulseConvolution(input_ptr, width, height); } } } @@ -98,11 +98,11 @@ TEST(Convolver, Halve) { input[i] = rand() * 255 / RAND_MAX; // Compute the filters. - ConvolusionFilter1D filter_x, filter_y; + ConvolutionFilter1D filter_x, filter_y; FillBoxFilter(dest_width, &filter_x); FillBoxFilter(dest_height, &filter_y); - // Do the convolusion. + // Do the convolution. BGRAConvolve2D(&input[0], src_width, true, filter_x, filter_y, &output[0]); // Compute the expected results and check, allowing for a small difference diff --git a/skia/ext/image_operations.cc b/skia/ext/image_operations.cc index afd7b51..ed4673b 100644 --- a/skia/ext/image_operations.cc +++ b/skia/ext/image_operations.cc @@ -1,24 +1,21 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// + #define _USE_MATH_DEFINES #include <cmath> #include <limits> -#include <vector> #include "skia/ext/image_operations.h" -#include "base/gfx/rect.h" -#include "base/gfx/size.h" +// TODO(pkasting): skia/ext should not depend on base/! #include "base/histogram.h" #include "base/logging.h" #include "base/stack_container.h" #include "base/time.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColorPriv.h" -#include "third_party/skia/include/core/SkUnPreMultiply.h" #include "skia/ext/convolver.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkRect.h" namespace skia { @@ -68,15 +65,15 @@ class ResizeFilter { ResizeFilter(ImageOperations::ResizeMethod method, int src_full_width, int src_full_height, int dest_width, int dest_height, - const gfx::Rect& dest_subset); + const SkIRect& dest_subset); // Returns the bounds in the input bitmap of data that is used in the output. // The filter offsets are within this rectangle. - const gfx::Rect& src_depend() { return src_depend_; } + const SkIRect& src_depend() { return src_depend_; } // Returns the filled filter values. - const ConvolusionFilter1D& x_filter() { return x_filter_; } - const ConvolusionFilter1D& y_filter() { return y_filter_; } + const ConvolutionFilter1D& x_filter() { return x_filter_; } + const ConvolutionFilter1D& y_filter() { return y_filter_; } private: // Returns the number of pixels that the filer spans, in filter space (the @@ -109,7 +106,7 @@ class ResizeFilter { void ComputeFilters(int src_size, int dest_subset_lo, int dest_subset_size, float scale, float src_support, - ConvolusionFilter1D* output); + ConvolutionFilter1D* output); // Computes the filter value given the coordinate in filter space. inline float ComputeFilter(float pos) { @@ -127,7 +124,7 @@ class ResizeFilter { ImageOperations::ResizeMethod method_; // Subset of source the filters will touch. - gfx::Rect src_depend_; + SkIRect src_depend_; // Size of the filter support on one side only in the destination space. // See GetFilterSupport. @@ -135,18 +132,18 @@ class ResizeFilter { float y_filter_support_; // Subset of scaled destination bitmap to compute. - gfx::Rect out_bounds_; + SkIRect out_bounds_; - ConvolusionFilter1D x_filter_; - ConvolusionFilter1D y_filter_; + ConvolutionFilter1D x_filter_; + ConvolutionFilter1D y_filter_; - DISALLOW_EVIL_CONSTRUCTORS(ResizeFilter); + DISALLOW_COPY_AND_ASSIGN(ResizeFilter); }; ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method, int src_full_width, int src_full_height, int dest_width, int dest_height, - const gfx::Rect& dest_subset) + const SkIRect& dest_subset) : method_(method), out_bounds_(dest_subset) { float scale_x = static_cast<float>(dest_width) / @@ -157,25 +154,24 @@ ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method, x_filter_support_ = GetFilterSupport(scale_x); y_filter_support_ = GetFilterSupport(scale_y); - gfx::Rect src_full(0, 0, src_full_width, src_full_height); - gfx::Rect dest_full(0, 0, - static_cast<int>(src_full_width * scale_x + 0.5), - static_cast<int>(src_full_height * scale_y + 0.5)); + SkIRect src_full = { 0, 0, src_full_width, src_full_height }; + SkIRect dest_full = { 0, 0, static_cast<int>(src_full_width * scale_x + 0.5), + static_cast<int>(src_full_height * scale_y + 0.5) }; // Support of the filter in source space. float src_x_support = x_filter_support_ / scale_x; float src_y_support = y_filter_support_ / scale_y; - ComputeFilters(src_full_width, dest_subset.x(), dest_subset.width(), + ComputeFilters(src_full_width, dest_subset.fLeft, dest_subset.width(), scale_x, src_x_support, &x_filter_); - ComputeFilters(src_full_height, dest_subset.y(), dest_subset.height(), + ComputeFilters(src_full_height, dest_subset.fTop, dest_subset.height(), scale_y, src_y_support, &y_filter_); } void ResizeFilter::ComputeFilters(int src_size, int dest_subset_lo, int dest_subset_size, float scale, float src_support, - ConvolusionFilter1D* output) { + ConvolutionFilter1D* output) { int dest_subset_hi = dest_subset_lo + dest_subset_size; // [lo, hi) // When we're doing a magnification, the scale will be larger than one. This @@ -259,11 +255,12 @@ void ResizeFilter::ComputeFilters(int src_size, SkBitmap ImageOperations::Resize(const SkBitmap& source, ResizeMethod method, int dest_width, int dest_height, - const gfx::Rect& dest_subset) { + const SkIRect& dest_subset) { // Time how long this takes to see if it's a problem for users. base::TimeTicks resize_start = base::TimeTicks::Now(); - DCHECK(gfx::Rect(dest_width, dest_height).Contains(dest_subset)) << + SkIRect dest = { 0, 0, dest_width, dest_height }; + DCHECK(dest.contains(dest_subset)) << "The supplied subset does not fall within the destination image."; // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just @@ -305,361 +302,9 @@ SkBitmap ImageOperations::Resize(const SkBitmap& source, SkBitmap ImageOperations::Resize(const SkBitmap& source, ResizeMethod method, int dest_width, int dest_height) { - gfx::Rect dest_subset(0, 0, dest_width, dest_height); + SkIRect dest_subset = { 0, 0, dest_width, dest_height }; return Resize(source, method, dest_width, dest_height, dest_subset); } -// static -SkBitmap ImageOperations::CreateBlendedBitmap(const SkBitmap& first, - const SkBitmap& second, - double alpha) { - DCHECK(alpha <= 1 && alpha >= 0); - DCHECK(first.width() == second.width()); - DCHECK(first.height() == second.height()); - DCHECK(first.bytesPerPixel() == second.bytesPerPixel()); - DCHECK(first.config() == SkBitmap::kARGB_8888_Config); - - // Optimize for case where we won't need to blend anything. - static const double alpha_min = 1.0 / 255; - static const double alpha_max = 254.0 / 255; - if (alpha < alpha_min) - return first; - else if (alpha > alpha_max) - return second; - - SkAutoLockPixels lock_first(first); - SkAutoLockPixels lock_second(second); - - SkBitmap blended; - blended.setConfig(SkBitmap::kARGB_8888_Config, first.width(), - first.height(), 0); - blended.allocPixels(); - blended.eraseARGB(0, 0, 0, 0); - - double first_alpha = 1 - alpha; - - for (int y = 0; y < first.height(); y++) { - uint32* first_row = first.getAddr32(0, y); - uint32* second_row = second.getAddr32(0, y); - uint32* dst_row = blended.getAddr32(0, y); - - for (int x = 0; x < first.width(); x++) { - uint32 first_pixel = first_row[x]; - uint32 second_pixel = second_row[x]; - - int a = static_cast<int>( - SkColorGetA(first_pixel) * first_alpha + - SkColorGetA(second_pixel) * alpha); - int r = static_cast<int>( - SkColorGetR(first_pixel) * first_alpha + - SkColorGetR(second_pixel) * alpha); - int g = static_cast<int>( - SkColorGetG(first_pixel) * first_alpha + - SkColorGetG(second_pixel) * alpha); - int b = static_cast<int>( - SkColorGetB(first_pixel) * first_alpha + - SkColorGetB(second_pixel) * alpha); - - dst_row[x] = SkColorSetARGB(a, r, g, b); - } - } - - return blended; -} - -// static -SkBitmap ImageOperations::CreateMaskedBitmap(const SkBitmap& rgb, - const SkBitmap& alpha) { - DCHECK(rgb.width() == alpha.width()); - DCHECK(rgb.height() == alpha.height()); - DCHECK(rgb.bytesPerPixel() == alpha.bytesPerPixel()); - DCHECK(rgb.config() == SkBitmap::kARGB_8888_Config); - DCHECK(alpha.config() == SkBitmap::kARGB_8888_Config); - - SkBitmap masked; - masked.setConfig(SkBitmap::kARGB_8888_Config, rgb.width(), rgb.height(), 0); - masked.allocPixels(); - masked.eraseARGB(0, 0, 0, 0); - - SkAutoLockPixels lock_rgb(rgb); - SkAutoLockPixels lock_alpha(alpha); - SkAutoLockPixels lock_masked(masked); - - for (int y = 0; y < masked.height(); y++) { - uint32* rgb_row = rgb.getAddr32(0, y); - uint32* alpha_row = alpha.getAddr32(0, y); - uint32* dst_row = masked.getAddr32(0, y); - - for (int x = 0; x < masked.width(); x++) { - uint32 alpha_pixel = alpha_row[x]; - SkColor rgb_pixel = SkUnPreMultiply::PMColorToColor(rgb_row[x]); - - int alpha = SkAlphaMul(SkColorGetA(rgb_pixel), SkColorGetA(alpha_pixel)); - dst_row[x] = SkColorSetARGB(alpha, - SkAlphaMul(SkColorGetR(rgb_pixel), alpha), - SkAlphaMul(SkColorGetG(rgb_pixel), alpha), - SkAlphaMul(SkColorGetB(rgb_pixel), alpha)); - } - } - - return masked; -} - -// static -SkBitmap ImageOperations::CreateButtonBackground(SkColor color, - const SkBitmap& image, - const SkBitmap& mask) { - DCHECK(image.config() == SkBitmap::kARGB_8888_Config); - DCHECK(mask.config() == SkBitmap::kARGB_8888_Config); - - SkBitmap background; - background.setConfig(SkBitmap::kARGB_8888_Config, - mask.width(), - mask.height(), 0); - background.allocPixels(); - - int bg_a = SkColorGetA(color); - int bg_r = SkColorGetR(color); - int bg_g = SkColorGetG(color); - int bg_b = SkColorGetB(color); - - SkAutoLockPixels lock_mask(mask); - SkAutoLockPixels lock_image(image); - SkAutoLockPixels lock_background(background); - - for (int y = 0; y < mask.height(); y++) { - uint32* dst_row = background.getAddr32(0, y); - uint32* image_row = image.getAddr32(0, y % image.height()); - uint32* mask_row = mask.getAddr32(0, y); - - for (int x = 0; x < mask.width(); x++) { - uint32 mask_pixel = mask_row[x]; - uint32 image_pixel = image_row[x % image.width()]; - - int img_a = SkColorGetA(image_pixel); - int img_r = SkColorGetR(image_pixel); - int img_g = SkColorGetG(image_pixel); - int img_b = SkColorGetB(image_pixel); - - double img_alpha = static_cast<double>(img_a) / 255.0; - double img_inv = 1 - img_alpha; - - double mask_a = static_cast<double>(SkColorGetA(mask_pixel)) / 255.0; - - dst_row[x] = SkColorSetARGB( - static_cast<int>(std::min(255, bg_a + img_a) * mask_a), - static_cast<int>((bg_r * img_inv + img_r * img_alpha) * mask_a), - static_cast<int>((bg_g * img_inv + img_g * img_alpha) * mask_a), - static_cast<int>((bg_b * img_inv + img_b * img_alpha) * mask_a)); - } - } - - return background; -} - - -SkBitmap ImageOperations::CreateBlurredBitmap(const SkBitmap& bitmap, - int blur_amount ) { - DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); - - // Blur factor (1 divided by how many pixels the blur takes place over). - double v = 1.0 / pow(static_cast<double>(blur_amount * 2 + 1), 2); - - SkBitmap blurred; - blurred.setConfig(SkBitmap::kARGB_8888_Config, bitmap.width(), - bitmap.height(), 0); - blurred.allocPixels(); - blurred.eraseARGB(0, 0, 0, 0); - - SkAutoLockPixels lock_bitmap(bitmap); - SkAutoLockPixels lock_blurred(blurred); - - // Loop through every pixel in the image. - for (int y = 0; y < bitmap.height(); y++) { // Skip top and bottom edges. - uint32* dst_row = blurred.getAddr32(0, y); - - for (int x = 0; x < bitmap.width(); x++) { // Skip left and right edges. - // Sums for this pixel. - double a = 0; - double r = 0; - double g = 0; - double b = 0; - - for (int ky = -blur_amount; ky <= blur_amount; ky++) { - for (int kx = -blur_amount; kx <= blur_amount; kx++) { - // Calculate the adjacent pixel for this kernel point. Blurs - // are wrapped. - int bx = (x + kx) % bitmap.width(); - while (bx < 0) - bx += bitmap.width(); - int by = (y + ky) % bitmap.height(); - while (by < 0) - by += bitmap.height(); - - uint32 src_pixel = bitmap.getAddr32(0, by)[bx]; - - a += v * static_cast<double>(SkColorGetA(src_pixel)); - r += v * static_cast<double>(SkColorGetR(src_pixel)); - g += v * static_cast<double>(SkColorGetG(src_pixel)); - b += v * static_cast<double>(SkColorGetB(src_pixel)); - } - } - - dst_row[x] = SkColorSetARGB( - static_cast<int>(a), - static_cast<int>(r), - static_cast<int>(g), - static_cast<int>(b)); - } - } - - return blurred; -} - -// static -SkBitmap ImageOperations::CreateHSLShiftedBitmap(const SkBitmap& bitmap, - HSL hsl_shift) { - DCHECK(bitmap.empty() == false); - DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); - - SkBitmap shifted; - shifted.setConfig(SkBitmap::kARGB_8888_Config, bitmap.width(), - bitmap.height(), 0); - shifted.allocPixels(); - shifted.eraseARGB(0, 0, 0, 0); - shifted.setIsOpaque(false); - - SkAutoLockPixels lock_bitmap(bitmap); - SkAutoLockPixels lock_shifted(shifted); - - // Loop through the pixels of the original bitmap. - for (int y = 0; y < bitmap.height(); y++) { - SkPMColor* pixels = bitmap.getAddr32(0, y); - SkPMColor* tinted_pixels = shifted.getAddr32(0, y); - - for (int x = 0; x < bitmap.width(); x++) { - SkColor color = SkUnPreMultiply::PMColorToColor(pixels[x]); - SkColor shifted = HSLShift(color, hsl_shift); - tinted_pixels[x] = SkPreMultiplyColor(shifted); - } - } - - return shifted; -} - -// static -SkBitmap ImageOperations::CreateTiledBitmap(const SkBitmap& source, - int src_x, int src_y, - int dst_w, int dst_h) { - DCHECK(source.getConfig() == SkBitmap::kARGB_8888_Config); - - SkBitmap cropped; - cropped.setConfig(SkBitmap::kARGB_8888_Config, dst_w, dst_h, 0); - cropped.allocPixels(); - cropped.eraseARGB(0, 0, 0, 0); - - SkAutoLockPixels lock_source(source); - SkAutoLockPixels lock_cropped(cropped); - - // Loop through the pixels of the original bitmap. - for (int y = 0; y < dst_h; y++) { - int y_pix = (src_y + y) % source.height(); - while (y_pix < 0) - y_pix += source.height(); - - uint32* source_row = source.getAddr32(0, y_pix); - uint32* dst_row = cropped.getAddr32(0, y); - - for (int x = 0; x < dst_w; x++) { - int x_pix = (src_x + x) % source.width(); - while (x_pix < 0) - x_pix += source.width(); - - dst_row[x] = source_row[x_pix]; - } - } - - return cropped; -} - -// static -SkBitmap ImageOperations::DownsampleByTwo(const SkBitmap& bitmap) { - // Handle the nop case. - if (bitmap.width() <= 1 || bitmap.height() <= 1) - return bitmap; - - SkBitmap result; - result.setConfig(SkBitmap::kARGB_8888_Config, - (bitmap.width() + 1) / 2, - (bitmap.height() + 1) / 2); - result.allocPixels(); - - SkAutoLockPixels lock(bitmap); - for (int dest_y = 0; dest_y < result.height(); dest_y++) { - for (int dest_x = 0; dest_x < result.width(); dest_x++ ) { - // This code is based on downsampleby2_proc32 in SkBitmap.cpp. It is very - // clever in that it does two channels at once: alpha and green ("ag") - // and red and blue ("rb"). Each channel gets averaged across 4 pixels - // to get the result. - int src_x = dest_x << 1; - int src_y = dest_y << 1; - const SkPMColor* cur_src = bitmap.getAddr32(src_x, src_y); - SkPMColor tmp, ag, rb; - - // Top left pixel of the 2x2 block. - tmp = *cur_src; - ag = (tmp >> 8) & 0xFF00FF; - rb = tmp & 0xFF00FF; - if (src_x < bitmap.width() - 1) - cur_src += 1; - - // Top right pixel of the 2x2 block. - tmp = *cur_src; - ag += (tmp >> 8) & 0xFF00FF; - rb += tmp & 0xFF00FF; - if (src_y < bitmap.height() - 1) - cur_src = bitmap.getAddr32(src_x, src_y + 1); - else - cur_src = bitmap.getAddr32(src_x, src_y); // Move back to the first. - - // Bottom left pixel of the 2x2 block. - tmp = *cur_src; - ag += (tmp >> 8) & 0xFF00FF; - rb += tmp & 0xFF00FF; - if (src_x < bitmap.width() - 1) - cur_src += 1; - - // Bottom right pixel of the 2x2 block. - tmp = *cur_src; - ag += (tmp >> 8) & 0xFF00FF; - rb += tmp & 0xFF00FF; - - // Put the channels back together, dividing each by 4 to get the average. - // |ag| has the alpha and green channels shifted right by 8 bits from - // there they should end up, so shifting left by 6 gives them in the - // correct position divided by 4. - *result.getAddr32(dest_x, dest_y) = - ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00); - } - } - - return result; -} - -// static -SkBitmap ImageOperations::DownsampleByTwoUntilSize(const SkBitmap& bitmap, - int min_w, int min_h) { - if (bitmap.width() <= min_w || bitmap.height() <= min_h || - min_w < 0 || min_h < 0) - return bitmap; - - // Since bitmaps are refcounted, this copy will be fast. - SkBitmap current = bitmap; - while (current.width() >= min_w * 2 && current.height() >= min_h * 2 && - current.width() > 1 && current.height() > 1) - current = DownsampleByTwo(current); - return current; -} - } // namespace skia diff --git a/skia/ext/image_operations.h b/skia/ext/image_operations.h index 3b172c8..e9f448b 100644 --- a/skia/ext/image_operations.h +++ b/skia/ext/image_operations.h @@ -1,16 +1,12 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef SKIA_EXT_IMAGE_OPERATIONS_H_ #define SKIA_EXT_IMAGE_OPERATIONS_H_ -#include "base/basictypes.h" -#include "base/gfx/rect.h" -#include "skia/ext/skia_utils.h" -#include "third_party/skia/include/core/SkColor.h" - class SkBitmap; +struct SkIRect; namespace skia { @@ -40,7 +36,7 @@ class ImageOperations { static SkBitmap Resize(const SkBitmap& source, ResizeMethod method, int dest_width, int dest_height, - const gfx::Rect& dest_subset); + const SkIRect& dest_subset); // Alternate version for resizing and returning the entire bitmap rather than // a subset. @@ -48,69 +44,6 @@ class ImageOperations { ResizeMethod method, int dest_width, int dest_height); - // Create a bitmap that is a blend of two others. The alpha argument - // specifies the opacity of the second bitmap. The provided bitmaps must - // use have the kARGB_8888_Config config and be of equal dimensions. - static SkBitmap CreateBlendedBitmap(const SkBitmap& first, - const SkBitmap& second, - double alpha); - - // Create a bitmap that is the original bitmap masked out by the mask defined - // in the alpha bitmap. The images must use the kARGB_8888_Config config and - // be of equal dimensions. - static SkBitmap CreateMaskedBitmap(const SkBitmap& first, - const SkBitmap& alpha); - - // We create a button background image by compositing the color and image - // together, then applying the mask. This is a highly specialized composite - // operation that is the equivalent of drawing a background in |color|, - // tiling |image| over the top, and then masking the result out with |mask|. - // The images must use kARGB_8888_Config config. - static SkBitmap CreateButtonBackground(SkColor color, - const SkBitmap& image, - const SkBitmap& mask); - - // Blur a bitmap using an average-blur algorithm over the rectangle defined - // by |blur_amount|. The blur will wrap around image edges. - static SkBitmap CreateBlurredBitmap(const SkBitmap& bitmap, int blur_amount); - - // Shift a bitmap's HSL values. The shift values are in the range of 0-1, - // with the option to specify -1 for 'no change'. The shift values are - // defined as: - // hsl_shift[0] (hue): The absolute hue value for the image - 0 and 1 map - // to 0 and 360 on the hue color wheel (red). - // hsl_shift[1] (saturation): A saturation shift for the image, with the - // following key values: - // 0 = remove all color. - // 0.5 = leave unchanged. - // 1 = fully saturate the image. - // hsl_shift[2] (lightness): A lightness shift for the image, with the - // following key values: - // 0 = remove all lightness (make all pixels black). - // 0.5 = leave unchanged. - // 1 = full lightness (make all pixels white). - static SkBitmap CreateHSLShiftedBitmap(const SkBitmap& bitmap, - HSL hsl_shift); - - // Create a bitmap that is cropped from another bitmap. This is special - // because it tiles the original bitmap, so your coordinates can extend - // outside the bounds of the original image. - static SkBitmap CreateTiledBitmap(const SkBitmap& bitmap, - int src_x, int src_y, - int dst_w, int dst_h); - - // Makes a bitmap half has large in each direction by averaging groups of - // 4 pixels. This is one step in generating a mipmap. - static SkBitmap DownsampleByTwo(const SkBitmap& bitmap); - - // Iteratively downsamples by 2 until the bitmap is no smaller than the - // input size. The normal use of this is to downsample the bitmap "close" to - // the final size, and then use traditional resampling on the result. - // Because the bitmap will be closer to the final size, it will be faster, - // and linear interpolation will generally work well as a second step. - static SkBitmap DownsampleByTwoUntilSize(const SkBitmap& bitmap, - int min_w, int min_h); - private: ImageOperations(); // Class for scoping only. }; diff --git a/skia/ext/image_operations_unittest.cc b/skia/ext/image_operations_unittest.cc index dbf4fce..52d13b9 100644 --- a/skia/ext/image_operations_unittest.cc +++ b/skia/ext/image_operations_unittest.cc @@ -1,14 +1,11 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <stdlib.h> - #include "skia/ext/image_operations.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColorPriv.h" -#include "third_party/skia/include/core/SkUnPreMultiply.h" +#include "third_party/skia/include/core/SkRect.h" namespace { @@ -105,7 +102,7 @@ TEST(ImageOperations, HalveSubset) { // Now do a halving of a a subset, recall the destination subset is in the // destination coordinate system (max = half of the original image size). - gfx::Rect subset_rect(2, 3, 3, 6); + SkIRect subset_rect = { 2, 3, 3, 6 }; SkBitmap subset_results = skia::ImageOperations::Resize( src, skia::ImageOperations::RESIZE_BOX, src_w / 2, src_h / 2, subset_rect); @@ -119,7 +116,7 @@ TEST(ImageOperations, HalveSubset) { for (int y = 0; y < subset_rect.height(); y++) { for (int x = 0; x < subset_rect.width(); x++) { ASSERT_EQ( - *full_results.getAddr32(x + subset_rect.x(), y + subset_rect.y()), + *full_results.getAddr32(x + subset_rect.fLeft, y + subset_rect.fTop), *subset_results.getAddr32(x, y)); } } @@ -147,372 +144,3 @@ TEST(ImageOperations, ResampleToSame) { } } } - -// Blend two bitmaps together at 50% alpha and verify that the result -// is the middle-blend of the two. -TEST(ImageOperations, CreateBlendedBitmap) { - int src_w = 16, src_h = 16; - SkBitmap src_a; - src_a.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h); - src_a.allocPixels(); - - SkBitmap src_b; - src_b.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h); - src_b.allocPixels(); - - for (int y = 0, i = 0; y < src_h; y++) { - for (int x = 0; x < src_w; x++) { - *src_a.getAddr32(x, y) = SkColorSetARGB(255, 0, i * 2 % 255, i % 255); - *src_b.getAddr32(x, y) = - SkColorSetARGB((255 - i) % 255, i % 255, i * 4 % 255, 0); - i++; - } - } - - // Shift to red. - SkBitmap blended = skia::ImageOperations::CreateBlendedBitmap( - src_a, src_b, 0.5); - SkAutoLockPixels srca_lock(src_a); - SkAutoLockPixels srcb_lock(src_b); - SkAutoLockPixels blended_lock(blended); - - for (int y = 0; y < src_h; y++) { - for (int x = 0; x < src_w; x++) { - int i = y * src_w + x; - EXPECT_EQ(static_cast<unsigned int>((255 + ((255 - i) % 255)) / 2), - SkColorGetA(*blended.getAddr32(x, y))); - EXPECT_EQ(static_cast<unsigned int>(i % 255 / 2), - SkColorGetR(*blended.getAddr32(x, y))); - EXPECT_EQ((static_cast<unsigned int>((i * 2) % 255 + (i * 4) % 255) / 2), - SkColorGetG(*blended.getAddr32(x, y))); - EXPECT_EQ(static_cast<unsigned int>(i % 255 / 2), - SkColorGetB(*blended.getAddr32(x, y))); - } - } -} - -// Test our masking functions. -TEST(ImageOperations, CreateMaskedBitmap) { - int src_w = 16, src_h = 16; - - SkBitmap src; - FillDataToBitmap(src_w, src_h, &src); - - // Generate alpha mask - SkBitmap alpha; - alpha.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h); - alpha.allocPixels(); - for (int y = 0, i = 0; y < src_h; y++) { - for (int x = 0; x < src_w; x++) { - *alpha.getAddr32(x, y) = SkColorSetARGB((i + 128) % 255, - (i + 128) % 255, - (i + 64) % 255, - (i + 0) % 255); - i++; - } - } - - SkBitmap masked = skia::ImageOperations::CreateMaskedBitmap(src, alpha); - - SkAutoLockPixels src_lock(src); - SkAutoLockPixels alpha_lock(alpha); - SkAutoLockPixels masked_lock(masked); - for (int y = 0; y < src_h; y++) { - for (int x = 0; x < src_w; x++) { - // Test that the alpha is equal. - SkColor src_pixel = SkUnPreMultiply::PMColorToColor(*src.getAddr32(x, y)); - SkColor alpha_pixel = - SkUnPreMultiply::PMColorToColor(*alpha.getAddr32(x, y)); - SkColor masked_pixel = *masked.getAddr32(x, y); - - int alpha_value = SkAlphaMul(SkColorGetA(src_pixel), - SkColorGetA(alpha_pixel)); - SkColor expected_pixel = SkColorSetARGB( - alpha_value, - SkAlphaMul(SkColorGetR(src_pixel), alpha_value), - SkAlphaMul(SkColorGetG(src_pixel), alpha_value), - SkAlphaMul(SkColorGetB(src_pixel), alpha_value)); - - EXPECT_TRUE(ColorsClose(expected_pixel, masked_pixel)); - } - } -} - -// Testing blur without reimplementing the blur algorithm here is tough, -// so we just check to see if the pixels have moved in the direction we -// think they should move in (and also checking the wrapping behavior). -// This will allow us to tweak the blur algorithm to suit speed/visual -// needs without breaking the fundamentals. -TEST(ImageOperations, CreateBlurredBitmap) { - int src_w = 4, src_h = 4; - SkBitmap src; - src.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h); - src.allocPixels(); - - for (int y = 0, i = 0; y < src_h; y++) { - for (int x = 0; x < src_w; x++) { - int r = (y == 0) ? 255 : 0; // Make the top row red. - int g = (i % 2 == 0) ? 255 : 0; // Make green alternate in each pixel. - int b = (y == src_h - 1) ? 255 : 0; // Make the bottom row blue. - - *src.getAddr32(x, y) = SkColorSetARGB(255, r, g, b); - i++; - } - } - - // Perform a small blur (enough to shove the values in the direction we - // need - more would just be an unneccessary unit test slowdown). - SkBitmap blurred = skia::ImageOperations::CreateBlurredBitmap(src, 2); - - SkAutoLockPixels src_lock(src); - SkAutoLockPixels blurred_lock(blurred); - for (int y = 0, i = 0; y < src_w; y++) { - for (int x = 0; x < src_h; x++) { - SkColor src_pixel = *src.getAddr32(x, y); - SkColor blurred_pixel = *blurred.getAddr32(x, y); - if (y == 0) { - // We expect our red to have decreased, but our blue to have - // increased (from the wrapping from the bottom line). - EXPECT_TRUE(SkColorGetR(blurred_pixel) < SkColorGetR(src_pixel)); - EXPECT_TRUE(SkColorGetB(blurred_pixel) > SkColorGetB(src_pixel)); - } else if (y == src_h - 1) { - // Now for the opposite. - EXPECT_TRUE(SkColorGetB(blurred_pixel) < SkColorGetB(src_pixel)); - EXPECT_TRUE(SkColorGetR(blurred_pixel) > SkColorGetR(src_pixel)); - } - - // Expect the green channel to have moved towards the center (but - // not past it). - if (i % 2 == 0) { - EXPECT_LT(SkColorGetG(blurred_pixel), SkColorGetG(src_pixel)); - EXPECT_GE(SkColorGetG(blurred_pixel), static_cast<uint32>(128)); - } else { - EXPECT_GT(SkColorGetG(blurred_pixel), SkColorGetG(src_pixel)); - EXPECT_LE(SkColorGetG(blurred_pixel), static_cast<uint32>(128)); - } - - i++; - } - } -} - -// Make sure that when shifting a bitmap without any shift parameters, -// the end result is close enough to the original (rounding errors -// notwithstanding). -TEST(ImageOperations, CreateHSLShiftedBitmapToSame) { - int src_w = 4, src_h = 4; - SkBitmap src; - src.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h); - src.allocPixels(); - - for (int y = 0, i = 0; y < src_h; y++) { - for (int x = 0; x < src_w; x++) { - *src.getAddr32(x, y) = SkColorSetARGB(i + 128 % 255, - i + 128 % 255, i + 64 % 255, i + 0 % 255); - i++; - } - } - - skia::HSL hsl = { -1, -1, -1 }; - - SkBitmap shifted = skia::ImageOperations::CreateHSLShiftedBitmap(src, hsl); - - SkAutoLockPixels src_lock(src); - SkAutoLockPixels shifted_lock(shifted); - - for (int y = 0; y < src_w; y++) { - for (int x = 0; x < src_h; x++) { - SkColor src_pixel = *src.getAddr32(x, y); - SkColor shifted_pixel = *shifted.getAddr32(x, y); - EXPECT_TRUE(ColorsClose(src_pixel, shifted_pixel)); - } - } -} - -// Shift a blue bitmap to red. -TEST(ImageOperations, CreateHSLShiftedBitmapHueOnly) { - int src_w = 16, src_h = 16; - SkBitmap src; - src.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h); - src.allocPixels(); - - for (int y = 0, i = 0; y < src_h; y++) { - for (int x = 0; x < src_w; x++) { - *src.getAddr32(x, y) = SkColorSetARGB(255, 0, 0, i % 255); - i++; - } - } - - // Shift to red. - skia::HSL hsl = { 0, -1, -1 }; - - SkBitmap shifted = skia::ImageOperations::CreateHSLShiftedBitmap(src, hsl); - - SkAutoLockPixels src_lock(src); - SkAutoLockPixels shifted_lock(shifted); - - for (int y = 0, i = 0; y < src_h; y++) { - for (int x = 0; x < src_w; x++) { - EXPECT_TRUE(ColorsClose(*shifted.getAddr32(x, y), - SkColorSetARGB(255, i % 255, 0, 0))); - i++; - } - } -} - -// Test our cropping. -TEST(ImageOperations, CreateCroppedBitmap) { - int src_w = 16, src_h = 16; - SkBitmap src; - FillDataToBitmap(src_w, src_h, &src); - - SkBitmap cropped = skia::ImageOperations::CreateTiledBitmap(src, 4, 4, - 8, 8); - ASSERT_EQ(8, cropped.width()); - ASSERT_EQ(8, cropped.height()); - - SkAutoLockPixels src_lock(src); - SkAutoLockPixels cropped_lock(cropped); - for (int y = 4; y < 12; y++) { - for (int x = 4; x < 12; x++) { - EXPECT_EQ(*src.getAddr32(x, y), - *cropped.getAddr32(x - 4, y - 4)); - } - } -} - -// Test whether our cropping correctly wraps across image boundaries. -TEST(ImageOperations, CreateCroppedBitmapWrapping) { - int src_w = 16, src_h = 16; - SkBitmap src; - FillDataToBitmap(src_w, src_h, &src); - - SkBitmap cropped = skia::ImageOperations::CreateTiledBitmap( - src, src_w / 2, src_h / 2, src_w, src_h); - ASSERT_EQ(src_w, cropped.width()); - ASSERT_EQ(src_h, cropped.height()); - - SkAutoLockPixels src_lock(src); - SkAutoLockPixels cropped_lock(cropped); - for (int y = 0; y < src_h; y++) { - for (int x = 0; x < src_w; x++) { - EXPECT_EQ(*src.getAddr32(x, y), - *cropped.getAddr32((x + src_w / 2) % src_w, - (y + src_h / 2) % src_h)); - } - } -} - -TEST(ImageOperations, DownsampleByTwo) { - // Use an odd-sized bitmap to make sure the edge cases where there isn't a - // 2x2 block of pixels is handled correctly. - // Here's the ARGB example - // - // 50% transparent green opaque 50% blue white - // 80008000 FF000080 FFFFFFFF - // - // 50% transparent red opaque 50% gray black - // 80800000 80808080 FF000000 - // - // black white 50% gray - // FF000000 FFFFFFFF FF808080 - // - // The result of this computation should be: - // A0404040 FF808080 - // FF808080 FF808080 - SkBitmap input; - input.setConfig(SkBitmap::kARGB_8888_Config, 3, 3); - input.allocPixels(); - - // The color order may be different, but we don't care (the channels are - // trated the same). - *input.getAddr32(0, 0) = 0x80008000; - *input.getAddr32(1, 0) = 0xFF000080; - *input.getAddr32(2, 0) = 0xFFFFFFFF; - *input.getAddr32(0, 1) = 0x80800000; - *input.getAddr32(1, 1) = 0x80808080; - *input.getAddr32(2, 1) = 0xFF000000; - *input.getAddr32(0, 2) = 0xFF000000; - *input.getAddr32(1, 2) = 0xFFFFFFFF; - *input.getAddr32(2, 2) = 0xFF808080; - - SkBitmap result = skia::ImageOperations::DownsampleByTwo(input); - EXPECT_EQ(2, result.width()); - EXPECT_EQ(2, result.height()); - - // Some of the values are off-by-one due to rounding. - SkAutoLockPixels lock(result); - EXPECT_EQ(0x9f404040, *result.getAddr32(0, 0)); - EXPECT_EQ(0xFF7f7f7f, *result.getAddr32(1, 0)); - EXPECT_EQ(0xFF7f7f7f, *result.getAddr32(0, 1)); - EXPECT_EQ(0xFF808080, *result.getAddr32(1, 1)); -} - -// Test edge cases for DownsampleByTwo. -TEST(ImageOperations, DownsampleByTwoSmall) { - SkPMColor reference = 0xFF4080FF; - - // Test a 1x1 bitmap. - SkBitmap one_by_one; - one_by_one.setConfig(SkBitmap::kARGB_8888_Config, 1, 1); - one_by_one.allocPixels(); - *one_by_one.getAddr32(0, 0) = reference; - SkBitmap result = skia::ImageOperations::DownsampleByTwo(one_by_one); - SkAutoLockPixels lock1(result); - EXPECT_EQ(1, result.width()); - EXPECT_EQ(1, result.height()); - EXPECT_EQ(reference, *result.getAddr32(0, 0)); - - // Test an n by 1 bitmap. - SkBitmap one_by_n; - one_by_n.setConfig(SkBitmap::kARGB_8888_Config, 300, 1); - one_by_n.allocPixels(); - result = skia::ImageOperations::DownsampleByTwo(one_by_n); - SkAutoLockPixels lock2(result); - EXPECT_EQ(300, result.width()); - EXPECT_EQ(1, result.height()); - - // Test a 1 by n bitmap. - SkBitmap n_by_one; - n_by_one.setConfig(SkBitmap::kARGB_8888_Config, 1, 300); - n_by_one.allocPixels(); - result = skia::ImageOperations::DownsampleByTwo(n_by_one); - SkAutoLockPixels lock3(result); - EXPECT_EQ(1, result.width()); - EXPECT_EQ(300, result.height()); - - // Test an empty bitmap - SkBitmap empty; - result = skia::ImageOperations::DownsampleByTwo(empty); - EXPECT_TRUE(result.isNull()); - EXPECT_EQ(0, result.width()); - EXPECT_EQ(0, result.height()); -} - -// Here we assume DownsampleByTwo works correctly (it's tested above) and -// just make sure that the -TEST(ImageOperations, DownsampleByTwoUntilSize) { - // First make sure a "too small" bitmap doesn't get modified at all. - SkBitmap too_small; - too_small.setConfig(SkBitmap::kARGB_8888_Config, 10, 10); - too_small.allocPixels(); - SkBitmap result = skia::ImageOperations::DownsampleByTwoUntilSize( - too_small, 16, 16); - EXPECT_EQ(10, result.width()); - EXPECT_EQ(10, result.height()); - - // Now make sure giving it a 0x0 target returns something reasonable. - result = skia::ImageOperations::DownsampleByTwoUntilSize(too_small, 0, 0); - EXPECT_EQ(1, result.width()); - EXPECT_EQ(1, result.height()); - - // Test multiple steps of downsampling. - SkBitmap large; - large.setConfig(SkBitmap::kARGB_8888_Config, 100, 43); - large.allocPixels(); - result = skia::ImageOperations::DownsampleByTwoUntilSize(large, 6, 6); - - // The result should be divided in half 100x43 -> 50x22 -> 25x11 - EXPECT_EQ(25, result.width()); - EXPECT_EQ(11, result.height()); -} diff --git a/skia/ext/skia_utils.cc b/skia/ext/skia_utils.cc index 84a003b..c95054d 100644 --- a/skia/ext/skia_utils.cc +++ b/skia/ext/skia_utils.cc @@ -1,9 +1,10 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "skia/ext/skia_utils.h" #include "third_party/skia/include/core/SkColorPriv.h" +#include "third_party/skia/include/core/SkShader.h" #include "third_party/skia/include/effects/SkGradientShader.h" namespace skia { @@ -21,150 +22,5 @@ SkShader* CreateGradientShader(int start_point, grad_points, grad_colors, NULL, 2, SkShader::kRepeat_TileMode); } -// Helper function for HSLToSKColor. -static inline double calcHue(double temp1, double temp2, double hueVal) { - if (hueVal < 0.0) - hueVal++; - else if (hueVal > 1.0) - hueVal--; - - if (hueVal * 6.0 < 1.0) - return temp1 + (temp2 - temp1) * hueVal * 6.0; - if (hueVal * 2.0 < 1.0) - return temp2; - if (hueVal * 3.0 < 2.0) - return temp1 + (temp2 - temp1) * (2.0 / 3.0 - hueVal) * 6.0; - - return temp1; -} - -SkColor HSLToSkColor(U8CPU alpha, HSL hsl) { - double hue = hsl.h; - double saturation = hsl.s; - double lightness = hsl.l; - - // If there's no color, we don't care about hue and can do everything based - // on brightness. - if (!saturation) { - U8CPU light; - - if (lightness < 0) - light = 0; - else if (lightness >= SK_Scalar1) - light = 255; - else - light = SkDoubleToFixed(lightness) >> 8; - - return SkColorSetARGB(alpha, light, light, light); - } - - double temp2 = (lightness < 0.5) ? - lightness * (1.0 + saturation) : - lightness + saturation - (lightness * saturation); - double temp1 = 2.0 * lightness - temp2; - - double rh = calcHue(temp1, temp2, hue + 1.0 / 3.0); - double gh = calcHue(temp1, temp2, hue); - double bh = calcHue(temp1, temp2, hue - 1.0 / 3.0); - - return SkColorSetARGB(alpha, - static_cast<int>(rh * 255), - static_cast<int>(gh * 255), - static_cast<int>(bh * 255)); -} - -void SkColorToHSL(SkColor c, HSL& hsl) { - double r = SkColorGetR(c) / 255.0; - double g = SkColorGetG(c) / 255.0; - double b = SkColorGetB(c) / 255.0; - - double h, s, l; - - double vmax = r > g ? r : g; - vmax = vmax > b ? vmax : b; - double vmin = r < g ? r : g; - vmin = vmin < b ? vmin : b; - double delta = vmax - vmin; - - l = (vmax + vmin) / 2; - - if (delta == 0) { - h = 0; - s = 0; - } else { - if (l < 0.5) - s = delta / (vmax + vmin); - else - s = delta / (2 - vmax - vmin); - - double dr = (((vmax - r) / 6.0) + (delta / 2.0)) / delta; - double dg = (((vmax - g) / 6.0) + (delta / 2.0)) / delta; - double db = (((vmax - b) / 6.0) + (delta / 2.0)) / delta; - - if (r == vmax) - h = db - dg; - else if (g == vmax) - h = (1.0 / 3.0) + dr - db; - else if (b == vmax) - h = (2.0 / 3.0) + dg - dr; - - if (h < 0) h += 1; - if (h > 1) h -= 1; - } - - hsl.h = h; - hsl.s = s; - hsl.l = l; -} - -SkColor HSLShift(SkColor color, HSL shift) { - HSL hsl; - int alpha = SkColorGetA(color); - SkColorToHSL(color, hsl); - - // Replace the hue with the tint's hue. - if (shift.h >= 0) - hsl.h = shift.h; - - // Change the saturation. - if (shift.s >= 0) { - if (shift.s <= 0.5) { - hsl.s *= shift.s * 2.0; - } else { - hsl.s = hsl.s + (1.0 - hsl.s) * - ((shift.s - 0.5) * 2.0); - } - } - - SkColor result = HSLToSkColor(alpha, hsl); - - // Lightness shifts in the style of popular image editors aren't - // actually represented in HSL - the L value does have some effect - // on saturation. - if (shift.l >= 0) { - double r = static_cast<double>SkColorGetR(result); - double g = static_cast<double>SkColorGetG(result); - double b = static_cast<double>SkColorGetB(result); - - if (shift.l <= 0.5) { - r *= (shift.l * 2.0); - g *= (shift.l * 2.0); - b *= (shift.l * 2.0); - } else { - r = (r + (255.0 - r) * ((shift.l - 0.5) * 2.0)); - g = (g + (255.0 - g) * ((shift.l - 0.5) * 2.0)); - b = (b + (255.0 - b) * ((shift.l - 0.5) * 2.0)); - } - - return SkColorSetARGB(alpha, - static_cast<int>(r), - static_cast<int>(g), - static_cast<int>(b)); - } else { - return result; - } -} - - } // namespace skia diff --git a/skia/ext/skia_utils.h b/skia/ext/skia_utils.h index 769d9d4..52d9d62 100644 --- a/skia/ext/skia_utils.h +++ b/skia/ext/skia_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,15 +6,10 @@ #define SKIA_EXT_SKIA_UTILS_H_ #include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkShader.h" -namespace skia { +class SkShader; -struct HSL { - double h; - double s; - double l; -}; +namespace skia { // Creates a vertical gradient shader. The caller owns the shader. // Example usage to avoid leaks: @@ -26,29 +21,6 @@ SkShader* CreateGradientShader(int start_point, int end_point, SkColor start_color, SkColor end_color); - -// Convert an SkColor to a HSL value. -void SkColorToHSL(SkColor c, HSL& hsl); - -// Convert a HSL color to an SkColor. -SkColor HSLToSkColor(U8CPU alpha, HSL hsl); - -// HSL-Shift an SkColor. The shift values are in the range of 0-1, with the -// option to specify -1 for 'no change'. The shift values are defined as: -// hsl_shift[0] (hue): The absolute hue value - 0 and 1 map -// to 0 and 360 on the hue color wheel (red). -// hsl_shift[1] (saturation): A saturation shift, with the -// following key values: -// 0 = remove all color. -// 0.5 = leave unchanged. -// 1 = fully saturate the image. -// hsl_shift[2] (lightness): A lightness shift, with the -// following key values: -// 0 = remove all lightness (make all pixels black). -// 0.5 = leave unchanged. -// 1 = full lightness (make all pixels white). -SkColor HSLShift(SkColor color, skia::HSL shift); - } // namespace skia #endif // SKIA_EXT_SKIA_UTILS_H_ diff --git a/skia/ext/skia_utils_unittest.cc b/skia/ext/skia_utils_unittest.cc deleted file mode 100644 index 3b590b4..0000000 --- a/skia/ext/skia_utils_unittest.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stdlib.h> - -#include "skia/ext/skia_utils.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColorPriv.h" - -TEST(SkiaUtils, SkColorToHSLRed) { - SkColor red = SkColorSetARGB(255, 255, 0, 0); - skia::HSL hsl = { 0, 0, 0 }; - skia::SkColorToHSL(red, hsl); - EXPECT_EQ(hsl.h, 0); - EXPECT_EQ(hsl.s, 1); - EXPECT_EQ(hsl.l, 0.5); -} - -TEST(SkiaUtils, SkColorToHSLGrey) { - SkColor red = SkColorSetARGB(255, 128, 128, 128); - skia::HSL hsl = { 0, 0, 0 }; - skia::SkColorToHSL(red, hsl); - EXPECT_EQ(hsl.h, 0); - EXPECT_EQ(hsl.s, 0); - EXPECT_EQ(static_cast<int>(hsl.l * 100), - static_cast<int>(0.5 * 100)); // Accurate to two decimal places. -} - -TEST(SkiaUtils, HSLToSkColorWithAlpha) { - SkColor red = SkColorSetARGB(128, 255, 0, 0); - - skia::HSL hsl = { 0, 1, 0.5 }; - - SkColor result = skia::HSLToSkColor(128, hsl); - EXPECT_EQ(SkColorGetA(red), SkColorGetA(result)); - EXPECT_EQ(SkColorGetR(red), SkColorGetR(result)); - EXPECT_EQ(SkColorGetG(red), SkColorGetG(result)); - EXPECT_EQ(SkColorGetB(red), SkColorGetB(result)); -} - |