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 CopyARGB
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 : // ScaleARGB ARGB, 1/2
31 : // This is an optimized version for scaling down a ARGB to 1/2 of
32 : // its original size.
33 0 : static void ScaleARGBDown2(int src_width,
34 : int src_height,
35 : int dst_width,
36 : int dst_height,
37 : int src_stride,
38 : int dst_stride,
39 : const uint8* src_argb,
40 : uint8* dst_argb,
41 : int x,
42 : int dx,
43 : int y,
44 : int dy,
45 : enum FilterMode filtering) {
46 : int j;
47 0 : int row_stride = src_stride * (dy >> 16);
48 : void (*ScaleARGBRowDown2)(const uint8* src_argb, ptrdiff_t src_stride,
49 : uint8* dst_argb, int dst_width) =
50 : filtering == kFilterNone
51 0 : ? ScaleARGBRowDown2_C
52 0 : : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_C
53 0 : : ScaleARGBRowDown2Box_C);
54 : (void)src_width;
55 : (void)src_height;
56 : (void)dx;
57 0 : assert(dx == 65536 * 2); // Test scale factor of 2.
58 0 : assert((dy & 0x1ffff) == 0); // Test vertical scale is multiple of 2.
59 : // Advance to odd row, even column.
60 0 : if (filtering == kFilterBilinear) {
61 0 : src_argb += (y >> 16) * src_stride + (x >> 16) * 4;
62 : } else {
63 0 : src_argb += (y >> 16) * src_stride + ((x >> 16) - 1) * 4;
64 : }
65 :
66 : #if defined(HAS_SCALEARGBROWDOWN2_SSE2)
67 0 : if (TestCpuFlag(kCpuHasSSE2)) {
68 0 : ScaleARGBRowDown2 =
69 : filtering == kFilterNone
70 0 : ? ScaleARGBRowDown2_Any_SSE2
71 0 : : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_Any_SSE2
72 : : ScaleARGBRowDown2Box_Any_SSE2);
73 0 : if (IS_ALIGNED(dst_width, 4)) {
74 0 : ScaleARGBRowDown2 =
75 : filtering == kFilterNone
76 0 : ? ScaleARGBRowDown2_SSE2
77 0 : : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_SSE2
78 : : ScaleARGBRowDown2Box_SSE2);
79 : }
80 : }
81 : #endif
82 : #if defined(HAS_SCALEARGBROWDOWN2_NEON)
83 : if (TestCpuFlag(kCpuHasNEON)) {
84 : ScaleARGBRowDown2 =
85 : filtering == kFilterNone
86 : ? ScaleARGBRowDown2_Any_NEON
87 : : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_Any_NEON
88 : : ScaleARGBRowDown2Box_Any_NEON);
89 : if (IS_ALIGNED(dst_width, 8)) {
90 : ScaleARGBRowDown2 =
91 : filtering == kFilterNone
92 : ? ScaleARGBRowDown2_NEON
93 : : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_NEON
94 : : ScaleARGBRowDown2Box_NEON);
95 : }
96 : }
97 : #endif
98 : #if defined(HAS_SCALEARGBROWDOWN2_MSA)
99 : if (TestCpuFlag(kCpuHasMSA)) {
100 : ScaleARGBRowDown2 =
101 : filtering == kFilterNone
102 : ? ScaleARGBRowDown2_Any_MSA
103 : : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_Any_MSA
104 : : ScaleARGBRowDown2Box_Any_MSA);
105 : if (IS_ALIGNED(dst_width, 4)) {
106 : ScaleARGBRowDown2 =
107 : filtering == kFilterNone
108 : ? ScaleARGBRowDown2_MSA
109 : : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_MSA
110 : : ScaleARGBRowDown2Box_MSA);
111 : }
112 : }
113 : #endif
114 :
115 0 : if (filtering == kFilterLinear) {
116 0 : src_stride = 0;
117 : }
118 0 : for (j = 0; j < dst_height; ++j) {
119 0 : ScaleARGBRowDown2(src_argb, src_stride, dst_argb, dst_width);
120 0 : src_argb += row_stride;
121 0 : dst_argb += dst_stride;
122 : }
123 0 : }
124 :
125 : // ScaleARGB ARGB, 1/4
126 : // This is an optimized version for scaling down a ARGB to 1/4 of
127 : // its original size.
128 0 : static void ScaleARGBDown4Box(int src_width,
129 : int src_height,
130 : int dst_width,
131 : int dst_height,
132 : int src_stride,
133 : int dst_stride,
134 : const uint8* src_argb,
135 : uint8* dst_argb,
136 : int x,
137 : int dx,
138 : int y,
139 : int dy) {
140 : int j;
141 : // Allocate 2 rows of ARGB.
142 0 : const int kRowSize = (dst_width * 2 * 4 + 31) & ~31;
143 0 : align_buffer_64(row, kRowSize * 2);
144 0 : int row_stride = src_stride * (dy >> 16);
145 : void (*ScaleARGBRowDown2)(const uint8* src_argb, ptrdiff_t src_stride,
146 : uint8* dst_argb, int dst_width) =
147 0 : ScaleARGBRowDown2Box_C;
148 : // Advance to odd row, even column.
149 0 : src_argb += (y >> 16) * src_stride + (x >> 16) * 4;
150 : (void)src_width;
151 : (void)src_height;
152 : (void)dx;
153 0 : assert(dx == 65536 * 4); // Test scale factor of 4.
154 0 : assert((dy & 0x3ffff) == 0); // Test vertical scale is multiple of 4.
155 : #if defined(HAS_SCALEARGBROWDOWN2_SSE2)
156 0 : if (TestCpuFlag(kCpuHasSSE2)) {
157 0 : ScaleARGBRowDown2 = ScaleARGBRowDown2Box_Any_SSE2;
158 0 : if (IS_ALIGNED(dst_width, 4)) {
159 0 : ScaleARGBRowDown2 = ScaleARGBRowDown2Box_SSE2;
160 : }
161 : }
162 : #endif
163 : #if defined(HAS_SCALEARGBROWDOWN2_NEON)
164 : if (TestCpuFlag(kCpuHasNEON)) {
165 : ScaleARGBRowDown2 = ScaleARGBRowDown2Box_Any_NEON;
166 : if (IS_ALIGNED(dst_width, 8)) {
167 : ScaleARGBRowDown2 = ScaleARGBRowDown2Box_NEON;
168 : }
169 : }
170 : #endif
171 :
172 0 : for (j = 0; j < dst_height; ++j) {
173 0 : ScaleARGBRowDown2(src_argb, src_stride, row, dst_width * 2);
174 0 : ScaleARGBRowDown2(src_argb + src_stride * 2, src_stride, row + kRowSize,
175 0 : dst_width * 2);
176 0 : ScaleARGBRowDown2(row, kRowSize, dst_argb, dst_width);
177 0 : src_argb += row_stride;
178 0 : dst_argb += dst_stride;
179 : }
180 0 : free_aligned_buffer_64(row);
181 0 : }
182 :
183 : // ScaleARGB ARGB Even
184 : // This is an optimized version for scaling down a ARGB to even
185 : // multiple of its original size.
186 0 : static void ScaleARGBDownEven(int src_width,
187 : int src_height,
188 : int dst_width,
189 : int dst_height,
190 : int src_stride,
191 : int dst_stride,
192 : const uint8* src_argb,
193 : uint8* dst_argb,
194 : int x,
195 : int dx,
196 : int y,
197 : int dy,
198 : enum FilterMode filtering) {
199 : int j;
200 0 : int col_step = dx >> 16;
201 0 : int row_stride = (dy >> 16) * src_stride;
202 : void (*ScaleARGBRowDownEven)(const uint8* src_argb, ptrdiff_t src_stride,
203 : int src_step, uint8* dst_argb, int dst_width) =
204 0 : filtering ? ScaleARGBRowDownEvenBox_C : ScaleARGBRowDownEven_C;
205 : (void)src_width;
206 : (void)src_height;
207 0 : assert(IS_ALIGNED(src_width, 2));
208 0 : assert(IS_ALIGNED(src_height, 2));
209 0 : src_argb += (y >> 16) * src_stride + (x >> 16) * 4;
210 : #if defined(HAS_SCALEARGBROWDOWNEVEN_SSE2)
211 0 : if (TestCpuFlag(kCpuHasSSE2)) {
212 0 : ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenBox_Any_SSE2
213 : : ScaleARGBRowDownEven_Any_SSE2;
214 0 : if (IS_ALIGNED(dst_width, 4)) {
215 0 : ScaleARGBRowDownEven =
216 0 : filtering ? ScaleARGBRowDownEvenBox_SSE2 : ScaleARGBRowDownEven_SSE2;
217 : }
218 : }
219 : #endif
220 : #if defined(HAS_SCALEARGBROWDOWNEVEN_NEON)
221 : if (TestCpuFlag(kCpuHasNEON)) {
222 : ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenBox_Any_NEON
223 : : ScaleARGBRowDownEven_Any_NEON;
224 : if (IS_ALIGNED(dst_width, 4)) {
225 : ScaleARGBRowDownEven =
226 : filtering ? ScaleARGBRowDownEvenBox_NEON : ScaleARGBRowDownEven_NEON;
227 : }
228 : }
229 : #endif
230 : #if defined(HAS_SCALEARGBROWDOWNEVEN_MSA)
231 : if (TestCpuFlag(kCpuHasMSA)) {
232 : ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenBox_Any_MSA
233 : : ScaleARGBRowDownEven_Any_MSA;
234 : if (IS_ALIGNED(dst_width, 4)) {
235 : ScaleARGBRowDownEven =
236 : filtering ? ScaleARGBRowDownEvenBox_MSA : ScaleARGBRowDownEven_MSA;
237 : }
238 : }
239 : #endif
240 :
241 0 : if (filtering == kFilterLinear) {
242 0 : src_stride = 0;
243 : }
244 0 : for (j = 0; j < dst_height; ++j) {
245 0 : ScaleARGBRowDownEven(src_argb, src_stride, col_step, dst_argb, dst_width);
246 0 : src_argb += row_stride;
247 0 : dst_argb += dst_stride;
248 : }
249 0 : }
250 :
251 : // Scale ARGB down with bilinear interpolation.
252 0 : static void ScaleARGBBilinearDown(int src_width,
253 : int src_height,
254 : int dst_width,
255 : int dst_height,
256 : int src_stride,
257 : int dst_stride,
258 : const uint8* src_argb,
259 : uint8* dst_argb,
260 : int x,
261 : int dx,
262 : int y,
263 : int dy,
264 : enum FilterMode filtering) {
265 : int j;
266 : void (*InterpolateRow)(uint8 * dst_argb, const uint8* src_argb,
267 : ptrdiff_t src_stride, int dst_width,
268 0 : int source_y_fraction) = InterpolateRow_C;
269 : void (*ScaleARGBFilterCols)(uint8 * dst_argb, const uint8* src_argb,
270 : int dst_width, int x, int dx) =
271 0 : (src_width >= 32768) ? ScaleARGBFilterCols64_C : ScaleARGBFilterCols_C;
272 0 : int64 xlast = x + (int64)(dst_width - 1) * dx;
273 0 : int64 xl = (dx >= 0) ? x : xlast;
274 0 : int64 xr = (dx >= 0) ? xlast : x;
275 : int clip_src_width;
276 0 : xl = (xl >> 16) & ~3; // Left edge aligned.
277 0 : xr = (xr >> 16) + 1; // Right most pixel used. Bilinear uses 2 pixels.
278 0 : xr = (xr + 1 + 3) & ~3; // 1 beyond 4 pixel aligned right most pixel.
279 0 : if (xr > src_width) {
280 0 : xr = src_width;
281 : }
282 0 : clip_src_width = (int)(xr - xl) * 4; // Width aligned to 4.
283 0 : src_argb += xl * 4;
284 0 : x -= (int)(xl << 16);
285 : #if defined(HAS_INTERPOLATEROW_SSSE3)
286 0 : if (TestCpuFlag(kCpuHasSSSE3)) {
287 0 : InterpolateRow = InterpolateRow_Any_SSSE3;
288 0 : if (IS_ALIGNED(clip_src_width, 16)) {
289 0 : InterpolateRow = InterpolateRow_SSSE3;
290 : }
291 : }
292 : #endif
293 : #if defined(HAS_INTERPOLATEROW_AVX2)
294 0 : if (TestCpuFlag(kCpuHasAVX2)) {
295 0 : InterpolateRow = InterpolateRow_Any_AVX2;
296 0 : if (IS_ALIGNED(clip_src_width, 32)) {
297 0 : InterpolateRow = InterpolateRow_AVX2;
298 : }
299 : }
300 : #endif
301 : #if defined(HAS_INTERPOLATEROW_NEON)
302 : if (TestCpuFlag(kCpuHasNEON)) {
303 : InterpolateRow = InterpolateRow_Any_NEON;
304 : if (IS_ALIGNED(clip_src_width, 16)) {
305 : InterpolateRow = InterpolateRow_NEON;
306 : }
307 : }
308 : #endif
309 : #if defined(HAS_INTERPOLATEROW_DSPR2)
310 : if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(src_argb, 4) &&
311 : IS_ALIGNED(src_stride, 4)) {
312 : InterpolateRow = InterpolateRow_Any_DSPR2;
313 : if (IS_ALIGNED(clip_src_width, 4)) {
314 : InterpolateRow = InterpolateRow_DSPR2;
315 : }
316 : }
317 : #endif
318 : #if defined(HAS_SCALEARGBFILTERCOLS_SSSE3)
319 0 : if (TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
320 0 : ScaleARGBFilterCols = ScaleARGBFilterCols_SSSE3;
321 : }
322 : #endif
323 : #if defined(HAS_SCALEARGBFILTERCOLS_NEON)
324 : if (TestCpuFlag(kCpuHasNEON)) {
325 : ScaleARGBFilterCols = ScaleARGBFilterCols_Any_NEON;
326 : if (IS_ALIGNED(dst_width, 4)) {
327 : ScaleARGBFilterCols = ScaleARGBFilterCols_NEON;
328 : }
329 : }
330 : #endif
331 : // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear.
332 : // Allocate a row of ARGB.
333 : {
334 0 : align_buffer_64(row, clip_src_width * 4);
335 :
336 0 : const int max_y = (src_height - 1) << 16;
337 0 : if (y > max_y) {
338 0 : y = max_y;
339 : }
340 0 : for (j = 0; j < dst_height; ++j) {
341 0 : int yi = y >> 16;
342 0 : const uint8* src = src_argb + yi * src_stride;
343 0 : if (filtering == kFilterLinear) {
344 0 : ScaleARGBFilterCols(dst_argb, src, dst_width, x, dx);
345 : } else {
346 0 : int yf = (y >> 8) & 255;
347 0 : InterpolateRow(row, src, src_stride, clip_src_width, yf);
348 0 : ScaleARGBFilterCols(dst_argb, row, dst_width, x, dx);
349 : }
350 0 : dst_argb += dst_stride;
351 0 : y += dy;
352 0 : if (y > max_y) {
353 0 : y = max_y;
354 : }
355 : }
356 0 : free_aligned_buffer_64(row);
357 : }
358 0 : }
359 :
360 : // Scale ARGB up with bilinear interpolation.
361 0 : static void ScaleARGBBilinearUp(int src_width,
362 : int src_height,
363 : int dst_width,
364 : int dst_height,
365 : int src_stride,
366 : int dst_stride,
367 : const uint8* src_argb,
368 : uint8* dst_argb,
369 : int x,
370 : int dx,
371 : int y,
372 : int dy,
373 : enum FilterMode filtering) {
374 : int j;
375 : void (*InterpolateRow)(uint8 * dst_argb, const uint8* src_argb,
376 : ptrdiff_t src_stride, int dst_width,
377 0 : int source_y_fraction) = InterpolateRow_C;
378 : void (*ScaleARGBFilterCols)(uint8 * dst_argb, const uint8* src_argb,
379 : int dst_width, int x, int dx) =
380 0 : filtering ? ScaleARGBFilterCols_C : ScaleARGBCols_C;
381 0 : const int max_y = (src_height - 1) << 16;
382 : #if defined(HAS_INTERPOLATEROW_SSSE3)
383 0 : if (TestCpuFlag(kCpuHasSSSE3)) {
384 0 : InterpolateRow = InterpolateRow_Any_SSSE3;
385 0 : if (IS_ALIGNED(dst_width, 4)) {
386 0 : InterpolateRow = InterpolateRow_SSSE3;
387 : }
388 : }
389 : #endif
390 : #if defined(HAS_INTERPOLATEROW_AVX2)
391 0 : if (TestCpuFlag(kCpuHasAVX2)) {
392 0 : InterpolateRow = InterpolateRow_Any_AVX2;
393 0 : if (IS_ALIGNED(dst_width, 8)) {
394 0 : InterpolateRow = InterpolateRow_AVX2;
395 : }
396 : }
397 : #endif
398 : #if defined(HAS_INTERPOLATEROW_NEON)
399 : if (TestCpuFlag(kCpuHasNEON)) {
400 : InterpolateRow = InterpolateRow_Any_NEON;
401 : if (IS_ALIGNED(dst_width, 4)) {
402 : InterpolateRow = InterpolateRow_NEON;
403 : }
404 : }
405 : #endif
406 : #if defined(HAS_INTERPOLATEROW_DSPR2)
407 : if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(dst_argb, 4) &&
408 : IS_ALIGNED(dst_stride, 4)) {
409 : InterpolateRow = InterpolateRow_DSPR2;
410 : }
411 : #endif
412 0 : if (src_width >= 32768) {
413 0 : ScaleARGBFilterCols =
414 0 : filtering ? ScaleARGBFilterCols64_C : ScaleARGBCols64_C;
415 : }
416 : #if defined(HAS_SCALEARGBFILTERCOLS_SSSE3)
417 0 : if (filtering && TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
418 0 : ScaleARGBFilterCols = ScaleARGBFilterCols_SSSE3;
419 : }
420 : #endif
421 : #if defined(HAS_SCALEARGBFILTERCOLS_NEON)
422 : if (filtering && TestCpuFlag(kCpuHasNEON)) {
423 : ScaleARGBFilterCols = ScaleARGBFilterCols_Any_NEON;
424 : if (IS_ALIGNED(dst_width, 4)) {
425 : ScaleARGBFilterCols = ScaleARGBFilterCols_NEON;
426 : }
427 : }
428 : #endif
429 : #if defined(HAS_SCALEARGBCOLS_SSE2)
430 0 : if (!filtering && TestCpuFlag(kCpuHasSSE2) && src_width < 32768) {
431 0 : ScaleARGBFilterCols = ScaleARGBCols_SSE2;
432 : }
433 : #endif
434 : #if defined(HAS_SCALEARGBCOLS_NEON)
435 : if (!filtering && TestCpuFlag(kCpuHasNEON)) {
436 : ScaleARGBFilterCols = ScaleARGBCols_Any_NEON;
437 : if (IS_ALIGNED(dst_width, 8)) {
438 : ScaleARGBFilterCols = ScaleARGBCols_NEON;
439 : }
440 : }
441 : #endif
442 0 : if (!filtering && src_width * 2 == dst_width && x < 0x8000) {
443 0 : ScaleARGBFilterCols = ScaleARGBColsUp2_C;
444 : #if defined(HAS_SCALEARGBCOLSUP2_SSE2)
445 0 : if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
446 0 : ScaleARGBFilterCols = ScaleARGBColsUp2_SSE2;
447 : }
448 : #endif
449 : }
450 :
451 0 : if (y > max_y) {
452 0 : y = max_y;
453 : }
454 :
455 : {
456 0 : int yi = y >> 16;
457 0 : const uint8* src = src_argb + yi * src_stride;
458 :
459 : // Allocate 2 rows of ARGB.
460 0 : const int kRowSize = (dst_width * 4 + 31) & ~31;
461 0 : align_buffer_64(row, kRowSize * 2);
462 :
463 0 : uint8* rowptr = row;
464 0 : int rowstride = kRowSize;
465 0 : int lasty = yi;
466 :
467 0 : ScaleARGBFilterCols(rowptr, src, dst_width, x, dx);
468 0 : if (src_height > 1) {
469 0 : src += src_stride;
470 : }
471 0 : ScaleARGBFilterCols(rowptr + rowstride, src, dst_width, x, dx);
472 0 : src += src_stride;
473 :
474 0 : for (j = 0; j < dst_height; ++j) {
475 0 : yi = y >> 16;
476 0 : if (yi != lasty) {
477 0 : if (y > max_y) {
478 0 : y = max_y;
479 0 : yi = y >> 16;
480 0 : src = src_argb + yi * src_stride;
481 : }
482 0 : if (yi != lasty) {
483 0 : ScaleARGBFilterCols(rowptr, src, dst_width, x, dx);
484 0 : rowptr += rowstride;
485 0 : rowstride = -rowstride;
486 0 : lasty = yi;
487 0 : src += src_stride;
488 : }
489 : }
490 0 : if (filtering == kFilterLinear) {
491 0 : InterpolateRow(dst_argb, rowptr, 0, dst_width * 4, 0);
492 : } else {
493 0 : int yf = (y >> 8) & 255;
494 0 : InterpolateRow(dst_argb, rowptr, rowstride, dst_width * 4, yf);
495 : }
496 0 : dst_argb += dst_stride;
497 0 : y += dy;
498 : }
499 0 : free_aligned_buffer_64(row);
500 : }
501 0 : }
502 :
503 : #ifdef YUVSCALEUP
504 : // Scale YUV to ARGB up with bilinear interpolation.
505 : static void ScaleYUVToARGBBilinearUp(int src_width,
506 : int src_height,
507 : int dst_width,
508 : int dst_height,
509 : int src_stride_y,
510 : int src_stride_u,
511 : int src_stride_v,
512 : int dst_stride_argb,
513 : const uint8* src_y,
514 : const uint8* src_u,
515 : const uint8* src_v,
516 : uint8* dst_argb,
517 : int x,
518 : int dx,
519 : int y,
520 : int dy,
521 : enum FilterMode filtering) {
522 : int j;
523 : void (*I422ToARGBRow)(const uint8* y_buf, const uint8* u_buf,
524 : const uint8* v_buf, uint8* rgb_buf, int width) =
525 : I422ToARGBRow_C;
526 : #if defined(HAS_I422TOARGBROW_SSSE3)
527 : if (TestCpuFlag(kCpuHasSSSE3)) {
528 : I422ToARGBRow = I422ToARGBRow_Any_SSSE3;
529 : if (IS_ALIGNED(src_width, 8)) {
530 : I422ToARGBRow = I422ToARGBRow_SSSE3;
531 : }
532 : }
533 : #endif
534 : #if defined(HAS_I422TOARGBROW_AVX2)
535 : if (TestCpuFlag(kCpuHasAVX2)) {
536 : I422ToARGBRow = I422ToARGBRow_Any_AVX2;
537 : if (IS_ALIGNED(src_width, 16)) {
538 : I422ToARGBRow = I422ToARGBRow_AVX2;
539 : }
540 : }
541 : #endif
542 : #if defined(HAS_I422TOARGBROW_NEON)
543 : if (TestCpuFlag(kCpuHasNEON)) {
544 : I422ToARGBRow = I422ToARGBRow_Any_NEON;
545 : if (IS_ALIGNED(src_width, 8)) {
546 : I422ToARGBRow = I422ToARGBRow_NEON;
547 : }
548 : }
549 : #endif
550 : #if defined(HAS_I422TOARGBROW_DSPR2)
551 : if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(src_width, 4) &&
552 : IS_ALIGNED(src_y, 4) && IS_ALIGNED(src_stride_y, 4) &&
553 : IS_ALIGNED(src_u, 2) && IS_ALIGNED(src_stride_u, 2) &&
554 : IS_ALIGNED(src_v, 2) && IS_ALIGNED(src_stride_v, 2) &&
555 : IS_ALIGNED(dst_argb, 4) && IS_ALIGNED(dst_stride_argb, 4)) {
556 : I422ToARGBRow = I422ToARGBRow_DSPR2;
557 : }
558 : #endif
559 : #if defined(HAS_I422TOARGBROW_MSA)
560 : if (TestCpuFlag(kCpuHasMSA)) {
561 : I422ToARGBRow = I422ToARGBRow_Any_MSA;
562 : if (IS_ALIGNED(src_width, 8)) {
563 : I422ToARGBRow = I422ToARGBRow_MSA;
564 : }
565 : }
566 : #endif
567 :
568 : void (*InterpolateRow)(uint8 * dst_argb, const uint8* src_argb,
569 : ptrdiff_t src_stride, int dst_width,
570 : int source_y_fraction) = InterpolateRow_C;
571 : #if defined(HAS_INTERPOLATEROW_SSSE3)
572 : if (TestCpuFlag(kCpuHasSSSE3)) {
573 : InterpolateRow = InterpolateRow_Any_SSSE3;
574 : if (IS_ALIGNED(dst_width, 4)) {
575 : InterpolateRow = InterpolateRow_SSSE3;
576 : }
577 : }
578 : #endif
579 : #if defined(HAS_INTERPOLATEROW_AVX2)
580 : if (TestCpuFlag(kCpuHasAVX2)) {
581 : InterpolateRow = InterpolateRow_Any_AVX2;
582 : if (IS_ALIGNED(dst_width, 8)) {
583 : InterpolateRow = InterpolateRow_AVX2;
584 : }
585 : }
586 : #endif
587 : #if defined(HAS_INTERPOLATEROW_NEON)
588 : if (TestCpuFlag(kCpuHasNEON)) {
589 : InterpolateRow = InterpolateRow_Any_NEON;
590 : if (IS_ALIGNED(dst_width, 4)) {
591 : InterpolateRow = InterpolateRow_NEON;
592 : }
593 : }
594 : #endif
595 : #if defined(HAS_INTERPOLATEROW_DSPR2)
596 : if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(dst_argb, 4) &&
597 : IS_ALIGNED(dst_stride_argb, 4)) {
598 : InterpolateRow = InterpolateRow_DSPR2;
599 : }
600 : #endif
601 :
602 : void (*ScaleARGBFilterCols)(uint8 * dst_argb, const uint8* src_argb,
603 : int dst_width, int x, int dx) =
604 : filtering ? ScaleARGBFilterCols_C : ScaleARGBCols_C;
605 : if (src_width >= 32768) {
606 : ScaleARGBFilterCols =
607 : filtering ? ScaleARGBFilterCols64_C : ScaleARGBCols64_C;
608 : }
609 : #if defined(HAS_SCALEARGBFILTERCOLS_SSSE3)
610 : if (filtering && TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
611 : ScaleARGBFilterCols = ScaleARGBFilterCols_SSSE3;
612 : }
613 : #endif
614 : #if defined(HAS_SCALEARGBFILTERCOLS_NEON)
615 : if (filtering && TestCpuFlag(kCpuHasNEON)) {
616 : ScaleARGBFilterCols = ScaleARGBFilterCols_Any_NEON;
617 : if (IS_ALIGNED(dst_width, 4)) {
618 : ScaleARGBFilterCols = ScaleARGBFilterCols_NEON;
619 : }
620 : }
621 : #endif
622 : #if defined(HAS_SCALEARGBCOLS_SSE2)
623 : if (!filtering && TestCpuFlag(kCpuHasSSE2) && src_width < 32768) {
624 : ScaleARGBFilterCols = ScaleARGBCols_SSE2;
625 : }
626 : #endif
627 : #if defined(HAS_SCALEARGBCOLS_NEON)
628 : if (!filtering && TestCpuFlag(kCpuHasNEON)) {
629 : ScaleARGBFilterCols = ScaleARGBCols_Any_NEON;
630 : if (IS_ALIGNED(dst_width, 8)) {
631 : ScaleARGBFilterCols = ScaleARGBCols_NEON;
632 : }
633 : }
634 : #endif
635 : if (!filtering && src_width * 2 == dst_width && x < 0x8000) {
636 : ScaleARGBFilterCols = ScaleARGBColsUp2_C;
637 : #if defined(HAS_SCALEARGBCOLSUP2_SSE2)
638 : if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
639 : ScaleARGBFilterCols = ScaleARGBColsUp2_SSE2;
640 : }
641 : #endif
642 : }
643 :
644 : const int max_y = (src_height - 1) << 16;
645 : if (y > max_y) {
646 : y = max_y;
647 : }
648 : const int kYShift = 1; // Shift Y by 1 to convert Y plane to UV coordinate.
649 : int yi = y >> 16;
650 : int uv_yi = yi >> kYShift;
651 : const uint8* src_row_y = src_y + yi * src_stride_y;
652 : const uint8* src_row_u = src_u + uv_yi * src_stride_u;
653 : const uint8* src_row_v = src_v + uv_yi * src_stride_v;
654 :
655 : // Allocate 2 rows of ARGB.
656 : const int kRowSize = (dst_width * 4 + 31) & ~31;
657 : align_buffer_64(row, kRowSize * 2);
658 :
659 : // Allocate 1 row of ARGB for source conversion.
660 : align_buffer_64(argb_row, src_width * 4);
661 :
662 : uint8* rowptr = row;
663 : int rowstride = kRowSize;
664 : int lasty = yi;
665 :
666 : // TODO(fbarchard): Convert first 2 rows of YUV to ARGB.
667 : ScaleARGBFilterCols(rowptr, src_row_y, dst_width, x, dx);
668 : if (src_height > 1) {
669 : src_row_y += src_stride_y;
670 : if (yi & 1) {
671 : src_row_u += src_stride_u;
672 : src_row_v += src_stride_v;
673 : }
674 : }
675 : ScaleARGBFilterCols(rowptr + rowstride, src_row_y, dst_width, x, dx);
676 : if (src_height > 2) {
677 : src_row_y += src_stride_y;
678 : if (!(yi & 1)) {
679 : src_row_u += src_stride_u;
680 : src_row_v += src_stride_v;
681 : }
682 : }
683 :
684 : for (j = 0; j < dst_height; ++j) {
685 : yi = y >> 16;
686 : if (yi != lasty) {
687 : if (y > max_y) {
688 : y = max_y;
689 : yi = y >> 16;
690 : uv_yi = yi >> kYShift;
691 : src_row_y = src_y + yi * src_stride_y;
692 : src_row_u = src_u + uv_yi * src_stride_u;
693 : src_row_v = src_v + uv_yi * src_stride_v;
694 : }
695 : if (yi != lasty) {
696 : // TODO(fbarchard): Convert the clipped region of row.
697 : I422ToARGBRow(src_row_y, src_row_u, src_row_v, argb_row, src_width);
698 : ScaleARGBFilterCols(rowptr, argb_row, dst_width, x, dx);
699 : rowptr += rowstride;
700 : rowstride = -rowstride;
701 : lasty = yi;
702 : src_row_y += src_stride_y;
703 : if (yi & 1) {
704 : src_row_u += src_stride_u;
705 : src_row_v += src_stride_v;
706 : }
707 : }
708 : }
709 : if (filtering == kFilterLinear) {
710 : InterpolateRow(dst_argb, rowptr, 0, dst_width * 4, 0);
711 : } else {
712 : int yf = (y >> 8) & 255;
713 : InterpolateRow(dst_argb, rowptr, rowstride, dst_width * 4, yf);
714 : }
715 : dst_argb += dst_stride_argb;
716 : y += dy;
717 : }
718 : free_aligned_buffer_64(row);
719 : free_aligned_buffer_64(row_argb);
720 : }
721 : #endif
722 :
723 : // Scale ARGB to/from any dimensions, without interpolation.
724 : // Fixed point math is used for performance: The upper 16 bits
725 : // of x and dx is the integer part of the source position and
726 : // the lower 16 bits are the fixed decimal part.
727 :
728 0 : static void ScaleARGBSimple(int src_width,
729 : int src_height,
730 : int dst_width,
731 : int dst_height,
732 : int src_stride,
733 : int dst_stride,
734 : const uint8* src_argb,
735 : uint8* dst_argb,
736 : int x,
737 : int dx,
738 : int y,
739 : int dy) {
740 : int j;
741 : void (*ScaleARGBCols)(uint8 * dst_argb, const uint8* src_argb, int dst_width,
742 : int x, int dx) =
743 0 : (src_width >= 32768) ? ScaleARGBCols64_C : ScaleARGBCols_C;
744 : (void)src_height;
745 : #if defined(HAS_SCALEARGBCOLS_SSE2)
746 0 : if (TestCpuFlag(kCpuHasSSE2) && src_width < 32768) {
747 0 : ScaleARGBCols = ScaleARGBCols_SSE2;
748 : }
749 : #endif
750 : #if defined(HAS_SCALEARGBCOLS_NEON)
751 : if (TestCpuFlag(kCpuHasNEON)) {
752 : ScaleARGBCols = ScaleARGBCols_Any_NEON;
753 : if (IS_ALIGNED(dst_width, 8)) {
754 : ScaleARGBCols = ScaleARGBCols_NEON;
755 : }
756 : }
757 : #endif
758 0 : if (src_width * 2 == dst_width && x < 0x8000) {
759 0 : ScaleARGBCols = ScaleARGBColsUp2_C;
760 : #if defined(HAS_SCALEARGBCOLSUP2_SSE2)
761 0 : if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
762 0 : ScaleARGBCols = ScaleARGBColsUp2_SSE2;
763 : }
764 : #endif
765 : }
766 :
767 0 : for (j = 0; j < dst_height; ++j) {
768 0 : ScaleARGBCols(dst_argb, src_argb + (y >> 16) * src_stride, dst_width, x,
769 0 : dx);
770 0 : dst_argb += dst_stride;
771 0 : y += dy;
772 : }
773 0 : }
774 :
775 : // ScaleARGB a ARGB.
776 : // This function in turn calls a scaling function
777 : // suitable for handling the desired resolutions.
778 0 : static void ScaleARGB(const uint8* src,
779 : int src_stride,
780 : int src_width,
781 : int src_height,
782 : uint8* dst,
783 : int dst_stride,
784 : int dst_width,
785 : int dst_height,
786 : int clip_x,
787 : int clip_y,
788 : int clip_width,
789 : int clip_height,
790 : enum FilterMode filtering) {
791 : // Initial source x/y coordinate and step values as 16.16 fixed point.
792 0 : int x = 0;
793 0 : int y = 0;
794 0 : int dx = 0;
795 0 : int dy = 0;
796 : // ARGB does not support box filter yet, but allow the user to pass it.
797 : // Simplify filtering when possible.
798 : filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height,
799 0 : filtering);
800 :
801 : // Negative src_height means invert the image.
802 0 : if (src_height < 0) {
803 0 : src_height = -src_height;
804 0 : src = src + (src_height - 1) * src_stride;
805 0 : src_stride = -src_stride;
806 : }
807 : ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
808 0 : &dx, &dy);
809 0 : src_width = Abs(src_width);
810 0 : if (clip_x) {
811 0 : int64 clipf = (int64)(clip_x)*dx;
812 0 : x += (clipf & 0xffff);
813 0 : src += (clipf >> 16) * 4;
814 0 : dst += clip_x * 4;
815 : }
816 0 : if (clip_y) {
817 0 : int64 clipf = (int64)(clip_y)*dy;
818 0 : y += (clipf & 0xffff);
819 0 : src += (clipf >> 16) * src_stride;
820 0 : dst += clip_y * dst_stride;
821 : }
822 :
823 : // Special case for integer step values.
824 0 : if (((dx | dy) & 0xffff) == 0) {
825 0 : if (!dx || !dy) { // 1 pixel wide and/or tall.
826 0 : filtering = kFilterNone;
827 : } else {
828 : // Optimized even scale down. ie 2, 4, 6, 8, 10x.
829 0 : if (!(dx & 0x10000) && !(dy & 0x10000)) {
830 0 : if (dx == 0x20000) {
831 : // Optimized 1/2 downsample.
832 : ScaleARGBDown2(src_width, src_height, clip_width, clip_height,
833 : src_stride, dst_stride, src, dst, x, dx, y, dy,
834 0 : filtering);
835 0 : return;
836 : }
837 0 : if (dx == 0x40000 && filtering == kFilterBox) {
838 : // Optimized 1/4 box downsample.
839 : ScaleARGBDown4Box(src_width, src_height, clip_width, clip_height,
840 0 : src_stride, dst_stride, src, dst, x, dx, y, dy);
841 0 : return;
842 : }
843 : ScaleARGBDownEven(src_width, src_height, clip_width, clip_height,
844 : src_stride, dst_stride, src, dst, x, dx, y, dy,
845 0 : filtering);
846 0 : return;
847 : }
848 : // Optimized odd scale down. ie 3, 5, 7, 9x.
849 0 : if ((dx & 0x10000) && (dy & 0x10000)) {
850 0 : filtering = kFilterNone;
851 0 : if (dx == 0x10000 && dy == 0x10000) {
852 : // Straight copy.
853 0 : ARGBCopy(src + (y >> 16) * src_stride + (x >> 16) * 4, src_stride,
854 0 : dst, dst_stride, clip_width, clip_height);
855 0 : return;
856 : }
857 : }
858 : }
859 : }
860 0 : if (dx == 0x10000 && (x & 0xffff) == 0) {
861 : // Arbitrary scale vertically, but unscaled vertically.
862 : ScalePlaneVertical(src_height, clip_width, clip_height, src_stride,
863 0 : dst_stride, src, dst, x, y, dy, 4, filtering);
864 0 : return;
865 : }
866 0 : if (filtering && dy < 65536) {
867 : ScaleARGBBilinearUp(src_width, src_height, clip_width, clip_height,
868 : src_stride, dst_stride, src, dst, x, dx, y, dy,
869 0 : filtering);
870 0 : return;
871 : }
872 0 : if (filtering) {
873 : ScaleARGBBilinearDown(src_width, src_height, clip_width, clip_height,
874 : src_stride, dst_stride, src, dst, x, dx, y, dy,
875 0 : filtering);
876 0 : return;
877 : }
878 : ScaleARGBSimple(src_width, src_height, clip_width, clip_height, src_stride,
879 0 : dst_stride, src, dst, x, dx, y, dy);
880 : }
881 :
882 : LIBYUV_API
883 0 : int ARGBScaleClip(const uint8* src_argb,
884 : int src_stride_argb,
885 : int src_width,
886 : int src_height,
887 : uint8* dst_argb,
888 : int dst_stride_argb,
889 : int dst_width,
890 : int dst_height,
891 : int clip_x,
892 : int clip_y,
893 : int clip_width,
894 : int clip_height,
895 : enum FilterMode filtering) {
896 0 : if (!src_argb || src_width == 0 || src_height == 0 || !dst_argb ||
897 0 : dst_width <= 0 || dst_height <= 0 || clip_x < 0 || clip_y < 0 ||
898 0 : clip_width > 32768 || clip_height > 32768 ||
899 0 : (clip_x + clip_width) > dst_width ||
900 0 : (clip_y + clip_height) > dst_height) {
901 0 : return -1;
902 : }
903 : ScaleARGB(src_argb, src_stride_argb, src_width, src_height, dst_argb,
904 : dst_stride_argb, dst_width, dst_height, clip_x, clip_y, clip_width,
905 0 : clip_height, filtering);
906 0 : return 0;
907 : }
908 :
909 : // Scale an ARGB image.
910 : LIBYUV_API
911 0 : int ARGBScale(const uint8* src_argb,
912 : int src_stride_argb,
913 : int src_width,
914 : int src_height,
915 : uint8* dst_argb,
916 : int dst_stride_argb,
917 : int dst_width,
918 : int dst_height,
919 : enum FilterMode filtering) {
920 0 : if (!src_argb || src_width == 0 || src_height == 0 || src_width > 32768 ||
921 0 : src_height > 32768 || !dst_argb || dst_width <= 0 || dst_height <= 0) {
922 0 : return -1;
923 : }
924 : ScaleARGB(src_argb, src_stride_argb, src_width, src_height, dst_argb,
925 : dst_stride_argb, dst_width, dst_height, 0, 0, dst_width, dst_height,
926 0 : filtering);
927 0 : return 0;
928 : }
929 :
930 : // Scale with YUV conversion to ARGB and clipping.
931 : LIBYUV_API
932 0 : int YUVToARGBScaleClip(const uint8* src_y,
933 : int src_stride_y,
934 : const uint8* src_u,
935 : int src_stride_u,
936 : const uint8* src_v,
937 : int src_stride_v,
938 : uint32 src_fourcc,
939 : int src_width,
940 : int src_height,
941 : uint8* dst_argb,
942 : int dst_stride_argb,
943 : uint32 dst_fourcc,
944 : int dst_width,
945 : int dst_height,
946 : int clip_x,
947 : int clip_y,
948 : int clip_width,
949 : int clip_height,
950 : enum FilterMode filtering) {
951 0 : uint8* argb_buffer = (uint8*)malloc(src_width * src_height * 4);
952 : int r;
953 : (void)src_fourcc; // TODO(fbarchard): implement and/or assert.
954 : (void)dst_fourcc;
955 0 : I420ToARGB(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
956 0 : argb_buffer, src_width * 4, src_width, src_height);
957 :
958 0 : r = ARGBScaleClip(argb_buffer, src_width * 4, src_width, src_height, dst_argb,
959 : dst_stride_argb, dst_width, dst_height, clip_x, clip_y,
960 0 : clip_width, clip_height, filtering);
961 0 : free(argb_buffer);
962 0 : return r;
963 : }
964 :
965 : #ifdef __cplusplus
966 : } // extern "C"
967 : } // namespace libyuv
968 : #endif
|