/src/libavif/third_party/libyuv/source/scale.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2011 The LibYuv Project Authors. All rights reserved. |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license |
5 | | * that can be found in the LICENSE file in the root of the source |
6 | | * tree. An additional intellectual property rights grant can be found |
7 | | * in the file PATENTS. All contributing project authors may |
8 | | * be found in the AUTHORS file in the root of the source tree. |
9 | | */ |
10 | | |
11 | | #include "libyuv/scale.h" |
12 | | |
13 | | #include <assert.h> |
14 | | #include <string.h> |
15 | | |
16 | | #include "libyuv/planar_functions.h" // For CopyPlane |
17 | | #include "libyuv/row.h" |
18 | | #include "libyuv/scale_row.h" |
19 | | |
20 | 3.41k | static __inline int Abs(int v) { |
21 | 3.41k | return v >= 0 ? v : -v; |
22 | 3.41k | } |
23 | | |
24 | 177 | #define CENTERSTART(dx, s) (dx < 0) ? -((-dx >> 1) + s) : ((dx >> 1) + s) |
25 | | |
26 | 187k | #define MIN1(x) ((x) < 1 ? 1 : (x)) |
27 | | |
28 | 28.2k | static __inline uint32_t SumPixels(int iboxwidth, const uint16_t* src_ptr) { |
29 | 28.2k | uint32_t sum = 0u; |
30 | 28.2k | int x; |
31 | 28.2k | assert(iboxwidth > 0); |
32 | 132k | for (x = 0; x < iboxwidth; ++x) { |
33 | 104k | sum += src_ptr[x]; |
34 | 104k | } |
35 | 28.2k | return sum; |
36 | 28.2k | } |
37 | | |
38 | 153k | static __inline uint32_t SumPixels_16(int iboxwidth, const uint32_t* src_ptr) { |
39 | 153k | uint32_t sum = 0u; |
40 | 153k | int x; |
41 | 153k | assert(iboxwidth > 0); |
42 | 587k | for (x = 0; x < iboxwidth; ++x) { |
43 | 433k | sum += src_ptr[x]; |
44 | 433k | } |
45 | 153k | return sum; |
46 | 153k | } |
47 | | |
48 | | static void ScaleAddCols2_C(int dst_width, |
49 | | int boxheight, |
50 | | int x, |
51 | | int dx, |
52 | | const uint16_t* src_ptr, |
53 | 1.19k | uint8_t* dst_ptr) { |
54 | 1.19k | int i; |
55 | 1.19k | int scaletbl[2]; |
56 | 1.19k | int minboxwidth = dx >> 16; |
57 | 1.19k | int boxwidth; |
58 | 1.19k | scaletbl[0] = 65536 / (MIN1(minboxwidth) * boxheight); |
59 | 1.19k | scaletbl[1] = 65536 / (MIN1(minboxwidth + 1) * boxheight); |
60 | 28.6k | for (i = 0; i < dst_width; ++i) { |
61 | 27.4k | int ix = x >> 16; |
62 | 27.4k | x += dx; |
63 | 27.4k | boxwidth = MIN1((x >> 16) - ix); |
64 | 27.4k | int scaletbl_index = boxwidth - minboxwidth; |
65 | 27.4k | assert((scaletbl_index == 0) || (scaletbl_index == 1)); |
66 | 27.4k | *dst_ptr++ = (uint8_t)(SumPixels(boxwidth, src_ptr + ix) * |
67 | 27.4k | scaletbl[scaletbl_index] >> |
68 | 27.4k | 16); |
69 | 27.4k | } |
70 | 1.19k | } |
71 | | |
72 | | static void ScaleAddCols2_16_C(int dst_width, |
73 | | int boxheight, |
74 | | int x, |
75 | | int dx, |
76 | | const uint32_t* src_ptr, |
77 | 2.05k | uint16_t* dst_ptr) { |
78 | 2.05k | int i; |
79 | 2.05k | int scaletbl[2]; |
80 | 2.05k | int minboxwidth = dx >> 16; |
81 | 2.05k | int boxwidth; |
82 | 2.05k | scaletbl[0] = 65536 / (MIN1(minboxwidth) * boxheight); |
83 | 2.05k | scaletbl[1] = 65536 / (MIN1(minboxwidth + 1) * boxheight); |
84 | 151k | for (i = 0; i < dst_width; ++i) { |
85 | 149k | int ix = x >> 16; |
86 | 149k | x += dx; |
87 | 149k | boxwidth = MIN1((x >> 16) - ix); |
88 | 149k | int scaletbl_index = boxwidth - minboxwidth; |
89 | 149k | assert((scaletbl_index == 0) || (scaletbl_index == 1)); |
90 | 149k | *dst_ptr++ = |
91 | 149k | SumPixels_16(boxwidth, src_ptr + ix) * scaletbl[scaletbl_index] >> 16; |
92 | 149k | } |
93 | 2.05k | } |
94 | | |
95 | | static void ScaleAddCols0_C(int dst_width, |
96 | | int boxheight, |
97 | | int x, |
98 | | int dx, |
99 | | const uint16_t* src_ptr, |
100 | 0 | uint8_t* dst_ptr) { |
101 | 0 | int scaleval = 65536 / boxheight; |
102 | 0 | int i; |
103 | 0 | (void)dx; |
104 | 0 | src_ptr += (x >> 16); |
105 | 0 | for (i = 0; i < dst_width; ++i) { |
106 | 0 | *dst_ptr++ = (uint8_t)(src_ptr[i] * scaleval >> 16); |
107 | 0 | } |
108 | 0 | } |
109 | | |
110 | | static void ScaleAddCols1_C(int dst_width, |
111 | | int boxheight, |
112 | | int x, |
113 | | int dx, |
114 | | const uint16_t* src_ptr, |
115 | 239 | uint8_t* dst_ptr) { |
116 | 239 | int boxwidth = MIN1(dx >> 16); |
117 | 239 | int scaleval = 65536 / (boxwidth * boxheight); |
118 | 239 | int i; |
119 | 239 | x >>= 16; |
120 | 1.06k | for (i = 0; i < dst_width; ++i) { |
121 | 824 | *dst_ptr++ = (uint8_t)(SumPixels(boxwidth, src_ptr + x) * scaleval >> 16); |
122 | 824 | x += boxwidth; |
123 | 824 | } |
124 | 239 | } |
125 | | |
126 | | static void ScaleAddCols1_16_C(int dst_width, |
127 | | int boxheight, |
128 | | int x, |
129 | | int dx, |
130 | | const uint32_t* src_ptr, |
131 | 371 | uint16_t* dst_ptr) { |
132 | 371 | int boxwidth = MIN1(dx >> 16); |
133 | 371 | int scaleval = 65536 / (boxwidth * boxheight); |
134 | 371 | int i; |
135 | 5.21k | for (i = 0; i < dst_width; ++i) { |
136 | 4.83k | *dst_ptr++ = SumPixels_16(boxwidth, src_ptr + x) * scaleval >> 16; |
137 | 4.83k | x += boxwidth; |
138 | 4.83k | } |
139 | 371 | } |
140 | | |
141 | | // Scale plane down to any dimensions, with interpolation. |
142 | | // (boxfilter). |
143 | | // |
144 | | // Same method as SimpleScale, which is fixed point, outputting |
145 | | // one pixel of destination using fixed point (16.16) to step |
146 | | // through source, sampling a box of pixel with simple |
147 | | // averaging. |
148 | | static int ScalePlaneBox(int src_width, |
149 | | int src_height, |
150 | | int dst_width, |
151 | | int dst_height, |
152 | | int src_stride, |
153 | | int dst_stride, |
154 | | const uint8_t* src_ptr, |
155 | 124 | uint8_t* dst_ptr) { |
156 | 124 | int j, k; |
157 | | // Initial source x/y coordinate and step values as 16.16 fixed point. |
158 | 124 | int x = 0; |
159 | 124 | int y = 0; |
160 | 124 | int dx = 0; |
161 | 124 | int dy = 0; |
162 | 124 | const int max_y = (src_height << 16); |
163 | 124 | ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterBox, &x, &y, |
164 | 124 | &dx, &dy); |
165 | 124 | src_width = Abs(src_width); |
166 | 124 | { |
167 | | // Allocate a row buffer of uint16_t. |
168 | 124 | align_buffer_64(row16, src_width * 2); |
169 | 124 | if (!row16) |
170 | 0 | return 1; |
171 | 124 | void (*ScaleAddCols)(int dst_width, int boxheight, int x, int dx, |
172 | 124 | const uint16_t* src_ptr, uint8_t* dst_ptr) = |
173 | 124 | (dx & 0xffff) ? ScaleAddCols2_C |
174 | 124 | : ((dx != 0x10000) ? ScaleAddCols1_C : ScaleAddCols0_C); |
175 | 124 | void (*ScaleAddRow)(const uint8_t* src_ptr, uint16_t* dst_ptr, |
176 | 124 | int src_width) = ScaleAddRow_C; |
177 | | |
178 | 1.55k | for (j = 0; j < dst_height; ++j) { |
179 | 1.43k | int boxheight; |
180 | 1.43k | int iy = y >> 16; |
181 | 1.43k | const uint8_t* src = src_ptr + iy * (int64_t)src_stride; |
182 | 1.43k | y += dy; |
183 | 1.43k | if (y > max_y) { |
184 | 0 | y = max_y; |
185 | 0 | } |
186 | 1.43k | boxheight = MIN1((y >> 16) - iy); |
187 | 1.43k | memset(row16, 0, src_width * 2); |
188 | 11.2k | for (k = 0; k < boxheight; ++k) { |
189 | 9.83k | ScaleAddRow(src, (uint16_t*)(row16), src_width); |
190 | 9.83k | src += src_stride; |
191 | 9.83k | } |
192 | 1.43k | ScaleAddCols(dst_width, boxheight, x, dx, (uint16_t*)(row16), dst_ptr); |
193 | 1.43k | dst_ptr += dst_stride; |
194 | 1.43k | } |
195 | 124 | free_aligned_buffer_64(row16); |
196 | 124 | } |
197 | 0 | return 0; |
198 | 124 | } |
199 | | |
200 | | static int ScalePlaneBox_16(int src_width, |
201 | | int src_height, |
202 | | int dst_width, |
203 | | int dst_height, |
204 | | int src_stride, |
205 | | int dst_stride, |
206 | | const uint16_t* src_ptr, |
207 | 77 | uint16_t* dst_ptr) { |
208 | 77 | int j, k; |
209 | | // Initial source x/y coordinate and step values as 16.16 fixed point. |
210 | 77 | int x = 0; |
211 | 77 | int y = 0; |
212 | 77 | int dx = 0; |
213 | 77 | int dy = 0; |
214 | 77 | const int max_y = (src_height << 16); |
215 | 77 | ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterBox, &x, &y, |
216 | 77 | &dx, &dy); |
217 | 77 | src_width = Abs(src_width); |
218 | 77 | { |
219 | | // Allocate a row buffer of uint32_t. |
220 | 77 | align_buffer_64(row32, src_width * 4); |
221 | 77 | if (!row32) |
222 | 0 | return 1; |
223 | 77 | void (*ScaleAddCols)(int dst_width, int boxheight, int x, int dx, |
224 | 77 | const uint32_t* src_ptr, uint16_t* dst_ptr) = |
225 | 77 | (dx & 0xffff) ? ScaleAddCols2_16_C : ScaleAddCols1_16_C; |
226 | 77 | void (*ScaleAddRow)(const uint16_t* src_ptr, uint32_t* dst_ptr, |
227 | 77 | int src_width) = ScaleAddRow_16_C; |
228 | | |
229 | | #if defined(HAS_SCALEADDROW_16_SSE2) |
230 | | if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(src_width, 16)) { |
231 | | ScaleAddRow = ScaleAddRow_16_SSE2; |
232 | | } |
233 | | #endif |
234 | | |
235 | 2.50k | for (j = 0; j < dst_height; ++j) { |
236 | 2.42k | int boxheight; |
237 | 2.42k | int iy = y >> 16; |
238 | 2.42k | const uint16_t* src = src_ptr + iy * (int64_t)src_stride; |
239 | 2.42k | y += dy; |
240 | 2.42k | if (y > max_y) { |
241 | 0 | y = max_y; |
242 | 0 | } |
243 | 2.42k | boxheight = MIN1((y >> 16) - iy); |
244 | 2.42k | memset(row32, 0, src_width * 4); |
245 | 22.0k | for (k = 0; k < boxheight; ++k) { |
246 | 19.6k | ScaleAddRow(src, (uint32_t*)(row32), src_width); |
247 | 19.6k | src += src_stride; |
248 | 19.6k | } |
249 | 2.42k | ScaleAddCols(dst_width, boxheight, x, dx, (uint32_t*)(row32), dst_ptr); |
250 | 2.42k | dst_ptr += dst_stride; |
251 | 2.42k | } |
252 | 77 | free_aligned_buffer_64(row32); |
253 | 77 | } |
254 | 0 | return 0; |
255 | 77 | } |
256 | | |
257 | | // Scale plane down with bilinear interpolation. |
258 | | static int ScalePlaneBilinearDown(int src_width, |
259 | | int src_height, |
260 | | int dst_width, |
261 | | int dst_height, |
262 | | int src_stride, |
263 | | int dst_stride, |
264 | | const uint8_t* src_ptr, |
265 | | uint8_t* dst_ptr, |
266 | 514 | enum FilterMode filtering) { |
267 | | // Initial source x/y coordinate and step values as 16.16 fixed point. |
268 | 514 | int x = 0; |
269 | 514 | int y = 0; |
270 | 514 | int dx = 0; |
271 | 514 | int dy = 0; |
272 | | // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear. |
273 | | // Allocate a row buffer. |
274 | 514 | align_buffer_64(row, src_width); |
275 | 514 | if (!row) |
276 | 0 | return 1; |
277 | | |
278 | 514 | const int max_y = (src_height - 1) << 16; |
279 | 514 | int j; |
280 | 514 | void (*ScaleFilterCols)(uint8_t* dst_ptr, const uint8_t* src_ptr, |
281 | 514 | int dst_width, int x, int dx) = |
282 | 514 | (src_width >= 32768) ? ScaleFilterCols64_C : ScaleFilterCols_C; |
283 | 514 | void (*InterpolateRow)(uint8_t* dst_ptr, const uint8_t* src_ptr, |
284 | 514 | ptrdiff_t src_stride, int dst_width, |
285 | 514 | int source_y_fraction) = InterpolateRow_C; |
286 | 514 | ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y, |
287 | 514 | &dx, &dy); |
288 | 514 | src_width = Abs(src_width); |
289 | | |
290 | 514 | if (y > max_y) { |
291 | 28 | y = max_y; |
292 | 28 | } |
293 | | |
294 | 59.0k | for (j = 0; j < dst_height; ++j) { |
295 | 58.5k | int yi = y >> 16; |
296 | 58.5k | const uint8_t* src = src_ptr + yi * (int64_t)src_stride; |
297 | 58.5k | if (filtering == kFilterLinear) { |
298 | 1.35k | ScaleFilterCols(dst_ptr, src, dst_width, x, dx); |
299 | 57.1k | } else { |
300 | 57.1k | int yf = (y >> 8) & 255; |
301 | 57.1k | InterpolateRow(row, src, src_stride, src_width, yf); |
302 | 57.1k | ScaleFilterCols(dst_ptr, row, dst_width, x, dx); |
303 | 57.1k | } |
304 | 58.5k | dst_ptr += dst_stride; |
305 | 58.5k | y += dy; |
306 | 58.5k | if (y > max_y) { |
307 | 538 | y = max_y; |
308 | 538 | } |
309 | 58.5k | } |
310 | 514 | free_aligned_buffer_64(row); |
311 | 514 | return 0; |
312 | 514 | } |
313 | | |
314 | | static int ScalePlaneBilinearDown_16(int src_width, |
315 | | int src_height, |
316 | | int dst_width, |
317 | | int dst_height, |
318 | | int src_stride, |
319 | | int dst_stride, |
320 | | const uint16_t* src_ptr, |
321 | | uint16_t* dst_ptr, |
322 | 516 | enum FilterMode filtering) { |
323 | | // Initial source x/y coordinate and step values as 16.16 fixed point. |
324 | 516 | int x = 0; |
325 | 516 | int y = 0; |
326 | 516 | int dx = 0; |
327 | 516 | int dy = 0; |
328 | | // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear. |
329 | | // Allocate a row buffer. |
330 | 516 | align_buffer_64(row, src_width * 2); |
331 | 516 | if (!row) |
332 | 0 | return 1; |
333 | | |
334 | 516 | const int max_y = (src_height - 1) << 16; |
335 | 516 | int j; |
336 | 516 | void (*ScaleFilterCols)(uint16_t* dst_ptr, const uint16_t* src_ptr, |
337 | 516 | int dst_width, int x, int dx) = |
338 | 516 | (src_width >= 32768) ? ScaleFilterCols64_16_C : ScaleFilterCols_16_C; |
339 | 516 | void (*InterpolateRow)(uint16_t* dst_ptr, const uint16_t* src_ptr, |
340 | 516 | ptrdiff_t src_stride, int dst_width, |
341 | 516 | int source_y_fraction) = InterpolateRow_16_C; |
342 | 516 | ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y, |
343 | 516 | &dx, &dy); |
344 | 516 | src_width = Abs(src_width); |
345 | | |
346 | 516 | if (y > max_y) { |
347 | 19 | y = max_y; |
348 | 19 | } |
349 | | |
350 | 46.3k | for (j = 0; j < dst_height; ++j) { |
351 | 45.7k | int yi = y >> 16; |
352 | 45.7k | const uint16_t* src = src_ptr + yi * (int64_t)src_stride; |
353 | 45.7k | if (filtering == kFilterLinear) { |
354 | 620 | ScaleFilterCols(dst_ptr, src, dst_width, x, dx); |
355 | 45.1k | } else { |
356 | 45.1k | int yf = (y >> 8) & 255; |
357 | 45.1k | InterpolateRow((uint16_t*)row, src, src_stride, src_width, yf); |
358 | 45.1k | ScaleFilterCols(dst_ptr, (uint16_t*)row, dst_width, x, dx); |
359 | 45.1k | } |
360 | 45.7k | dst_ptr += dst_stride; |
361 | 45.7k | y += dy; |
362 | 45.7k | if (y > max_y) { |
363 | 530 | y = max_y; |
364 | 530 | } |
365 | 45.7k | } |
366 | 516 | free_aligned_buffer_64(row); |
367 | 516 | return 0; |
368 | 516 | } |
369 | | |
370 | | // Scale up down with bilinear interpolation. |
371 | | static int ScalePlaneBilinearUp(int src_width, |
372 | | int src_height, |
373 | | int dst_width, |
374 | | int dst_height, |
375 | | int src_stride, |
376 | | int dst_stride, |
377 | | const uint8_t* src_ptr, |
378 | | uint8_t* dst_ptr, |
379 | 544 | enum FilterMode filtering) { |
380 | 544 | int j; |
381 | | // Initial source x/y coordinate and step values as 16.16 fixed point. |
382 | 544 | int x = 0; |
383 | 544 | int y = 0; |
384 | 544 | int dx = 0; |
385 | 544 | int dy = 0; |
386 | 544 | const int max_y = (src_height - 1) << 16; |
387 | 544 | void (*InterpolateRow)(uint8_t* dst_ptr, const uint8_t* src_ptr, |
388 | 544 | ptrdiff_t src_stride, int dst_width, |
389 | 544 | int source_y_fraction) = InterpolateRow_C; |
390 | 544 | void (*ScaleFilterCols)(uint8_t* dst_ptr, const uint8_t* src_ptr, |
391 | 544 | int dst_width, int x, int dx) = |
392 | 544 | filtering ? ScaleFilterCols_C : ScaleCols_C; |
393 | 544 | ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y, |
394 | 544 | &dx, &dy); |
395 | 544 | src_width = Abs(src_width); |
396 | | |
397 | 544 | if (filtering && src_width >= 32768) { |
398 | 0 | ScaleFilterCols = ScaleFilterCols64_C; |
399 | 0 | } |
400 | 544 | if (!filtering && src_width * 2 == dst_width && x < 0x8000) { |
401 | 0 | ScaleFilterCols = ScaleColsUp2_C; |
402 | 0 | } |
403 | | |
404 | 544 | if (y > max_y) { |
405 | 81 | y = max_y; |
406 | 81 | } |
407 | 544 | { |
408 | 544 | int yi = y >> 16; |
409 | 544 | const uint8_t* src = src_ptr + yi * (int64_t)src_stride; |
410 | | |
411 | | // Allocate 2 row buffers. |
412 | 544 | const int row_size = (dst_width + 31) & ~31; |
413 | 544 | align_buffer_64(row, row_size * 2); |
414 | 544 | if (!row) |
415 | 0 | return 1; |
416 | | |
417 | 544 | uint8_t* rowptr = row; |
418 | 544 | int rowstride = row_size; |
419 | 544 | int lasty = yi; |
420 | | |
421 | 544 | ScaleFilterCols(rowptr, src, dst_width, x, dx); |
422 | 544 | if (src_height > 1) { |
423 | 456 | src += src_stride; |
424 | 456 | } |
425 | 544 | ScaleFilterCols(rowptr + rowstride, src, dst_width, x, dx); |
426 | 544 | if (src_height > 2) { |
427 | 413 | src += src_stride; |
428 | 413 | } |
429 | | |
430 | 5.84M | for (j = 0; j < dst_height; ++j) { |
431 | 5.84M | yi = y >> 16; |
432 | 5.84M | if (yi != lasty) { |
433 | 94.3k | if (y > max_y) { |
434 | 0 | y = max_y; |
435 | 0 | yi = y >> 16; |
436 | 0 | src = src_ptr + yi * (int64_t)src_stride; |
437 | 0 | } |
438 | 94.3k | if (yi != lasty) { |
439 | 94.3k | ScaleFilterCols(rowptr, src, dst_width, x, dx); |
440 | 94.3k | rowptr += rowstride; |
441 | 94.3k | rowstride = -rowstride; |
442 | 94.3k | lasty = yi; |
443 | 94.3k | if ((y + 65536) < max_y) { |
444 | 93.9k | src += src_stride; |
445 | 93.9k | } |
446 | 94.3k | } |
447 | 94.3k | } |
448 | 5.84M | if (filtering == kFilterLinear) { |
449 | 366k | InterpolateRow(dst_ptr, rowptr, 0, dst_width, 0); |
450 | 5.47M | } else { |
451 | 5.47M | int yf = (y >> 8) & 255; |
452 | 5.47M | InterpolateRow(dst_ptr, rowptr, rowstride, dst_width, yf); |
453 | 5.47M | } |
454 | 5.84M | dst_ptr += dst_stride; |
455 | 5.84M | y += dy; |
456 | 5.84M | } |
457 | 544 | free_aligned_buffer_64(row); |
458 | 544 | } |
459 | 0 | return 0; |
460 | 544 | } |
461 | | |
462 | | // Scale plane, horizontally up by 2 times. |
463 | | // Uses linear filter horizontally, nearest vertically. |
464 | | // This is an optimized version for scaling up a plane to 2 times of |
465 | | // its original width, using linear interpolation. |
466 | | // This is used to scale U and V planes of I422 to I444. |
467 | | static void ScalePlaneUp2_Linear(int src_width, |
468 | | int src_height, |
469 | | int dst_width, |
470 | | int dst_height, |
471 | | int src_stride, |
472 | | int dst_stride, |
473 | | const uint8_t* src_ptr, |
474 | 51 | uint8_t* dst_ptr) { |
475 | 51 | void (*ScaleRowUp)(const uint8_t* src_ptr, uint8_t* dst_ptr, int dst_width) = |
476 | 51 | ScaleRowUp2_Linear_Any_C; |
477 | 51 | int i; |
478 | 51 | int y; |
479 | 51 | int dy; |
480 | | |
481 | 51 | (void)src_width; |
482 | | // This function can only scale up by 2 times horizontally. |
483 | 51 | assert(src_width == ((dst_width + 1) / 2)); |
484 | | |
485 | 51 | if (dst_height == 1) { |
486 | 11 | ScaleRowUp(src_ptr + ((src_height - 1) / 2) * (int64_t)src_stride, dst_ptr, |
487 | 11 | dst_width); |
488 | 40 | } else { |
489 | 40 | dy = FixedDiv(src_height - 1, dst_height - 1); |
490 | 40 | y = (1 << 15) - 1; |
491 | 266k | for (i = 0; i < dst_height; ++i) { |
492 | 266k | ScaleRowUp(src_ptr + (y >> 16) * (int64_t)src_stride, dst_ptr, dst_width); |
493 | 266k | dst_ptr += dst_stride; |
494 | 266k | y += dy; |
495 | 266k | } |
496 | 40 | } |
497 | 51 | } |
498 | | |
499 | | // Scale plane, up by 2 times. |
500 | | // This is an optimized version for scaling up a plane to 2 times of |
501 | | // its original size, using bilinear interpolation. |
502 | | // This is used to scale U and V planes of I420 to I444. |
503 | | static void ScalePlaneUp2_Bilinear(int src_width, |
504 | | int src_height, |
505 | | int dst_width, |
506 | | int dst_height, |
507 | | int src_stride, |
508 | | int dst_stride, |
509 | | const uint8_t* src_ptr, |
510 | 31 | uint8_t* dst_ptr) { |
511 | 31 | void (*Scale2RowUp)(const uint8_t* src_ptr, ptrdiff_t src_stride, |
512 | 31 | uint8_t* dst_ptr, ptrdiff_t dst_stride, int dst_width) = |
513 | 31 | ScaleRowUp2_Bilinear_Any_C; |
514 | 31 | int x; |
515 | | |
516 | 31 | (void)src_width; |
517 | | // This function can only scale up by 2 times. |
518 | 31 | assert(src_width == ((dst_width + 1) / 2)); |
519 | 31 | assert(src_height == ((dst_height + 1) / 2)); |
520 | | |
521 | 31 | Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); |
522 | 31 | dst_ptr += dst_stride; |
523 | 119 | for (x = 0; x < src_height - 1; ++x) { |
524 | 88 | Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width); |
525 | 88 | src_ptr += src_stride; |
526 | | // TODO(fbarchard): Test performance of writing one row of destination at a |
527 | | // time. |
528 | 88 | dst_ptr += 2 * dst_stride; |
529 | 88 | } |
530 | 31 | if (!(dst_height & 1)) { |
531 | 14 | Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); |
532 | 14 | } |
533 | 31 | } |
534 | | |
535 | | // Scale at most 14 bit plane, horizontally up by 2 times. |
536 | | // This is an optimized version for scaling up a plane to 2 times of |
537 | | // its original width, using linear interpolation. |
538 | | // stride is in count of uint16_t. |
539 | | // This is used to scale U and V planes of I210 to I410 and I212 to I412. |
540 | | static void ScalePlaneUp2_12_Linear(int src_width, |
541 | | int src_height, |
542 | | int dst_width, |
543 | | int dst_height, |
544 | | int src_stride, |
545 | | int dst_stride, |
546 | | const uint16_t* src_ptr, |
547 | 47 | uint16_t* dst_ptr) { |
548 | 47 | void (*ScaleRowUp)(const uint16_t* src_ptr, uint16_t* dst_ptr, |
549 | 47 | int dst_width) = ScaleRowUp2_Linear_16_Any_C; |
550 | 47 | int i; |
551 | 47 | int y; |
552 | 47 | int dy; |
553 | | |
554 | 47 | (void)src_width; |
555 | | // This function can only scale up by 2 times horizontally. |
556 | 47 | assert(src_width == ((dst_width + 1) / 2)); |
557 | | |
558 | 47 | if (dst_height == 1) { |
559 | 10 | ScaleRowUp(src_ptr + ((src_height - 1) / 2) * (int64_t)src_stride, dst_ptr, |
560 | 10 | dst_width); |
561 | 37 | } else { |
562 | 37 | dy = FixedDiv(src_height - 1, dst_height - 1); |
563 | 37 | y = (1 << 15) - 1; |
564 | 200k | for (i = 0; i < dst_height; ++i) { |
565 | 200k | ScaleRowUp(src_ptr + (y >> 16) * (int64_t)src_stride, dst_ptr, dst_width); |
566 | 200k | dst_ptr += dst_stride; |
567 | 200k | y += dy; |
568 | 200k | } |
569 | 37 | } |
570 | 47 | } |
571 | | |
572 | | // Scale at most 12 bit plane, up by 2 times. |
573 | | // This is an optimized version for scaling up a plane to 2 times of |
574 | | // its original size, using bilinear interpolation. |
575 | | // stride is in count of uint16_t. |
576 | | // This is used to scale U and V planes of I010 to I410 and I012 to I412. |
577 | | static void ScalePlaneUp2_12_Bilinear(int src_width, |
578 | | int src_height, |
579 | | int dst_width, |
580 | | int dst_height, |
581 | | int src_stride, |
582 | | int dst_stride, |
583 | | const uint16_t* src_ptr, |
584 | 31 | uint16_t* dst_ptr) { |
585 | 31 | void (*Scale2RowUp)(const uint16_t* src_ptr, ptrdiff_t src_stride, |
586 | 31 | uint16_t* dst_ptr, ptrdiff_t dst_stride, int dst_width) = |
587 | 31 | ScaleRowUp2_Bilinear_16_Any_C; |
588 | 31 | int x; |
589 | | |
590 | 31 | (void)src_width; |
591 | | // This function can only scale up by 2 times. |
592 | 31 | assert(src_width == ((dst_width + 1) / 2)); |
593 | 31 | assert(src_height == ((dst_height + 1) / 2)); |
594 | | |
595 | 31 | Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); |
596 | 31 | dst_ptr += dst_stride; |
597 | 119 | for (x = 0; x < src_height - 1; ++x) { |
598 | 88 | Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width); |
599 | 88 | src_ptr += src_stride; |
600 | 88 | dst_ptr += 2 * dst_stride; |
601 | 88 | } |
602 | 31 | if (!(dst_height & 1)) { |
603 | 23 | Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); |
604 | 23 | } |
605 | 31 | } |
606 | | |
607 | | static void ScalePlaneUp2_16_Linear(int src_width, |
608 | | int src_height, |
609 | | int dst_width, |
610 | | int dst_height, |
611 | | int src_stride, |
612 | | int dst_stride, |
613 | | const uint16_t* src_ptr, |
614 | 0 | uint16_t* dst_ptr) { |
615 | 0 | void (*ScaleRowUp)(const uint16_t* src_ptr, uint16_t* dst_ptr, |
616 | 0 | int dst_width) = ScaleRowUp2_Linear_16_Any_C; |
617 | 0 | int i; |
618 | 0 | int y; |
619 | 0 | int dy; |
620 | |
|
621 | 0 | (void)src_width; |
622 | | // This function can only scale up by 2 times horizontally. |
623 | 0 | assert(src_width == ((dst_width + 1) / 2)); |
624 | | |
625 | 0 | if (dst_height == 1) { |
626 | 0 | ScaleRowUp(src_ptr + ((src_height - 1) / 2) * (int64_t)src_stride, dst_ptr, |
627 | 0 | dst_width); |
628 | 0 | } else { |
629 | 0 | dy = FixedDiv(src_height - 1, dst_height - 1); |
630 | 0 | y = (1 << 15) - 1; |
631 | 0 | for (i = 0; i < dst_height; ++i) { |
632 | 0 | ScaleRowUp(src_ptr + (y >> 16) * (int64_t)src_stride, dst_ptr, dst_width); |
633 | 0 | dst_ptr += dst_stride; |
634 | 0 | y += dy; |
635 | 0 | } |
636 | 0 | } |
637 | 0 | } |
638 | | |
639 | | static void ScalePlaneUp2_16_Bilinear(int src_width, |
640 | | int src_height, |
641 | | int dst_width, |
642 | | int dst_height, |
643 | | int src_stride, |
644 | | int dst_stride, |
645 | | const uint16_t* src_ptr, |
646 | 0 | uint16_t* dst_ptr) { |
647 | 0 | void (*Scale2RowUp)(const uint16_t* src_ptr, ptrdiff_t src_stride, |
648 | 0 | uint16_t* dst_ptr, ptrdiff_t dst_stride, int dst_width) = |
649 | 0 | ScaleRowUp2_Bilinear_16_Any_C; |
650 | 0 | int x; |
651 | |
|
652 | 0 | (void)src_width; |
653 | | // This function can only scale up by 2 times. |
654 | 0 | assert(src_width == ((dst_width + 1) / 2)); |
655 | 0 | assert(src_height == ((dst_height + 1) / 2)); |
656 | | |
657 | 0 | Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); |
658 | 0 | dst_ptr += dst_stride; |
659 | 0 | for (x = 0; x < src_height - 1; ++x) { |
660 | 0 | Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width); |
661 | 0 | src_ptr += src_stride; |
662 | 0 | dst_ptr += 2 * dst_stride; |
663 | 0 | } |
664 | 0 | if (!(dst_height & 1)) { |
665 | 0 | Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); |
666 | 0 | } |
667 | 0 | } |
668 | | |
669 | | static int ScalePlaneBilinearUp_16(int src_width, |
670 | | int src_height, |
671 | | int dst_width, |
672 | | int dst_height, |
673 | | int src_stride, |
674 | | int dst_stride, |
675 | | const uint16_t* src_ptr, |
676 | | uint16_t* dst_ptr, |
677 | 784 | enum FilterMode filtering) { |
678 | 784 | int j; |
679 | | // Initial source x/y coordinate and step values as 16.16 fixed point. |
680 | 784 | int x = 0; |
681 | 784 | int y = 0; |
682 | 784 | int dx = 0; |
683 | 784 | int dy = 0; |
684 | 784 | const int max_y = (src_height - 1) << 16; |
685 | 784 | void (*InterpolateRow)(uint16_t* dst_ptr, const uint16_t* src_ptr, |
686 | 784 | ptrdiff_t src_stride, int dst_width, |
687 | 784 | int source_y_fraction) = InterpolateRow_16_C; |
688 | 784 | void (*ScaleFilterCols)(uint16_t* dst_ptr, const uint16_t* src_ptr, |
689 | 784 | int dst_width, int x, int dx) = |
690 | 784 | filtering ? ScaleFilterCols_16_C : ScaleCols_16_C; |
691 | 784 | ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y, |
692 | 784 | &dx, &dy); |
693 | 784 | src_width = Abs(src_width); |
694 | | |
695 | 784 | if (filtering && src_width >= 32768) { |
696 | 0 | ScaleFilterCols = ScaleFilterCols64_16_C; |
697 | 0 | } |
698 | 784 | if (!filtering && src_width * 2 == dst_width && x < 0x8000) { |
699 | 0 | ScaleFilterCols = ScaleColsUp2_16_C; |
700 | 0 | } |
701 | 784 | if (y > max_y) { |
702 | 161 | y = max_y; |
703 | 161 | } |
704 | 784 | { |
705 | 784 | int yi = y >> 16; |
706 | 784 | const uint16_t* src = src_ptr + yi * (int64_t)src_stride; |
707 | | |
708 | | // Allocate 2 row buffers. |
709 | 784 | const int row_size = (dst_width + 31) & ~31; |
710 | 784 | align_buffer_64(row, row_size * 4); |
711 | 784 | int rowstride = row_size; |
712 | 784 | int lasty = yi; |
713 | 784 | uint16_t* rowptr = (uint16_t*)row; |
714 | 784 | if (!row) |
715 | 0 | return 1; |
716 | | |
717 | 784 | ScaleFilterCols(rowptr, src, dst_width, x, dx); |
718 | 784 | if (src_height > 1) { |
719 | 617 | src += src_stride; |
720 | 617 | } |
721 | 784 | ScaleFilterCols(rowptr + rowstride, src, dst_width, x, dx); |
722 | 784 | if (src_height > 2) { |
723 | 523 | src += src_stride; |
724 | 523 | } |
725 | | |
726 | 7.48M | for (j = 0; j < dst_height; ++j) { |
727 | 7.48M | yi = y >> 16; |
728 | 7.48M | if (yi != lasty) { |
729 | 73.5k | if (y > max_y) { |
730 | 0 | y = max_y; |
731 | 0 | yi = y >> 16; |
732 | 0 | src = src_ptr + yi * (int64_t)src_stride; |
733 | 0 | } |
734 | 73.5k | if (yi != lasty) { |
735 | 73.5k | ScaleFilterCols(rowptr, src, dst_width, x, dx); |
736 | 73.5k | rowptr += rowstride; |
737 | 73.5k | rowstride = -rowstride; |
738 | 73.5k | lasty = yi; |
739 | 73.5k | if ((y + 65536) < max_y) { |
740 | 73.0k | src += src_stride; |
741 | 73.0k | } |
742 | 73.5k | } |
743 | 73.5k | } |
744 | 7.48M | if (filtering == kFilterLinear) { |
745 | 551k | InterpolateRow(dst_ptr, rowptr, 0, dst_width, 0); |
746 | 6.93M | } else { |
747 | 6.93M | int yf = (y >> 8) & 255; |
748 | 6.93M | InterpolateRow(dst_ptr, rowptr, rowstride, dst_width, yf); |
749 | 6.93M | } |
750 | 7.48M | dst_ptr += dst_stride; |
751 | 7.48M | y += dy; |
752 | 7.48M | } |
753 | 784 | free_aligned_buffer_64(row); |
754 | 784 | } |
755 | 0 | return 0; |
756 | 784 | } |
757 | | |
758 | | // Scale Plane to/from any dimensions, without interpolation. |
759 | | // Fixed point math is used for performance: The upper 16 bits |
760 | | // of x and dx is the integer part of the source position and |
761 | | // the lower 16 bits are the fixed decimal part. |
762 | | |
763 | | static void ScalePlaneSimple(int src_width, |
764 | | int src_height, |
765 | | int dst_width, |
766 | | int dst_height, |
767 | | int src_stride, |
768 | | int dst_stride, |
769 | | const uint8_t* src_ptr, |
770 | 458 | uint8_t* dst_ptr) { |
771 | 458 | int i; |
772 | 458 | void (*ScaleCols)(uint8_t* dst_ptr, const uint8_t* src_ptr, int dst_width, |
773 | 458 | int x, int dx) = ScaleCols_C; |
774 | | // Initial source x/y coordinate and step values as 16.16 fixed point. |
775 | 458 | int x = 0; |
776 | 458 | int y = 0; |
777 | 458 | int dx = 0; |
778 | 458 | int dy = 0; |
779 | 458 | ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterNone, &x, &y, |
780 | 458 | &dx, &dy); |
781 | 458 | src_width = Abs(src_width); |
782 | | |
783 | 458 | if (src_width * 2 == dst_width && x < 0x8000) { |
784 | 106 | ScaleCols = ScaleColsUp2_C; |
785 | 106 | } |
786 | | |
787 | 793k | for (i = 0; i < dst_height; ++i) { |
788 | 792k | ScaleCols(dst_ptr, src_ptr + (y >> 16) * (int64_t)src_stride, dst_width, x, |
789 | 792k | dx); |
790 | 792k | dst_ptr += dst_stride; |
791 | 792k | y += dy; |
792 | 792k | } |
793 | 458 | } |
794 | | |
795 | | static void ScalePlaneSimple_16(int src_width, |
796 | | int src_height, |
797 | | int dst_width, |
798 | | int dst_height, |
799 | | int src_stride, |
800 | | int dst_stride, |
801 | | const uint16_t* src_ptr, |
802 | 401 | uint16_t* dst_ptr) { |
803 | 401 | int i; |
804 | 401 | void (*ScaleCols)(uint16_t* dst_ptr, const uint16_t* src_ptr, int dst_width, |
805 | 401 | int x, int dx) = ScaleCols_16_C; |
806 | | // Initial source x/y coordinate and step values as 16.16 fixed point. |
807 | 401 | int x = 0; |
808 | 401 | int y = 0; |
809 | 401 | int dx = 0; |
810 | 401 | int dy = 0; |
811 | 401 | ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterNone, &x, &y, |
812 | 401 | &dx, &dy); |
813 | 401 | src_width = Abs(src_width); |
814 | | |
815 | 401 | if (src_width * 2 == dst_width && x < 0x8000) { |
816 | 129 | ScaleCols = ScaleColsUp2_16_C; |
817 | 129 | } |
818 | | |
819 | 1.11M | for (i = 0; i < dst_height; ++i) { |
820 | 1.11M | ScaleCols(dst_ptr, src_ptr + (y >> 16) * (int64_t)src_stride, dst_width, x, |
821 | 1.11M | dx); |
822 | 1.11M | dst_ptr += dst_stride; |
823 | 1.11M | y += dy; |
824 | 1.11M | } |
825 | 401 | } |
826 | | |
827 | | // Scale a plane. |
828 | | // This function dispatches to a specialized scaler based on scale factor. |
829 | | int ScalePlane(const uint8_t* src, |
830 | | int src_stride, |
831 | | int src_width, |
832 | | int src_height, |
833 | | uint8_t* dst, |
834 | | int dst_stride, |
835 | | int dst_width, |
836 | | int dst_height, |
837 | 2.01k | enum FilterMode filtering) { |
838 | | // Simplify filtering when possible. |
839 | 2.01k | filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height, |
840 | 2.01k | filtering); |
841 | | |
842 | | // Negative height means invert the image. |
843 | 2.01k | if (src_height < 0) { |
844 | 0 | src_height = -src_height; |
845 | 0 | src = src + (src_height - 1) * (int64_t)src_stride; |
846 | 0 | src_stride = -src_stride; |
847 | 0 | } |
848 | | // Use specialized scales to improve performance for common resolutions. |
849 | | // For example, all the 1/2 scalings will use ScalePlaneDown2() |
850 | 2.01k | if (dst_width == src_width && dst_height == src_height) { |
851 | | // Straight copy. |
852 | 10 | CopyPlane(src, src_stride, dst, dst_stride, dst_width, dst_height); |
853 | 10 | return 0; |
854 | 10 | } |
855 | 2.00k | if (dst_width == src_width && filtering != kFilterBox) { |
856 | 287 | int dy = 0; |
857 | 287 | int y = 0; |
858 | | // When scaling down, use the center 2 rows to filter. |
859 | | // When scaling up, last row of destination uses the last 2 source rows. |
860 | 287 | if (dst_height <= src_height) { |
861 | 127 | dy = FixedDiv(src_height, dst_height); |
862 | 127 | y = CENTERSTART(dy, -32768); // Subtract 0.5 (32768) to center filter. |
863 | 160 | } else if (src_height > 1 && dst_height > 1) { |
864 | 67 | dy = FixedDiv1(src_height, dst_height); |
865 | 67 | } |
866 | | // Arbitrary scale vertically, but unscaled horizontally. |
867 | 287 | ScalePlaneVertical(src_height, dst_width, dst_height, src_stride, |
868 | 287 | dst_stride, src, dst, 0, y, dy, /*bpp=*/1, filtering); |
869 | 287 | return 0; |
870 | 287 | } |
871 | 1.72k | if (filtering == kFilterBox && dst_height * 2 < src_height) { |
872 | 124 | return ScalePlaneBox(src_width, src_height, dst_width, dst_height, |
873 | 124 | src_stride, dst_stride, src, dst); |
874 | 124 | } |
875 | 1.59k | if ((dst_width + 1) / 2 == src_width && filtering == kFilterLinear) { |
876 | 51 | ScalePlaneUp2_Linear(src_width, src_height, dst_width, dst_height, |
877 | 51 | src_stride, dst_stride, src, dst); |
878 | 51 | return 0; |
879 | 51 | } |
880 | 1.54k | if ((dst_height + 1) / 2 == src_height && (dst_width + 1) / 2 == src_width && |
881 | 1.54k | (filtering == kFilterBilinear || filtering == kFilterBox)) { |
882 | 31 | ScalePlaneUp2_Bilinear(src_width, src_height, dst_width, dst_height, |
883 | 31 | src_stride, dst_stride, src, dst); |
884 | 31 | return 0; |
885 | 31 | } |
886 | 1.51k | if (filtering && dst_height > src_height) { |
887 | 544 | return ScalePlaneBilinearUp(src_width, src_height, dst_width, dst_height, |
888 | 544 | src_stride, dst_stride, src, dst, filtering); |
889 | 544 | } |
890 | 972 | if (filtering) { |
891 | 514 | return ScalePlaneBilinearDown(src_width, src_height, dst_width, dst_height, |
892 | 514 | src_stride, dst_stride, src, dst, filtering); |
893 | 514 | } |
894 | 458 | ScalePlaneSimple(src_width, src_height, dst_width, dst_height, src_stride, |
895 | 458 | dst_stride, src, dst); |
896 | 458 | return 0; |
897 | 972 | } |
898 | | |
899 | | int ScalePlane_16(const uint16_t* src, |
900 | | int src_stride, |
901 | | int src_width, |
902 | | int src_height, |
903 | | uint16_t* dst, |
904 | | int dst_stride, |
905 | | int dst_width, |
906 | | int dst_height, |
907 | 1.90k | enum FilterMode filtering) { |
908 | | // Simplify filtering when possible. |
909 | 1.90k | filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height, |
910 | 1.90k | filtering); |
911 | | |
912 | | // Negative height means invert the image. |
913 | 1.90k | if (src_height < 0) { |
914 | 0 | src_height = -src_height; |
915 | 0 | src = src + (src_height - 1) * (int64_t)src_stride; |
916 | 0 | src_stride = -src_stride; |
917 | 0 | } |
918 | | // Use specialized scales to improve performance for common resolutions. |
919 | | // For example, all the 1/2 scalings will use ScalePlaneDown2() |
920 | 1.90k | if (dst_width == src_width && dst_height == src_height) { |
921 | | // Straight copy. |
922 | 16 | CopyPlane_16(src, src_stride, dst, dst_stride, dst_width, dst_height); |
923 | 16 | return 0; |
924 | 16 | } |
925 | 1.88k | if (dst_width == src_width && filtering != kFilterBox) { |
926 | 110 | int dy = 0; |
927 | 110 | int y = 0; |
928 | | // When scaling down, use the center 2 rows to filter. |
929 | | // When scaling up, last row of destination uses the last 2 source rows. |
930 | 110 | if (dst_height <= src_height) { |
931 | 50 | dy = FixedDiv(src_height, dst_height); |
932 | 50 | y = CENTERSTART(dy, -32768); // Subtract 0.5 (32768) to center filter. |
933 | | // When scaling up, ensure the last row of destination uses the last |
934 | | // source. Avoid divide by zero for dst_height but will do no scaling |
935 | | // later. |
936 | 60 | } else if (src_height > 1 && dst_height > 1) { |
937 | 40 | dy = FixedDiv1(src_height, dst_height); |
938 | 40 | } |
939 | | // Arbitrary scale vertically, but unscaled horizontally. |
940 | 110 | ScalePlaneVertical_16(src_height, dst_width, dst_height, src_stride, |
941 | 110 | dst_stride, src, dst, 0, y, dy, /*bpp=*/1, filtering); |
942 | 110 | return 0; |
943 | 110 | } |
944 | 1.77k | if (filtering == kFilterBox && dst_height * 2 < src_height) { |
945 | 77 | return ScalePlaneBox_16(src_width, src_height, dst_width, dst_height, |
946 | 77 | src_stride, dst_stride, src, dst); |
947 | 77 | } |
948 | 1.70k | if ((dst_width + 1) / 2 == src_width && filtering == kFilterLinear) { |
949 | 0 | ScalePlaneUp2_16_Linear(src_width, src_height, dst_width, dst_height, |
950 | 0 | src_stride, dst_stride, src, dst); |
951 | 0 | return 0; |
952 | 0 | } |
953 | 1.70k | if ((dst_height + 1) / 2 == src_height && (dst_width + 1) / 2 == src_width && |
954 | 1.70k | (filtering == kFilterBilinear || filtering == kFilterBox)) { |
955 | 0 | ScalePlaneUp2_16_Bilinear(src_width, src_height, dst_width, dst_height, |
956 | 0 | src_stride, dst_stride, src, dst); |
957 | 0 | return 0; |
958 | 0 | } |
959 | 1.70k | if (filtering && dst_height > src_height) { |
960 | 784 | return ScalePlaneBilinearUp_16(src_width, src_height, dst_width, dst_height, |
961 | 784 | src_stride, dst_stride, src, dst, filtering); |
962 | 784 | } |
963 | 917 | if (filtering) { |
964 | 516 | return ScalePlaneBilinearDown_16(src_width, src_height, dst_width, |
965 | 516 | dst_height, src_stride, dst_stride, src, |
966 | 516 | dst, filtering); |
967 | 516 | } |
968 | 401 | ScalePlaneSimple_16(src_width, src_height, dst_width, dst_height, src_stride, |
969 | 401 | dst_stride, src, dst); |
970 | 401 | return 0; |
971 | 917 | } |
972 | | |
973 | | int ScalePlane_12(const uint16_t* src, |
974 | | int src_stride, |
975 | | int src_width, |
976 | | int src_height, |
977 | | uint16_t* dst, |
978 | | int dst_stride, |
979 | | int dst_width, |
980 | | int dst_height, |
981 | 1.98k | enum FilterMode filtering) { |
982 | | // Simplify filtering when possible. |
983 | 1.98k | filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height, |
984 | 1.98k | filtering); |
985 | | |
986 | | // Negative height means invert the image. |
987 | 1.98k | if (src_height < 0) { |
988 | 0 | src_height = -src_height; |
989 | 0 | src = src + (src_height - 1) * (int64_t)src_stride; |
990 | 0 | src_stride = -src_stride; |
991 | 0 | } |
992 | | |
993 | 1.98k | if ((dst_width + 1) / 2 == src_width && filtering == kFilterLinear) { |
994 | 47 | ScalePlaneUp2_12_Linear(src_width, src_height, dst_width, dst_height, |
995 | 47 | src_stride, dst_stride, src, dst); |
996 | 47 | return 0; |
997 | 47 | } |
998 | 1.93k | if ((dst_height + 1) / 2 == src_height && (dst_width + 1) / 2 == src_width && |
999 | 1.93k | (filtering == kFilterBilinear || filtering == kFilterBox)) { |
1000 | 31 | ScalePlaneUp2_12_Bilinear(src_width, src_height, dst_width, dst_height, |
1001 | 31 | src_stride, dst_stride, src, dst); |
1002 | 31 | return 0; |
1003 | 31 | } |
1004 | | |
1005 | 1.90k | return ScalePlane_16(src, src_stride, src_width, src_height, dst, dst_stride, |
1006 | 1.90k | dst_width, dst_height, filtering); |
1007 | 1.93k | } |