1 /*
2 * Copyright 2012 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/convert_from.h"
12
13 #include "libyuv/basic_types.h"
14 #include "libyuv/convert.h" // For I420Copy
15 #include "libyuv/cpu_id.h"
16 #include "libyuv/planar_functions.h"
17 #include "libyuv/rotate.h"
18 #include "libyuv/row.h"
19 #include "libyuv/scale.h" // For ScalePlane()
20 #include "libyuv/video_common.h"
21
22 #ifdef __cplusplus
23 namespace libyuv {
24 extern "C" {
25 #endif
26
27 #define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s)
Abs(int v)28 static __inline int Abs(int v) {
29 return v >= 0 ? v : -v;
30 }
31
32 // I420 To any I4xx YUV format with mirroring.
I420ToI4xx(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_y,int dst_stride_y,uint8_t * dst_u,int dst_stride_u,uint8_t * dst_v,int dst_stride_v,int src_y_width,int src_y_height,int dst_uv_width,int dst_uv_height)33 static int I420ToI4xx(const uint8_t* src_y,
34 int src_stride_y,
35 const uint8_t* src_u,
36 int src_stride_u,
37 const uint8_t* src_v,
38 int src_stride_v,
39 uint8_t* dst_y,
40 int dst_stride_y,
41 uint8_t* dst_u,
42 int dst_stride_u,
43 uint8_t* dst_v,
44 int dst_stride_v,
45 int src_y_width,
46 int src_y_height,
47 int dst_uv_width,
48 int dst_uv_height) {
49 const int dst_y_width = Abs(src_y_width);
50 const int dst_y_height = Abs(src_y_height);
51 const int src_uv_width = SUBSAMPLE(src_y_width, 1, 1);
52 const int src_uv_height = SUBSAMPLE(src_y_height, 1, 1);
53 if (src_y_width == 0 || src_y_height == 0 || dst_uv_width <= 0 ||
54 dst_uv_height <= 0) {
55 return -1;
56 }
57 if (dst_y) {
58 ScalePlane(src_y, src_stride_y, src_y_width, src_y_height, dst_y,
59 dst_stride_y, dst_y_width, dst_y_height, kFilterBilinear);
60 }
61 ScalePlane(src_u, src_stride_u, src_uv_width, src_uv_height, dst_u,
62 dst_stride_u, dst_uv_width, dst_uv_height, kFilterBilinear);
63 ScalePlane(src_v, src_stride_v, src_uv_width, src_uv_height, dst_v,
64 dst_stride_v, dst_uv_width, dst_uv_height, kFilterBilinear);
65 return 0;
66 }
67
68 // Convert 8 bit YUV to 10 bit.
69 LIBYUV_API
I420ToI010(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint16_t * dst_y,int dst_stride_y,uint16_t * dst_u,int dst_stride_u,uint16_t * dst_v,int dst_stride_v,int width,int height)70 int I420ToI010(const uint8_t* src_y,
71 int src_stride_y,
72 const uint8_t* src_u,
73 int src_stride_u,
74 const uint8_t* src_v,
75 int src_stride_v,
76 uint16_t* dst_y,
77 int dst_stride_y,
78 uint16_t* dst_u,
79 int dst_stride_u,
80 uint16_t* dst_v,
81 int dst_stride_v,
82 int width,
83 int height) {
84 int halfwidth = (width + 1) >> 1;
85 int halfheight = (height + 1) >> 1;
86 if (!src_u || !src_v || !dst_u || !dst_v || width <= 0 || height == 0) {
87 return -1;
88 }
89 // Negative height means invert the image.
90 if (height < 0) {
91 height = -height;
92 halfheight = (height + 1) >> 1;
93 src_y = src_y + (height - 1) * src_stride_y;
94 src_u = src_u + (halfheight - 1) * src_stride_u;
95 src_v = src_v + (halfheight - 1) * src_stride_v;
96 src_stride_y = -src_stride_y;
97 src_stride_u = -src_stride_u;
98 src_stride_v = -src_stride_v;
99 }
100
101 // Convert Y plane.
102 Convert8To16Plane(src_y, src_stride_y, dst_y, dst_stride_y, 1024, width,
103 height);
104 // Convert UV planes.
105 Convert8To16Plane(src_u, src_stride_u, dst_u, dst_stride_u, 1024, halfwidth,
106 halfheight);
107 Convert8To16Plane(src_v, src_stride_v, dst_v, dst_stride_v, 1024, halfwidth,
108 halfheight);
109 return 0;
110 }
111
112 // 420 chroma is 1/2 width, 1/2 height
113 // 422 chroma is 1/2 width, 1x height
114 LIBYUV_API
I420ToI422(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_y,int dst_stride_y,uint8_t * dst_u,int dst_stride_u,uint8_t * dst_v,int dst_stride_v,int width,int height)115 int I420ToI422(const uint8_t* src_y,
116 int src_stride_y,
117 const uint8_t* src_u,
118 int src_stride_u,
119 const uint8_t* src_v,
120 int src_stride_v,
121 uint8_t* dst_y,
122 int dst_stride_y,
123 uint8_t* dst_u,
124 int dst_stride_u,
125 uint8_t* dst_v,
126 int dst_stride_v,
127 int width,
128 int height) {
129 const int dst_uv_width = (Abs(width) + 1) >> 1;
130 const int dst_uv_height = Abs(height);
131 return I420ToI4xx(src_y, src_stride_y, src_u, src_stride_u, src_v,
132 src_stride_v, dst_y, dst_stride_y, dst_u, dst_stride_u,
133 dst_v, dst_stride_v, width, height, dst_uv_width,
134 dst_uv_height);
135 }
136
137 // 420 chroma is 1/2 width, 1/2 height
138 // 444 chroma is 1x width, 1x height
139 LIBYUV_API
I420ToI444(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_y,int dst_stride_y,uint8_t * dst_u,int dst_stride_u,uint8_t * dst_v,int dst_stride_v,int width,int height)140 int I420ToI444(const uint8_t* src_y,
141 int src_stride_y,
142 const uint8_t* src_u,
143 int src_stride_u,
144 const uint8_t* src_v,
145 int src_stride_v,
146 uint8_t* dst_y,
147 int dst_stride_y,
148 uint8_t* dst_u,
149 int dst_stride_u,
150 uint8_t* dst_v,
151 int dst_stride_v,
152 int width,
153 int height) {
154 const int dst_uv_width = Abs(width);
155 const int dst_uv_height = Abs(height);
156 return I420ToI4xx(src_y, src_stride_y, src_u, src_stride_u, src_v,
157 src_stride_v, dst_y, dst_stride_y, dst_u, dst_stride_u,
158 dst_v, dst_stride_v, width, height, dst_uv_width,
159 dst_uv_height);
160 }
161
162 // Copy to I400. Source can be I420,422,444,400,NV12,NV21
163 LIBYUV_API
I400Copy(const uint8_t * src_y,int src_stride_y,uint8_t * dst_y,int dst_stride_y,int width,int height)164 int I400Copy(const uint8_t* src_y,
165 int src_stride_y,
166 uint8_t* dst_y,
167 int dst_stride_y,
168 int width,
169 int height) {
170 if (!src_y || !dst_y || width <= 0 || height == 0) {
171 return -1;
172 }
173 // Negative height means invert the image.
174 if (height < 0) {
175 height = -height;
176 src_y = src_y + (height - 1) * src_stride_y;
177 src_stride_y = -src_stride_y;
178 }
179 CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
180 return 0;
181 }
182
183 LIBYUV_API
I422ToYUY2(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_yuy2,int dst_stride_yuy2,int width,int height)184 int I422ToYUY2(const uint8_t* src_y,
185 int src_stride_y,
186 const uint8_t* src_u,
187 int src_stride_u,
188 const uint8_t* src_v,
189 int src_stride_v,
190 uint8_t* dst_yuy2,
191 int dst_stride_yuy2,
192 int width,
193 int height) {
194 int y;
195 void (*I422ToYUY2Row)(const uint8_t* src_y, const uint8_t* src_u,
196 const uint8_t* src_v, uint8_t* dst_yuy2, int width) =
197 I422ToYUY2Row_C;
198 if (!src_y || !src_u || !src_v || !dst_yuy2 || width <= 0 || height == 0) {
199 return -1;
200 }
201 // Negative height means invert the image.
202 if (height < 0) {
203 height = -height;
204 dst_yuy2 = dst_yuy2 + (height - 1) * dst_stride_yuy2;
205 dst_stride_yuy2 = -dst_stride_yuy2;
206 }
207 // Coalesce rows.
208 if (src_stride_y == width && src_stride_u * 2 == width &&
209 src_stride_v * 2 == width && dst_stride_yuy2 == width * 2) {
210 width *= height;
211 height = 1;
212 src_stride_y = src_stride_u = src_stride_v = dst_stride_yuy2 = 0;
213 }
214 #if defined(HAS_I422TOYUY2ROW_SSE2)
215 if (TestCpuFlag(kCpuHasSSE2)) {
216 I422ToYUY2Row = I422ToYUY2Row_Any_SSE2;
217 if (IS_ALIGNED(width, 16)) {
218 I422ToYUY2Row = I422ToYUY2Row_SSE2;
219 }
220 }
221 #endif
222 #if defined(HAS_I422TOYUY2ROW_AVX2)
223 if (TestCpuFlag(kCpuHasAVX2)) {
224 I422ToYUY2Row = I422ToYUY2Row_Any_AVX2;
225 if (IS_ALIGNED(width, 32)) {
226 I422ToYUY2Row = I422ToYUY2Row_AVX2;
227 }
228 }
229 #endif
230 #if defined(HAS_I422TOYUY2ROW_NEON)
231 if (TestCpuFlag(kCpuHasNEON)) {
232 I422ToYUY2Row = I422ToYUY2Row_Any_NEON;
233 if (IS_ALIGNED(width, 16)) {
234 I422ToYUY2Row = I422ToYUY2Row_NEON;
235 }
236 }
237 #endif
238
239 for (y = 0; y < height; ++y) {
240 I422ToYUY2Row(src_y, src_u, src_v, dst_yuy2, width);
241 src_y += src_stride_y;
242 src_u += src_stride_u;
243 src_v += src_stride_v;
244 dst_yuy2 += dst_stride_yuy2;
245 }
246 return 0;
247 }
248
249 LIBYUV_API
I420ToYUY2(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_yuy2,int dst_stride_yuy2,int width,int height)250 int I420ToYUY2(const uint8_t* src_y,
251 int src_stride_y,
252 const uint8_t* src_u,
253 int src_stride_u,
254 const uint8_t* src_v,
255 int src_stride_v,
256 uint8_t* dst_yuy2,
257 int dst_stride_yuy2,
258 int width,
259 int height) {
260 int y;
261 void (*I422ToYUY2Row)(const uint8_t* src_y, const uint8_t* src_u,
262 const uint8_t* src_v, uint8_t* dst_yuy2, int width) =
263 I422ToYUY2Row_C;
264 if (!src_y || !src_u || !src_v || !dst_yuy2 || width <= 0 || height == 0) {
265 return -1;
266 }
267 // Negative height means invert the image.
268 if (height < 0) {
269 height = -height;
270 dst_yuy2 = dst_yuy2 + (height - 1) * dst_stride_yuy2;
271 dst_stride_yuy2 = -dst_stride_yuy2;
272 }
273 #if defined(HAS_I422TOYUY2ROW_SSE2)
274 if (TestCpuFlag(kCpuHasSSE2)) {
275 I422ToYUY2Row = I422ToYUY2Row_Any_SSE2;
276 if (IS_ALIGNED(width, 16)) {
277 I422ToYUY2Row = I422ToYUY2Row_SSE2;
278 }
279 }
280 #endif
281 #if defined(HAS_I422TOYUY2ROW_AVX2)
282 if (TestCpuFlag(kCpuHasAVX2)) {
283 I422ToYUY2Row = I422ToYUY2Row_Any_AVX2;
284 if (IS_ALIGNED(width, 32)) {
285 I422ToYUY2Row = I422ToYUY2Row_AVX2;
286 }
287 }
288 #endif
289 #if defined(HAS_I422TOYUY2ROW_NEON)
290 if (TestCpuFlag(kCpuHasNEON)) {
291 I422ToYUY2Row = I422ToYUY2Row_Any_NEON;
292 if (IS_ALIGNED(width, 16)) {
293 I422ToYUY2Row = I422ToYUY2Row_NEON;
294 }
295 }
296 #endif
297 #if defined(HAS_I422TOYUY2ROW_MSA)
298 if (TestCpuFlag(kCpuHasMSA)) {
299 I422ToYUY2Row = I422ToYUY2Row_Any_MSA;
300 if (IS_ALIGNED(width, 32)) {
301 I422ToYUY2Row = I422ToYUY2Row_MSA;
302 }
303 }
304 #endif
305 #if defined(HAS_I422TOYUY2ROW_MMI)
306 if (TestCpuFlag(kCpuHasMMI)) {
307 I422ToYUY2Row = I422ToYUY2Row_Any_MMI;
308 if (IS_ALIGNED(width, 8)) {
309 I422ToYUY2Row = I422ToYUY2Row_MMI;
310 }
311 }
312 #endif
313
314 for (y = 0; y < height - 1; y += 2) {
315 I422ToYUY2Row(src_y, src_u, src_v, dst_yuy2, width);
316 I422ToYUY2Row(src_y + src_stride_y, src_u, src_v,
317 dst_yuy2 + dst_stride_yuy2, width);
318 src_y += src_stride_y * 2;
319 src_u += src_stride_u;
320 src_v += src_stride_v;
321 dst_yuy2 += dst_stride_yuy2 * 2;
322 }
323 if (height & 1) {
324 I422ToYUY2Row(src_y, src_u, src_v, dst_yuy2, width);
325 }
326 return 0;
327 }
328
329 LIBYUV_API
I422ToUYVY(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_uyvy,int dst_stride_uyvy,int width,int height)330 int I422ToUYVY(const uint8_t* src_y,
331 int src_stride_y,
332 const uint8_t* src_u,
333 int src_stride_u,
334 const uint8_t* src_v,
335 int src_stride_v,
336 uint8_t* dst_uyvy,
337 int dst_stride_uyvy,
338 int width,
339 int height) {
340 int y;
341 void (*I422ToUYVYRow)(const uint8_t* src_y, const uint8_t* src_u,
342 const uint8_t* src_v, uint8_t* dst_uyvy, int width) =
343 I422ToUYVYRow_C;
344 if (!src_y || !src_u || !src_v || !dst_uyvy || width <= 0 || height == 0) {
345 return -1;
346 }
347 // Negative height means invert the image.
348 if (height < 0) {
349 height = -height;
350 dst_uyvy = dst_uyvy + (height - 1) * dst_stride_uyvy;
351 dst_stride_uyvy = -dst_stride_uyvy;
352 }
353 // Coalesce rows.
354 if (src_stride_y == width && src_stride_u * 2 == width &&
355 src_stride_v * 2 == width && dst_stride_uyvy == width * 2) {
356 width *= height;
357 height = 1;
358 src_stride_y = src_stride_u = src_stride_v = dst_stride_uyvy = 0;
359 }
360 #if defined(HAS_I422TOUYVYROW_SSE2)
361 if (TestCpuFlag(kCpuHasSSE2)) {
362 I422ToUYVYRow = I422ToUYVYRow_Any_SSE2;
363 if (IS_ALIGNED(width, 16)) {
364 I422ToUYVYRow = I422ToUYVYRow_SSE2;
365 }
366 }
367 #endif
368 #if defined(HAS_I422TOUYVYROW_AVX2)
369 if (TestCpuFlag(kCpuHasAVX2)) {
370 I422ToUYVYRow = I422ToUYVYRow_Any_AVX2;
371 if (IS_ALIGNED(width, 32)) {
372 I422ToUYVYRow = I422ToUYVYRow_AVX2;
373 }
374 }
375 #endif
376 #if defined(HAS_I422TOUYVYROW_NEON)
377 if (TestCpuFlag(kCpuHasNEON)) {
378 I422ToUYVYRow = I422ToUYVYRow_Any_NEON;
379 if (IS_ALIGNED(width, 16)) {
380 I422ToUYVYRow = I422ToUYVYRow_NEON;
381 }
382 }
383 #endif
384 #if defined(HAS_I422TOUYVYROW_MSA)
385 if (TestCpuFlag(kCpuHasMSA)) {
386 I422ToUYVYRow = I422ToUYVYRow_Any_MSA;
387 if (IS_ALIGNED(width, 32)) {
388 I422ToUYVYRow = I422ToUYVYRow_MSA;
389 }
390 }
391 #endif
392 #if defined(HAS_I422TOUYVYROW_MMI)
393 if (TestCpuFlag(kCpuHasMMI)) {
394 I422ToUYVYRow = I422ToUYVYRow_Any_MMI;
395 if (IS_ALIGNED(width, 8)) {
396 I422ToUYVYRow = I422ToUYVYRow_MMI;
397 }
398 }
399 #endif
400
401 for (y = 0; y < height; ++y) {
402 I422ToUYVYRow(src_y, src_u, src_v, dst_uyvy, width);
403 src_y += src_stride_y;
404 src_u += src_stride_u;
405 src_v += src_stride_v;
406 dst_uyvy += dst_stride_uyvy;
407 }
408 return 0;
409 }
410
411 LIBYUV_API
I420ToUYVY(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_uyvy,int dst_stride_uyvy,int width,int height)412 int I420ToUYVY(const uint8_t* src_y,
413 int src_stride_y,
414 const uint8_t* src_u,
415 int src_stride_u,
416 const uint8_t* src_v,
417 int src_stride_v,
418 uint8_t* dst_uyvy,
419 int dst_stride_uyvy,
420 int width,
421 int height) {
422 int y;
423 void (*I422ToUYVYRow)(const uint8_t* src_y, const uint8_t* src_u,
424 const uint8_t* src_v, uint8_t* dst_uyvy, int width) =
425 I422ToUYVYRow_C;
426 if (!src_y || !src_u || !src_v || !dst_uyvy || width <= 0 || height == 0) {
427 return -1;
428 }
429 // Negative height means invert the image.
430 if (height < 0) {
431 height = -height;
432 dst_uyvy = dst_uyvy + (height - 1) * dst_stride_uyvy;
433 dst_stride_uyvy = -dst_stride_uyvy;
434 }
435 #if defined(HAS_I422TOUYVYROW_SSE2)
436 if (TestCpuFlag(kCpuHasSSE2)) {
437 I422ToUYVYRow = I422ToUYVYRow_Any_SSE2;
438 if (IS_ALIGNED(width, 16)) {
439 I422ToUYVYRow = I422ToUYVYRow_SSE2;
440 }
441 }
442 #endif
443 #if defined(HAS_I422TOUYVYROW_AVX2)
444 if (TestCpuFlag(kCpuHasAVX2)) {
445 I422ToUYVYRow = I422ToUYVYRow_Any_AVX2;
446 if (IS_ALIGNED(width, 32)) {
447 I422ToUYVYRow = I422ToUYVYRow_AVX2;
448 }
449 }
450 #endif
451 #if defined(HAS_I422TOUYVYROW_NEON)
452 if (TestCpuFlag(kCpuHasNEON)) {
453 I422ToUYVYRow = I422ToUYVYRow_Any_NEON;
454 if (IS_ALIGNED(width, 16)) {
455 I422ToUYVYRow = I422ToUYVYRow_NEON;
456 }
457 }
458 #endif
459 #if defined(HAS_I422TOUYVYROW_MSA)
460 if (TestCpuFlag(kCpuHasMSA)) {
461 I422ToUYVYRow = I422ToUYVYRow_Any_MSA;
462 if (IS_ALIGNED(width, 32)) {
463 I422ToUYVYRow = I422ToUYVYRow_MSA;
464 }
465 }
466 #endif
467 #if defined(HAS_I422TOUYVYROW_MMI)
468 if (TestCpuFlag(kCpuHasMMI)) {
469 I422ToUYVYRow = I422ToUYVYRow_Any_MMI;
470 if (IS_ALIGNED(width, 8)) {
471 I422ToUYVYRow = I422ToUYVYRow_MMI;
472 }
473 }
474 #endif
475
476 for (y = 0; y < height - 1; y += 2) {
477 I422ToUYVYRow(src_y, src_u, src_v, dst_uyvy, width);
478 I422ToUYVYRow(src_y + src_stride_y, src_u, src_v,
479 dst_uyvy + dst_stride_uyvy, width);
480 src_y += src_stride_y * 2;
481 src_u += src_stride_u;
482 src_v += src_stride_v;
483 dst_uyvy += dst_stride_uyvy * 2;
484 }
485 if (height & 1) {
486 I422ToUYVYRow(src_y, src_u, src_v, dst_uyvy, width);
487 }
488 return 0;
489 }
490
491 // TODO(fbarchard): test negative height for invert.
492 LIBYUV_API
I420ToNV12(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_y,int dst_stride_y,uint8_t * dst_uv,int dst_stride_uv,int width,int height)493 int I420ToNV12(const uint8_t* src_y,
494 int src_stride_y,
495 const uint8_t* src_u,
496 int src_stride_u,
497 const uint8_t* src_v,
498 int src_stride_v,
499 uint8_t* dst_y,
500 int dst_stride_y,
501 uint8_t* dst_uv,
502 int dst_stride_uv,
503 int width,
504 int height) {
505 if (!src_y || !src_u || !src_v || !dst_y || !dst_uv || width <= 0 ||
506 height == 0) {
507 return -1;
508 }
509 int halfwidth = (width + 1) / 2;
510 int halfheight = height > 0 ? (height + 1) / 2 : (height - 1) / 2;
511 if (dst_y) {
512 CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
513 }
514 MergeUVPlane(src_u, src_stride_u, src_v, src_stride_v, dst_uv, dst_stride_uv,
515 halfwidth, halfheight);
516 return 0;
517 }
518
519 LIBYUV_API
I420ToNV21(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_y,int dst_stride_y,uint8_t * dst_vu,int dst_stride_vu,int width,int height)520 int I420ToNV21(const uint8_t* src_y,
521 int src_stride_y,
522 const uint8_t* src_u,
523 int src_stride_u,
524 const uint8_t* src_v,
525 int src_stride_v,
526 uint8_t* dst_y,
527 int dst_stride_y,
528 uint8_t* dst_vu,
529 int dst_stride_vu,
530 int width,
531 int height) {
532 return I420ToNV12(src_y, src_stride_y, src_v, src_stride_v, src_u,
533 src_stride_u, dst_y, dst_stride_y, dst_vu, dst_stride_vu,
534 width, height);
535 }
536
537 // Convert I422 to RGBA with matrix
I420ToRGBAMatrix(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgba,int dst_stride_rgba,const struct YuvConstants * yuvconstants,int width,int height)538 static int I420ToRGBAMatrix(const uint8_t* src_y,
539 int src_stride_y,
540 const uint8_t* src_u,
541 int src_stride_u,
542 const uint8_t* src_v,
543 int src_stride_v,
544 uint8_t* dst_rgba,
545 int dst_stride_rgba,
546 const struct YuvConstants* yuvconstants,
547 int width,
548 int height) {
549 int y;
550 void (*I422ToRGBARow)(const uint8_t* y_buf, const uint8_t* u_buf,
551 const uint8_t* v_buf, uint8_t* rgb_buf,
552 const struct YuvConstants* yuvconstants, int width) =
553 I422ToRGBARow_C;
554 if (!src_y || !src_u || !src_v || !dst_rgba || width <= 0 || height == 0) {
555 return -1;
556 }
557 // Negative height means invert the image.
558 if (height < 0) {
559 height = -height;
560 dst_rgba = dst_rgba + (height - 1) * dst_stride_rgba;
561 dst_stride_rgba = -dst_stride_rgba;
562 }
563 #if defined(HAS_I422TORGBAROW_SSSE3)
564 if (TestCpuFlag(kCpuHasSSSE3)) {
565 I422ToRGBARow = I422ToRGBARow_Any_SSSE3;
566 if (IS_ALIGNED(width, 8)) {
567 I422ToRGBARow = I422ToRGBARow_SSSE3;
568 }
569 }
570 #endif
571 #if defined(HAS_I422TORGBAROW_AVX2)
572 if (TestCpuFlag(kCpuHasAVX2)) {
573 I422ToRGBARow = I422ToRGBARow_Any_AVX2;
574 if (IS_ALIGNED(width, 16)) {
575 I422ToRGBARow = I422ToRGBARow_AVX2;
576 }
577 }
578 #endif
579 #if defined(HAS_I422TORGBAROW_NEON)
580 if (TestCpuFlag(kCpuHasNEON)) {
581 I422ToRGBARow = I422ToRGBARow_Any_NEON;
582 if (IS_ALIGNED(width, 8)) {
583 I422ToRGBARow = I422ToRGBARow_NEON;
584 }
585 }
586 #endif
587 #if defined(HAS_I422TORGBAROW_MSA)
588 if (TestCpuFlag(kCpuHasMSA)) {
589 I422ToRGBARow = I422ToRGBARow_Any_MSA;
590 if (IS_ALIGNED(width, 8)) {
591 I422ToRGBARow = I422ToRGBARow_MSA;
592 }
593 }
594 #endif
595 #if defined(HAS_I422TORGBAROW_MMI)
596 if (TestCpuFlag(kCpuHasMMI)) {
597 I422ToRGBARow = I422ToRGBARow_Any_MMI;
598 if (IS_ALIGNED(width, 4)) {
599 I422ToRGBARow = I422ToRGBARow_MMI;
600 }
601 }
602 #endif
603
604 for (y = 0; y < height; ++y) {
605 I422ToRGBARow(src_y, src_u, src_v, dst_rgba, yuvconstants, width);
606 dst_rgba += dst_stride_rgba;
607 src_y += src_stride_y;
608 if (y & 1) {
609 src_u += src_stride_u;
610 src_v += src_stride_v;
611 }
612 }
613 return 0;
614 }
615
616 // Convert I420 to RGBA.
617 LIBYUV_API
I420ToRGBA(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgba,int dst_stride_rgba,int width,int height)618 int I420ToRGBA(const uint8_t* src_y,
619 int src_stride_y,
620 const uint8_t* src_u,
621 int src_stride_u,
622 const uint8_t* src_v,
623 int src_stride_v,
624 uint8_t* dst_rgba,
625 int dst_stride_rgba,
626 int width,
627 int height) {
628 return I420ToRGBAMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
629 src_stride_v, dst_rgba, dst_stride_rgba,
630 &kYuvI601Constants, width, height);
631 }
632
633 // Convert I420 to BGRA.
634 LIBYUV_API
I420ToBGRA(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_bgra,int dst_stride_bgra,int width,int height)635 int I420ToBGRA(const uint8_t* src_y,
636 int src_stride_y,
637 const uint8_t* src_u,
638 int src_stride_u,
639 const uint8_t* src_v,
640 int src_stride_v,
641 uint8_t* dst_bgra,
642 int dst_stride_bgra,
643 int width,
644 int height) {
645 return I420ToRGBAMatrix(src_y, src_stride_y, src_v,
646 src_stride_v, // Swap U and V
647 src_u, src_stride_u, dst_bgra, dst_stride_bgra,
648 &kYvuI601Constants, // Use Yvu matrix
649 width, height);
650 }
651
652 // Convert I420 to RGB24 with matrix
I420ToRGB24Matrix(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgb24,int dst_stride_rgb24,const struct YuvConstants * yuvconstants,int width,int height)653 static int I420ToRGB24Matrix(const uint8_t* src_y,
654 int src_stride_y,
655 const uint8_t* src_u,
656 int src_stride_u,
657 const uint8_t* src_v,
658 int src_stride_v,
659 uint8_t* dst_rgb24,
660 int dst_stride_rgb24,
661 const struct YuvConstants* yuvconstants,
662 int width,
663 int height) {
664 int y;
665 void (*I422ToRGB24Row)(const uint8_t* y_buf, const uint8_t* u_buf,
666 const uint8_t* v_buf, uint8_t* rgb_buf,
667 const struct YuvConstants* yuvconstants, int width) =
668 I422ToRGB24Row_C;
669 if (!src_y || !src_u || !src_v || !dst_rgb24 || width <= 0 || height == 0) {
670 return -1;
671 }
672 // Negative height means invert the image.
673 if (height < 0) {
674 height = -height;
675 dst_rgb24 = dst_rgb24 + (height - 1) * dst_stride_rgb24;
676 dst_stride_rgb24 = -dst_stride_rgb24;
677 }
678 #if defined(HAS_I422TORGB24ROW_SSSE3)
679 if (TestCpuFlag(kCpuHasSSSE3)) {
680 I422ToRGB24Row = I422ToRGB24Row_Any_SSSE3;
681 if (IS_ALIGNED(width, 16)) {
682 I422ToRGB24Row = I422ToRGB24Row_SSSE3;
683 }
684 }
685 #endif
686 #if defined(HAS_I422TORGB24ROW_AVX2)
687 if (TestCpuFlag(kCpuHasAVX2)) {
688 I422ToRGB24Row = I422ToRGB24Row_Any_AVX2;
689 if (IS_ALIGNED(width, 32)) {
690 I422ToRGB24Row = I422ToRGB24Row_AVX2;
691 }
692 }
693 #endif
694 #if defined(HAS_I422TORGB24ROW_NEON)
695 if (TestCpuFlag(kCpuHasNEON)) {
696 I422ToRGB24Row = I422ToRGB24Row_Any_NEON;
697 if (IS_ALIGNED(width, 8)) {
698 I422ToRGB24Row = I422ToRGB24Row_NEON;
699 }
700 }
701 #endif
702 #if defined(HAS_I422TORGB24ROW_MSA)
703 if (TestCpuFlag(kCpuHasMSA)) {
704 I422ToRGB24Row = I422ToRGB24Row_Any_MSA;
705 if (IS_ALIGNED(width, 16)) {
706 I422ToRGB24Row = I422ToRGB24Row_MSA;
707 }
708 }
709 #endif
710 #if defined(HAS_I422TORGB24ROW_MMI)
711 if (TestCpuFlag(kCpuHasMMI)) {
712 I422ToRGB24Row = I422ToRGB24Row_Any_MMI;
713 if (IS_ALIGNED(width, 4)) {
714 I422ToRGB24Row = I422ToRGB24Row_MMI;
715 }
716 }
717 #endif
718
719 for (y = 0; y < height; ++y) {
720 I422ToRGB24Row(src_y, src_u, src_v, dst_rgb24, yuvconstants, width);
721 dst_rgb24 += dst_stride_rgb24;
722 src_y += src_stride_y;
723 if (y & 1) {
724 src_u += src_stride_u;
725 src_v += src_stride_v;
726 }
727 }
728 return 0;
729 }
730
731 // Convert I420 to RGB24.
732 LIBYUV_API
I420ToRGB24(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgb24,int dst_stride_rgb24,int width,int height)733 int I420ToRGB24(const uint8_t* src_y,
734 int src_stride_y,
735 const uint8_t* src_u,
736 int src_stride_u,
737 const uint8_t* src_v,
738 int src_stride_v,
739 uint8_t* dst_rgb24,
740 int dst_stride_rgb24,
741 int width,
742 int height) {
743 return I420ToRGB24Matrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
744 src_stride_v, dst_rgb24, dst_stride_rgb24,
745 &kYuvI601Constants, width, height);
746 }
747
748 // Convert I420 to RAW.
749 LIBYUV_API
I420ToRAW(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_raw,int dst_stride_raw,int width,int height)750 int I420ToRAW(const uint8_t* src_y,
751 int src_stride_y,
752 const uint8_t* src_u,
753 int src_stride_u,
754 const uint8_t* src_v,
755 int src_stride_v,
756 uint8_t* dst_raw,
757 int dst_stride_raw,
758 int width,
759 int height) {
760 return I420ToRGB24Matrix(src_y, src_stride_y, src_v,
761 src_stride_v, // Swap U and V
762 src_u, src_stride_u, dst_raw, dst_stride_raw,
763 &kYvuI601Constants, // Use Yvu matrix
764 width, height);
765 }
766
767 // Convert J420 to RGB24.
768 LIBYUV_API
J420ToRGB24(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgb24,int dst_stride_rgb24,int width,int height)769 int J420ToRGB24(const uint8_t* src_y,
770 int src_stride_y,
771 const uint8_t* src_u,
772 int src_stride_u,
773 const uint8_t* src_v,
774 int src_stride_v,
775 uint8_t* dst_rgb24,
776 int dst_stride_rgb24,
777 int width,
778 int height) {
779 return I420ToRGB24Matrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
780 src_stride_v, dst_rgb24, dst_stride_rgb24,
781 &kYuvJPEGConstants, width, height);
782 }
783
784 // Convert J420 to RAW.
785 LIBYUV_API
J420ToRAW(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_raw,int dst_stride_raw,int width,int height)786 int J420ToRAW(const uint8_t* src_y,
787 int src_stride_y,
788 const uint8_t* src_u,
789 int src_stride_u,
790 const uint8_t* src_v,
791 int src_stride_v,
792 uint8_t* dst_raw,
793 int dst_stride_raw,
794 int width,
795 int height) {
796 return I420ToRGB24Matrix(src_y, src_stride_y, src_v,
797 src_stride_v, // Swap U and V
798 src_u, src_stride_u, dst_raw, dst_stride_raw,
799 &kYvuJPEGConstants, // Use Yvu matrix
800 width, height);
801 }
802
803 // Convert H420 to RGB24.
804 LIBYUV_API
H420ToRGB24(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgb24,int dst_stride_rgb24,int width,int height)805 int H420ToRGB24(const uint8_t* src_y,
806 int src_stride_y,
807 const uint8_t* src_u,
808 int src_stride_u,
809 const uint8_t* src_v,
810 int src_stride_v,
811 uint8_t* dst_rgb24,
812 int dst_stride_rgb24,
813 int width,
814 int height) {
815 return I420ToRGB24Matrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
816 src_stride_v, dst_rgb24, dst_stride_rgb24,
817 &kYuvH709Constants, width, height);
818 }
819
820 // Convert H420 to RAW.
821 LIBYUV_API
H420ToRAW(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_raw,int dst_stride_raw,int width,int height)822 int H420ToRAW(const uint8_t* src_y,
823 int src_stride_y,
824 const uint8_t* src_u,
825 int src_stride_u,
826 const uint8_t* src_v,
827 int src_stride_v,
828 uint8_t* dst_raw,
829 int dst_stride_raw,
830 int width,
831 int height) {
832 return I420ToRGB24Matrix(src_y, src_stride_y, src_v,
833 src_stride_v, // Swap U and V
834 src_u, src_stride_u, dst_raw, dst_stride_raw,
835 &kYvuH709Constants, // Use Yvu matrix
836 width, height);
837 }
838
839 // Convert I420 to ARGB1555.
840 LIBYUV_API
I420ToARGB1555(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_argb1555,int dst_stride_argb1555,int width,int height)841 int I420ToARGB1555(const uint8_t* src_y,
842 int src_stride_y,
843 const uint8_t* src_u,
844 int src_stride_u,
845 const uint8_t* src_v,
846 int src_stride_v,
847 uint8_t* dst_argb1555,
848 int dst_stride_argb1555,
849 int width,
850 int height) {
851 int y;
852 void (*I422ToARGB1555Row)(const uint8_t* y_buf, const uint8_t* u_buf,
853 const uint8_t* v_buf, uint8_t* rgb_buf,
854 const struct YuvConstants* yuvconstants,
855 int width) = I422ToARGB1555Row_C;
856 if (!src_y || !src_u || !src_v || !dst_argb1555 || width <= 0 ||
857 height == 0) {
858 return -1;
859 }
860 // Negative height means invert the image.
861 if (height < 0) {
862 height = -height;
863 dst_argb1555 = dst_argb1555 + (height - 1) * dst_stride_argb1555;
864 dst_stride_argb1555 = -dst_stride_argb1555;
865 }
866 #if defined(HAS_I422TOARGB1555ROW_SSSE3)
867 if (TestCpuFlag(kCpuHasSSSE3)) {
868 I422ToARGB1555Row = I422ToARGB1555Row_Any_SSSE3;
869 if (IS_ALIGNED(width, 8)) {
870 I422ToARGB1555Row = I422ToARGB1555Row_SSSE3;
871 }
872 }
873 #endif
874 #if defined(HAS_I422TOARGB1555ROW_AVX2)
875 if (TestCpuFlag(kCpuHasAVX2)) {
876 I422ToARGB1555Row = I422ToARGB1555Row_Any_AVX2;
877 if (IS_ALIGNED(width, 16)) {
878 I422ToARGB1555Row = I422ToARGB1555Row_AVX2;
879 }
880 }
881 #endif
882 #if defined(HAS_I422TOARGB1555ROW_NEON)
883 if (TestCpuFlag(kCpuHasNEON)) {
884 I422ToARGB1555Row = I422ToARGB1555Row_Any_NEON;
885 if (IS_ALIGNED(width, 8)) {
886 I422ToARGB1555Row = I422ToARGB1555Row_NEON;
887 }
888 }
889 #endif
890 #if defined(HAS_I422TOARGB1555ROW_MSA)
891 if (TestCpuFlag(kCpuHasMSA)) {
892 I422ToARGB1555Row = I422ToARGB1555Row_Any_MSA;
893 if (IS_ALIGNED(width, 8)) {
894 I422ToARGB1555Row = I422ToARGB1555Row_MSA;
895 }
896 }
897 #endif
898 #if defined(HAS_I422TOARGB1555ROW_MMI)
899 if (TestCpuFlag(kCpuHasMMI)) {
900 I422ToARGB1555Row = I422ToARGB1555Row_Any_MMI;
901 if (IS_ALIGNED(width, 4)) {
902 I422ToARGB1555Row = I422ToARGB1555Row_MMI;
903 }
904 }
905 #endif
906
907 for (y = 0; y < height; ++y) {
908 I422ToARGB1555Row(src_y, src_u, src_v, dst_argb1555, &kYuvI601Constants,
909 width);
910 dst_argb1555 += dst_stride_argb1555;
911 src_y += src_stride_y;
912 if (y & 1) {
913 src_u += src_stride_u;
914 src_v += src_stride_v;
915 }
916 }
917 return 0;
918 }
919
920 // Convert I420 to ARGB4444.
921 LIBYUV_API
I420ToARGB4444(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_argb4444,int dst_stride_argb4444,int width,int height)922 int I420ToARGB4444(const uint8_t* src_y,
923 int src_stride_y,
924 const uint8_t* src_u,
925 int src_stride_u,
926 const uint8_t* src_v,
927 int src_stride_v,
928 uint8_t* dst_argb4444,
929 int dst_stride_argb4444,
930 int width,
931 int height) {
932 int y;
933 void (*I422ToARGB4444Row)(const uint8_t* y_buf, const uint8_t* u_buf,
934 const uint8_t* v_buf, uint8_t* rgb_buf,
935 const struct YuvConstants* yuvconstants,
936 int width) = I422ToARGB4444Row_C;
937 if (!src_y || !src_u || !src_v || !dst_argb4444 || width <= 0 ||
938 height == 0) {
939 return -1;
940 }
941 // Negative height means invert the image.
942 if (height < 0) {
943 height = -height;
944 dst_argb4444 = dst_argb4444 + (height - 1) * dst_stride_argb4444;
945 dst_stride_argb4444 = -dst_stride_argb4444;
946 }
947 #if defined(HAS_I422TOARGB4444ROW_SSSE3)
948 if (TestCpuFlag(kCpuHasSSSE3)) {
949 I422ToARGB4444Row = I422ToARGB4444Row_Any_SSSE3;
950 if (IS_ALIGNED(width, 8)) {
951 I422ToARGB4444Row = I422ToARGB4444Row_SSSE3;
952 }
953 }
954 #endif
955 #if defined(HAS_I422TOARGB4444ROW_AVX2)
956 if (TestCpuFlag(kCpuHasAVX2)) {
957 I422ToARGB4444Row = I422ToARGB4444Row_Any_AVX2;
958 if (IS_ALIGNED(width, 16)) {
959 I422ToARGB4444Row = I422ToARGB4444Row_AVX2;
960 }
961 }
962 #endif
963 #if defined(HAS_I422TOARGB4444ROW_NEON)
964 if (TestCpuFlag(kCpuHasNEON)) {
965 I422ToARGB4444Row = I422ToARGB4444Row_Any_NEON;
966 if (IS_ALIGNED(width, 8)) {
967 I422ToARGB4444Row = I422ToARGB4444Row_NEON;
968 }
969 }
970 #endif
971 #if defined(HAS_I422TOARGB4444ROW_MSA)
972 if (TestCpuFlag(kCpuHasMSA)) {
973 I422ToARGB4444Row = I422ToARGB4444Row_Any_MSA;
974 if (IS_ALIGNED(width, 8)) {
975 I422ToARGB4444Row = I422ToARGB4444Row_MSA;
976 }
977 }
978 #endif
979 #if defined(HAS_I422TOARGB4444ROW_MMI)
980 if (TestCpuFlag(kCpuHasMMI)) {
981 I422ToARGB4444Row = I422ToARGB4444Row_Any_MMI;
982 if (IS_ALIGNED(width, 4)) {
983 I422ToARGB4444Row = I422ToARGB4444Row_MMI;
984 }
985 }
986 #endif
987
988 for (y = 0; y < height; ++y) {
989 I422ToARGB4444Row(src_y, src_u, src_v, dst_argb4444, &kYuvI601Constants,
990 width);
991 dst_argb4444 += dst_stride_argb4444;
992 src_y += src_stride_y;
993 if (y & 1) {
994 src_u += src_stride_u;
995 src_v += src_stride_v;
996 }
997 }
998 return 0;
999 }
1000
1001 // Convert I420 to RGB565 with specified color matrix.
1002 LIBYUV_API
I420ToRGB565Matrix(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgb565,int dst_stride_rgb565,const struct YuvConstants * yuvconstants,int width,int height)1003 int I420ToRGB565Matrix(const uint8_t* src_y,
1004 int src_stride_y,
1005 const uint8_t* src_u,
1006 int src_stride_u,
1007 const uint8_t* src_v,
1008 int src_stride_v,
1009 uint8_t* dst_rgb565,
1010 int dst_stride_rgb565,
1011 const struct YuvConstants* yuvconstants,
1012 int width,
1013 int height) {
1014 int y;
1015 void (*I422ToRGB565Row)(const uint8_t* y_buf, const uint8_t* u_buf,
1016 const uint8_t* v_buf, uint8_t* rgb_buf,
1017 const struct YuvConstants* yuvconstants, int width) =
1018 I422ToRGB565Row_C;
1019 if (!src_y || !src_u || !src_v || !dst_rgb565 || width <= 0 || height == 0) {
1020 return -1;
1021 }
1022 // Negative height means invert the image.
1023 if (height < 0) {
1024 height = -height;
1025 dst_rgb565 = dst_rgb565 + (height - 1) * dst_stride_rgb565;
1026 dst_stride_rgb565 = -dst_stride_rgb565;
1027 }
1028 #if defined(HAS_I422TORGB565ROW_SSSE3)
1029 if (TestCpuFlag(kCpuHasSSSE3)) {
1030 I422ToRGB565Row = I422ToRGB565Row_Any_SSSE3;
1031 if (IS_ALIGNED(width, 8)) {
1032 I422ToRGB565Row = I422ToRGB565Row_SSSE3;
1033 }
1034 }
1035 #endif
1036 #if defined(HAS_I422TORGB565ROW_AVX2)
1037 if (TestCpuFlag(kCpuHasAVX2)) {
1038 I422ToRGB565Row = I422ToRGB565Row_Any_AVX2;
1039 if (IS_ALIGNED(width, 16)) {
1040 I422ToRGB565Row = I422ToRGB565Row_AVX2;
1041 }
1042 }
1043 #endif
1044 #if defined(HAS_I422TORGB565ROW_NEON)
1045 if (TestCpuFlag(kCpuHasNEON)) {
1046 I422ToRGB565Row = I422ToRGB565Row_Any_NEON;
1047 if (IS_ALIGNED(width, 8)) {
1048 I422ToRGB565Row = I422ToRGB565Row_NEON;
1049 }
1050 }
1051 #endif
1052 #if defined(HAS_I422TORGB565ROW_MSA)
1053 if (TestCpuFlag(kCpuHasMSA)) {
1054 I422ToRGB565Row = I422ToRGB565Row_Any_MSA;
1055 if (IS_ALIGNED(width, 8)) {
1056 I422ToRGB565Row = I422ToRGB565Row_MSA;
1057 }
1058 }
1059 #endif
1060 #if defined(HAS_I422TORGB565ROW_MMI)
1061 if (TestCpuFlag(kCpuHasMMI)) {
1062 I422ToRGB565Row = I422ToRGB565Row_Any_MMI;
1063 if (IS_ALIGNED(width, 4)) {
1064 I422ToRGB565Row = I422ToRGB565Row_MMI;
1065 }
1066 }
1067 #endif
1068
1069 for (y = 0; y < height; ++y) {
1070 I422ToRGB565Row(src_y, src_u, src_v, dst_rgb565, yuvconstants, width);
1071 dst_rgb565 += dst_stride_rgb565;
1072 src_y += src_stride_y;
1073 if (y & 1) {
1074 src_u += src_stride_u;
1075 src_v += src_stride_v;
1076 }
1077 }
1078 return 0;
1079 }
1080
1081 // Convert I420 to RGB565.
1082 LIBYUV_API
I420ToRGB565(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgb565,int dst_stride_rgb565,int width,int height)1083 int I420ToRGB565(const uint8_t* src_y,
1084 int src_stride_y,
1085 const uint8_t* src_u,
1086 int src_stride_u,
1087 const uint8_t* src_v,
1088 int src_stride_v,
1089 uint8_t* dst_rgb565,
1090 int dst_stride_rgb565,
1091 int width,
1092 int height) {
1093 return I420ToRGB565Matrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
1094 src_stride_v, dst_rgb565, dst_stride_rgb565,
1095 &kYuvI601Constants, width, height);
1096 }
1097
1098 // Convert J420 to RGB565.
1099 LIBYUV_API
J420ToRGB565(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgb565,int dst_stride_rgb565,int width,int height)1100 int J420ToRGB565(const uint8_t* src_y,
1101 int src_stride_y,
1102 const uint8_t* src_u,
1103 int src_stride_u,
1104 const uint8_t* src_v,
1105 int src_stride_v,
1106 uint8_t* dst_rgb565,
1107 int dst_stride_rgb565,
1108 int width,
1109 int height) {
1110 return I420ToRGB565Matrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
1111 src_stride_v, dst_rgb565, dst_stride_rgb565,
1112 &kYuvJPEGConstants, width, height);
1113 }
1114
1115 // Convert H420 to RGB565.
1116 LIBYUV_API
H420ToRGB565(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgb565,int dst_stride_rgb565,int width,int height)1117 int H420ToRGB565(const uint8_t* src_y,
1118 int src_stride_y,
1119 const uint8_t* src_u,
1120 int src_stride_u,
1121 const uint8_t* src_v,
1122 int src_stride_v,
1123 uint8_t* dst_rgb565,
1124 int dst_stride_rgb565,
1125 int width,
1126 int height) {
1127 return I420ToRGB565Matrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
1128 src_stride_v, dst_rgb565, dst_stride_rgb565,
1129 &kYuvH709Constants, width, height);
1130 }
1131
1132 // Convert I422 to RGB565.
1133 LIBYUV_API
I422ToRGB565(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgb565,int dst_stride_rgb565,int width,int height)1134 int I422ToRGB565(const uint8_t* src_y,
1135 int src_stride_y,
1136 const uint8_t* src_u,
1137 int src_stride_u,
1138 const uint8_t* src_v,
1139 int src_stride_v,
1140 uint8_t* dst_rgb565,
1141 int dst_stride_rgb565,
1142 int width,
1143 int height) {
1144 int y;
1145 void (*I422ToRGB565Row)(const uint8_t* y_buf, const uint8_t* u_buf,
1146 const uint8_t* v_buf, uint8_t* rgb_buf,
1147 const struct YuvConstants* yuvconstants, int width) =
1148 I422ToRGB565Row_C;
1149 if (!src_y || !src_u || !src_v || !dst_rgb565 || width <= 0 || height == 0) {
1150 return -1;
1151 }
1152 // Negative height means invert the image.
1153 if (height < 0) {
1154 height = -height;
1155 dst_rgb565 = dst_rgb565 + (height - 1) * dst_stride_rgb565;
1156 dst_stride_rgb565 = -dst_stride_rgb565;
1157 }
1158 #if defined(HAS_I422TORGB565ROW_SSSE3)
1159 if (TestCpuFlag(kCpuHasSSSE3)) {
1160 I422ToRGB565Row = I422ToRGB565Row_Any_SSSE3;
1161 if (IS_ALIGNED(width, 8)) {
1162 I422ToRGB565Row = I422ToRGB565Row_SSSE3;
1163 }
1164 }
1165 #endif
1166 #if defined(HAS_I422TORGB565ROW_AVX2)
1167 if (TestCpuFlag(kCpuHasAVX2)) {
1168 I422ToRGB565Row = I422ToRGB565Row_Any_AVX2;
1169 if (IS_ALIGNED(width, 16)) {
1170 I422ToRGB565Row = I422ToRGB565Row_AVX2;
1171 }
1172 }
1173 #endif
1174 #if defined(HAS_I422TORGB565ROW_NEON)
1175 if (TestCpuFlag(kCpuHasNEON)) {
1176 I422ToRGB565Row = I422ToRGB565Row_Any_NEON;
1177 if (IS_ALIGNED(width, 8)) {
1178 I422ToRGB565Row = I422ToRGB565Row_NEON;
1179 }
1180 }
1181 #endif
1182 #if defined(HAS_I422TORGB565ROW_MSA)
1183 if (TestCpuFlag(kCpuHasMSA)) {
1184 I422ToRGB565Row = I422ToRGB565Row_Any_MSA;
1185 if (IS_ALIGNED(width, 8)) {
1186 I422ToRGB565Row = I422ToRGB565Row_MSA;
1187 }
1188 }
1189 #endif
1190
1191 for (y = 0; y < height; ++y) {
1192 I422ToRGB565Row(src_y, src_u, src_v, dst_rgb565, &kYuvI601Constants, width);
1193 dst_rgb565 += dst_stride_rgb565;
1194 src_y += src_stride_y;
1195 src_u += src_stride_u;
1196 src_v += src_stride_v;
1197 }
1198 return 0;
1199 }
1200
1201 // Ordered 8x8 dither for 888 to 565. Values from 0 to 7.
1202 static const uint8_t kDither565_4x4[16] = {
1203 0, 4, 1, 5, 6, 2, 7, 3, 1, 5, 0, 4, 7, 3, 6, 2,
1204 };
1205
1206 // Convert I420 to RGB565 with dithering.
1207 LIBYUV_API
I420ToRGB565Dither(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_rgb565,int dst_stride_rgb565,const uint8_t * dither4x4,int width,int height)1208 int I420ToRGB565Dither(const uint8_t* src_y,
1209 int src_stride_y,
1210 const uint8_t* src_u,
1211 int src_stride_u,
1212 const uint8_t* src_v,
1213 int src_stride_v,
1214 uint8_t* dst_rgb565,
1215 int dst_stride_rgb565,
1216 const uint8_t* dither4x4,
1217 int width,
1218 int height) {
1219 int y;
1220 void (*I422ToARGBRow)(const uint8_t* y_buf, const uint8_t* u_buf,
1221 const uint8_t* v_buf, uint8_t* rgb_buf,
1222 const struct YuvConstants* yuvconstants, int width) =
1223 I422ToARGBRow_C;
1224 void (*ARGBToRGB565DitherRow)(const uint8_t* src_argb, uint8_t* dst_rgb,
1225 const uint32_t dither4, int width) =
1226 ARGBToRGB565DitherRow_C;
1227 if (!src_y || !src_u || !src_v || !dst_rgb565 || width <= 0 || height == 0) {
1228 return -1;
1229 }
1230 // Negative height means invert the image.
1231 if (height < 0) {
1232 height = -height;
1233 dst_rgb565 = dst_rgb565 + (height - 1) * dst_stride_rgb565;
1234 dst_stride_rgb565 = -dst_stride_rgb565;
1235 }
1236 if (!dither4x4) {
1237 dither4x4 = kDither565_4x4;
1238 }
1239 #if defined(HAS_I422TOARGBROW_SSSE3)
1240 if (TestCpuFlag(kCpuHasSSSE3)) {
1241 I422ToARGBRow = I422ToARGBRow_Any_SSSE3;
1242 if (IS_ALIGNED(width, 8)) {
1243 I422ToARGBRow = I422ToARGBRow_SSSE3;
1244 }
1245 }
1246 #endif
1247 #if defined(HAS_I422TOARGBROW_AVX2)
1248 if (TestCpuFlag(kCpuHasAVX2)) {
1249 I422ToARGBRow = I422ToARGBRow_Any_AVX2;
1250 if (IS_ALIGNED(width, 16)) {
1251 I422ToARGBRow = I422ToARGBRow_AVX2;
1252 }
1253 }
1254 #endif
1255 #if defined(HAS_I422TOARGBROW_NEON)
1256 if (TestCpuFlag(kCpuHasNEON)) {
1257 I422ToARGBRow = I422ToARGBRow_Any_NEON;
1258 if (IS_ALIGNED(width, 8)) {
1259 I422ToARGBRow = I422ToARGBRow_NEON;
1260 }
1261 }
1262 #endif
1263 #if defined(HAS_I422TOARGBROW_MSA)
1264 if (TestCpuFlag(kCpuHasMSA)) {
1265 I422ToARGBRow = I422ToARGBRow_Any_MSA;
1266 if (IS_ALIGNED(width, 8)) {
1267 I422ToARGBRow = I422ToARGBRow_MSA;
1268 }
1269 }
1270 #endif
1271 #if defined(HAS_I422TOARGBROW_MMI)
1272 if (TestCpuFlag(kCpuHasMMI)) {
1273 I422ToARGBRow = I422ToARGBRow_Any_MMI;
1274 if (IS_ALIGNED(width, 4)) {
1275 I422ToARGBRow = I422ToARGBRow_MMI;
1276 }
1277 }
1278 #endif
1279 #if defined(HAS_ARGBTORGB565DITHERROW_SSE2)
1280 if (TestCpuFlag(kCpuHasSSE2)) {
1281 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_Any_SSE2;
1282 if (IS_ALIGNED(width, 4)) {
1283 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_SSE2;
1284 }
1285 }
1286 #endif
1287 #if defined(HAS_ARGBTORGB565DITHERROW_AVX2)
1288 if (TestCpuFlag(kCpuHasAVX2)) {
1289 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_Any_AVX2;
1290 if (IS_ALIGNED(width, 8)) {
1291 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_AVX2;
1292 }
1293 }
1294 #endif
1295 #if defined(HAS_ARGBTORGB565DITHERROW_NEON)
1296 if (TestCpuFlag(kCpuHasNEON)) {
1297 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_Any_NEON;
1298 if (IS_ALIGNED(width, 8)) {
1299 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_NEON;
1300 }
1301 }
1302 #endif
1303 #if defined(HAS_ARGBTORGB565DITHERROW_MSA)
1304 if (TestCpuFlag(kCpuHasMSA)) {
1305 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_Any_MSA;
1306 if (IS_ALIGNED(width, 8)) {
1307 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_MSA;
1308 }
1309 }
1310 #endif
1311 #if defined(HAS_ARGBTORGB565DITHERROW_MMI)
1312 if (TestCpuFlag(kCpuHasMMI)) {
1313 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_Any_MMI;
1314 if (IS_ALIGNED(width, 4)) {
1315 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_MMI;
1316 }
1317 }
1318 #endif
1319 {
1320 // Allocate a row of argb.
1321 align_buffer_64(row_argb, width * 4);
1322 for (y = 0; y < height; ++y) {
1323 I422ToARGBRow(src_y, src_u, src_v, row_argb, &kYuvI601Constants, width);
1324 ARGBToRGB565DitherRow(row_argb, dst_rgb565,
1325 *(const uint32_t*)(dither4x4 + ((y & 3) << 2)),
1326 width);
1327 dst_rgb565 += dst_stride_rgb565;
1328 src_y += src_stride_y;
1329 if (y & 1) {
1330 src_u += src_stride_u;
1331 src_v += src_stride_v;
1332 }
1333 }
1334 free_aligned_buffer_64(row_argb);
1335 }
1336 return 0;
1337 }
1338
1339 // Convert I420 to AR30 with matrix
I420ToAR30Matrix(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_ar30,int dst_stride_ar30,const struct YuvConstants * yuvconstants,int width,int height)1340 static int I420ToAR30Matrix(const uint8_t* src_y,
1341 int src_stride_y,
1342 const uint8_t* src_u,
1343 int src_stride_u,
1344 const uint8_t* src_v,
1345 int src_stride_v,
1346 uint8_t* dst_ar30,
1347 int dst_stride_ar30,
1348 const struct YuvConstants* yuvconstants,
1349 int width,
1350 int height) {
1351 int y;
1352 void (*I422ToAR30Row)(const uint8_t* y_buf, const uint8_t* u_buf,
1353 const uint8_t* v_buf, uint8_t* rgb_buf,
1354 const struct YuvConstants* yuvconstants, int width) =
1355 I422ToAR30Row_C;
1356
1357 if (!src_y || !src_u || !src_v || !dst_ar30 || width <= 0 || height == 0) {
1358 return -1;
1359 }
1360 // Negative height means invert the image.
1361 if (height < 0) {
1362 height = -height;
1363 dst_ar30 = dst_ar30 + (height - 1) * dst_stride_ar30;
1364 dst_stride_ar30 = -dst_stride_ar30;
1365 }
1366
1367 #if defined(HAS_I422TOAR30ROW_SSSE3)
1368 if (TestCpuFlag(kCpuHasSSSE3)) {
1369 I422ToAR30Row = I422ToAR30Row_Any_SSSE3;
1370 if (IS_ALIGNED(width, 8)) {
1371 I422ToAR30Row = I422ToAR30Row_SSSE3;
1372 }
1373 }
1374 #endif
1375 #if defined(HAS_I422TOAR30ROW_AVX2)
1376 if (TestCpuFlag(kCpuHasAVX2)) {
1377 I422ToAR30Row = I422ToAR30Row_Any_AVX2;
1378 if (IS_ALIGNED(width, 16)) {
1379 I422ToAR30Row = I422ToAR30Row_AVX2;
1380 }
1381 }
1382 #endif
1383
1384 for (y = 0; y < height; ++y) {
1385 I422ToAR30Row(src_y, src_u, src_v, dst_ar30, yuvconstants, width);
1386 dst_ar30 += dst_stride_ar30;
1387 src_y += src_stride_y;
1388 if (y & 1) {
1389 src_u += src_stride_u;
1390 src_v += src_stride_v;
1391 }
1392 }
1393 return 0;
1394 }
1395
1396 // Convert I420 to AR30.
1397 LIBYUV_API
I420ToAR30(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_ar30,int dst_stride_ar30,int width,int height)1398 int I420ToAR30(const uint8_t* src_y,
1399 int src_stride_y,
1400 const uint8_t* src_u,
1401 int src_stride_u,
1402 const uint8_t* src_v,
1403 int src_stride_v,
1404 uint8_t* dst_ar30,
1405 int dst_stride_ar30,
1406 int width,
1407 int height) {
1408 return I420ToAR30Matrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
1409 src_stride_v, dst_ar30, dst_stride_ar30,
1410 &kYuvI601Constants, width, height);
1411 }
1412
1413 // Convert H420 to AR30.
1414 LIBYUV_API
H420ToAR30(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_ar30,int dst_stride_ar30,int width,int height)1415 int H420ToAR30(const uint8_t* src_y,
1416 int src_stride_y,
1417 const uint8_t* src_u,
1418 int src_stride_u,
1419 const uint8_t* src_v,
1420 int src_stride_v,
1421 uint8_t* dst_ar30,
1422 int dst_stride_ar30,
1423 int width,
1424 int height) {
1425 return I420ToAR30Matrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
1426 src_stride_v, dst_ar30, dst_stride_ar30,
1427 &kYvuH709Constants, width, height);
1428 }
1429
1430 // Convert I420 to specified format
1431 LIBYUV_API
ConvertFromI420(const uint8_t * y,int y_stride,const uint8_t * u,int u_stride,const uint8_t * v,int v_stride,uint8_t * dst_sample,int dst_sample_stride,int width,int height,uint32_t fourcc)1432 int ConvertFromI420(const uint8_t* y,
1433 int y_stride,
1434 const uint8_t* u,
1435 int u_stride,
1436 const uint8_t* v,
1437 int v_stride,
1438 uint8_t* dst_sample,
1439 int dst_sample_stride,
1440 int width,
1441 int height,
1442 uint32_t fourcc) {
1443 uint32_t format = CanonicalFourCC(fourcc);
1444 int r = 0;
1445 if (!y || !u || !v || !dst_sample || width <= 0 || height == 0) {
1446 return -1;
1447 }
1448 switch (format) {
1449 // Single plane formats
1450 case FOURCC_YUY2:
1451 r = I420ToYUY2(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1452 dst_sample_stride ? dst_sample_stride : width * 2, width,
1453 height);
1454 break;
1455 case FOURCC_UYVY:
1456 r = I420ToUYVY(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1457 dst_sample_stride ? dst_sample_stride : width * 2, width,
1458 height);
1459 break;
1460 case FOURCC_RGBP:
1461 r = I420ToRGB565(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1462 dst_sample_stride ? dst_sample_stride : width * 2, width,
1463 height);
1464 break;
1465 case FOURCC_RGBO:
1466 r = I420ToARGB1555(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1467 dst_sample_stride ? dst_sample_stride : width * 2,
1468 width, height);
1469 break;
1470 case FOURCC_R444:
1471 r = I420ToARGB4444(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1472 dst_sample_stride ? dst_sample_stride : width * 2,
1473 width, height);
1474 break;
1475 case FOURCC_24BG:
1476 r = I420ToRGB24(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1477 dst_sample_stride ? dst_sample_stride : width * 3, width,
1478 height);
1479 break;
1480 case FOURCC_RAW:
1481 r = I420ToRAW(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1482 dst_sample_stride ? dst_sample_stride : width * 3, width,
1483 height);
1484 break;
1485 case FOURCC_ARGB:
1486 r = I420ToARGB(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1487 dst_sample_stride ? dst_sample_stride : width * 4, width,
1488 height);
1489 break;
1490 case FOURCC_BGRA:
1491 r = I420ToBGRA(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1492 dst_sample_stride ? dst_sample_stride : width * 4, width,
1493 height);
1494 break;
1495 case FOURCC_ABGR:
1496 r = I420ToABGR(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1497 dst_sample_stride ? dst_sample_stride : width * 4, width,
1498 height);
1499 break;
1500 case FOURCC_RGBA:
1501 r = I420ToRGBA(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1502 dst_sample_stride ? dst_sample_stride : width * 4, width,
1503 height);
1504 break;
1505 case FOURCC_AR30:
1506 r = I420ToAR30(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1507 dst_sample_stride ? dst_sample_stride : width * 4, width,
1508 height);
1509 break;
1510 case FOURCC_I400:
1511 r = I400Copy(y, y_stride, dst_sample,
1512 dst_sample_stride ? dst_sample_stride : width, width,
1513 height);
1514 break;
1515 case FOURCC_NV12: {
1516 uint8_t* dst_uv = dst_sample + width * height;
1517 r = I420ToNV12(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1518 dst_sample_stride ? dst_sample_stride : width, dst_uv,
1519 dst_sample_stride ? dst_sample_stride : width, width,
1520 height);
1521 break;
1522 }
1523 case FOURCC_NV21: {
1524 uint8_t* dst_vu = dst_sample + width * height;
1525 r = I420ToNV21(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1526 dst_sample_stride ? dst_sample_stride : width, dst_vu,
1527 dst_sample_stride ? dst_sample_stride : width, width,
1528 height);
1529 break;
1530 }
1531 // TODO(fbarchard): Add M420.
1532 // Triplanar formats
1533 case FOURCC_I420:
1534 case FOURCC_YV12: {
1535 dst_sample_stride = dst_sample_stride ? dst_sample_stride : width;
1536 int halfstride = (dst_sample_stride + 1) / 2;
1537 int halfheight = (height + 1) / 2;
1538 uint8_t* dst_u;
1539 uint8_t* dst_v;
1540 if (format == FOURCC_YV12) {
1541 dst_v = dst_sample + dst_sample_stride * height;
1542 dst_u = dst_v + halfstride * halfheight;
1543 } else {
1544 dst_u = dst_sample + dst_sample_stride * height;
1545 dst_v = dst_u + halfstride * halfheight;
1546 }
1547 r = I420Copy(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1548 dst_sample_stride, dst_u, halfstride, dst_v, halfstride,
1549 width, height);
1550 break;
1551 }
1552 case FOURCC_I422:
1553 case FOURCC_YV16: {
1554 dst_sample_stride = dst_sample_stride ? dst_sample_stride : width;
1555 int halfstride = (dst_sample_stride + 1) / 2;
1556 uint8_t* dst_u;
1557 uint8_t* dst_v;
1558 if (format == FOURCC_YV16) {
1559 dst_v = dst_sample + dst_sample_stride * height;
1560 dst_u = dst_v + halfstride * height;
1561 } else {
1562 dst_u = dst_sample + dst_sample_stride * height;
1563 dst_v = dst_u + halfstride * height;
1564 }
1565 r = I420ToI422(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1566 dst_sample_stride, dst_u, halfstride, dst_v, halfstride,
1567 width, height);
1568 break;
1569 }
1570 case FOURCC_I444:
1571 case FOURCC_YV24: {
1572 dst_sample_stride = dst_sample_stride ? dst_sample_stride : width;
1573 uint8_t* dst_u;
1574 uint8_t* dst_v;
1575 if (format == FOURCC_YV24) {
1576 dst_v = dst_sample + dst_sample_stride * height;
1577 dst_u = dst_v + dst_sample_stride * height;
1578 } else {
1579 dst_u = dst_sample + dst_sample_stride * height;
1580 dst_v = dst_u + dst_sample_stride * height;
1581 }
1582 r = I420ToI444(y, y_stride, u, u_stride, v, v_stride, dst_sample,
1583 dst_sample_stride, dst_u, dst_sample_stride, dst_v,
1584 dst_sample_stride, width, height);
1585 break;
1586 }
1587 // Formats not supported - MJPG, biplanar, some rgb formats.
1588 default:
1589 return -1; // unknown fourcc - return failure code.
1590 }
1591 return r;
1592 }
1593
1594 #ifdef __cplusplus
1595 } // extern "C"
1596 } // namespace libyuv
1597 #endif
1598