Chromium Code Reviews| Index: skia/ext/convolver.cc |
| diff --git a/skia/ext/convolver.cc b/skia/ext/convolver.cc |
| index ee9d056fa436336338d92f8a3620d285c1d634cb..ab285dc4a39cfab3a639248d11622978f3f254a9 100644 |
| --- a/skia/ext/convolver.cc |
| +++ b/skia/ext/convolver.cc |
| @@ -5,6 +5,7 @@ |
| #include <algorithm> |
| #include "skia/ext/convolver.h" |
| +#include "third_party/skia/include/core/SkSize.h" |
| #include "third_party/skia/include/core/SkTypes.h" |
| #if defined(SIMD_SSE2) |
| @@ -682,6 +683,7 @@ void ConvolutionFilter1D::AddFilter(int filter_offset, |
| // cases it is beneficial to only store the central factors. |
| // For a scaling to 1/4th in each dimension using a Lanczos-2 filter on |
| // a 1080p image this optimization gives a ~10% speed improvement. |
| + int filter_size = filter_length; |
| int first_non_zero = 0; |
| while (first_non_zero < filter_length && filter_values[first_non_zero] == 0) |
| first_non_zero++; |
| @@ -710,6 +712,7 @@ void ConvolutionFilter1D::AddFilter(int filter_offset, |
| filter_length); |
| instance.offset = filter_offset; |
| instance.length = filter_length; |
| + instance.filter_size = filter_size; |
| filters_.push_back(instance); |
| max_filter_ = std::max(max_filter_, filter_length); |
| @@ -857,4 +860,114 @@ void BGRAConvolve2D(const unsigned char* source_data, |
| } |
| } |
| +void SingleChannelConvolve1D_X(const unsigned char* source_data, |
| + int source_byte_row_stride, |
| + int input_channel_index, |
| + int input_channel_count, |
| + const ConvolutionFilter1D& filter, |
| + const SkISize& image_size, |
| + unsigned char* output, |
| + int output_byte_row_stride, |
| + int output_channel_index, |
| + int output_channel_count, |
| + bool absolute_values) { |
| + int filter_offset, filter_length, filter_size; |
| + // Very much unlike BGRAConvolve2D, here we expect to have the same filter |
| + // for all pixels. |
| + const ConvolutionFilter1D::Fixed* filter_values = |
| + filter.GetSingleFilter(&filter_size, &filter_offset, &filter_length); |
| + |
| + if (filter_values == NULL) |
| + return; |
| + |
| + int centrepoint = filter_length / 2; |
| + if (filter_size - filter_offset != 2 * filter_offset) { |
| + // This means the original filter was not symmetrical AND |
| + // got clipped from one side more than from the other. |
| + centrepoint = filter_size / 2 - filter_offset; |
| + } |
| + |
| + const unsigned char* source_data_row = source_data; |
| + unsigned char* output_row = output; |
| + |
| + for (int r = 0; r < image_size.height(); ++r) { |
| + unsigned char* target_byte = output_row + output_channel_index; |
| + for (int c = 0; c < image_size.width(); ++c) { |
| + int accval = 0; |
| + for (int i = 0; i < filter_length; ++i) { |
| + ConvolutionFilter1D::Fixed cur_filter = filter_values[i]; |
| + int pixel_index = c + i - centrepoint; |
|
Stephen White
2013/04/10 15:20:31
Perf: could probably hoist this out of the loop.
motek.
2013/04/11 16:18:38
I have reworked that from the perf angle.
|
| + // Handling of edges is always wrap-around. Clip to 0 / width. |
|
Stephen White
2013/04/10 15:20:31
Seems strange to wrap-around instead of clamp, but
motek.
2013/04/11 16:18:38
Padding would be most reasonable for this applicat
|
| + pixel_index = (pixel_index + image_size.width()) % image_size.width(); |
|
Stephen White
2013/04/10 15:20:31
Perf: could compute the margins in separate inner
motek.
2013/04/11 16:18:38
Done.
|
| + const unsigned char src_value = source_data_row[ |
| + pixel_index * input_channel_count + input_channel_index]; |
| + accval += cur_filter * src_value; |
| + } |
| + // Bring this value back in range. All of the filter scaling factors |
| + // are in fixed point with kShiftBits bits of fractional part. |
| + accval >>= ConvolutionFilter1D::kShiftBits; |
| + if (absolute_values) |
| + accval = std::abs(accval); |
| + *target_byte = ClampTo8(accval); |
| + target_byte += output_channel_count; |
| + } |
| + |
| + source_data_row += source_byte_row_stride; |
| + output_row += output_byte_row_stride; |
| + } |
| +} |
| + |
| +void SingleChannelConvolve1D_Y(const unsigned char* source_data, |
| + int source_byte_row_stride, |
| + int input_channel_index, |
| + int input_channel_count, |
| + const ConvolutionFilter1D& filter, |
| + const SkISize& image_size, |
| + unsigned char* output, |
| + int output_byte_row_stride, |
| + int output_channel_index, |
| + int output_channel_count, |
| + bool absolute_values) { |
| + int filter_offset, filter_length, filter_size; |
| + // Very much unlike BGRAConvolve2D, here we expect to have the same filter |
| + // for all pixels. |
| + const ConvolutionFilter1D::Fixed* filter_values = |
| + filter.GetSingleFilter(&filter_size, &filter_offset, &filter_length); |
| + |
| + if (filter_values == NULL) |
| + return; |
| + |
| + int centrepoint = filter_length / 2; |
| + if (filter_size - filter_offset != 2 * filter_offset) { |
| + // This means the original filter was not symmetrical AND |
| + // got clipped from one side more than from the other. |
| + centrepoint = filter_size / 2 - filter_offset; |
| + } |
| + |
| + for (int c = 0; c < image_size.width(); ++c) { |
| + for (int r = 0; r < image_size.height(); ++r) { |
| + unsigned char* target_byte = output + |
| + r * output_byte_row_stride + |
| + c * output_channel_count + |
| + output_channel_index; |
| + int accval = 0; |
| + for (int i = 0; i < filter_length; ++i) { |
| + ConvolutionFilter1D::Fixed cur_filter = filter_values[i]; |
| + int pixel_index = r + i - centrepoint; |
| + pixel_index = (pixel_index + image_size.height()) % image_size.height(); |
| + |
| + const unsigned char src_value = source_data[ |
| + pixel_index * source_byte_row_stride + |
| + c * input_channel_count + |
| + input_channel_index]; |
| + accval += cur_filter * src_value; |
| + } |
| + accval >>= ConvolutionFilter1D::kShiftBits; |
| + if (absolute_values) |
| + accval = std::abs(accval); |
| + *target_byte = ClampTo8(accval); |
| + } |
| + } |
| +} |
| + |
| } // namespace skia |