OLD | NEW |
---|---|
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 #include <algorithm> | 5 #include <algorithm> |
6 | 6 |
7 #include "skia/ext/convolver.h" | 7 #include "skia/ext/convolver.h" |
8 #include "third_party/skia/include/core/SkTypes.h" | 8 #include "third_party/skia/include/core/SkTypes.h" |
9 | 9 |
10 namespace skia { | 10 namespace skia { |
11 | 11 |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
226 ConvolutionFilter1D::ConvolutionFilter1D() | 226 ConvolutionFilter1D::ConvolutionFilter1D() |
227 : max_filter_(0) { | 227 : max_filter_(0) { |
228 } | 228 } |
229 | 229 |
230 ConvolutionFilter1D::~ConvolutionFilter1D() { | 230 ConvolutionFilter1D::~ConvolutionFilter1D() { |
231 } | 231 } |
232 | 232 |
233 void ConvolutionFilter1D::AddFilter(int filter_offset, | 233 void ConvolutionFilter1D::AddFilter(int filter_offset, |
234 const float* filter_values, | 234 const float* filter_values, |
235 int filter_length) { | 235 int filter_length) { |
236 FilterInstance instance; | 236 SkASSERT(filter_length > 0); |
237 instance.data_location = static_cast<int>(filter_values_.size()); | |
238 instance.offset = filter_offset; | |
239 instance.length = filter_length; | |
240 filters_.push_back(instance); | |
241 | 237 |
242 SkASSERT(filter_length > 0); | 238 std::vector<Fixed> fixed_values; |
243 for (int i = 0; i < filter_length; i++) | 239 fixed_values.reserve(filter_length); |
244 filter_values_.push_back(FloatToFixed(filter_values[i])); | |
245 | 240 |
246 max_filter_ = std::max(max_filter_, filter_length); | 241 for (int i = 0; i < filter_length; ++i) |
242 fixed_values.push_back(FloatToFixed(filter_values[i])); | |
243 | |
244 AddFilter(filter_offset, &fixed_values[0], filter_length); | |
247 } | 245 } |
248 | 246 |
249 void ConvolutionFilter1D::AddFilter(int filter_offset, | 247 void ConvolutionFilter1D::AddFilter(int filter_offset, |
250 const Fixed* filter_values, | 248 const Fixed* filter_values, |
251 int filter_length) { | 249 int filter_length) { |
250 // It is common for leading/trailing filter values to be zeros. In such | |
251 // cases it is beneficial to only store the central factors. | |
252 // For a scaling to 1/4th in each dimension using a Lanczos-2 filter on | |
253 // a 1080p image this optimization gives a ~10% speed improvement. | |
254 int first_non_zero = 0; | |
255 while (first_non_zero < filter_length && filter_values[first_non_zero] == 0) { | |
brettw
2011/01/20 21:48:06
Be consistent about usage for {} for single-line c
evannier
2011/01/24 21:09:58
Done.
| |
256 first_non_zero++; | |
257 } | |
258 | |
259 if (first_non_zero < filter_length) { | |
260 // Here we have at least one non-zero factor. | |
261 int last_non_zero = filter_length - 1; | |
262 while (last_non_zero >= 0 && filter_values[last_non_zero] == 0) { | |
263 last_non_zero--; | |
264 } | |
265 | |
266 filter_offset += first_non_zero; | |
267 filter_length = last_non_zero + 1 - first_non_zero; | |
268 SkASSERT(filter_length > 0); | |
269 | |
270 for (int i = first_non_zero; i <= last_non_zero; i++) | |
271 filter_values_.push_back(filter_values[i]); | |
272 } else { | |
273 // Here all the factors were zeroes. | |
274 filter_length = 0; | |
275 } | |
276 | |
252 FilterInstance instance; | 277 FilterInstance instance; |
253 instance.data_location = static_cast<int>(filter_values_.size()); | 278 |
279 // We pushed filter_length elements onto filter_values_ | |
280 instance.data_location = (static_cast<int>(filter_values_.size()) - | |
281 filter_length); | |
254 instance.offset = filter_offset; | 282 instance.offset = filter_offset; |
255 instance.length = filter_length; | 283 instance.length = filter_length; |
256 filters_.push_back(instance); | 284 filters_.push_back(instance); |
257 | 285 |
258 SkASSERT(filter_length > 0); | |
259 for (int i = 0; i < filter_length; i++) | |
260 filter_values_.push_back(filter_values[i]); | |
261 | |
262 max_filter_ = std::max(max_filter_, filter_length); | 286 max_filter_ = std::max(max_filter_, filter_length); |
263 } | 287 } |
264 | 288 |
265 // BGRAConvolve2D ------------------------------------------------------------- | 289 // BGRAConvolve2D ------------------------------------------------------------- |
266 | 290 |
267 void BGRAConvolve2D(const unsigned char* source_data, | 291 void BGRAConvolve2D(const unsigned char* source_data, |
268 int source_byte_row_stride, | 292 int source_byte_row_stride, |
269 bool source_has_alpha, | 293 bool source_has_alpha, |
270 const ConvolutionFilter1D& filter_x, | 294 const ConvolutionFilter1D& filter_x, |
271 const ConvolutionFilter1D& filter_y, | 295 const ConvolutionFilter1D& filter_y, |
296 int output_byte_row_stride, | |
272 unsigned char* output) { | 297 unsigned char* output) { |
273 int max_y_filter_size = filter_y.max_filter(); | 298 int max_y_filter_size = filter_y.max_filter(); |
274 | 299 |
275 // The next row in the input that we will generate a horizontally | 300 // The next row in the input that we will generate a horizontally |
276 // convolved row for. If the filter doesn't start at the beginning of the | 301 // convolved row for. If the filter doesn't start at the beginning of the |
277 // image (this is the case when we are only resizing a subset), then we | 302 // image (this is the case when we are only resizing a subset), then we |
278 // don't want to generate any output rows before that. Compute the starting | 303 // don't want to generate any output rows before that. Compute the starting |
279 // row for convolution as the first pixel for the first vertical filter. | 304 // row for convolution as the first pixel for the first vertical filter. |
280 int filter_offset, filter_length; | 305 int filter_offset, filter_length; |
281 const ConvolutionFilter1D::Fixed* filter_values = | 306 const ConvolutionFilter1D::Fixed* filter_values = |
282 filter_y.FilterForValue(0, &filter_offset, &filter_length); | 307 filter_y.FilterForValue(0, &filter_offset, &filter_length); |
283 int next_x_row = filter_offset; | 308 int next_x_row = filter_offset; |
284 | 309 |
285 // We loop over each row in the input doing a horizontal convolution. This | 310 // We loop over each row in the input doing a horizontal convolution. This |
286 // will result in a horizontally convolved image. We write the results into | 311 // will result in a horizontally convolved image. We write the results into |
287 // a circular buffer of convolved rows and do vertical convolution as rows | 312 // a circular buffer of convolved rows and do vertical convolution as rows |
288 // are available. This prevents us from having to store the entire | 313 // are available. This prevents us from having to store the entire |
289 // intermediate image and helps cache coherency. | 314 // intermediate image and helps cache coherency. |
290 CircularRowBuffer row_buffer(filter_x.num_values(), max_y_filter_size, | 315 CircularRowBuffer row_buffer(filter_x.num_values(), max_y_filter_size, |
291 filter_offset); | 316 filter_offset); |
292 | 317 |
293 // Loop over every possible output row, processing just enough horizontal | 318 // Loop over every possible output row, processing just enough horizontal |
294 // convolutions to run each subsequent vertical convolution. | 319 // convolutions to run each subsequent vertical convolution. |
295 int output_row_byte_width = filter_x.num_values() * 4; | 320 SkASSERT(output_byte_row_stride >= filter_x.num_values() * 4); |
296 int num_output_rows = filter_y.num_values(); | 321 int num_output_rows = filter_y.num_values(); |
297 for (int out_y = 0; out_y < num_output_rows; out_y++) { | 322 for (int out_y = 0; out_y < num_output_rows; out_y++) { |
298 filter_values = filter_y.FilterForValue(out_y, | 323 filter_values = filter_y.FilterForValue(out_y, |
299 &filter_offset, &filter_length); | 324 &filter_offset, &filter_length); |
300 | 325 |
301 // Generate output rows until we have enough to run the current filter. | 326 // Generate output rows until we have enough to run the current filter. |
302 while (next_x_row < filter_offset + filter_length) { | 327 while (next_x_row < filter_offset + filter_length) { |
303 if (source_has_alpha) { | 328 if (source_has_alpha) { |
304 ConvolveHorizontally<true>( | 329 ConvolveHorizontally<true>( |
305 &source_data[next_x_row * source_byte_row_stride], | 330 &source_data[next_x_row * source_byte_row_stride], |
306 filter_x, row_buffer.AdvanceRow()); | 331 filter_x, row_buffer.AdvanceRow()); |
307 } else { | 332 } else { |
308 ConvolveHorizontally<false>( | 333 ConvolveHorizontally<false>( |
309 &source_data[next_x_row * source_byte_row_stride], | 334 &source_data[next_x_row * source_byte_row_stride], |
310 filter_x, row_buffer.AdvanceRow()); | 335 filter_x, row_buffer.AdvanceRow()); |
311 } | 336 } |
312 next_x_row++; | 337 next_x_row++; |
313 } | 338 } |
314 | 339 |
315 // Compute where in the output image this row of final data will go. | 340 // Compute where in the output image this row of final data will go. |
316 unsigned char* cur_output_row = &output[out_y * output_row_byte_width]; | 341 unsigned char* cur_output_row = &output[out_y * output_byte_row_stride]; |
317 | 342 |
318 // Get the list of rows that the circular buffer has, in order. | 343 // Get the list of rows that the circular buffer has, in order. |
319 int first_row_in_circular_buffer; | 344 int first_row_in_circular_buffer; |
320 unsigned char* const* rows_to_convolve = | 345 unsigned char* const* rows_to_convolve = |
321 row_buffer.GetRowAddresses(&first_row_in_circular_buffer); | 346 row_buffer.GetRowAddresses(&first_row_in_circular_buffer); |
322 | 347 |
323 // Now compute the start of the subset of those rows that the filter | 348 // Now compute the start of the subset of those rows that the filter |
324 // needs. | 349 // needs. |
325 unsigned char* const* first_row_for_filter = | 350 unsigned char* const* first_row_for_filter = |
326 &rows_to_convolve[filter_offset - first_row_in_circular_buffer]; | 351 &rows_to_convolve[filter_offset - first_row_in_circular_buffer]; |
327 | 352 |
328 if (source_has_alpha) { | 353 if (source_has_alpha) { |
329 ConvolveVertically<true>(filter_values, filter_length, | 354 ConvolveVertically<true>(filter_values, filter_length, |
330 first_row_for_filter, | 355 first_row_for_filter, |
331 filter_x.num_values(), cur_output_row); | 356 filter_x.num_values(), cur_output_row); |
332 } else { | 357 } else { |
333 ConvolveVertically<false>(filter_values, filter_length, | 358 ConvolveVertically<false>(filter_values, filter_length, |
334 first_row_for_filter, | 359 first_row_for_filter, |
335 filter_x.num_values(), cur_output_row); | 360 filter_x.num_values(), cur_output_row); |
336 } | 361 } |
337 } | 362 } |
338 } | 363 } |
339 | 364 |
340 } // namespace skia | 365 } // namespace skia |
341 | |
OLD | NEW |