Line data Source code
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/cpu_id.h"
17 : #include "libyuv/planar_functions.h" // For CopyPlane
18 : #include "libyuv/row.h"
19 : #include "libyuv/scale_row.h"
20 :
21 : #ifdef __cplusplus
22 : namespace libyuv {
23 : extern "C" {
24 : #endif
25 :
26 0 : static __inline int Abs(int v) {
27 0 : return v >= 0 ? v : -v;
28 : }
29 :
30 : #define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s)
31 :
32 : // Scale plane, 1/2
33 : // This is an optimized version for scaling down a plane to 1/2 of
34 : // its original size.
35 :
36 0 : static void ScalePlaneDown2(int src_width,
37 : int src_height,
38 : int dst_width,
39 : int dst_height,
40 : int src_stride,
41 : int dst_stride,
42 : const uint8* src_ptr,
43 : uint8* dst_ptr,
44 : enum FilterMode filtering) {
45 : int y;
46 : void (*ScaleRowDown2)(const uint8* src_ptr, ptrdiff_t src_stride,
47 : uint8* dst_ptr, int dst_width) =
48 : filtering == kFilterNone
49 0 : ? ScaleRowDown2_C
50 0 : : (filtering == kFilterLinear ? ScaleRowDown2Linear_C
51 0 : : ScaleRowDown2Box_C);
52 0 : int row_stride = src_stride << 1;
53 : (void)src_width;
54 : (void)src_height;
55 0 : if (!filtering) {
56 0 : src_ptr += src_stride; // Point to odd rows.
57 0 : src_stride = 0;
58 : }
59 :
60 : #if defined(HAS_SCALEROWDOWN2_NEON)
61 : if (TestCpuFlag(kCpuHasNEON)) {
62 : ScaleRowDown2 =
63 : filtering == kFilterNone
64 : ? ScaleRowDown2_Any_NEON
65 : : (filtering == kFilterLinear ? ScaleRowDown2Linear_Any_NEON
66 : : ScaleRowDown2Box_Any_NEON);
67 : if (IS_ALIGNED(dst_width, 16)) {
68 : ScaleRowDown2 = filtering == kFilterNone ? ScaleRowDown2_NEON
69 : : (filtering == kFilterLinear
70 : ? ScaleRowDown2Linear_NEON
71 : : ScaleRowDown2Box_NEON);
72 : }
73 : }
74 : #endif
75 : #if defined(HAS_SCALEROWDOWN2_SSSE3)
76 0 : if (TestCpuFlag(kCpuHasSSSE3)) {
77 0 : ScaleRowDown2 =
78 : filtering == kFilterNone
79 0 : ? ScaleRowDown2_Any_SSSE3
80 0 : : (filtering == kFilterLinear ? ScaleRowDown2Linear_Any_SSSE3
81 : : ScaleRowDown2Box_Any_SSSE3);
82 0 : if (IS_ALIGNED(dst_width, 16)) {
83 0 : ScaleRowDown2 =
84 : filtering == kFilterNone
85 0 : ? ScaleRowDown2_SSSE3
86 0 : : (filtering == kFilterLinear ? ScaleRowDown2Linear_SSSE3
87 : : ScaleRowDown2Box_SSSE3);
88 : }
89 : }
90 : #endif
91 : #if defined(HAS_SCALEROWDOWN2_AVX2)
92 0 : if (TestCpuFlag(kCpuHasAVX2)) {
93 0 : ScaleRowDown2 =
94 : filtering == kFilterNone
95 0 : ? ScaleRowDown2_Any_AVX2
96 0 : : (filtering == kFilterLinear ? ScaleRowDown2Linear_Any_AVX2
97 : : ScaleRowDown2Box_Any_AVX2);
98 0 : if (IS_ALIGNED(dst_width, 32)) {
99 0 : ScaleRowDown2 = filtering == kFilterNone ? ScaleRowDown2_AVX2
100 : : (filtering == kFilterLinear
101 0 : ? ScaleRowDown2Linear_AVX2
102 : : ScaleRowDown2Box_AVX2);
103 : }
104 : }
105 : #endif
106 : #if defined(HAS_SCALEROWDOWN2_DSPR2)
107 : if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(src_ptr, 4) &&
108 : IS_ALIGNED(src_stride, 4) && IS_ALIGNED(row_stride, 4) &&
109 : IS_ALIGNED(dst_ptr, 4) && IS_ALIGNED(dst_stride, 4)) {
110 : ScaleRowDown2 = filtering ? ScaleRowDown2Box_DSPR2 : ScaleRowDown2_DSPR2;
111 : }
112 : #endif
113 : #if defined(HAS_SCALEROWDOWN2_MSA)
114 : if (TestCpuFlag(kCpuHasMSA)) {
115 : ScaleRowDown2 =
116 : filtering == kFilterNone
117 : ? ScaleRowDown2_Any_MSA
118 : : (filtering == kFilterLinear ? ScaleRowDown2Linear_Any_MSA
119 : : ScaleRowDown2Box_Any_MSA);
120 : if (IS_ALIGNED(dst_width, 32)) {
121 : ScaleRowDown2 = filtering == kFilterNone ? ScaleRowDown2_MSA
122 : : (filtering == kFilterLinear
123 : ? ScaleRowDown2Linear_MSA
124 : : ScaleRowDown2Box_MSA);
125 : }
126 : }
127 : #endif
128 :
129 0 : if (filtering == kFilterLinear) {
130 0 : src_stride = 0;
131 : }
132 : // TODO(fbarchard): Loop through source height to allow odd height.
133 0 : for (y = 0; y < dst_height; ++y) {
134 0 : ScaleRowDown2(src_ptr, src_stride, dst_ptr, dst_width);
135 0 : src_ptr += row_stride;
136 0 : dst_ptr += dst_stride;
137 : }
138 0 : }
139 :
140 0 : static void ScalePlaneDown2_16(int src_width,
141 : int src_height,
142 : int dst_width,
143 : int dst_height,
144 : int src_stride,
145 : int dst_stride,
146 : const uint16* src_ptr,
147 : uint16* dst_ptr,
148 : enum FilterMode filtering) {
149 : int y;
150 : void (*ScaleRowDown2)(const uint16* src_ptr, ptrdiff_t src_stride,
151 : uint16* dst_ptr, int dst_width) =
152 : filtering == kFilterNone
153 0 : ? ScaleRowDown2_16_C
154 0 : : (filtering == kFilterLinear ? ScaleRowDown2Linear_16_C
155 0 : : ScaleRowDown2Box_16_C);
156 0 : int row_stride = src_stride << 1;
157 : (void)src_width;
158 : (void)src_height;
159 0 : if (!filtering) {
160 0 : src_ptr += src_stride; // Point to odd rows.
161 0 : src_stride = 0;
162 : }
163 :
164 : #if defined(HAS_SCALEROWDOWN2_16_NEON)
165 : if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(dst_width, 16)) {
166 : ScaleRowDown2 =
167 : filtering ? ScaleRowDown2Box_16_NEON : ScaleRowDown2_16_NEON;
168 : }
169 : #endif
170 : #if defined(HAS_SCALEROWDOWN2_16_SSE2)
171 : if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 16)) {
172 : ScaleRowDown2 =
173 : filtering == kFilterNone
174 : ? ScaleRowDown2_16_SSE2
175 : : (filtering == kFilterLinear ? ScaleRowDown2Linear_16_SSE2
176 : : ScaleRowDown2Box_16_SSE2);
177 : }
178 : #endif
179 : #if defined(HAS_SCALEROWDOWN2_16_DSPR2)
180 : if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(src_ptr, 4) &&
181 : IS_ALIGNED(src_stride, 4) && IS_ALIGNED(row_stride, 4) &&
182 : IS_ALIGNED(dst_ptr, 4) && IS_ALIGNED(dst_stride, 4)) {
183 : ScaleRowDown2 =
184 : filtering ? ScaleRowDown2Box_16_DSPR2 : ScaleRowDown2_16_DSPR2;
185 : }
186 : #endif
187 :
188 0 : if (filtering == kFilterLinear) {
189 0 : src_stride = 0;
190 : }
191 : // TODO(fbarchard): Loop through source height to allow odd height.
192 0 : for (y = 0; y < dst_height; ++y) {
193 0 : ScaleRowDown2(src_ptr, src_stride, dst_ptr, dst_width);
194 0 : src_ptr += row_stride;
195 0 : dst_ptr += dst_stride;
196 : }
197 0 : }
198 :
199 : // Scale plane, 1/4
200 : // This is an optimized version for scaling down a plane to 1/4 of
201 : // its original size.
202 :
203 0 : static void ScalePlaneDown4(int src_width,
204 : int src_height,
205 : int dst_width,
206 : int dst_height,
207 : int src_stride,
208 : int dst_stride,
209 : const uint8* src_ptr,
210 : uint8* dst_ptr,
211 : enum FilterMode filtering) {
212 : int y;
213 : void (*ScaleRowDown4)(const uint8* src_ptr, ptrdiff_t src_stride,
214 : uint8* dst_ptr, int dst_width) =
215 0 : filtering ? ScaleRowDown4Box_C : ScaleRowDown4_C;
216 0 : int row_stride = src_stride << 2;
217 : (void)src_width;
218 : (void)src_height;
219 0 : if (!filtering) {
220 0 : src_ptr += src_stride * 2; // Point to row 2.
221 0 : src_stride = 0;
222 : }
223 : #if defined(HAS_SCALEROWDOWN4_NEON)
224 : if (TestCpuFlag(kCpuHasNEON)) {
225 : ScaleRowDown4 =
226 : filtering ? ScaleRowDown4Box_Any_NEON : ScaleRowDown4_Any_NEON;
227 : if (IS_ALIGNED(dst_width, 8)) {
228 : ScaleRowDown4 = filtering ? ScaleRowDown4Box_NEON : ScaleRowDown4_NEON;
229 : }
230 : }
231 : #endif
232 : #if defined(HAS_SCALEROWDOWN4_SSSE3)
233 0 : if (TestCpuFlag(kCpuHasSSSE3)) {
234 0 : ScaleRowDown4 =
235 0 : filtering ? ScaleRowDown4Box_Any_SSSE3 : ScaleRowDown4_Any_SSSE3;
236 0 : if (IS_ALIGNED(dst_width, 8)) {
237 0 : ScaleRowDown4 = filtering ? ScaleRowDown4Box_SSSE3 : ScaleRowDown4_SSSE3;
238 : }
239 : }
240 : #endif
241 : #if defined(HAS_SCALEROWDOWN4_AVX2)
242 0 : if (TestCpuFlag(kCpuHasAVX2)) {
243 0 : ScaleRowDown4 =
244 0 : filtering ? ScaleRowDown4Box_Any_AVX2 : ScaleRowDown4_Any_AVX2;
245 0 : if (IS_ALIGNED(dst_width, 16)) {
246 0 : ScaleRowDown4 = filtering ? ScaleRowDown4Box_AVX2 : ScaleRowDown4_AVX2;
247 : }
248 : }
249 : #endif
250 : #if defined(HAS_SCALEROWDOWN4_DSPR2)
251 : if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(row_stride, 4) &&
252 : IS_ALIGNED(src_ptr, 4) && IS_ALIGNED(src_stride, 4) &&
253 : IS_ALIGNED(dst_ptr, 4) && IS_ALIGNED(dst_stride, 4)) {
254 : ScaleRowDown4 = filtering ? ScaleRowDown4Box_DSPR2 : ScaleRowDown4_DSPR2;
255 : }
256 : #endif
257 : #if defined(HAS_SCALEROWDOWN4_MSA)
258 : if (TestCpuFlag(kCpuHasMSA)) {
259 : ScaleRowDown4 =
260 : filtering ? ScaleRowDown4Box_Any_MSA : ScaleRowDown4_Any_MSA;
261 : if (IS_ALIGNED(dst_width, 16)) {
262 : ScaleRowDown4 = filtering ? ScaleRowDown4Box_MSA : ScaleRowDown4_MSA;
263 : }
264 : }
265 : #endif
266 :
267 0 : if (filtering == kFilterLinear) {
268 0 : src_stride = 0;
269 : }
270 0 : for (y = 0; y < dst_height; ++y) {
271 0 : ScaleRowDown4(src_ptr, src_stride, dst_ptr, dst_width);
272 0 : src_ptr += row_stride;
273 0 : dst_ptr += dst_stride;
274 : }
275 0 : }
276 :
277 0 : static void ScalePlaneDown4_16(int src_width,
278 : int src_height,
279 : int dst_width,
280 : int dst_height,
281 : int src_stride,
282 : int dst_stride,
283 : const uint16* src_ptr,
284 : uint16* dst_ptr,
285 : enum FilterMode filtering) {
286 : int y;
287 : void (*ScaleRowDown4)(const uint16* src_ptr, ptrdiff_t src_stride,
288 : uint16* dst_ptr, int dst_width) =
289 0 : filtering ? ScaleRowDown4Box_16_C : ScaleRowDown4_16_C;
290 0 : int row_stride = src_stride << 2;
291 : (void)src_width;
292 : (void)src_height;
293 0 : if (!filtering) {
294 0 : src_ptr += src_stride * 2; // Point to row 2.
295 0 : src_stride = 0;
296 : }
297 : #if defined(HAS_SCALEROWDOWN4_16_NEON)
298 : if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(dst_width, 8)) {
299 : ScaleRowDown4 =
300 : filtering ? ScaleRowDown4Box_16_NEON : ScaleRowDown4_16_NEON;
301 : }
302 : #endif
303 : #if defined(HAS_SCALEROWDOWN4_16_SSE2)
304 : if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
305 : ScaleRowDown4 =
306 : filtering ? ScaleRowDown4Box_16_SSE2 : ScaleRowDown4_16_SSE2;
307 : }
308 : #endif
309 : #if defined(HAS_SCALEROWDOWN4_16_DSPR2)
310 : if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(row_stride, 4) &&
311 : IS_ALIGNED(src_ptr, 4) && IS_ALIGNED(src_stride, 4) &&
312 : IS_ALIGNED(dst_ptr, 4) && IS_ALIGNED(dst_stride, 4)) {
313 : ScaleRowDown4 =
314 : filtering ? ScaleRowDown4Box_16_DSPR2 : ScaleRowDown4_16_DSPR2;
315 : }
316 : #endif
317 :
318 0 : if (filtering == kFilterLinear) {
319 0 : src_stride = 0;
320 : }
321 0 : for (y = 0; y < dst_height; ++y) {
322 0 : ScaleRowDown4(src_ptr, src_stride, dst_ptr, dst_width);
323 0 : src_ptr += row_stride;
324 0 : dst_ptr += dst_stride;
325 : }
326 0 : }
327 :
328 : // Scale plane down, 3/4
329 0 : static void ScalePlaneDown34(int src_width,
330 : int src_height,
331 : int dst_width,
332 : int dst_height,
333 : int src_stride,
334 : int dst_stride,
335 : const uint8* src_ptr,
336 : uint8* dst_ptr,
337 : enum FilterMode filtering) {
338 : int y;
339 : void (*ScaleRowDown34_0)(const uint8* src_ptr, ptrdiff_t src_stride,
340 : uint8* dst_ptr, int dst_width);
341 : void (*ScaleRowDown34_1)(const uint8* src_ptr, ptrdiff_t src_stride,
342 : uint8* dst_ptr, int dst_width);
343 0 : const int filter_stride = (filtering == kFilterLinear) ? 0 : src_stride;
344 : (void)src_width;
345 : (void)src_height;
346 0 : assert(dst_width % 3 == 0);
347 0 : if (!filtering) {
348 0 : ScaleRowDown34_0 = ScaleRowDown34_C;
349 0 : ScaleRowDown34_1 = ScaleRowDown34_C;
350 : } else {
351 0 : ScaleRowDown34_0 = ScaleRowDown34_0_Box_C;
352 0 : ScaleRowDown34_1 = ScaleRowDown34_1_Box_C;
353 : }
354 : #if defined(HAS_SCALEROWDOWN34_NEON)
355 : if (TestCpuFlag(kCpuHasNEON)) {
356 : if (!filtering) {
357 : ScaleRowDown34_0 = ScaleRowDown34_Any_NEON;
358 : ScaleRowDown34_1 = ScaleRowDown34_Any_NEON;
359 : } else {
360 : ScaleRowDown34_0 = ScaleRowDown34_0_Box_Any_NEON;
361 : ScaleRowDown34_1 = ScaleRowDown34_1_Box_Any_NEON;
362 : }
363 : if (dst_width % 24 == 0) {
364 : if (!filtering) {
365 : ScaleRowDown34_0 = ScaleRowDown34_NEON;
366 : ScaleRowDown34_1 = ScaleRowDown34_NEON;
367 : } else {
368 : ScaleRowDown34_0 = ScaleRowDown34_0_Box_NEON;
369 : ScaleRowDown34_1 = ScaleRowDown34_1_Box_NEON;
370 : }
371 : }
372 : }
373 : #endif
374 : #if defined(HAS_SCALEROWDOWN34_SSSE3)
375 0 : if (TestCpuFlag(kCpuHasSSSE3)) {
376 0 : if (!filtering) {
377 0 : ScaleRowDown34_0 = ScaleRowDown34_Any_SSSE3;
378 0 : ScaleRowDown34_1 = ScaleRowDown34_Any_SSSE3;
379 : } else {
380 0 : ScaleRowDown34_0 = ScaleRowDown34_0_Box_Any_SSSE3;
381 0 : ScaleRowDown34_1 = ScaleRowDown34_1_Box_Any_SSSE3;
382 : }
383 0 : if (dst_width % 24 == 0) {
384 0 : if (!filtering) {
385 0 : ScaleRowDown34_0 = ScaleRowDown34_SSSE3;
386 0 : ScaleRowDown34_1 = ScaleRowDown34_SSSE3;
387 : } else {
388 0 : ScaleRowDown34_0 = ScaleRowDown34_0_Box_SSSE3;
389 0 : ScaleRowDown34_1 = ScaleRowDown34_1_Box_SSSE3;
390 : }
391 : }
392 : }
393 : #endif
394 : #if defined(HAS_SCALEROWDOWN34_DSPR2)
395 : if (TestCpuFlag(kCpuHasDSPR2) && (dst_width % 24 == 0) &&
396 : IS_ALIGNED(src_ptr, 4) && IS_ALIGNED(src_stride, 4) &&
397 : IS_ALIGNED(dst_ptr, 4) && IS_ALIGNED(dst_stride, 4)) {
398 : if (!filtering) {
399 : ScaleRowDown34_0 = ScaleRowDown34_DSPR2;
400 : ScaleRowDown34_1 = ScaleRowDown34_DSPR2;
401 : } else {
402 : ScaleRowDown34_0 = ScaleRowDown34_0_Box_DSPR2;
403 : ScaleRowDown34_1 = ScaleRowDown34_1_Box_DSPR2;
404 : }
405 : }
406 : #endif
407 :
408 0 : for (y = 0; y < dst_height - 2; y += 3) {
409 0 : ScaleRowDown34_0(src_ptr, filter_stride, dst_ptr, dst_width);
410 0 : src_ptr += src_stride;
411 0 : dst_ptr += dst_stride;
412 0 : ScaleRowDown34_1(src_ptr, filter_stride, dst_ptr, dst_width);
413 0 : src_ptr += src_stride;
414 0 : dst_ptr += dst_stride;
415 0 : ScaleRowDown34_0(src_ptr + src_stride, -filter_stride, dst_ptr, dst_width);
416 0 : src_ptr += src_stride * 2;
417 0 : dst_ptr += dst_stride;
418 : }
419 :
420 : // Remainder 1 or 2 rows with last row vertically unfiltered
421 0 : if ((dst_height % 3) == 2) {
422 0 : ScaleRowDown34_0(src_ptr, filter_stride, dst_ptr, dst_width);
423 0 : src_ptr += src_stride;
424 0 : dst_ptr += dst_stride;
425 0 : ScaleRowDown34_1(src_ptr, 0, dst_ptr, dst_width);
426 0 : } else if ((dst_height % 3) == 1) {
427 0 : ScaleRowDown34_0(src_ptr, 0, dst_ptr, dst_width);
428 : }
429 0 : }
430 :
431 0 : static void ScalePlaneDown34_16(int src_width,
432 : int src_height,
433 : int dst_width,
434 : int dst_height,
435 : int src_stride,
436 : int dst_stride,
437 : const uint16* src_ptr,
438 : uint16* dst_ptr,
439 : enum FilterMode filtering) {
440 : int y;
441 : void (*ScaleRowDown34_0)(const uint16* src_ptr, ptrdiff_t src_stride,
442 : uint16* dst_ptr, int dst_width);
443 : void (*ScaleRowDown34_1)(const uint16* src_ptr, ptrdiff_t src_stride,
444 : uint16* dst_ptr, int dst_width);
445 0 : const int filter_stride = (filtering == kFilterLinear) ? 0 : src_stride;
446 : (void)src_width;
447 : (void)src_height;
448 0 : assert(dst_width % 3 == 0);
449 0 : if (!filtering) {
450 0 : ScaleRowDown34_0 = ScaleRowDown34_16_C;
451 0 : ScaleRowDown34_1 = ScaleRowDown34_16_C;
452 : } else {
453 0 : ScaleRowDown34_0 = ScaleRowDown34_0_Box_16_C;
454 0 : ScaleRowDown34_1 = ScaleRowDown34_1_Box_16_C;
455 : }
456 : #if defined(HAS_SCALEROWDOWN34_16_NEON)
457 : if (TestCpuFlag(kCpuHasNEON) && (dst_width % 24 == 0)) {
458 : if (!filtering) {
459 : ScaleRowDown34_0 = ScaleRowDown34_16_NEON;
460 : ScaleRowDown34_1 = ScaleRowDown34_16_NEON;
461 : } else {
462 : ScaleRowDown34_0 = ScaleRowDown34_0_Box_16_NEON;
463 : ScaleRowDown34_1 = ScaleRowDown34_1_Box_16_NEON;
464 : }
465 : }
466 : #endif
467 : #if defined(HAS_SCALEROWDOWN34_16_SSSE3)
468 : if (TestCpuFlag(kCpuHasSSSE3) && (dst_width % 24 == 0)) {
469 : if (!filtering) {
470 : ScaleRowDown34_0 = ScaleRowDown34_16_SSSE3;
471 : ScaleRowDown34_1 = ScaleRowDown34_16_SSSE3;
472 : } else {
473 : ScaleRowDown34_0 = ScaleRowDown34_0_Box_16_SSSE3;
474 : ScaleRowDown34_1 = ScaleRowDown34_1_Box_16_SSSE3;
475 : }
476 : }
477 : #endif
478 : #if defined(HAS_SCALEROWDOWN34_16_DSPR2)
479 : if (TestCpuFlag(kCpuHasDSPR2) && (dst_width % 24 == 0) &&
480 : IS_ALIGNED(src_ptr, 4) && IS_ALIGNED(src_stride, 4) &&
481 : IS_ALIGNED(dst_ptr, 4) && IS_ALIGNED(dst_stride, 4)) {
482 : if (!filtering) {
483 : ScaleRowDown34_0 = ScaleRowDown34_16_DSPR2;
484 : ScaleRowDown34_1 = ScaleRowDown34_16_DSPR2;
485 : } else {
486 : ScaleRowDown34_0 = ScaleRowDown34_0_Box_16_DSPR2;
487 : ScaleRowDown34_1 = ScaleRowDown34_1_Box_16_DSPR2;
488 : }
489 : }
490 : #endif
491 :
492 0 : for (y = 0; y < dst_height - 2; y += 3) {
493 0 : ScaleRowDown34_0(src_ptr, filter_stride, dst_ptr, dst_width);
494 0 : src_ptr += src_stride;
495 0 : dst_ptr += dst_stride;
496 0 : ScaleRowDown34_1(src_ptr, filter_stride, dst_ptr, dst_width);
497 0 : src_ptr += src_stride;
498 0 : dst_ptr += dst_stride;
499 0 : ScaleRowDown34_0(src_ptr + src_stride, -filter_stride, dst_ptr, dst_width);
500 0 : src_ptr += src_stride * 2;
501 0 : dst_ptr += dst_stride;
502 : }
503 :
504 : // Remainder 1 or 2 rows with last row vertically unfiltered
505 0 : if ((dst_height % 3) == 2) {
506 0 : ScaleRowDown34_0(src_ptr, filter_stride, dst_ptr, dst_width);
507 0 : src_ptr += src_stride;
508 0 : dst_ptr += dst_stride;
509 0 : ScaleRowDown34_1(src_ptr, 0, dst_ptr, dst_width);
510 0 : } else if ((dst_height % 3) == 1) {
511 0 : ScaleRowDown34_0(src_ptr, 0, dst_ptr, dst_width);
512 : }
513 0 : }
514 :
515 : // Scale plane, 3/8
516 : // This is an optimized version for scaling down a plane to 3/8
517 : // of its original size.
518 : //
519 : // Uses box filter arranges like this
520 : // aaabbbcc -> abc
521 : // aaabbbcc def
522 : // aaabbbcc ghi
523 : // dddeeeff
524 : // dddeeeff
525 : // dddeeeff
526 : // ggghhhii
527 : // ggghhhii
528 : // Boxes are 3x3, 2x3, 3x2 and 2x2
529 :
530 0 : static void ScalePlaneDown38(int src_width,
531 : int src_height,
532 : int dst_width,
533 : int dst_height,
534 : int src_stride,
535 : int dst_stride,
536 : const uint8* src_ptr,
537 : uint8* dst_ptr,
538 : enum FilterMode filtering) {
539 : int y;
540 : void (*ScaleRowDown38_3)(const uint8* src_ptr, ptrdiff_t src_stride,
541 : uint8* dst_ptr, int dst_width);
542 : void (*ScaleRowDown38_2)(const uint8* src_ptr, ptrdiff_t src_stride,
543 : uint8* dst_ptr, int dst_width);
544 0 : const int filter_stride = (filtering == kFilterLinear) ? 0 : src_stride;
545 0 : assert(dst_width % 3 == 0);
546 : (void)src_width;
547 : (void)src_height;
548 0 : if (!filtering) {
549 0 : ScaleRowDown38_3 = ScaleRowDown38_C;
550 0 : ScaleRowDown38_2 = ScaleRowDown38_C;
551 : } else {
552 0 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_C;
553 0 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_C;
554 : }
555 :
556 : #if defined(HAS_SCALEROWDOWN38_NEON)
557 : if (TestCpuFlag(kCpuHasNEON)) {
558 : if (!filtering) {
559 : ScaleRowDown38_3 = ScaleRowDown38_Any_NEON;
560 : ScaleRowDown38_2 = ScaleRowDown38_Any_NEON;
561 : } else {
562 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_Any_NEON;
563 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_Any_NEON;
564 : }
565 : if (dst_width % 12 == 0) {
566 : if (!filtering) {
567 : ScaleRowDown38_3 = ScaleRowDown38_NEON;
568 : ScaleRowDown38_2 = ScaleRowDown38_NEON;
569 : } else {
570 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_NEON;
571 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_NEON;
572 : }
573 : }
574 : }
575 : #endif
576 : #if defined(HAS_SCALEROWDOWN38_SSSE3)
577 0 : if (TestCpuFlag(kCpuHasSSSE3)) {
578 0 : if (!filtering) {
579 0 : ScaleRowDown38_3 = ScaleRowDown38_Any_SSSE3;
580 0 : ScaleRowDown38_2 = ScaleRowDown38_Any_SSSE3;
581 : } else {
582 0 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_Any_SSSE3;
583 0 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_Any_SSSE3;
584 : }
585 0 : if (dst_width % 12 == 0 && !filtering) {
586 0 : ScaleRowDown38_3 = ScaleRowDown38_SSSE3;
587 0 : ScaleRowDown38_2 = ScaleRowDown38_SSSE3;
588 : }
589 0 : if (dst_width % 6 == 0 && filtering) {
590 0 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_SSSE3;
591 0 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_SSSE3;
592 : }
593 : }
594 : #endif
595 : #if defined(HAS_SCALEROWDOWN38_DSPR2)
596 : if (TestCpuFlag(kCpuHasDSPR2) && (dst_width % 12 == 0) &&
597 : IS_ALIGNED(src_ptr, 4) && IS_ALIGNED(src_stride, 4) &&
598 : IS_ALIGNED(dst_ptr, 4) && IS_ALIGNED(dst_stride, 4)) {
599 : if (!filtering) {
600 : ScaleRowDown38_3 = ScaleRowDown38_DSPR2;
601 : ScaleRowDown38_2 = ScaleRowDown38_DSPR2;
602 : } else {
603 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_DSPR2;
604 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_DSPR2;
605 : }
606 : }
607 : #endif
608 : #if defined(HAS_SCALEROWDOWN38_MSA)
609 : if (TestCpuFlag(kCpuHasMSA)) {
610 : if (!filtering) {
611 : ScaleRowDown38_3 = ScaleRowDown38_Any_MSA;
612 : ScaleRowDown38_2 = ScaleRowDown38_Any_MSA;
613 : } else {
614 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_Any_MSA;
615 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_Any_MSA;
616 : }
617 : if (dst_width % 12 == 0) {
618 : if (!filtering) {
619 : ScaleRowDown38_3 = ScaleRowDown38_MSA;
620 : ScaleRowDown38_2 = ScaleRowDown38_MSA;
621 : } else {
622 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_MSA;
623 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_MSA;
624 : }
625 : }
626 : }
627 : #endif
628 :
629 0 : for (y = 0; y < dst_height - 2; y += 3) {
630 0 : ScaleRowDown38_3(src_ptr, filter_stride, dst_ptr, dst_width);
631 0 : src_ptr += src_stride * 3;
632 0 : dst_ptr += dst_stride;
633 0 : ScaleRowDown38_3(src_ptr, filter_stride, dst_ptr, dst_width);
634 0 : src_ptr += src_stride * 3;
635 0 : dst_ptr += dst_stride;
636 0 : ScaleRowDown38_2(src_ptr, filter_stride, dst_ptr, dst_width);
637 0 : src_ptr += src_stride * 2;
638 0 : dst_ptr += dst_stride;
639 : }
640 :
641 : // Remainder 1 or 2 rows with last row vertically unfiltered
642 0 : if ((dst_height % 3) == 2) {
643 0 : ScaleRowDown38_3(src_ptr, filter_stride, dst_ptr, dst_width);
644 0 : src_ptr += src_stride * 3;
645 0 : dst_ptr += dst_stride;
646 0 : ScaleRowDown38_3(src_ptr, 0, dst_ptr, dst_width);
647 0 : } else if ((dst_height % 3) == 1) {
648 0 : ScaleRowDown38_3(src_ptr, 0, dst_ptr, dst_width);
649 : }
650 0 : }
651 :
652 0 : static void ScalePlaneDown38_16(int src_width,
653 : int src_height,
654 : int dst_width,
655 : int dst_height,
656 : int src_stride,
657 : int dst_stride,
658 : const uint16* src_ptr,
659 : uint16* dst_ptr,
660 : enum FilterMode filtering) {
661 : int y;
662 : void (*ScaleRowDown38_3)(const uint16* src_ptr, ptrdiff_t src_stride,
663 : uint16* dst_ptr, int dst_width);
664 : void (*ScaleRowDown38_2)(const uint16* src_ptr, ptrdiff_t src_stride,
665 : uint16* dst_ptr, int dst_width);
666 0 : const int filter_stride = (filtering == kFilterLinear) ? 0 : src_stride;
667 : (void)src_width;
668 : (void)src_height;
669 0 : assert(dst_width % 3 == 0);
670 0 : if (!filtering) {
671 0 : ScaleRowDown38_3 = ScaleRowDown38_16_C;
672 0 : ScaleRowDown38_2 = ScaleRowDown38_16_C;
673 : } else {
674 0 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_16_C;
675 0 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_16_C;
676 : }
677 : #if defined(HAS_SCALEROWDOWN38_16_NEON)
678 : if (TestCpuFlag(kCpuHasNEON) && (dst_width % 12 == 0)) {
679 : if (!filtering) {
680 : ScaleRowDown38_3 = ScaleRowDown38_16_NEON;
681 : ScaleRowDown38_2 = ScaleRowDown38_16_NEON;
682 : } else {
683 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_16_NEON;
684 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_16_NEON;
685 : }
686 : }
687 : #endif
688 : #if defined(HAS_SCALEROWDOWN38_16_SSSE3)
689 : if (TestCpuFlag(kCpuHasSSSE3) && (dst_width % 24 == 0)) {
690 : if (!filtering) {
691 : ScaleRowDown38_3 = ScaleRowDown38_16_SSSE3;
692 : ScaleRowDown38_2 = ScaleRowDown38_16_SSSE3;
693 : } else {
694 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_16_SSSE3;
695 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_16_SSSE3;
696 : }
697 : }
698 : #endif
699 : #if defined(HAS_SCALEROWDOWN38_16_DSPR2)
700 : if (TestCpuFlag(kCpuHasDSPR2) && (dst_width % 12 == 0) &&
701 : IS_ALIGNED(src_ptr, 4) && IS_ALIGNED(src_stride, 4) &&
702 : IS_ALIGNED(dst_ptr, 4) && IS_ALIGNED(dst_stride, 4)) {
703 : if (!filtering) {
704 : ScaleRowDown38_3 = ScaleRowDown38_16_DSPR2;
705 : ScaleRowDown38_2 = ScaleRowDown38_16_DSPR2;
706 : } else {
707 : ScaleRowDown38_3 = ScaleRowDown38_3_Box_16_DSPR2;
708 : ScaleRowDown38_2 = ScaleRowDown38_2_Box_16_DSPR2;
709 : }
710 : }
711 : #endif
712 :
713 0 : for (y = 0; y < dst_height - 2; y += 3) {
714 0 : ScaleRowDown38_3(src_ptr, filter_stride, dst_ptr, dst_width);
715 0 : src_ptr += src_stride * 3;
716 0 : dst_ptr += dst_stride;
717 0 : ScaleRowDown38_3(src_ptr, filter_stride, dst_ptr, dst_width);
718 0 : src_ptr += src_stride * 3;
719 0 : dst_ptr += dst_stride;
720 0 : ScaleRowDown38_2(src_ptr, filter_stride, dst_ptr, dst_width);
721 0 : src_ptr += src_stride * 2;
722 0 : dst_ptr += dst_stride;
723 : }
724 :
725 : // Remainder 1 or 2 rows with last row vertically unfiltered
726 0 : if ((dst_height % 3) == 2) {
727 0 : ScaleRowDown38_3(src_ptr, filter_stride, dst_ptr, dst_width);
728 0 : src_ptr += src_stride * 3;
729 0 : dst_ptr += dst_stride;
730 0 : ScaleRowDown38_3(src_ptr, 0, dst_ptr, dst_width);
731 0 : } else if ((dst_height % 3) == 1) {
732 0 : ScaleRowDown38_3(src_ptr, 0, dst_ptr, dst_width);
733 : }
734 0 : }
735 :
736 : #define MIN1(x) ((x) < 1 ? 1 : (x))
737 :
738 0 : static __inline uint32 SumPixels(int iboxwidth, const uint16* src_ptr) {
739 0 : uint32 sum = 0u;
740 : int x;
741 0 : assert(iboxwidth > 0);
742 0 : for (x = 0; x < iboxwidth; ++x) {
743 0 : sum += src_ptr[x];
744 : }
745 0 : return sum;
746 : }
747 :
748 0 : static __inline uint32 SumPixels_16(int iboxwidth, const uint32* src_ptr) {
749 0 : uint32 sum = 0u;
750 : int x;
751 0 : assert(iboxwidth > 0);
752 0 : for (x = 0; x < iboxwidth; ++x) {
753 0 : sum += src_ptr[x];
754 : }
755 0 : return sum;
756 : }
757 :
758 0 : static void ScaleAddCols2_C(int dst_width,
759 : int boxheight,
760 : int x,
761 : int dx,
762 : const uint16* src_ptr,
763 : uint8* dst_ptr) {
764 : int i;
765 : int scaletbl[2];
766 0 : int minboxwidth = dx >> 16;
767 : int boxwidth;
768 0 : scaletbl[0] = 65536 / (MIN1(minboxwidth) * boxheight);
769 0 : scaletbl[1] = 65536 / (MIN1(minboxwidth + 1) * boxheight);
770 0 : for (i = 0; i < dst_width; ++i) {
771 0 : int ix = x >> 16;
772 0 : x += dx;
773 0 : boxwidth = MIN1((x >> 16) - ix);
774 0 : *dst_ptr++ =
775 0 : SumPixels(boxwidth, src_ptr + ix) * scaletbl[boxwidth - minboxwidth] >>
776 0 : 16;
777 : }
778 0 : }
779 :
780 0 : static void ScaleAddCols2_16_C(int dst_width,
781 : int boxheight,
782 : int x,
783 : int dx,
784 : const uint32* src_ptr,
785 : uint16* dst_ptr) {
786 : int i;
787 : int scaletbl[2];
788 0 : int minboxwidth = dx >> 16;
789 : int boxwidth;
790 0 : scaletbl[0] = 65536 / (MIN1(minboxwidth) * boxheight);
791 0 : scaletbl[1] = 65536 / (MIN1(minboxwidth + 1) * boxheight);
792 0 : for (i = 0; i < dst_width; ++i) {
793 0 : int ix = x >> 16;
794 0 : x += dx;
795 0 : boxwidth = MIN1((x >> 16) - ix);
796 0 : *dst_ptr++ = SumPixels_16(boxwidth, src_ptr + ix) *
797 0 : scaletbl[boxwidth - minboxwidth] >>
798 0 : 16;
799 : }
800 0 : }
801 :
802 0 : static void ScaleAddCols0_C(int dst_width,
803 : int boxheight,
804 : int x,
805 : int,
806 : const uint16* src_ptr,
807 : uint8* dst_ptr) {
808 0 : int scaleval = 65536 / boxheight;
809 : int i;
810 0 : src_ptr += (x >> 16);
811 0 : for (i = 0; i < dst_width; ++i) {
812 0 : *dst_ptr++ = src_ptr[i] * scaleval >> 16;
813 : }
814 0 : }
815 :
816 0 : static void ScaleAddCols1_C(int dst_width,
817 : int boxheight,
818 : int x,
819 : int dx,
820 : const uint16* src_ptr,
821 : uint8* dst_ptr) {
822 0 : int boxwidth = MIN1(dx >> 16);
823 0 : int scaleval = 65536 / (boxwidth * boxheight);
824 : int i;
825 0 : x >>= 16;
826 0 : for (i = 0; i < dst_width; ++i) {
827 0 : *dst_ptr++ = SumPixels(boxwidth, src_ptr + x) * scaleval >> 16;
828 0 : x += boxwidth;
829 : }
830 0 : }
831 :
832 0 : static void ScaleAddCols1_16_C(int dst_width,
833 : int boxheight,
834 : int x,
835 : int dx,
836 : const uint32* src_ptr,
837 : uint16* dst_ptr) {
838 0 : int boxwidth = MIN1(dx >> 16);
839 0 : int scaleval = 65536 / (boxwidth * boxheight);
840 : int i;
841 0 : for (i = 0; i < dst_width; ++i) {
842 0 : *dst_ptr++ = SumPixels_16(boxwidth, src_ptr + x) * scaleval >> 16;
843 0 : x += boxwidth;
844 : }
845 0 : }
846 :
847 : // Scale plane down to any dimensions, with interpolation.
848 : // (boxfilter).
849 : //
850 : // Same method as SimpleScale, which is fixed point, outputting
851 : // one pixel of destination using fixed point (16.16) to step
852 : // through source, sampling a box of pixel with simple
853 : // averaging.
854 0 : static void ScalePlaneBox(int src_width,
855 : int src_height,
856 : int dst_width,
857 : int dst_height,
858 : int src_stride,
859 : int dst_stride,
860 : const uint8* src_ptr,
861 : uint8* dst_ptr) {
862 : int j, k;
863 : // Initial source x/y coordinate and step values as 16.16 fixed point.
864 0 : int x = 0;
865 0 : int y = 0;
866 0 : int dx = 0;
867 0 : int dy = 0;
868 0 : const int max_y = (src_height << 16);
869 : ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterBox, &x, &y,
870 0 : &dx, &dy);
871 0 : src_width = Abs(src_width);
872 : {
873 : // Allocate a row buffer of uint16.
874 0 : align_buffer_64(row16, src_width * 2);
875 : void (*ScaleAddCols)(int dst_width, int boxheight, int x, int dx,
876 : const uint16* src_ptr, uint8* dst_ptr) =
877 0 : (dx & 0xffff) ? ScaleAddCols2_C
878 0 : : ((dx != 0x10000) ? ScaleAddCols1_C : ScaleAddCols0_C);
879 : void (*ScaleAddRow)(const uint8* src_ptr, uint16* dst_ptr, int src_width) =
880 0 : ScaleAddRow_C;
881 : #if defined(HAS_SCALEADDROW_SSE2)
882 0 : if (TestCpuFlag(kCpuHasSSE2)) {
883 0 : ScaleAddRow = ScaleAddRow_Any_SSE2;
884 0 : if (IS_ALIGNED(src_width, 16)) {
885 0 : ScaleAddRow = ScaleAddRow_SSE2;
886 : }
887 : }
888 : #endif
889 : #if defined(HAS_SCALEADDROW_AVX2)
890 0 : if (TestCpuFlag(kCpuHasAVX2)) {
891 0 : ScaleAddRow = ScaleAddRow_Any_AVX2;
892 0 : if (IS_ALIGNED(src_width, 32)) {
893 0 : ScaleAddRow = ScaleAddRow_AVX2;
894 : }
895 : }
896 : #endif
897 : #if defined(HAS_SCALEADDROW_NEON)
898 : if (TestCpuFlag(kCpuHasNEON)) {
899 : ScaleAddRow = ScaleAddRow_Any_NEON;
900 : if (IS_ALIGNED(src_width, 16)) {
901 : ScaleAddRow = ScaleAddRow_NEON;
902 : }
903 : }
904 : #endif
905 : #if defined(HAS_SCALEADDROW_MSA)
906 : if (TestCpuFlag(kCpuHasMSA)) {
907 : ScaleAddRow = ScaleAddRow_Any_MSA;
908 : if (IS_ALIGNED(src_width, 16)) {
909 : ScaleAddRow = ScaleAddRow_MSA;
910 : }
911 : }
912 : #endif
913 : #if defined(HAS_SCALEADDROW_DSPR2)
914 : if (TestCpuFlag(kCpuHasDSPR2)) {
915 : ScaleAddRow = ScaleAddRow_Any_DSPR2;
916 : if (IS_ALIGNED(src_width, 16)) {
917 : ScaleAddRow = ScaleAddRow_DSPR2;
918 : }
919 : }
920 : #endif
921 :
922 0 : for (j = 0; j < dst_height; ++j) {
923 : int boxheight;
924 0 : int iy = y >> 16;
925 0 : const uint8* src = src_ptr + iy * src_stride;
926 0 : y += dy;
927 0 : if (y > max_y) {
928 0 : y = max_y;
929 : }
930 0 : boxheight = MIN1((y >> 16) - iy);
931 0 : memset(row16, 0, src_width * 2);
932 0 : for (k = 0; k < boxheight; ++k) {
933 0 : ScaleAddRow(src, (uint16*)(row16), src_width);
934 0 : src += src_stride;
935 : }
936 0 : ScaleAddCols(dst_width, boxheight, x, dx, (uint16*)(row16), dst_ptr);
937 0 : dst_ptr += dst_stride;
938 : }
939 0 : free_aligned_buffer_64(row16);
940 : }
941 0 : }
942 :
943 0 : static void ScalePlaneBox_16(int src_width,
944 : int src_height,
945 : int dst_width,
946 : int dst_height,
947 : int src_stride,
948 : int dst_stride,
949 : const uint16* src_ptr,
950 : uint16* dst_ptr) {
951 : int j, k;
952 : // Initial source x/y coordinate and step values as 16.16 fixed point.
953 0 : int x = 0;
954 0 : int y = 0;
955 0 : int dx = 0;
956 0 : int dy = 0;
957 0 : const int max_y = (src_height << 16);
958 : ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterBox, &x, &y,
959 0 : &dx, &dy);
960 0 : src_width = Abs(src_width);
961 : {
962 : // Allocate a row buffer of uint32.
963 0 : align_buffer_64(row32, src_width * 4);
964 : void (*ScaleAddCols)(int dst_width, int boxheight, int x, int dx,
965 : const uint32* src_ptr, uint16* dst_ptr) =
966 0 : (dx & 0xffff) ? ScaleAddCols2_16_C : ScaleAddCols1_16_C;
967 : void (*ScaleAddRow)(const uint16* src_ptr, uint32* dst_ptr, int src_width) =
968 0 : ScaleAddRow_16_C;
969 :
970 : #if defined(HAS_SCALEADDROW_16_SSE2)
971 : if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(src_width, 16)) {
972 : ScaleAddRow = ScaleAddRow_16_SSE2;
973 : }
974 : #endif
975 :
976 0 : for (j = 0; j < dst_height; ++j) {
977 : int boxheight;
978 0 : int iy = y >> 16;
979 0 : const uint16* src = src_ptr + iy * src_stride;
980 0 : y += dy;
981 0 : if (y > max_y) {
982 0 : y = max_y;
983 : }
984 0 : boxheight = MIN1((y >> 16) - iy);
985 0 : memset(row32, 0, src_width * 4);
986 0 : for (k = 0; k < boxheight; ++k) {
987 0 : ScaleAddRow(src, (uint32*)(row32), src_width);
988 0 : src += src_stride;
989 : }
990 0 : ScaleAddCols(dst_width, boxheight, x, dx, (uint32*)(row32), dst_ptr);
991 0 : dst_ptr += dst_stride;
992 : }
993 0 : free_aligned_buffer_64(row32);
994 : }
995 0 : }
996 :
997 : // Scale plane down with bilinear interpolation.
998 0 : void ScalePlaneBilinearDown(int src_width,
999 : int src_height,
1000 : int dst_width,
1001 : int dst_height,
1002 : int src_stride,
1003 : int dst_stride,
1004 : const uint8* src_ptr,
1005 : uint8* dst_ptr,
1006 : enum FilterMode filtering) {
1007 : // Initial source x/y coordinate and step values as 16.16 fixed point.
1008 0 : int x = 0;
1009 0 : int y = 0;
1010 0 : int dx = 0;
1011 0 : int dy = 0;
1012 : // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear.
1013 : // Allocate a row buffer.
1014 0 : align_buffer_64(row, src_width);
1015 :
1016 0 : const int max_y = (src_height - 1) << 16;
1017 : int j;
1018 : void (*ScaleFilterCols)(uint8 * dst_ptr, const uint8* src_ptr, int dst_width,
1019 : int x, int dx) =
1020 0 : (src_width >= 32768) ? ScaleFilterCols64_C : ScaleFilterCols_C;
1021 : void (*InterpolateRow)(uint8 * dst_ptr, const uint8* src_ptr,
1022 : ptrdiff_t src_stride, int dst_width,
1023 0 : int source_y_fraction) = InterpolateRow_C;
1024 : ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
1025 0 : &dx, &dy);
1026 0 : src_width = Abs(src_width);
1027 :
1028 : #if defined(HAS_INTERPOLATEROW_SSSE3)
1029 0 : if (TestCpuFlag(kCpuHasSSSE3)) {
1030 0 : InterpolateRow = InterpolateRow_Any_SSSE3;
1031 0 : if (IS_ALIGNED(src_width, 16)) {
1032 0 : InterpolateRow = InterpolateRow_SSSE3;
1033 : }
1034 : }
1035 : #endif
1036 : #if defined(HAS_INTERPOLATEROW_AVX2)
1037 0 : if (TestCpuFlag(kCpuHasAVX2)) {
1038 0 : InterpolateRow = InterpolateRow_Any_AVX2;
1039 0 : if (IS_ALIGNED(src_width, 32)) {
1040 0 : InterpolateRow = InterpolateRow_AVX2;
1041 : }
1042 : }
1043 : #endif
1044 : #if defined(HAS_INTERPOLATEROW_NEON)
1045 : if (TestCpuFlag(kCpuHasNEON)) {
1046 : InterpolateRow = InterpolateRow_Any_NEON;
1047 : if (IS_ALIGNED(src_width, 16)) {
1048 : InterpolateRow = InterpolateRow_NEON;
1049 : }
1050 : }
1051 : #endif
1052 : #if defined(HAS_INTERPOLATEROW_DSPR2)
1053 : if (TestCpuFlag(kCpuHasDSPR2)) {
1054 : InterpolateRow = InterpolateRow_Any_DSPR2;
1055 : if (IS_ALIGNED(src_width, 4)) {
1056 : InterpolateRow = InterpolateRow_DSPR2;
1057 : }
1058 : }
1059 : #endif
1060 :
1061 : #if defined(HAS_SCALEFILTERCOLS_SSSE3)
1062 0 : if (TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
1063 0 : ScaleFilterCols = ScaleFilterCols_SSSE3;
1064 : }
1065 : #endif
1066 : #if defined(HAS_SCALEFILTERCOLS_NEON)
1067 : if (TestCpuFlag(kCpuHasNEON) && src_width < 32768) {
1068 : ScaleFilterCols = ScaleFilterCols_Any_NEON;
1069 : if (IS_ALIGNED(dst_width, 8)) {
1070 : ScaleFilterCols = ScaleFilterCols_NEON;
1071 : }
1072 : }
1073 : #endif
1074 0 : if (y > max_y) {
1075 0 : y = max_y;
1076 : }
1077 :
1078 0 : for (j = 0; j < dst_height; ++j) {
1079 0 : int yi = y >> 16;
1080 0 : const uint8* src = src_ptr + yi * src_stride;
1081 0 : if (filtering == kFilterLinear) {
1082 0 : ScaleFilterCols(dst_ptr, src, dst_width, x, dx);
1083 : } else {
1084 0 : int yf = (y >> 8) & 255;
1085 0 : InterpolateRow(row, src, src_stride, src_width, yf);
1086 0 : ScaleFilterCols(dst_ptr, row, dst_width, x, dx);
1087 : }
1088 0 : dst_ptr += dst_stride;
1089 0 : y += dy;
1090 0 : if (y > max_y) {
1091 0 : y = max_y;
1092 : }
1093 : }
1094 0 : free_aligned_buffer_64(row);
1095 0 : }
1096 :
1097 0 : void ScalePlaneBilinearDown_16(int src_width,
1098 : int src_height,
1099 : int dst_width,
1100 : int dst_height,
1101 : int src_stride,
1102 : int dst_stride,
1103 : const uint16* src_ptr,
1104 : uint16* dst_ptr,
1105 : enum FilterMode filtering) {
1106 : // Initial source x/y coordinate and step values as 16.16 fixed point.
1107 0 : int x = 0;
1108 0 : int y = 0;
1109 0 : int dx = 0;
1110 0 : int dy = 0;
1111 : // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear.
1112 : // Allocate a row buffer.
1113 0 : align_buffer_64(row, src_width * 2);
1114 :
1115 0 : const int max_y = (src_height - 1) << 16;
1116 : int j;
1117 : void (*ScaleFilterCols)(uint16 * dst_ptr, const uint16* src_ptr,
1118 : int dst_width, int x, int dx) =
1119 0 : (src_width >= 32768) ? ScaleFilterCols64_16_C : ScaleFilterCols_16_C;
1120 : void (*InterpolateRow)(uint16 * dst_ptr, const uint16* src_ptr,
1121 : ptrdiff_t src_stride, int dst_width,
1122 0 : int source_y_fraction) = InterpolateRow_16_C;
1123 : ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
1124 0 : &dx, &dy);
1125 0 : src_width = Abs(src_width);
1126 :
1127 : #if defined(HAS_INTERPOLATEROW_16_SSE2)
1128 : if (TestCpuFlag(kCpuHasSSE2)) {
1129 : InterpolateRow = InterpolateRow_Any_16_SSE2;
1130 : if (IS_ALIGNED(src_width, 16)) {
1131 : InterpolateRow = InterpolateRow_16_SSE2;
1132 : }
1133 : }
1134 : #endif
1135 : #if defined(HAS_INTERPOLATEROW_16_SSSE3)
1136 : if (TestCpuFlag(kCpuHasSSSE3)) {
1137 : InterpolateRow = InterpolateRow_Any_16_SSSE3;
1138 : if (IS_ALIGNED(src_width, 16)) {
1139 : InterpolateRow = InterpolateRow_16_SSSE3;
1140 : }
1141 : }
1142 : #endif
1143 : #if defined(HAS_INTERPOLATEROW_16_AVX2)
1144 : if (TestCpuFlag(kCpuHasAVX2)) {
1145 : InterpolateRow = InterpolateRow_Any_16_AVX2;
1146 : if (IS_ALIGNED(src_width, 32)) {
1147 : InterpolateRow = InterpolateRow_16_AVX2;
1148 : }
1149 : }
1150 : #endif
1151 : #if defined(HAS_INTERPOLATEROW_16_NEON)
1152 : if (TestCpuFlag(kCpuHasNEON)) {
1153 : InterpolateRow = InterpolateRow_Any_16_NEON;
1154 : if (IS_ALIGNED(src_width, 16)) {
1155 : InterpolateRow = InterpolateRow_16_NEON;
1156 : }
1157 : }
1158 : #endif
1159 : #if defined(HAS_INTERPOLATEROW_16_DSPR2)
1160 : if (TestCpuFlag(kCpuHasDSPR2)) {
1161 : InterpolateRow = InterpolateRow_Any_16_DSPR2;
1162 : if (IS_ALIGNED(src_width, 4)) {
1163 : InterpolateRow = InterpolateRow_16_DSPR2;
1164 : }
1165 : }
1166 : #endif
1167 :
1168 : #if defined(HAS_SCALEFILTERCOLS_16_SSSE3)
1169 : if (TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
1170 : ScaleFilterCols = ScaleFilterCols_16_SSSE3;
1171 : }
1172 : #endif
1173 0 : if (y > max_y) {
1174 0 : y = max_y;
1175 : }
1176 :
1177 0 : for (j = 0; j < dst_height; ++j) {
1178 0 : int yi = y >> 16;
1179 0 : const uint16* src = src_ptr + yi * src_stride;
1180 0 : if (filtering == kFilterLinear) {
1181 0 : ScaleFilterCols(dst_ptr, src, dst_width, x, dx);
1182 : } else {
1183 0 : int yf = (y >> 8) & 255;
1184 0 : InterpolateRow((uint16*)row, src, src_stride, src_width, yf);
1185 0 : ScaleFilterCols(dst_ptr, (uint16*)row, dst_width, x, dx);
1186 : }
1187 0 : dst_ptr += dst_stride;
1188 0 : y += dy;
1189 0 : if (y > max_y) {
1190 0 : y = max_y;
1191 : }
1192 : }
1193 0 : free_aligned_buffer_64(row);
1194 0 : }
1195 :
1196 : // Scale up down with bilinear interpolation.
1197 0 : void ScalePlaneBilinearUp(int src_width,
1198 : int src_height,
1199 : int dst_width,
1200 : int dst_height,
1201 : int src_stride,
1202 : int dst_stride,
1203 : const uint8* src_ptr,
1204 : uint8* dst_ptr,
1205 : enum FilterMode filtering) {
1206 : int j;
1207 : // Initial source x/y coordinate and step values as 16.16 fixed point.
1208 0 : int x = 0;
1209 0 : int y = 0;
1210 0 : int dx = 0;
1211 0 : int dy = 0;
1212 0 : const int max_y = (src_height - 1) << 16;
1213 : void (*InterpolateRow)(uint8 * dst_ptr, const uint8* src_ptr,
1214 : ptrdiff_t src_stride, int dst_width,
1215 0 : int source_y_fraction) = InterpolateRow_C;
1216 : void (*ScaleFilterCols)(uint8 * dst_ptr, const uint8* src_ptr, int dst_width,
1217 : int x, int dx) =
1218 0 : filtering ? ScaleFilterCols_C : ScaleCols_C;
1219 : ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
1220 0 : &dx, &dy);
1221 0 : src_width = Abs(src_width);
1222 :
1223 : #if defined(HAS_INTERPOLATEROW_SSSE3)
1224 0 : if (TestCpuFlag(kCpuHasSSSE3)) {
1225 0 : InterpolateRow = InterpolateRow_Any_SSSE3;
1226 0 : if (IS_ALIGNED(dst_width, 16)) {
1227 0 : InterpolateRow = InterpolateRow_SSSE3;
1228 : }
1229 : }
1230 : #endif
1231 : #if defined(HAS_INTERPOLATEROW_AVX2)
1232 0 : if (TestCpuFlag(kCpuHasAVX2)) {
1233 0 : InterpolateRow = InterpolateRow_Any_AVX2;
1234 0 : if (IS_ALIGNED(dst_width, 32)) {
1235 0 : InterpolateRow = InterpolateRow_AVX2;
1236 : }
1237 : }
1238 : #endif
1239 : #if defined(HAS_INTERPOLATEROW_NEON)
1240 : if (TestCpuFlag(kCpuHasNEON)) {
1241 : InterpolateRow = InterpolateRow_Any_NEON;
1242 : if (IS_ALIGNED(dst_width, 16)) {
1243 : InterpolateRow = InterpolateRow_NEON;
1244 : }
1245 : }
1246 : #endif
1247 : #if defined(HAS_INTERPOLATEROW_DSPR2)
1248 : if (TestCpuFlag(kCpuHasDSPR2)) {
1249 : InterpolateRow = InterpolateRow_Any_DSPR2;
1250 : if (IS_ALIGNED(dst_width, 4)) {
1251 : InterpolateRow = InterpolateRow_DSPR2;
1252 : }
1253 : }
1254 : #endif
1255 :
1256 0 : if (filtering && src_width >= 32768) {
1257 0 : ScaleFilterCols = ScaleFilterCols64_C;
1258 : }
1259 : #if defined(HAS_SCALEFILTERCOLS_SSSE3)
1260 0 : if (filtering && TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
1261 0 : ScaleFilterCols = ScaleFilterCols_SSSE3;
1262 : }
1263 : #endif
1264 : #if defined(HAS_SCALEFILTERCOLS_NEON)
1265 : if (filtering && TestCpuFlag(kCpuHasNEON) && src_width < 32768) {
1266 : ScaleFilterCols = ScaleFilterCols_Any_NEON;
1267 : if (IS_ALIGNED(dst_width, 8)) {
1268 : ScaleFilterCols = ScaleFilterCols_NEON;
1269 : }
1270 : }
1271 : #endif
1272 0 : if (!filtering && src_width * 2 == dst_width && x < 0x8000) {
1273 0 : ScaleFilterCols = ScaleColsUp2_C;
1274 : #if defined(HAS_SCALECOLS_SSE2)
1275 : if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
1276 : ScaleFilterCols = ScaleColsUp2_SSE2;
1277 : }
1278 : #endif
1279 : }
1280 :
1281 0 : if (y > max_y) {
1282 0 : y = max_y;
1283 : }
1284 : {
1285 0 : int yi = y >> 16;
1286 0 : const uint8* src = src_ptr + yi * src_stride;
1287 :
1288 : // Allocate 2 row buffers.
1289 0 : const int kRowSize = (dst_width + 31) & ~31;
1290 0 : align_buffer_64(row, kRowSize * 2);
1291 :
1292 0 : uint8* rowptr = row;
1293 0 : int rowstride = kRowSize;
1294 0 : int lasty = yi;
1295 :
1296 0 : ScaleFilterCols(rowptr, src, dst_width, x, dx);
1297 0 : if (src_height > 1) {
1298 0 : src += src_stride;
1299 : }
1300 0 : ScaleFilterCols(rowptr + rowstride, src, dst_width, x, dx);
1301 0 : src += src_stride;
1302 :
1303 0 : for (j = 0; j < dst_height; ++j) {
1304 0 : yi = y >> 16;
1305 0 : if (yi != lasty) {
1306 0 : if (y > max_y) {
1307 0 : y = max_y;
1308 0 : yi = y >> 16;
1309 0 : src = src_ptr + yi * src_stride;
1310 : }
1311 0 : if (yi != lasty) {
1312 0 : ScaleFilterCols(rowptr, src, dst_width, x, dx);
1313 0 : rowptr += rowstride;
1314 0 : rowstride = -rowstride;
1315 0 : lasty = yi;
1316 0 : src += src_stride;
1317 : }
1318 : }
1319 0 : if (filtering == kFilterLinear) {
1320 0 : InterpolateRow(dst_ptr, rowptr, 0, dst_width, 0);
1321 : } else {
1322 0 : int yf = (y >> 8) & 255;
1323 0 : InterpolateRow(dst_ptr, rowptr, rowstride, dst_width, yf);
1324 : }
1325 0 : dst_ptr += dst_stride;
1326 0 : y += dy;
1327 : }
1328 0 : free_aligned_buffer_64(row);
1329 : }
1330 0 : }
1331 :
1332 0 : void ScalePlaneBilinearUp_16(int src_width,
1333 : int src_height,
1334 : int dst_width,
1335 : int dst_height,
1336 : int src_stride,
1337 : int dst_stride,
1338 : const uint16* src_ptr,
1339 : uint16* dst_ptr,
1340 : enum FilterMode filtering) {
1341 : int j;
1342 : // Initial source x/y coordinate and step values as 16.16 fixed point.
1343 0 : int x = 0;
1344 0 : int y = 0;
1345 0 : int dx = 0;
1346 0 : int dy = 0;
1347 0 : const int max_y = (src_height - 1) << 16;
1348 : void (*InterpolateRow)(uint16 * dst_ptr, const uint16* src_ptr,
1349 : ptrdiff_t src_stride, int dst_width,
1350 0 : int source_y_fraction) = InterpolateRow_16_C;
1351 : void (*ScaleFilterCols)(uint16 * dst_ptr, const uint16* src_ptr,
1352 : int dst_width, int x, int dx) =
1353 0 : filtering ? ScaleFilterCols_16_C : ScaleCols_16_C;
1354 : ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
1355 0 : &dx, &dy);
1356 0 : src_width = Abs(src_width);
1357 :
1358 : #if defined(HAS_INTERPOLATEROW_16_SSE2)
1359 : if (TestCpuFlag(kCpuHasSSE2)) {
1360 : InterpolateRow = InterpolateRow_Any_16_SSE2;
1361 : if (IS_ALIGNED(dst_width, 16)) {
1362 : InterpolateRow = InterpolateRow_16_SSE2;
1363 : }
1364 : }
1365 : #endif
1366 : #if defined(HAS_INTERPOLATEROW_16_SSSE3)
1367 : if (TestCpuFlag(kCpuHasSSSE3)) {
1368 : InterpolateRow = InterpolateRow_Any_16_SSSE3;
1369 : if (IS_ALIGNED(dst_width, 16)) {
1370 : InterpolateRow = InterpolateRow_16_SSSE3;
1371 : }
1372 : }
1373 : #endif
1374 : #if defined(HAS_INTERPOLATEROW_16_AVX2)
1375 : if (TestCpuFlag(kCpuHasAVX2)) {
1376 : InterpolateRow = InterpolateRow_Any_16_AVX2;
1377 : if (IS_ALIGNED(dst_width, 32)) {
1378 : InterpolateRow = InterpolateRow_16_AVX2;
1379 : }
1380 : }
1381 : #endif
1382 : #if defined(HAS_INTERPOLATEROW_16_NEON)
1383 : if (TestCpuFlag(kCpuHasNEON)) {
1384 : InterpolateRow = InterpolateRow_Any_16_NEON;
1385 : if (IS_ALIGNED(dst_width, 16)) {
1386 : InterpolateRow = InterpolateRow_16_NEON;
1387 : }
1388 : }
1389 : #endif
1390 : #if defined(HAS_INTERPOLATEROW_16_DSPR2)
1391 : if (TestCpuFlag(kCpuHasDSPR2)) {
1392 : InterpolateRow = InterpolateRow_Any_16_DSPR2;
1393 : if (IS_ALIGNED(dst_width, 4)) {
1394 : InterpolateRow = InterpolateRow_16_DSPR2;
1395 : }
1396 : }
1397 : #endif
1398 :
1399 0 : if (filtering && src_width >= 32768) {
1400 0 : ScaleFilterCols = ScaleFilterCols64_16_C;
1401 : }
1402 : #if defined(HAS_SCALEFILTERCOLS_16_SSSE3)
1403 : if (filtering && TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
1404 : ScaleFilterCols = ScaleFilterCols_16_SSSE3;
1405 : }
1406 : #endif
1407 0 : if (!filtering && src_width * 2 == dst_width && x < 0x8000) {
1408 0 : ScaleFilterCols = ScaleColsUp2_16_C;
1409 : #if defined(HAS_SCALECOLS_16_SSE2)
1410 : if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
1411 : ScaleFilterCols = ScaleColsUp2_16_SSE2;
1412 : }
1413 : #endif
1414 : }
1415 :
1416 0 : if (y > max_y) {
1417 0 : y = max_y;
1418 : }
1419 : {
1420 0 : int yi = y >> 16;
1421 0 : const uint16* src = src_ptr + yi * src_stride;
1422 :
1423 : // Allocate 2 row buffers.
1424 0 : const int kRowSize = (dst_width + 31) & ~31;
1425 0 : align_buffer_64(row, kRowSize * 4);
1426 :
1427 0 : uint16* rowptr = (uint16*)row;
1428 0 : int rowstride = kRowSize;
1429 0 : int lasty = yi;
1430 :
1431 0 : ScaleFilterCols(rowptr, src, dst_width, x, dx);
1432 0 : if (src_height > 1) {
1433 0 : src += src_stride;
1434 : }
1435 0 : ScaleFilterCols(rowptr + rowstride, src, dst_width, x, dx);
1436 0 : src += src_stride;
1437 :
1438 0 : for (j = 0; j < dst_height; ++j) {
1439 0 : yi = y >> 16;
1440 0 : if (yi != lasty) {
1441 0 : if (y > max_y) {
1442 0 : y = max_y;
1443 0 : yi = y >> 16;
1444 0 : src = src_ptr + yi * src_stride;
1445 : }
1446 0 : if (yi != lasty) {
1447 0 : ScaleFilterCols(rowptr, src, dst_width, x, dx);
1448 0 : rowptr += rowstride;
1449 0 : rowstride = -rowstride;
1450 0 : lasty = yi;
1451 0 : src += src_stride;
1452 : }
1453 : }
1454 0 : if (filtering == kFilterLinear) {
1455 0 : InterpolateRow(dst_ptr, rowptr, 0, dst_width, 0);
1456 : } else {
1457 0 : int yf = (y >> 8) & 255;
1458 0 : InterpolateRow(dst_ptr, rowptr, rowstride, dst_width, yf);
1459 : }
1460 0 : dst_ptr += dst_stride;
1461 0 : y += dy;
1462 : }
1463 0 : free_aligned_buffer_64(row);
1464 : }
1465 0 : }
1466 :
1467 : // Scale Plane to/from any dimensions, without interpolation.
1468 : // Fixed point math is used for performance: The upper 16 bits
1469 : // of x and dx is the integer part of the source position and
1470 : // the lower 16 bits are the fixed decimal part.
1471 :
1472 0 : static void ScalePlaneSimple(int src_width,
1473 : int src_height,
1474 : int dst_width,
1475 : int dst_height,
1476 : int src_stride,
1477 : int dst_stride,
1478 : const uint8* src_ptr,
1479 : uint8* dst_ptr) {
1480 : int i;
1481 : void (*ScaleCols)(uint8 * dst_ptr, const uint8* src_ptr, int dst_width, int x,
1482 0 : int dx) = ScaleCols_C;
1483 : // Initial source x/y coordinate and step values as 16.16 fixed point.
1484 0 : int x = 0;
1485 0 : int y = 0;
1486 0 : int dx = 0;
1487 0 : int dy = 0;
1488 : ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterNone, &x, &y,
1489 0 : &dx, &dy);
1490 0 : src_width = Abs(src_width);
1491 :
1492 0 : if (src_width * 2 == dst_width && x < 0x8000) {
1493 0 : ScaleCols = ScaleColsUp2_C;
1494 : #if defined(HAS_SCALECOLS_SSE2)
1495 : if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
1496 : ScaleCols = ScaleColsUp2_SSE2;
1497 : }
1498 : #endif
1499 : }
1500 :
1501 0 : for (i = 0; i < dst_height; ++i) {
1502 0 : ScaleCols(dst_ptr, src_ptr + (y >> 16) * src_stride, dst_width, x, dx);
1503 0 : dst_ptr += dst_stride;
1504 0 : y += dy;
1505 : }
1506 0 : }
1507 :
1508 0 : static void ScalePlaneSimple_16(int src_width,
1509 : int src_height,
1510 : int dst_width,
1511 : int dst_height,
1512 : int src_stride,
1513 : int dst_stride,
1514 : const uint16* src_ptr,
1515 : uint16* dst_ptr) {
1516 : int i;
1517 : void (*ScaleCols)(uint16 * dst_ptr, const uint16* src_ptr, int dst_width,
1518 0 : int x, int dx) = ScaleCols_16_C;
1519 : // Initial source x/y coordinate and step values as 16.16 fixed point.
1520 0 : int x = 0;
1521 0 : int y = 0;
1522 0 : int dx = 0;
1523 0 : int dy = 0;
1524 : ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterNone, &x, &y,
1525 0 : &dx, &dy);
1526 0 : src_width = Abs(src_width);
1527 :
1528 0 : if (src_width * 2 == dst_width && x < 0x8000) {
1529 0 : ScaleCols = ScaleColsUp2_16_C;
1530 : #if defined(HAS_SCALECOLS_16_SSE2)
1531 : if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
1532 : ScaleCols = ScaleColsUp2_16_SSE2;
1533 : }
1534 : #endif
1535 : }
1536 :
1537 0 : for (i = 0; i < dst_height; ++i) {
1538 0 : ScaleCols(dst_ptr, src_ptr + (y >> 16) * src_stride, dst_width, x, dx);
1539 0 : dst_ptr += dst_stride;
1540 0 : y += dy;
1541 : }
1542 0 : }
1543 :
1544 : // Scale a plane.
1545 : // This function dispatches to a specialized scaler based on scale factor.
1546 :
1547 : LIBYUV_API
1548 0 : void ScalePlane(const uint8* src,
1549 : int src_stride,
1550 : int src_width,
1551 : int src_height,
1552 : uint8* dst,
1553 : int dst_stride,
1554 : int dst_width,
1555 : int dst_height,
1556 : enum FilterMode filtering) {
1557 : // Simplify filtering when possible.
1558 : filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height,
1559 0 : filtering);
1560 :
1561 : // Negative height means invert the image.
1562 0 : if (src_height < 0) {
1563 0 : src_height = -src_height;
1564 0 : src = src + (src_height - 1) * src_stride;
1565 0 : src_stride = -src_stride;
1566 : }
1567 :
1568 : // Use specialized scales to improve performance for common resolutions.
1569 : // For example, all the 1/2 scalings will use ScalePlaneDown2()
1570 0 : if (dst_width == src_width && dst_height == src_height) {
1571 : // Straight copy.
1572 0 : CopyPlane(src, src_stride, dst, dst_stride, dst_width, dst_height);
1573 0 : return;
1574 : }
1575 0 : if (dst_width == src_width && filtering != kFilterBox) {
1576 0 : int dy = FixedDiv(src_height, dst_height);
1577 : // Arbitrary scale vertically, but unscaled horizontally.
1578 : ScalePlaneVertical(src_height, dst_width, dst_height, src_stride,
1579 0 : dst_stride, src, dst, 0, 0, dy, 1, filtering);
1580 0 : return;
1581 : }
1582 0 : if (dst_width <= Abs(src_width) && dst_height <= src_height) {
1583 : // Scale down.
1584 0 : if (4 * dst_width == 3 * src_width && 4 * dst_height == 3 * src_height) {
1585 : // optimized, 3/4
1586 : ScalePlaneDown34(src_width, src_height, dst_width, dst_height, src_stride,
1587 0 : dst_stride, src, dst, filtering);
1588 0 : return;
1589 : }
1590 0 : if (2 * dst_width == src_width && 2 * dst_height == src_height) {
1591 : // optimized, 1/2
1592 : ScalePlaneDown2(src_width, src_height, dst_width, dst_height, src_stride,
1593 0 : dst_stride, src, dst, filtering);
1594 0 : return;
1595 : }
1596 : // 3/8 rounded up for odd sized chroma height.
1597 0 : if (8 * dst_width == 3 * src_width && 8 * dst_height == 3 * src_height) {
1598 : // optimized, 3/8
1599 : ScalePlaneDown38(src_width, src_height, dst_width, dst_height, src_stride,
1600 0 : dst_stride, src, dst, filtering);
1601 0 : return;
1602 : }
1603 0 : if (4 * dst_width == src_width && 4 * dst_height == src_height &&
1604 0 : (filtering == kFilterBox || filtering == kFilterNone)) {
1605 : // optimized, 1/4
1606 : ScalePlaneDown4(src_width, src_height, dst_width, dst_height, src_stride,
1607 0 : dst_stride, src, dst, filtering);
1608 0 : return;
1609 : }
1610 : }
1611 0 : if (filtering == kFilterBox && dst_height * 2 < src_height) {
1612 : ScalePlaneBox(src_width, src_height, dst_width, dst_height, src_stride,
1613 0 : dst_stride, src, dst);
1614 0 : return;
1615 : }
1616 0 : if (filtering && dst_height > src_height) {
1617 : ScalePlaneBilinearUp(src_width, src_height, dst_width, dst_height,
1618 0 : src_stride, dst_stride, src, dst, filtering);
1619 0 : return;
1620 : }
1621 0 : if (filtering) {
1622 : ScalePlaneBilinearDown(src_width, src_height, dst_width, dst_height,
1623 0 : src_stride, dst_stride, src, dst, filtering);
1624 0 : return;
1625 : }
1626 : ScalePlaneSimple(src_width, src_height, dst_width, dst_height, src_stride,
1627 0 : dst_stride, src, dst);
1628 : }
1629 :
1630 : LIBYUV_API
1631 0 : void ScalePlane_16(const uint16* src,
1632 : int src_stride,
1633 : int src_width,
1634 : int src_height,
1635 : uint16* dst,
1636 : int dst_stride,
1637 : int dst_width,
1638 : int dst_height,
1639 : enum FilterMode filtering) {
1640 : // Simplify filtering when possible.
1641 : filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height,
1642 0 : filtering);
1643 :
1644 : // Negative height means invert the image.
1645 0 : if (src_height < 0) {
1646 0 : src_height = -src_height;
1647 0 : src = src + (src_height - 1) * src_stride;
1648 0 : src_stride = -src_stride;
1649 : }
1650 :
1651 : // Use specialized scales to improve performance for common resolutions.
1652 : // For example, all the 1/2 scalings will use ScalePlaneDown2()
1653 0 : if (dst_width == src_width && dst_height == src_height) {
1654 : // Straight copy.
1655 0 : CopyPlane_16(src, src_stride, dst, dst_stride, dst_width, dst_height);
1656 0 : return;
1657 : }
1658 0 : if (dst_width == src_width) {
1659 0 : int dy = FixedDiv(src_height, dst_height);
1660 : // Arbitrary scale vertically, but unscaled vertically.
1661 : ScalePlaneVertical_16(src_height, dst_width, dst_height, src_stride,
1662 0 : dst_stride, src, dst, 0, 0, dy, 1, filtering);
1663 0 : return;
1664 : }
1665 0 : if (dst_width <= Abs(src_width) && dst_height <= src_height) {
1666 : // Scale down.
1667 0 : if (4 * dst_width == 3 * src_width && 4 * dst_height == 3 * src_height) {
1668 : // optimized, 3/4
1669 : ScalePlaneDown34_16(src_width, src_height, dst_width, dst_height,
1670 0 : src_stride, dst_stride, src, dst, filtering);
1671 0 : return;
1672 : }
1673 0 : if (2 * dst_width == src_width && 2 * dst_height == src_height) {
1674 : // optimized, 1/2
1675 : ScalePlaneDown2_16(src_width, src_height, dst_width, dst_height,
1676 0 : src_stride, dst_stride, src, dst, filtering);
1677 0 : return;
1678 : }
1679 : // 3/8 rounded up for odd sized chroma height.
1680 0 : if (8 * dst_width == 3 * src_width && 8 * dst_height == 3 * src_height) {
1681 : // optimized, 3/8
1682 : ScalePlaneDown38_16(src_width, src_height, dst_width, dst_height,
1683 0 : src_stride, dst_stride, src, dst, filtering);
1684 0 : return;
1685 : }
1686 0 : if (4 * dst_width == src_width && 4 * dst_height == src_height &&
1687 : filtering != kFilterBilinear) {
1688 : // optimized, 1/4
1689 : ScalePlaneDown4_16(src_width, src_height, dst_width, dst_height,
1690 0 : src_stride, dst_stride, src, dst, filtering);
1691 0 : return;
1692 : }
1693 : }
1694 0 : if (filtering == kFilterBox && dst_height * 2 < src_height) {
1695 : ScalePlaneBox_16(src_width, src_height, dst_width, dst_height, src_stride,
1696 0 : dst_stride, src, dst);
1697 0 : return;
1698 : }
1699 0 : if (filtering && dst_height > src_height) {
1700 : ScalePlaneBilinearUp_16(src_width, src_height, dst_width, dst_height,
1701 0 : src_stride, dst_stride, src, dst, filtering);
1702 0 : return;
1703 : }
1704 0 : if (filtering) {
1705 : ScalePlaneBilinearDown_16(src_width, src_height, dst_width, dst_height,
1706 0 : src_stride, dst_stride, src, dst, filtering);
1707 0 : return;
1708 : }
1709 : ScalePlaneSimple_16(src_width, src_height, dst_width, dst_height, src_stride,
1710 0 : dst_stride, src, dst);
1711 : }
1712 :
1713 : // Scale an I420 image.
1714 : // This function in turn calls a scaling function for each plane.
1715 :
1716 : LIBYUV_API
1717 0 : int I420Scale(const uint8* src_y,
1718 : int src_stride_y,
1719 : const uint8* src_u,
1720 : int src_stride_u,
1721 : const uint8* src_v,
1722 : int src_stride_v,
1723 : int src_width,
1724 : int src_height,
1725 : uint8* dst_y,
1726 : int dst_stride_y,
1727 : uint8* dst_u,
1728 : int dst_stride_u,
1729 : uint8* dst_v,
1730 : int dst_stride_v,
1731 : int dst_width,
1732 : int dst_height,
1733 : enum FilterMode filtering) {
1734 0 : int src_halfwidth = SUBSAMPLE(src_width, 1, 1);
1735 0 : int src_halfheight = SUBSAMPLE(src_height, 1, 1);
1736 0 : int dst_halfwidth = SUBSAMPLE(dst_width, 1, 1);
1737 0 : int dst_halfheight = SUBSAMPLE(dst_height, 1, 1);
1738 0 : if (!src_y || !src_u || !src_v || src_width == 0 || src_height == 0 ||
1739 0 : src_width > 32768 || src_height > 32768 || !dst_y || !dst_u || !dst_v ||
1740 0 : dst_width <= 0 || dst_height <= 0) {
1741 0 : return -1;
1742 : }
1743 :
1744 : ScalePlane(src_y, src_stride_y, src_width, src_height, dst_y, dst_stride_y,
1745 0 : dst_width, dst_height, filtering);
1746 : ScalePlane(src_u, src_stride_u, src_halfwidth, src_halfheight, dst_u,
1747 0 : dst_stride_u, dst_halfwidth, dst_halfheight, filtering);
1748 : ScalePlane(src_v, src_stride_v, src_halfwidth, src_halfheight, dst_v,
1749 0 : dst_stride_v, dst_halfwidth, dst_halfheight, filtering);
1750 0 : return 0;
1751 : }
1752 :
1753 : LIBYUV_API
1754 0 : int I420Scale_16(const uint16* src_y,
1755 : int src_stride_y,
1756 : const uint16* src_u,
1757 : int src_stride_u,
1758 : const uint16* src_v,
1759 : int src_stride_v,
1760 : int src_width,
1761 : int src_height,
1762 : uint16* dst_y,
1763 : int dst_stride_y,
1764 : uint16* dst_u,
1765 : int dst_stride_u,
1766 : uint16* dst_v,
1767 : int dst_stride_v,
1768 : int dst_width,
1769 : int dst_height,
1770 : enum FilterMode filtering) {
1771 0 : int src_halfwidth = SUBSAMPLE(src_width, 1, 1);
1772 0 : int src_halfheight = SUBSAMPLE(src_height, 1, 1);
1773 0 : int dst_halfwidth = SUBSAMPLE(dst_width, 1, 1);
1774 0 : int dst_halfheight = SUBSAMPLE(dst_height, 1, 1);
1775 0 : if (!src_y || !src_u || !src_v || src_width == 0 || src_height == 0 ||
1776 0 : src_width > 32768 || src_height > 32768 || !dst_y || !dst_u || !dst_v ||
1777 0 : dst_width <= 0 || dst_height <= 0) {
1778 0 : return -1;
1779 : }
1780 :
1781 : ScalePlane_16(src_y, src_stride_y, src_width, src_height, dst_y, dst_stride_y,
1782 0 : dst_width, dst_height, filtering);
1783 : ScalePlane_16(src_u, src_stride_u, src_halfwidth, src_halfheight, dst_u,
1784 0 : dst_stride_u, dst_halfwidth, dst_halfheight, filtering);
1785 : ScalePlane_16(src_v, src_stride_v, src_halfwidth, src_halfheight, dst_v,
1786 0 : dst_stride_v, dst_halfwidth, dst_halfheight, filtering);
1787 0 : return 0;
1788 : }
1789 :
1790 : // Deprecated api
1791 : LIBYUV_API
1792 0 : int Scale(const uint8* src_y,
1793 : const uint8* src_u,
1794 : const uint8* src_v,
1795 : int src_stride_y,
1796 : int src_stride_u,
1797 : int src_stride_v,
1798 : int src_width,
1799 : int src_height,
1800 : uint8* dst_y,
1801 : uint8* dst_u,
1802 : uint8* dst_v,
1803 : int dst_stride_y,
1804 : int dst_stride_u,
1805 : int dst_stride_v,
1806 : int dst_width,
1807 : int dst_height,
1808 : LIBYUV_BOOL interpolate) {
1809 0 : return I420Scale(src_y, src_stride_y, src_u, src_stride_u, src_v,
1810 : src_stride_v, src_width, src_height, dst_y, dst_stride_y,
1811 : dst_u, dst_stride_u, dst_v, dst_stride_v, dst_width,
1812 0 : dst_height, interpolate ? kFilterBox : kFilterNone);
1813 : }
1814 :
1815 : // Deprecated api
1816 : LIBYUV_API
1817 0 : int ScaleOffset(const uint8* src,
1818 : int src_width,
1819 : int src_height,
1820 : uint8* dst,
1821 : int dst_width,
1822 : int dst_height,
1823 : int dst_yoffset,
1824 : LIBYUV_BOOL interpolate) {
1825 : // Chroma requires offset to multiple of 2.
1826 0 : int dst_yoffset_even = dst_yoffset & ~1;
1827 0 : int src_halfwidth = SUBSAMPLE(src_width, 1, 1);
1828 0 : int src_halfheight = SUBSAMPLE(src_height, 1, 1);
1829 0 : int dst_halfwidth = SUBSAMPLE(dst_width, 1, 1);
1830 0 : int dst_halfheight = SUBSAMPLE(dst_height, 1, 1);
1831 0 : int aheight = dst_height - dst_yoffset_even * 2; // actual output height
1832 0 : const uint8* src_y = src;
1833 0 : const uint8* src_u = src + src_width * src_height;
1834 : const uint8* src_v =
1835 0 : src + src_width * src_height + src_halfwidth * src_halfheight;
1836 0 : uint8* dst_y = dst + dst_yoffset_even * dst_width;
1837 : uint8* dst_u =
1838 0 : dst + dst_width * dst_height + (dst_yoffset_even >> 1) * dst_halfwidth;
1839 0 : uint8* dst_v = dst + dst_width * dst_height + dst_halfwidth * dst_halfheight +
1840 0 : (dst_yoffset_even >> 1) * dst_halfwidth;
1841 0 : if (!src || src_width <= 0 || src_height <= 0 || !dst || dst_width <= 0 ||
1842 0 : dst_height <= 0 || dst_yoffset_even < 0 ||
1843 : dst_yoffset_even >= dst_height) {
1844 0 : return -1;
1845 : }
1846 0 : return I420Scale(src_y, src_width, src_u, src_halfwidth, src_v, src_halfwidth,
1847 : src_width, src_height, dst_y, dst_width, dst_u,
1848 : dst_halfwidth, dst_v, dst_halfwidth, dst_width, aheight,
1849 0 : interpolate ? kFilterBox : kFilterNone);
1850 : }
1851 :
1852 : #ifdef __cplusplus
1853 : } // extern "C"
1854 : } // namespace libyuv
1855 : #endif
|