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) |
| 256 first_non_zero++; |
| 257 |
| 258 if (first_non_zero < filter_length) { |
| 259 // Here we have at least one non-zero factor. |
| 260 int last_non_zero = filter_length - 1; |
| 261 while (last_non_zero >= 0 && filter_values[last_non_zero] == 0) |
| 262 last_non_zero--; |
| 263 |
| 264 filter_offset += first_non_zero; |
| 265 filter_length = last_non_zero + 1 - first_non_zero; |
| 266 SkASSERT(filter_length > 0); |
| 267 |
| 268 for (int i = first_non_zero; i <= last_non_zero; i++) |
| 269 filter_values_.push_back(filter_values[i]); |
| 270 } else { |
| 271 // Here all the factors were zeroes. |
| 272 filter_length = 0; |
| 273 } |
| 274 |
252 FilterInstance instance; | 275 FilterInstance instance; |
253 instance.data_location = static_cast<int>(filter_values_.size()); | 276 |
| 277 // We pushed filter_length elements onto filter_values_ |
| 278 instance.data_location = (static_cast<int>(filter_values_.size()) - |
| 279 filter_length); |
254 instance.offset = filter_offset; | 280 instance.offset = filter_offset; |
255 instance.length = filter_length; | 281 instance.length = filter_length; |
256 filters_.push_back(instance); | 282 filters_.push_back(instance); |
257 | 283 |
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); | 284 max_filter_ = std::max(max_filter_, filter_length); |
263 } | 285 } |
264 | 286 |
265 // BGRAConvolve2D ------------------------------------------------------------- | 287 // BGRAConvolve2D ------------------------------------------------------------- |
266 | 288 |
267 void BGRAConvolve2D(const unsigned char* source_data, | 289 void BGRAConvolve2D(const unsigned char* source_data, |
268 int source_byte_row_stride, | 290 int source_byte_row_stride, |
269 bool source_has_alpha, | 291 bool source_has_alpha, |
270 const ConvolutionFilter1D& filter_x, | 292 const ConvolutionFilter1D& filter_x, |
271 const ConvolutionFilter1D& filter_y, | 293 const ConvolutionFilter1D& filter_y, |
| 294 int output_byte_row_stride, |
272 unsigned char* output) { | 295 unsigned char* output) { |
273 int max_y_filter_size = filter_y.max_filter(); | 296 int max_y_filter_size = filter_y.max_filter(); |
274 | 297 |
275 // The next row in the input that we will generate a horizontally | 298 // 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 | 299 // 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 | 300 // 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 | 301 // 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. | 302 // row for convolution as the first pixel for the first vertical filter. |
280 int filter_offset, filter_length; | 303 int filter_offset, filter_length; |
281 const ConvolutionFilter1D::Fixed* filter_values = | 304 const ConvolutionFilter1D::Fixed* filter_values = |
282 filter_y.FilterForValue(0, &filter_offset, &filter_length); | 305 filter_y.FilterForValue(0, &filter_offset, &filter_length); |
283 int next_x_row = filter_offset; | 306 int next_x_row = filter_offset; |
284 | 307 |
285 // We loop over each row in the input doing a horizontal convolution. This | 308 // 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 | 309 // 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 | 310 // a circular buffer of convolved rows and do vertical convolution as rows |
288 // are available. This prevents us from having to store the entire | 311 // are available. This prevents us from having to store the entire |
289 // intermediate image and helps cache coherency. | 312 // intermediate image and helps cache coherency. |
290 CircularRowBuffer row_buffer(filter_x.num_values(), max_y_filter_size, | 313 CircularRowBuffer row_buffer(filter_x.num_values(), max_y_filter_size, |
291 filter_offset); | 314 filter_offset); |
292 | 315 |
293 // Loop over every possible output row, processing just enough horizontal | 316 // Loop over every possible output row, processing just enough horizontal |
294 // convolutions to run each subsequent vertical convolution. | 317 // convolutions to run each subsequent vertical convolution. |
295 int output_row_byte_width = filter_x.num_values() * 4; | 318 SkASSERT(output_byte_row_stride >= filter_x.num_values() * 4); |
296 int num_output_rows = filter_y.num_values(); | 319 int num_output_rows = filter_y.num_values(); |
297 for (int out_y = 0; out_y < num_output_rows; out_y++) { | 320 for (int out_y = 0; out_y < num_output_rows; out_y++) { |
298 filter_values = filter_y.FilterForValue(out_y, | 321 filter_values = filter_y.FilterForValue(out_y, |
299 &filter_offset, &filter_length); | 322 &filter_offset, &filter_length); |
300 | 323 |
301 // Generate output rows until we have enough to run the current filter. | 324 // Generate output rows until we have enough to run the current filter. |
302 while (next_x_row < filter_offset + filter_length) { | 325 while (next_x_row < filter_offset + filter_length) { |
303 if (source_has_alpha) { | 326 if (source_has_alpha) { |
304 ConvolveHorizontally<true>( | 327 ConvolveHorizontally<true>( |
305 &source_data[next_x_row * source_byte_row_stride], | 328 &source_data[next_x_row * source_byte_row_stride], |
306 filter_x, row_buffer.AdvanceRow()); | 329 filter_x, row_buffer.AdvanceRow()); |
307 } else { | 330 } else { |
308 ConvolveHorizontally<false>( | 331 ConvolveHorizontally<false>( |
309 &source_data[next_x_row * source_byte_row_stride], | 332 &source_data[next_x_row * source_byte_row_stride], |
310 filter_x, row_buffer.AdvanceRow()); | 333 filter_x, row_buffer.AdvanceRow()); |
311 } | 334 } |
312 next_x_row++; | 335 next_x_row++; |
313 } | 336 } |
314 | 337 |
315 // Compute where in the output image this row of final data will go. | 338 // 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]; | 339 unsigned char* cur_output_row = &output[out_y * output_byte_row_stride]; |
317 | 340 |
318 // Get the list of rows that the circular buffer has, in order. | 341 // Get the list of rows that the circular buffer has, in order. |
319 int first_row_in_circular_buffer; | 342 int first_row_in_circular_buffer; |
320 unsigned char* const* rows_to_convolve = | 343 unsigned char* const* rows_to_convolve = |
321 row_buffer.GetRowAddresses(&first_row_in_circular_buffer); | 344 row_buffer.GetRowAddresses(&first_row_in_circular_buffer); |
322 | 345 |
323 // Now compute the start of the subset of those rows that the filter | 346 // Now compute the start of the subset of those rows that the filter |
324 // needs. | 347 // needs. |
325 unsigned char* const* first_row_for_filter = | 348 unsigned char* const* first_row_for_filter = |
326 &rows_to_convolve[filter_offset - first_row_in_circular_buffer]; | 349 &rows_to_convolve[filter_offset - first_row_in_circular_buffer]; |
327 | 350 |
328 if (source_has_alpha) { | 351 if (source_has_alpha) { |
329 ConvolveVertically<true>(filter_values, filter_length, | 352 ConvolveVertically<true>(filter_values, filter_length, |
330 first_row_for_filter, | 353 first_row_for_filter, |
331 filter_x.num_values(), cur_output_row); | 354 filter_x.num_values(), cur_output_row); |
332 } else { | 355 } else { |
333 ConvolveVertically<false>(filter_values, filter_length, | 356 ConvolveVertically<false>(filter_values, filter_length, |
334 first_row_for_filter, | 357 first_row_for_filter, |
335 filter_x.num_values(), cur_output_row); | 358 filter_x.num_values(), cur_output_row); |
336 } | 359 } |
337 } | 360 } |
338 } | 361 } |
339 | 362 |
340 } // namespace skia | 363 } // namespace skia |
341 | |
OLD | NEW |