convolver.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  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
  3. // found in the LICENSE file.
  4. #include <algorithm>
  5. #include "base/check_op.h"
  6. #include "base/notreached.h"
  7. #include "skia/ext/convolver.h"
  8. #include "skia/ext/convolver_SSE2.h"
  9. #include "skia/ext/convolver_mips_dspr2.h"
  10. #include "skia/ext/convolver_neon.h"
  11. #include "third_party/skia/include/core/SkSize.h"
  12. #include "third_party/skia/include/core/SkTypes.h"
  13. namespace skia {
  14. namespace {
  15. // Converts the argument to an 8-bit unsigned value by clamping to the range
  16. // 0-255.
  17. inline unsigned char ClampTo8(int a) {
  18. if (static_cast<unsigned>(a) < 256)
  19. return a; // Avoid the extra check in the common case.
  20. if (a < 0)
  21. return 0;
  22. return 255;
  23. }
  24. // Takes the value produced by accumulating element-wise product of image with
  25. // a kernel and brings it back into range.
  26. // All of the filter scaling factors are in fixed point with kShiftBits bits of
  27. // fractional part.
  28. inline unsigned char BringBackTo8(int a, bool take_absolute) {
  29. a >>= ConvolutionFilter1D::kShiftBits;
  30. if (take_absolute)
  31. a = std::abs(a);
  32. return ClampTo8(a);
  33. }
  34. // Stores a list of rows in a circular buffer. The usage is you write into it
  35. // by calling AdvanceRow. It will keep track of which row in the buffer it
  36. // should use next, and the total number of rows added.
  37. class CircularRowBuffer {
  38. public:
  39. // The number of pixels in each row is given in |source_row_pixel_width|.
  40. // The maximum number of rows needed in the buffer is |max_y_filter_size|
  41. // (we only need to store enough rows for the biggest filter).
  42. //
  43. // We use the |first_input_row| to compute the coordinates of all of the
  44. // following rows returned by Advance().
  45. CircularRowBuffer(int dest_row_pixel_width, int max_y_filter_size,
  46. int first_input_row)
  47. : row_byte_width_(dest_row_pixel_width * 4),
  48. num_rows_(max_y_filter_size),
  49. next_row_(0),
  50. next_row_coordinate_(first_input_row) {
  51. buffer_.resize(row_byte_width_ * max_y_filter_size);
  52. row_addresses_.resize(num_rows_);
  53. }
  54. // Moves to the next row in the buffer, returning a pointer to the beginning
  55. // of it.
  56. unsigned char* AdvanceRow() {
  57. unsigned char* row = &buffer_[next_row_ * row_byte_width_];
  58. next_row_coordinate_++;
  59. // Set the pointer to the next row to use, wrapping around if necessary.
  60. next_row_++;
  61. if (next_row_ == num_rows_)
  62. next_row_ = 0;
  63. return row;
  64. }
  65. // Returns a pointer to an "unrolled" array of rows. These rows will start
  66. // at the y coordinate placed into |*first_row_index| and will continue in
  67. // order for the maximum number of rows in this circular buffer.
  68. //
  69. // The |first_row_index_| may be negative. This means the circular buffer
  70. // starts before the top of the image (it hasn't been filled yet).
  71. unsigned char* const* GetRowAddresses(int* first_row_index) {
  72. // Example for a 4-element circular buffer holding coords 6-9.
  73. // Row 0 Coord 8
  74. // Row 1 Coord 9
  75. // Row 2 Coord 6 <- next_row_ = 2, next_row_coordinate_ = 10.
  76. // Row 3 Coord 7
  77. //
  78. // The "next" row is also the first (lowest) coordinate. This computation
  79. // may yield a negative value, but that's OK, the math will work out
  80. // since the user of this buffer will compute the offset relative
  81. // to the first_row_index and the negative rows will never be used.
  82. *first_row_index = next_row_coordinate_ - num_rows_;
  83. int cur_row = next_row_;
  84. for (int i = 0; i < num_rows_; i++) {
  85. row_addresses_[i] = &buffer_[cur_row * row_byte_width_];
  86. // Advance to the next row, wrapping if necessary.
  87. cur_row++;
  88. if (cur_row == num_rows_)
  89. cur_row = 0;
  90. }
  91. return &row_addresses_[0];
  92. }
  93. private:
  94. // The buffer storing the rows. They are packed, each one row_byte_width_.
  95. std::vector<unsigned char> buffer_;
  96. // Number of bytes per row in the |buffer_|.
  97. int row_byte_width_;
  98. // The number of rows available in the buffer.
  99. int num_rows_;
  100. // The next row index we should write into. This wraps around as the
  101. // circular buffer is used.
  102. int next_row_;
  103. // The y coordinate of the |next_row_|. This is incremented each time a
  104. // new row is appended and does not wrap.
  105. int next_row_coordinate_;
  106. // Buffer used by GetRowAddresses().
  107. std::vector<unsigned char*> row_addresses_;
  108. };
  109. // Convolves horizontally along a single row. The row data is given in
  110. // |src_data| and continues for the num_values() of the filter.
  111. template<bool has_alpha>
  112. void ConvolveHorizontally(const unsigned char* src_data,
  113. const ConvolutionFilter1D& filter,
  114. unsigned char* out_row) {
  115. // Loop over each pixel on this row in the output image.
  116. int num_values = filter.num_values();
  117. for (int out_x = 0; out_x < num_values; out_x++) {
  118. // Get the filter that determines the current output pixel.
  119. int filter_offset, filter_length;
  120. const ConvolutionFilter1D::Fixed* filter_values =
  121. filter.FilterForValue(out_x, &filter_offset, &filter_length);
  122. // Compute the first pixel in this row that the filter affects. It will
  123. // touch |filter_length| pixels (4 bytes each) after this.
  124. const unsigned char* row_to_filter = &src_data[filter_offset * 4];
  125. // Apply the filter to the row to get the destination pixel in |accum|.
  126. int accum[4] = {0};
  127. for (int filter_x = 0; filter_x < filter_length; filter_x++) {
  128. ConvolutionFilter1D::Fixed cur_filter = filter_values[filter_x];
  129. accum[0] += cur_filter * row_to_filter[filter_x * 4 + 0];
  130. accum[1] += cur_filter * row_to_filter[filter_x * 4 + 1];
  131. accum[2] += cur_filter * row_to_filter[filter_x * 4 + 2];
  132. if (has_alpha)
  133. accum[3] += cur_filter * row_to_filter[filter_x * 4 + 3];
  134. }
  135. // Bring this value back in range. All of the filter scaling factors
  136. // are in fixed point with kShiftBits bits of fractional part.
  137. accum[0] >>= ConvolutionFilter1D::kShiftBits;
  138. accum[1] >>= ConvolutionFilter1D::kShiftBits;
  139. accum[2] >>= ConvolutionFilter1D::kShiftBits;
  140. if (has_alpha)
  141. accum[3] >>= ConvolutionFilter1D::kShiftBits;
  142. // Store the new pixel.
  143. out_row[out_x * 4 + 0] = ClampTo8(accum[0]);
  144. out_row[out_x * 4 + 1] = ClampTo8(accum[1]);
  145. out_row[out_x * 4 + 2] = ClampTo8(accum[2]);
  146. if (has_alpha)
  147. out_row[out_x * 4 + 3] = ClampTo8(accum[3]);
  148. }
  149. }
  150. // Does vertical convolution to produce one output row. The filter values and
  151. // length are given in the first two parameters. These are applied to each
  152. // of the rows pointed to in the |source_data_rows| array, with each row
  153. // being |pixel_width| wide.
  154. //
  155. // The output must have room for |pixel_width * 4| bytes.
  156. template<bool has_alpha>
  157. void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
  158. int filter_length,
  159. unsigned char* const* source_data_rows,
  160. int pixel_width,
  161. unsigned char* out_row) {
  162. // We go through each column in the output and do a vertical convolution,
  163. // generating one output pixel each time.
  164. for (int out_x = 0; out_x < pixel_width; out_x++) {
  165. // Compute the number of bytes over in each row that the current column
  166. // we're convolving starts at. The pixel will cover the next 4 bytes.
  167. int byte_offset = out_x * 4;
  168. // Apply the filter to one column of pixels.
  169. int accum[4] = {0};
  170. for (int filter_y = 0; filter_y < filter_length; filter_y++) {
  171. ConvolutionFilter1D::Fixed cur_filter = filter_values[filter_y];
  172. accum[0] += cur_filter * source_data_rows[filter_y][byte_offset + 0];
  173. accum[1] += cur_filter * source_data_rows[filter_y][byte_offset + 1];
  174. accum[2] += cur_filter * source_data_rows[filter_y][byte_offset + 2];
  175. if (has_alpha)
  176. accum[3] += cur_filter * source_data_rows[filter_y][byte_offset + 3];
  177. }
  178. // Bring this value back in range. All of the filter scaling factors
  179. // are in fixed point with kShiftBits bits of precision.
  180. accum[0] >>= ConvolutionFilter1D::kShiftBits;
  181. accum[1] >>= ConvolutionFilter1D::kShiftBits;
  182. accum[2] >>= ConvolutionFilter1D::kShiftBits;
  183. if (has_alpha)
  184. accum[3] >>= ConvolutionFilter1D::kShiftBits;
  185. // Store the new pixel.
  186. out_row[byte_offset + 0] = ClampTo8(accum[0]);
  187. out_row[byte_offset + 1] = ClampTo8(accum[1]);
  188. out_row[byte_offset + 2] = ClampTo8(accum[2]);
  189. if (has_alpha) {
  190. unsigned char alpha = ClampTo8(accum[3]);
  191. // Make sure the alpha channel doesn't come out smaller than any of the
  192. // color channels. We use premultipled alpha channels, so this should
  193. // never happen, but rounding errors will cause this from time to time.
  194. // These "impossible" colors will cause overflows (and hence random pixel
  195. // values) when the resulting bitmap is drawn to the screen.
  196. //
  197. // We only need to do this when generating the final output row (here).
  198. int max_color_channel = std::max(out_row[byte_offset + 0],
  199. std::max(out_row[byte_offset + 1], out_row[byte_offset + 2]));
  200. if (alpha < max_color_channel)
  201. out_row[byte_offset + 3] = max_color_channel;
  202. else
  203. out_row[byte_offset + 3] = alpha;
  204. } else {
  205. // No alpha channel, the image is opaque.
  206. out_row[byte_offset + 3] = 0xff;
  207. }
  208. }
  209. }
  210. void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
  211. int filter_length,
  212. unsigned char* const* source_data_rows,
  213. int pixel_width,
  214. unsigned char* out_row,
  215. bool source_has_alpha) {
  216. if (source_has_alpha) {
  217. ConvolveVertically<true>(filter_values, filter_length,
  218. source_data_rows,
  219. pixel_width,
  220. out_row);
  221. } else {
  222. ConvolveVertically<false>(filter_values, filter_length,
  223. source_data_rows,
  224. pixel_width,
  225. out_row);
  226. }
  227. }
  228. } // namespace
  229. // ConvolutionFilter1D ---------------------------------------------------------
  230. ConvolutionFilter1D::ConvolutionFilter1D()
  231. : max_filter_(0) {
  232. }
  233. ConvolutionFilter1D::~ConvolutionFilter1D() = default;
  234. void ConvolutionFilter1D::AddFilter(int filter_offset,
  235. const float* filter_values,
  236. int filter_length) {
  237. SkASSERT(filter_length > 0);
  238. std::vector<Fixed> fixed_values;
  239. fixed_values.reserve(filter_length);
  240. for (int i = 0; i < filter_length; ++i)
  241. fixed_values.push_back(FloatToFixed(filter_values[i]));
  242. AddFilter(filter_offset, &fixed_values[0], filter_length);
  243. }
  244. void ConvolutionFilter1D::AddFilter(int filter_offset,
  245. const Fixed* filter_values,
  246. int filter_length) {
  247. // It is common for leading/trailing filter values to be zeros. In such
  248. // cases it is beneficial to only store the central factors.
  249. // For a scaling to 1/4th in each dimension using a Lanczos-2 filter on
  250. // a 1080p image this optimization gives a ~10% speed improvement.
  251. int filter_size = filter_length;
  252. int first_non_zero = 0;
  253. while (first_non_zero < filter_length && filter_values[first_non_zero] == 0)
  254. first_non_zero++;
  255. if (first_non_zero < filter_length) {
  256. // Here we have at least one non-zero factor.
  257. int last_non_zero = filter_length - 1;
  258. while (last_non_zero >= 0 && filter_values[last_non_zero] == 0)
  259. last_non_zero--;
  260. filter_offset += first_non_zero;
  261. filter_length = last_non_zero + 1 - first_non_zero;
  262. SkASSERT(filter_length > 0);
  263. for (int i = first_non_zero; i <= last_non_zero; i++)
  264. filter_values_.push_back(filter_values[i]);
  265. } else {
  266. // Here all the factors were zeroes.
  267. filter_length = 0;
  268. }
  269. FilterInstance instance;
  270. // We pushed filter_length elements onto filter_values_
  271. instance.data_location = (static_cast<int>(filter_values_.size()) -
  272. filter_length);
  273. instance.offset = filter_offset;
  274. instance.trimmed_length = filter_length;
  275. instance.length = filter_size;
  276. filters_.push_back(instance);
  277. max_filter_ = std::max(max_filter_, filter_length);
  278. }
  279. const ConvolutionFilter1D::Fixed* ConvolutionFilter1D::GetSingleFilter(
  280. int* specified_filter_length,
  281. int* filter_offset,
  282. int* filter_length) const {
  283. const FilterInstance& filter = filters_[0];
  284. *filter_offset = filter.offset;
  285. *filter_length = filter.trimmed_length;
  286. *specified_filter_length = filter.length;
  287. if (filter.trimmed_length == 0)
  288. return NULL;
  289. return &filter_values_[filter.data_location];
  290. }
  291. typedef void (*ConvolveVertically_pointer)(
  292. const ConvolutionFilter1D::Fixed* filter_values,
  293. int filter_length,
  294. unsigned char* const* source_data_rows,
  295. int pixel_width,
  296. unsigned char* out_row,
  297. bool has_alpha);
  298. typedef void (*Convolve4RowsHorizontally_pointer)(
  299. const unsigned char* src_data[4],
  300. const ConvolutionFilter1D& filter,
  301. unsigned char* out_row[4]);
  302. typedef void (*ConvolveHorizontally_pointer)(
  303. const unsigned char* src_data,
  304. const ConvolutionFilter1D& filter,
  305. unsigned char* out_row,
  306. bool has_alpha);
  307. struct ConvolveProcs {
  308. // This is how many extra pixels may be read by the
  309. // conolve*horizontally functions.
  310. int extra_horizontal_reads;
  311. ConvolveVertically_pointer convolve_vertically;
  312. Convolve4RowsHorizontally_pointer convolve_4rows_horizontally;
  313. ConvolveHorizontally_pointer convolve_horizontally;
  314. };
  315. void SetupSIMD(ConvolveProcs *procs) {
  316. #ifdef SIMD_SSE2
  317. procs->extra_horizontal_reads = 3;
  318. procs->convolve_vertically = &ConvolveVertically_SSE2;
  319. procs->convolve_4rows_horizontally = &Convolve4RowsHorizontally_SSE2;
  320. procs->convolve_horizontally = &ConvolveHorizontally_SSE2;
  321. #elif defined SIMD_MIPS_DSPR2
  322. procs->extra_horizontal_reads = 3;
  323. procs->convolve_vertically = &ConvolveVertically_mips_dspr2;
  324. procs->convolve_horizontally = &ConvolveHorizontally_mips_dspr2;
  325. #elif defined SIMD_NEON
  326. procs->extra_horizontal_reads = 3;
  327. procs->convolve_vertically = &ConvolveVertically_Neon;
  328. procs->convolve_4rows_horizontally = &Convolve4RowsHorizontally_Neon;
  329. procs->convolve_horizontally = &ConvolveHorizontally_Neon;
  330. #endif
  331. }
  332. void BGRAConvolve2D(const unsigned char* source_data,
  333. int source_byte_row_stride,
  334. bool source_has_alpha,
  335. const ConvolutionFilter1D& filter_x,
  336. const ConvolutionFilter1D& filter_y,
  337. int output_byte_row_stride,
  338. unsigned char* output,
  339. bool use_simd_if_possible) {
  340. ConvolveProcs simd;
  341. simd.extra_horizontal_reads = 0;
  342. simd.convolve_vertically = NULL;
  343. simd.convolve_4rows_horizontally = NULL;
  344. simd.convolve_horizontally = NULL;
  345. if (use_simd_if_possible) {
  346. SetupSIMD(&simd);
  347. }
  348. int max_y_filter_size = filter_y.max_filter();
  349. // The next row in the input that we will generate a horizontally
  350. // convolved row for. If the filter doesn't start at the beginning of the
  351. // image (this is the case when we are only resizing a subset), then we
  352. // don't want to generate any output rows before that. Compute the starting
  353. // row for convolution as the first pixel for the first vertical filter.
  354. int filter_offset, filter_length;
  355. const ConvolutionFilter1D::Fixed* filter_values =
  356. filter_y.FilterForValue(0, &filter_offset, &filter_length);
  357. int next_x_row = filter_offset;
  358. // We loop over each row in the input doing a horizontal convolution. This
  359. // will result in a horizontally convolved image. We write the results into
  360. // a circular buffer of convolved rows and do vertical convolution as rows
  361. // are available. This prevents us from having to store the entire
  362. // intermediate image and helps cache coherency.
  363. // We will need four extra rows to allow horizontal convolution could be done
  364. // simultaneously. We also padding each row in row buffer to be aligned-up to
  365. // 16 bytes.
  366. // TODO(jiesun): We do not use aligned load from row buffer in vertical
  367. // convolution pass yet. Somehow Windows does not like it.
  368. int row_buffer_width = (filter_x.num_values() + 15) & ~0xF;
  369. int row_buffer_height = max_y_filter_size +
  370. (simd.convolve_4rows_horizontally ? 4 : 0);
  371. CircularRowBuffer row_buffer(row_buffer_width,
  372. row_buffer_height,
  373. filter_offset);
  374. // Loop over every possible output row, processing just enough horizontal
  375. // convolutions to run each subsequent vertical convolution.
  376. SkASSERT(output_byte_row_stride >= filter_x.num_values() * 4);
  377. int num_output_rows = filter_y.num_values();
  378. // We need to check which is the last line to convolve before we advance 4
  379. // lines in one iteration.
  380. int last_filter_offset, last_filter_length;
  381. // SSE2 can access up to 3 extra pixels past the end of the
  382. // buffer. At the bottom of the image, we have to be careful
  383. // not to access data past the end of the buffer. Normally
  384. // we fall back to the C++ implementation for the last row.
  385. // If the last row is less than 3 pixels wide, we may have to fall
  386. // back to the C++ version for more rows. Compute how many
  387. // rows we need to avoid the SSE implementation for here.
  388. filter_x.FilterForValue(filter_x.num_values() - 1, &last_filter_offset,
  389. &last_filter_length);
  390. int avoid_simd_rows = 1 + simd.extra_horizontal_reads /
  391. (last_filter_offset + last_filter_length);
  392. filter_y.FilterForValue(num_output_rows - 1, &last_filter_offset,
  393. &last_filter_length);
  394. for (int out_y = 0; out_y < num_output_rows; out_y++) {
  395. filter_values = filter_y.FilterForValue(out_y,
  396. &filter_offset, &filter_length);
  397. // Generate output rows until we have enough to run the current filter.
  398. while (next_x_row < filter_offset + filter_length) {
  399. if (simd.convolve_4rows_horizontally &&
  400. next_x_row + 3 < last_filter_offset + last_filter_length -
  401. avoid_simd_rows) {
  402. const unsigned char* src[4];
  403. unsigned char* out_row[4];
  404. for (int i = 0; i < 4; ++i) {
  405. src[i] = &source_data[(next_x_row + i) * source_byte_row_stride];
  406. out_row[i] = row_buffer.AdvanceRow();
  407. }
  408. simd.convolve_4rows_horizontally(src, filter_x, out_row);
  409. next_x_row += 4;
  410. } else {
  411. // Check if we need to avoid SSE2 for this row.
  412. if (simd.convolve_horizontally &&
  413. next_x_row < last_filter_offset + last_filter_length -
  414. avoid_simd_rows) {
  415. simd.convolve_horizontally(
  416. &source_data[next_x_row * source_byte_row_stride],
  417. filter_x, row_buffer.AdvanceRow(), source_has_alpha);
  418. } else {
  419. if (source_has_alpha) {
  420. ConvolveHorizontally<true>(
  421. &source_data[next_x_row * source_byte_row_stride],
  422. filter_x, row_buffer.AdvanceRow());
  423. } else {
  424. ConvolveHorizontally<false>(
  425. &source_data[next_x_row * source_byte_row_stride],
  426. filter_x, row_buffer.AdvanceRow());
  427. }
  428. }
  429. next_x_row++;
  430. }
  431. }
  432. // Compute where in the output image this row of final data will go.
  433. unsigned char* cur_output_row = &output[out_y * output_byte_row_stride];
  434. // Get the list of rows that the circular buffer has, in order.
  435. int first_row_in_circular_buffer;
  436. unsigned char* const* rows_to_convolve =
  437. row_buffer.GetRowAddresses(&first_row_in_circular_buffer);
  438. // Now compute the start of the subset of those rows that the filter
  439. // needs.
  440. unsigned char* const* first_row_for_filter =
  441. &rows_to_convolve[filter_offset - first_row_in_circular_buffer];
  442. if (simd.convolve_vertically) {
  443. simd.convolve_vertically(filter_values, filter_length,
  444. first_row_for_filter,
  445. filter_x.num_values(), cur_output_row,
  446. source_has_alpha);
  447. } else {
  448. ConvolveVertically(filter_values, filter_length,
  449. first_row_for_filter,
  450. filter_x.num_values(), cur_output_row,
  451. source_has_alpha);
  452. }
  453. }
  454. }
  455. void SingleChannelConvolveX1D(const unsigned char* source_data,
  456. int source_byte_row_stride,
  457. int input_channel_index,
  458. int input_channel_count,
  459. const ConvolutionFilter1D& filter,
  460. const SkISize& image_size,
  461. unsigned char* output,
  462. int output_byte_row_stride,
  463. int output_channel_index,
  464. int output_channel_count,
  465. bool absolute_values) {
  466. int filter_offset, filter_length, filter_size;
  467. // Very much unlike BGRAConvolve2D, here we expect to have the same filter
  468. // for all pixels.
  469. const ConvolutionFilter1D::Fixed* filter_values =
  470. filter.GetSingleFilter(&filter_size, &filter_offset, &filter_length);
  471. if (filter_values == NULL || image_size.width() < filter_size) {
  472. NOTREACHED();
  473. return;
  474. }
  475. int centrepoint = filter_length / 2;
  476. if (filter_size - filter_offset != 2 * filter_offset) {
  477. // This means the original filter was not symmetrical AND
  478. // got clipped from one side more than from the other.
  479. centrepoint = filter_size / 2 - filter_offset;
  480. }
  481. const unsigned char* source_data_row = source_data;
  482. unsigned char* output_row = output;
  483. for (int r = 0; r < image_size.height(); ++r) {
  484. unsigned char* target_byte = output_row + output_channel_index;
  485. // Process the lead part, padding image to the left with the first pixel.
  486. int c = 0;
  487. for (; c < centrepoint; ++c, target_byte += output_channel_count) {
  488. int accval = 0;
  489. int i = 0;
  490. int pixel_byte_index = input_channel_index;
  491. for (; i < centrepoint - c; ++i) // Padding part.
  492. accval += filter_values[i] * source_data_row[pixel_byte_index];
  493. for (; i < filter_length; ++i, pixel_byte_index += input_channel_count)
  494. accval += filter_values[i] * source_data_row[pixel_byte_index];
  495. *target_byte = BringBackTo8(accval, absolute_values);
  496. }
  497. // Now for the main event.
  498. for (; c < image_size.width() - centrepoint;
  499. ++c, target_byte += output_channel_count) {
  500. int accval = 0;
  501. int pixel_byte_index = (c - centrepoint) * input_channel_count +
  502. input_channel_index;
  503. for (int i = 0; i < filter_length;
  504. ++i, pixel_byte_index += input_channel_count) {
  505. accval += filter_values[i] * source_data_row[pixel_byte_index];
  506. }
  507. *target_byte = BringBackTo8(accval, absolute_values);
  508. }
  509. for (; c < image_size.width(); ++c, target_byte += output_channel_count) {
  510. int accval = 0;
  511. int overlap_taps = image_size.width() - c + centrepoint;
  512. int pixel_byte_index = (c - centrepoint) * input_channel_count +
  513. input_channel_index;
  514. int i = 0;
  515. for (; i < overlap_taps - 1; ++i, pixel_byte_index += input_channel_count)
  516. accval += filter_values[i] * source_data_row[pixel_byte_index];
  517. for (; i < filter_length; ++i)
  518. accval += filter_values[i] * source_data_row[pixel_byte_index];
  519. *target_byte = BringBackTo8(accval, absolute_values);
  520. }
  521. source_data_row += source_byte_row_stride;
  522. output_row += output_byte_row_stride;
  523. }
  524. }
  525. void SingleChannelConvolveY1D(const unsigned char* source_data,
  526. int source_byte_row_stride,
  527. int input_channel_index,
  528. int input_channel_count,
  529. const ConvolutionFilter1D& filter,
  530. const SkISize& image_size,
  531. unsigned char* output,
  532. int output_byte_row_stride,
  533. int output_channel_index,
  534. int output_channel_count,
  535. bool absolute_values) {
  536. int filter_offset, filter_length, filter_size;
  537. // Very much unlike BGRAConvolve2D, here we expect to have the same filter
  538. // for all pixels.
  539. const ConvolutionFilter1D::Fixed* filter_values =
  540. filter.GetSingleFilter(&filter_size, &filter_offset, &filter_length);
  541. if (filter_values == NULL || image_size.height() < filter_size) {
  542. NOTREACHED();
  543. return;
  544. }
  545. int centrepoint = filter_length / 2;
  546. if (filter_size - filter_offset != 2 * filter_offset) {
  547. // This means the original filter was not symmetrical AND
  548. // got clipped from one side more than from the other.
  549. centrepoint = filter_size / 2 - filter_offset;
  550. }
  551. for (int c = 0; c < image_size.width(); ++c) {
  552. unsigned char* target_byte = output + c * output_channel_count +
  553. output_channel_index;
  554. int r = 0;
  555. for (; r < centrepoint; ++r, target_byte += output_byte_row_stride) {
  556. int accval = 0;
  557. int i = 0;
  558. int pixel_byte_index = c * input_channel_count + input_channel_index;
  559. for (; i < centrepoint - r; ++i) // Padding part.
  560. accval += filter_values[i] * source_data[pixel_byte_index];
  561. for (; i < filter_length; ++i, pixel_byte_index += source_byte_row_stride)
  562. accval += filter_values[i] * source_data[pixel_byte_index];
  563. *target_byte = BringBackTo8(accval, absolute_values);
  564. }
  565. for (; r < image_size.height() - centrepoint;
  566. ++r, target_byte += output_byte_row_stride) {
  567. int accval = 0;
  568. int pixel_byte_index = (r - centrepoint) * source_byte_row_stride +
  569. c * input_channel_count + input_channel_index;
  570. for (int i = 0; i < filter_length;
  571. ++i, pixel_byte_index += source_byte_row_stride) {
  572. accval += filter_values[i] * source_data[pixel_byte_index];
  573. }
  574. *target_byte = BringBackTo8(accval, absolute_values);
  575. }
  576. for (; r < image_size.height();
  577. ++r, target_byte += output_byte_row_stride) {
  578. int accval = 0;
  579. int overlap_taps = image_size.height() - r + centrepoint;
  580. int pixel_byte_index = (r - centrepoint) * source_byte_row_stride +
  581. c * input_channel_count + input_channel_index;
  582. int i = 0;
  583. for (; i < overlap_taps - 1;
  584. ++i, pixel_byte_index += source_byte_row_stride) {
  585. accval += filter_values[i] * source_data[pixel_byte_index];
  586. }
  587. for (; i < filter_length; ++i)
  588. accval += filter_values[i] * source_data[pixel_byte_index];
  589. *target_byte = BringBackTo8(accval, absolute_values);
  590. }
  591. }
  592. }
  593. void SetUpGaussianConvolutionKernel(ConvolutionFilter1D* filter,
  594. float kernel_sigma,
  595. bool derivative) {
  596. DCHECK(filter != NULL);
  597. DCHECK_GT(kernel_sigma, 0.0);
  598. const int tail_length = static_cast<int>(4.0f * kernel_sigma + 0.5f);
  599. const int kernel_size = tail_length * 2 + 1;
  600. const float sigmasq = kernel_sigma * kernel_sigma;
  601. std::vector<float> kernel_weights(kernel_size, 0.0);
  602. float kernel_sum = 1.0f;
  603. kernel_weights[tail_length] = 1.0f;
  604. for (int ii = 1; ii <= tail_length; ++ii) {
  605. float v = std::exp(-0.5f * ii * ii / sigmasq);
  606. kernel_weights[tail_length + ii] = v;
  607. kernel_weights[tail_length - ii] = v;
  608. kernel_sum += 2.0f * v;
  609. }
  610. for (int i = 0; i < kernel_size; ++i)
  611. kernel_weights[i] /= kernel_sum;
  612. if (derivative) {
  613. kernel_weights[tail_length] = 0.0;
  614. for (int ii = 1; ii <= tail_length; ++ii) {
  615. float v = sigmasq * kernel_weights[tail_length + ii] / ii;
  616. kernel_weights[tail_length + ii] = v;
  617. kernel_weights[tail_length - ii] = -v;
  618. }
  619. }
  620. filter->AddFilter(0, &kernel_weights[0], kernel_weights.size());
  621. }
  622. } // namespace skia