• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/planar_functions.h"
12 
13 #include <string.h>  // for memset()
14 
15 #include "libyuv/cpu_id.h"
16 #ifdef HAVE_JPEG
17 #include "libyuv/mjpeg_decoder.h"
18 #endif
19 #include "libyuv/row.h"
20 
21 #ifdef __cplusplus
22 namespace libyuv {
23 extern "C" {
24 #endif
25 
26 // Copy a plane of data
27 LIBYUV_API
CopyPlane(const uint8 * src_y,int src_stride_y,uint8 * dst_y,int dst_stride_y,int width,int height)28 void CopyPlane(const uint8* src_y, int src_stride_y,
29                uint8* dst_y, int dst_stride_y,
30                int width, int height) {
31   void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
32 #if defined(HAS_COPYROW_NEON)
33   if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 64)) {
34     CopyRow = CopyRow_NEON;
35   }
36 #endif
37 #if defined(HAS_COPYROW_X86)
38   if (TestCpuFlag(kCpuHasX86) && IS_ALIGNED(width, 4)) {
39     CopyRow = CopyRow_X86;
40   }
41 #endif
42 #if defined(HAS_COPYROW_SSE2)
43   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 32) &&
44       IS_ALIGNED(src_y, 16) && IS_ALIGNED(src_stride_y, 16) &&
45       IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
46     CopyRow = CopyRow_SSE2;
47   }
48 #endif
49 
50   // Copy plane
51   for (int y = 0; y < height; ++y) {
52     CopyRow(src_y, dst_y, width);
53     src_y += src_stride_y;
54     dst_y += dst_stride_y;
55   }
56 }
57 
58 // Convert I420 to I400.
59 LIBYUV_API
I420ToI400(const uint8 * src_y,int src_stride_y,uint8 *,int,uint8 *,int,uint8 * dst_y,int dst_stride_y,int width,int height)60 int I420ToI400(const uint8* src_y, int src_stride_y,
61                uint8*, int,  // src_u
62                uint8*, int,  // src_v
63                uint8* dst_y, int dst_stride_y,
64                int width, int height) {
65   if (!src_y || !dst_y || width <= 0 || height == 0) {
66     return -1;
67   }
68   // Negative height means invert the image.
69   if (height < 0) {
70     height = -height;
71     src_y = src_y + (height - 1) * src_stride_y;
72     src_stride_y = -src_stride_y;
73   }
74   CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
75   return 0;
76 }
77 
78 // Mirror a plane of data
MirrorPlane(const uint8 * src_y,int src_stride_y,uint8 * dst_y,int dst_stride_y,int width,int height)79 void MirrorPlane(const uint8* src_y, int src_stride_y,
80                  uint8* dst_y, int dst_stride_y,
81                  int width, int height) {
82   void (*MirrorRow)(const uint8* src, uint8* dst, int width) = MirrorRow_C;
83 #if defined(HAS_MIRRORROW_NEON)
84   if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 16)) {
85     MirrorRow = MirrorRow_NEON;
86   }
87 #endif
88 #if defined(HAS_MIRRORROW_SSE2)
89   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 16)) {
90     MirrorRow = MirrorRow_SSE2;
91 #if defined(HAS_MIRRORROW_SSSE3)
92     if (TestCpuFlag(kCpuHasSSSE3) &&
93         IS_ALIGNED(src_y, 16) && IS_ALIGNED(src_stride_y, 16)) {
94       MirrorRow = MirrorRow_SSSE3;
95     }
96 #endif
97   }
98 #endif
99 
100   // Mirror plane
101   for (int y = 0; y < height; ++y) {
102     MirrorRow(src_y, dst_y, width);
103     src_y += src_stride_y;
104     dst_y += dst_stride_y;
105   }
106 }
107 
108 // Convert YUY2 to I422.
109 LIBYUV_API
YUY2ToI422(const uint8 * src_yuy2,int src_stride_yuy2,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)110 int YUY2ToI422(const uint8* src_yuy2, int src_stride_yuy2,
111                uint8* dst_y, int dst_stride_y,
112                uint8* dst_u, int dst_stride_u,
113                uint8* dst_v, int dst_stride_v,
114                int width, int height) {
115   // Negative height means invert the image.
116   if (height < 0) {
117     height = -height;
118     src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
119     src_stride_yuy2 = -src_stride_yuy2;
120   }
121   void (*YUY2ToUV422Row)(const uint8* src_yuy2,
122                       uint8* dst_u, uint8* dst_v, int pix);
123   void (*YUY2ToYRow)(const uint8* src_yuy2,
124                      uint8* dst_y, int pix);
125   YUY2ToYRow = YUY2ToYRow_C;
126   YUY2ToUV422Row = YUY2ToUV422Row_C;
127 #if defined(HAS_YUY2TOYROW_SSE2)
128   if (TestCpuFlag(kCpuHasSSE2)) {
129     if (width > 16) {
130       YUY2ToUV422Row = YUY2ToUV422Row_Any_SSE2;
131       YUY2ToYRow = YUY2ToYRow_Any_SSE2;
132     }
133     if (IS_ALIGNED(width, 16)) {
134       YUY2ToUV422Row = YUY2ToUV422Row_Unaligned_SSE2;
135       YUY2ToYRow = YUY2ToYRow_Unaligned_SSE2;
136       if (IS_ALIGNED(src_yuy2, 16) && IS_ALIGNED(src_stride_yuy2, 16)) {
137         YUY2ToUV422Row = YUY2ToUV422Row_SSE2;
138         if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
139           YUY2ToYRow = YUY2ToYRow_SSE2;
140         }
141       }
142     }
143   }
144 #elif defined(HAS_YUY2TOYROW_NEON)
145   if (TestCpuFlag(kCpuHasNEON)) {
146     if (width > 8) {
147       YUY2ToYRow = YUY2ToYRow_Any_NEON;
148       if (width > 16) {
149         YUY2ToUV422Row = YUY2ToUV422Row_Any_NEON;
150       }
151     }
152     if (IS_ALIGNED(width, 16)) {
153       YUY2ToYRow = YUY2ToYRow_NEON;
154       YUY2ToUV422Row = YUY2ToUV422Row_NEON;
155     }
156   }
157 #endif
158 
159   for (int y = 0; y < height; ++y) {
160     YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
161     YUY2ToYRow(src_yuy2, dst_y, width);
162     src_yuy2 += src_stride_yuy2;
163     dst_y += dst_stride_y;
164     dst_u += dst_stride_u;
165     dst_v += dst_stride_v;
166   }
167   return 0;
168 }
169 
170 // Convert UYVY to I422.
171 LIBYUV_API
UYVYToI422(const uint8 * src_uyvy,int src_stride_uyvy,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)172 int UYVYToI422(const uint8* src_uyvy, int src_stride_uyvy,
173                uint8* dst_y, int dst_stride_y,
174                uint8* dst_u, int dst_stride_u,
175                uint8* dst_v, int dst_stride_v,
176                int width, int height) {
177   // Negative height means invert the image.
178   if (height < 0) {
179     height = -height;
180     src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy;
181     src_stride_uyvy = -src_stride_uyvy;
182   }
183   void (*UYVYToUV422Row)(const uint8* src_uyvy,
184                       uint8* dst_u, uint8* dst_v, int pix);
185   void (*UYVYToYRow)(const uint8* src_uyvy,
186                      uint8* dst_y, int pix);
187   UYVYToYRow = UYVYToYRow_C;
188   UYVYToUV422Row = UYVYToUV422Row_C;
189 #if defined(HAS_UYVYTOYROW_SSE2)
190   if (TestCpuFlag(kCpuHasSSE2)) {
191     if (width > 16) {
192       UYVYToUV422Row = UYVYToUV422Row_Any_SSE2;
193       UYVYToYRow = UYVYToYRow_Any_SSE2;
194     }
195     if (IS_ALIGNED(width, 16)) {
196       UYVYToUV422Row = UYVYToUV422Row_Unaligned_SSE2;
197       UYVYToYRow = UYVYToYRow_Unaligned_SSE2;
198       if (IS_ALIGNED(src_uyvy, 16) && IS_ALIGNED(src_stride_uyvy, 16)) {
199         UYVYToUV422Row = UYVYToUV422Row_SSE2;
200         if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
201           UYVYToYRow = UYVYToYRow_SSE2;
202         }
203       }
204     }
205   }
206 #elif defined(HAS_UYVYTOYROW_NEON)
207   if (TestCpuFlag(kCpuHasNEON)) {
208     if (width > 8) {
209       UYVYToYRow = UYVYToYRow_Any_NEON;
210       if (width > 16) {
211         UYVYToUV422Row = UYVYToUV422Row_Any_NEON;
212       }
213     }
214     if (IS_ALIGNED(width, 16)) {
215       UYVYToYRow = UYVYToYRow_NEON;
216       UYVYToUV422Row = UYVYToUV422Row_NEON;
217     }
218   }
219 #endif
220 
221   for (int y = 0; y < height; ++y) {
222     UYVYToUV422Row(src_uyvy, dst_u, dst_v, width);
223     UYVYToYRow(src_uyvy, dst_y, width);
224     src_uyvy += src_stride_uyvy;
225     dst_y += dst_stride_y;
226     dst_u += dst_stride_u;
227     dst_v += dst_stride_v;
228   }
229   return 0;
230 }
231 
232 // Mirror I420 with optional flipping
233 LIBYUV_API
I420Mirror(const uint8 * src_y,int src_stride_y,const uint8 * src_u,int src_stride_u,const uint8 * src_v,int src_stride_v,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)234 int I420Mirror(const uint8* src_y, int src_stride_y,
235                const uint8* src_u, int src_stride_u,
236                const uint8* src_v, int src_stride_v,
237                uint8* dst_y, int dst_stride_y,
238                uint8* dst_u, int dst_stride_u,
239                uint8* dst_v, int dst_stride_v,
240                int width, int height) {
241   if (!src_y || !src_u || !src_v || !dst_y || !dst_u || !dst_v ||
242       width <= 0 || height == 0) {
243     return -1;
244   }
245   // Negative height means invert the image.
246   if (height < 0) {
247     height = -height;
248     int halfheight = (height + 1) >> 1;
249     src_y = src_y + (height - 1) * src_stride_y;
250     src_u = src_u + (halfheight - 1) * src_stride_u;
251     src_v = src_v + (halfheight - 1) * src_stride_v;
252     src_stride_y = -src_stride_y;
253     src_stride_u = -src_stride_u;
254     src_stride_v = -src_stride_v;
255   }
256 
257   int halfwidth = (width + 1) >> 1;
258   int halfheight = (height + 1) >> 1;
259   if (dst_y) {
260     MirrorPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
261   }
262   MirrorPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight);
263   MirrorPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight);
264   return 0;
265 }
266 
267 // ARGB mirror.
268 LIBYUV_API
ARGBMirror(const uint8 * src_argb,int src_stride_argb,uint8 * dst_argb,int dst_stride_argb,int width,int height)269 int ARGBMirror(const uint8* src_argb, int src_stride_argb,
270                uint8* dst_argb, int dst_stride_argb,
271                int width, int height) {
272   if (!src_argb || !dst_argb || width <= 0 || height == 0) {
273     return -1;
274   }
275   // Negative height means invert the image.
276   if (height < 0) {
277     height = -height;
278     src_argb = src_argb + (height - 1) * src_stride_argb;
279     src_stride_argb = -src_stride_argb;
280   }
281 
282   void (*ARGBMirrorRow)(const uint8* src, uint8* dst, int width) =
283       ARGBMirrorRow_C;
284 #if defined(HAS_ARGBMIRRORROW_SSSE3)
285   if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 4) &&
286       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16) &&
287       IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
288     ARGBMirrorRow = ARGBMirrorRow_SSSE3;
289   }
290 #endif
291 
292   // Mirror plane
293   for (int y = 0; y < height; ++y) {
294     ARGBMirrorRow(src_argb, dst_argb, width);
295     src_argb += src_stride_argb;
296     dst_argb += dst_stride_argb;
297   }
298   return 0;
299 }
300 
301 // Get a blender that optimized for the CPU, alignment and pixel count.
302 // As there are 6 blenders to choose from, the caller should try to use
303 // the same blend function for all pixels if possible.
304 LIBYUV_API
GetARGBBlend()305 ARGBBlendRow GetARGBBlend() {
306   void (*ARGBBlendRow)(const uint8* src_argb, const uint8* src_argb1,
307                        uint8* dst_argb, int width) = ARGBBlendRow_C;
308 #if defined(HAS_ARGBBLENDROW_SSSE3)
309   if (TestCpuFlag(kCpuHasSSSE3)) {
310     ARGBBlendRow = ARGBBlendRow_SSSE3;
311     return ARGBBlendRow;
312   }
313 #endif
314 #if defined(HAS_ARGBBLENDROW_SSE2)
315   if (TestCpuFlag(kCpuHasSSE2)) {
316     ARGBBlendRow = ARGBBlendRow_SSE2;
317   }
318 #endif
319   return ARGBBlendRow;
320 }
321 
322 // Alpha Blend 2 ARGB images and store to destination.
323 LIBYUV_API
ARGBBlend(const uint8 * src_argb0,int src_stride_argb0,const uint8 * src_argb1,int src_stride_argb1,uint8 * dst_argb,int dst_stride_argb,int width,int height)324 int ARGBBlend(const uint8* src_argb0, int src_stride_argb0,
325               const uint8* src_argb1, int src_stride_argb1,
326               uint8* dst_argb, int dst_stride_argb,
327               int width, int height) {
328   if (!src_argb0 || !src_argb1 || !dst_argb || width <= 0 || height == 0) {
329     return -1;
330   }
331   // Negative height means invert the image.
332   if (height < 0) {
333     height = -height;
334     dst_argb = dst_argb + (height - 1) * dst_stride_argb;
335     dst_stride_argb = -dst_stride_argb;
336   }
337   void (*ARGBBlendRow)(const uint8* src_argb, const uint8* src_argb1,
338                        uint8* dst_argb, int width) = GetARGBBlend();
339 
340   for (int y = 0; y < height; ++y) {
341     ARGBBlendRow(src_argb0, src_argb1, dst_argb, width);
342     src_argb0 += src_stride_argb0;
343     src_argb1 += src_stride_argb1;
344     dst_argb += dst_stride_argb;
345   }
346   return 0;
347 }
348 
349 // Convert ARGB to I400.
350 LIBYUV_API
ARGBToI400(const uint8 * src_argb,int src_stride_argb,uint8 * dst_y,int dst_stride_y,int width,int height)351 int ARGBToI400(const uint8* src_argb, int src_stride_argb,
352                uint8* dst_y, int dst_stride_y,
353                int width, int height) {
354   if (!src_argb || !dst_y || width <= 0 || height == 0) {
355     return -1;
356   }
357   if (height < 0) {
358     height = -height;
359     src_argb = src_argb + (height - 1) * src_stride_argb;
360     src_stride_argb = -src_stride_argb;
361   }
362   void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
363       ARGBToYRow_C;
364 #if defined(HAS_ARGBTOYROW_SSSE3)
365   if (TestCpuFlag(kCpuHasSSSE3) &&
366       IS_ALIGNED(width, 4) &&
367       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16) &&
368       IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
369     ARGBToYRow = ARGBToYRow_SSSE3;
370   }
371 #endif
372 
373   for (int y = 0; y < height; ++y) {
374     ARGBToYRow(src_argb, dst_y, width);
375     src_argb += src_stride_argb;
376     dst_y += dst_stride_y;
377   }
378   return 0;
379 }
380 
381 // ARGB little endian (bgra in memory) to I422
382 // same as I420 except UV plane is full height
383 LIBYUV_API
ARGBToI422(const uint8 * src_argb,int src_stride_argb,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)384 int ARGBToI422(const uint8* src_argb, int src_stride_argb,
385                uint8* dst_y, int dst_stride_y,
386                uint8* dst_u, int dst_stride_u,
387                uint8* dst_v, int dst_stride_v,
388                int width, int height) {
389   if (!src_argb || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0) {
390     return -1;
391   }
392   if (height < 0) {
393     height = -height;
394     src_argb = src_argb + (height - 1) * src_stride_argb;
395     src_stride_argb = -src_stride_argb;
396   }
397   void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
398       ARGBToYRow_C;
399   void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
400                       uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
401 #if defined(HAS_ARGBTOYROW_SSSE3)
402   if (TestCpuFlag(kCpuHasSSSE3)) {
403     if (width > 16) {
404       ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
405       ARGBToYRow = ARGBToYRow_Any_SSSE3;
406     }
407     if (IS_ALIGNED(width, 16)) {
408       ARGBToUVRow = ARGBToUVRow_Unaligned_SSSE3;
409       ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
410       if (IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) {
411         ARGBToUVRow = ARGBToUVRow_SSSE3;
412         if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
413           ARGBToYRow = ARGBToYRow_SSSE3;
414         }
415       }
416     }
417   }
418 #endif
419 
420   for (int y = 0; y < height; ++y) {
421     ARGBToUVRow(src_argb, 0, dst_u, dst_v, width);
422     ARGBToYRow(src_argb, dst_y, width);
423     src_argb += src_stride_argb;
424     dst_y += dst_stride_y;
425     dst_u += dst_stride_u;
426     dst_v += dst_stride_v;
427   }
428   return 0;
429 }
430 
431 // Convert I422 to BGRA.
432 LIBYUV_API
I422ToBGRA(const uint8 * src_y,int src_stride_y,const uint8 * src_u,int src_stride_u,const uint8 * src_v,int src_stride_v,uint8 * dst_bgra,int dst_stride_bgra,int width,int height)433 int I422ToBGRA(const uint8* src_y, int src_stride_y,
434                const uint8* src_u, int src_stride_u,
435                const uint8* src_v, int src_stride_v,
436                uint8* dst_bgra, int dst_stride_bgra,
437                int width, int height) {
438   if (!src_y || !src_u || !src_v ||
439       !dst_bgra ||
440       width <= 0 || height == 0) {
441     return -1;
442   }
443   // Negative height means invert the image.
444   if (height < 0) {
445     height = -height;
446     dst_bgra = dst_bgra + (height - 1) * dst_stride_bgra;
447     dst_stride_bgra = -dst_stride_bgra;
448   }
449   void (*I422ToBGRARow)(const uint8* y_buf,
450                         const uint8* u_buf,
451                         const uint8* v_buf,
452                         uint8* rgb_buf,
453                         int width) = I422ToBGRARow_C;
454 #if defined(HAS_I422TOBGRAROW_NEON)
455   if (TestCpuFlag(kCpuHasNEON)) {
456     I422ToBGRARow = I422ToBGRARow_Any_NEON;
457     if (IS_ALIGNED(width, 16)) {
458       I422ToBGRARow = I422ToBGRARow_NEON;
459     }
460   }
461 #elif defined(HAS_I422TOBGRAROW_SSSE3)
462   if (TestCpuFlag(kCpuHasSSSE3) && width >= 8) {
463     I422ToBGRARow = I422ToBGRARow_Any_SSSE3;
464     if (IS_ALIGNED(width, 8)) {
465       I422ToBGRARow = I422ToBGRARow_Unaligned_SSSE3;
466       if (IS_ALIGNED(dst_bgra, 16) && IS_ALIGNED(dst_stride_bgra, 16)) {
467         I422ToBGRARow = I422ToBGRARow_SSSE3;
468       }
469     }
470   }
471 #endif
472 
473   for (int y = 0; y < height; ++y) {
474     I422ToBGRARow(src_y, src_u, src_v, dst_bgra, width);
475     dst_bgra += dst_stride_bgra;
476     src_y += src_stride_y;
477     src_u += src_stride_u;
478     src_v += src_stride_v;
479   }
480   return 0;
481 }
482 
483 // Convert I422 to ABGR.
484 LIBYUV_API
I422ToABGR(const uint8 * src_y,int src_stride_y,const uint8 * src_u,int src_stride_u,const uint8 * src_v,int src_stride_v,uint8 * dst_abgr,int dst_stride_abgr,int width,int height)485 int I422ToABGR(const uint8* src_y, int src_stride_y,
486                const uint8* src_u, int src_stride_u,
487                const uint8* src_v, int src_stride_v,
488                uint8* dst_abgr, int dst_stride_abgr,
489                int width, int height) {
490   if (!src_y || !src_u || !src_v ||
491       !dst_abgr ||
492       width <= 0 || height == 0) {
493     return -1;
494   }
495   // Negative height means invert the image.
496   if (height < 0) {
497     height = -height;
498     dst_abgr = dst_abgr + (height - 1) * dst_stride_abgr;
499     dst_stride_abgr = -dst_stride_abgr;
500   }
501   void (*I422ToABGRRow)(const uint8* y_buf,
502                         const uint8* u_buf,
503                         const uint8* v_buf,
504                         uint8* rgb_buf,
505                         int width) = I422ToABGRRow_C;
506 #if defined(HAS_I422TOABGRROW_NEON)
507   if (TestCpuFlag(kCpuHasNEON)) {
508     I422ToABGRRow = I422ToABGRRow_Any_NEON;
509     if (IS_ALIGNED(width, 16)) {
510       I422ToABGRRow = I422ToABGRRow_NEON;
511     }
512   }
513 #elif defined(HAS_I422TOABGRROW_SSSE3)
514   if (TestCpuFlag(kCpuHasSSSE3) && width >= 8) {
515     I422ToABGRRow = I422ToABGRRow_Any_SSSE3;
516     if (IS_ALIGNED(width, 8)) {
517       I422ToABGRRow = I422ToABGRRow_Unaligned_SSSE3;
518       if (IS_ALIGNED(dst_abgr, 16) && IS_ALIGNED(dst_stride_abgr, 16)) {
519         I422ToABGRRow = I422ToABGRRow_SSSE3;
520       }
521     }
522   }
523 #endif
524 
525   for (int y = 0; y < height; ++y) {
526     I422ToABGRRow(src_y, src_u, src_v, dst_abgr, width);
527     dst_abgr += dst_stride_abgr;
528     src_y += src_stride_y;
529     src_u += src_stride_u;
530     src_v += src_stride_v;
531   }
532   return 0;
533 }
534 
535 // Convert I422 to RGBA.
536 LIBYUV_API
I422ToRGBA(const uint8 * src_y,int src_stride_y,const uint8 * src_u,int src_stride_u,const uint8 * src_v,int src_stride_v,uint8 * dst_rgba,int dst_stride_rgba,int width,int height)537 int I422ToRGBA(const uint8* src_y, int src_stride_y,
538                const uint8* src_u, int src_stride_u,
539                const uint8* src_v, int src_stride_v,
540                uint8* dst_rgba, int dst_stride_rgba,
541                int width, int height) {
542   if (!src_y || !src_u || !src_v ||
543       !dst_rgba ||
544       width <= 0 || height == 0) {
545     return -1;
546   }
547   // Negative height means invert the image.
548   if (height < 0) {
549     height = -height;
550     dst_rgba = dst_rgba + (height - 1) * dst_stride_rgba;
551     dst_stride_rgba = -dst_stride_rgba;
552   }
553   void (*I422ToRGBARow)(const uint8* y_buf,
554                         const uint8* u_buf,
555                         const uint8* v_buf,
556                         uint8* rgb_buf,
557                         int width) = I422ToRGBARow_C;
558 #if defined(HAS_I422TORGBAROW_NEON)
559   if (TestCpuFlag(kCpuHasNEON)) {
560     I422ToRGBARow = I422ToRGBARow_Any_NEON;
561     if (IS_ALIGNED(width, 16)) {
562       I422ToRGBARow = I422ToRGBARow_NEON;
563     }
564   }
565 #elif defined(HAS_I422TORGBAROW_SSSE3)
566   if (TestCpuFlag(kCpuHasSSSE3) && width >= 8) {
567     I422ToRGBARow = I422ToRGBARow_Any_SSSE3;
568     if (IS_ALIGNED(width, 8)) {
569       I422ToRGBARow = I422ToRGBARow_Unaligned_SSSE3;
570       if (IS_ALIGNED(dst_rgba, 16) && IS_ALIGNED(dst_stride_rgba, 16)) {
571         I422ToRGBARow = I422ToRGBARow_SSSE3;
572       }
573     }
574   }
575 #endif
576 
577   for (int y = 0; y < height; ++y) {
578     I422ToRGBARow(src_y, src_u, src_v, dst_rgba, width);
579     dst_rgba += dst_stride_rgba;
580     src_y += src_stride_y;
581     src_u += src_stride_u;
582     src_v += src_stride_v;
583   }
584   return 0;
585 }
586 
587 // Convert ARGB to RGBA.
588 LIBYUV_API
ARGBToRGBA(const uint8 * src_argb,int src_stride_argb,uint8 * dst_rgba,int dst_stride_rgba,int width,int height)589 int ARGBToRGBA(const uint8* src_argb, int src_stride_argb,
590                uint8* dst_rgba, int dst_stride_rgba,
591                int width, int height) {
592   if (!src_argb || !dst_rgba ||
593       width <= 0 || height == 0) {
594     return -1;
595   }
596   // Negative height means invert the image.
597   if (height < 0) {
598     height = -height;
599     src_argb = src_argb + (height - 1) * src_stride_argb;
600     src_stride_argb = -src_stride_argb;
601   }
602   void (*ARGBToRGBARow)(const uint8* src_argb, uint8* dst_rgba, int pix) =
603       ARGBToRGBARow_C;
604 #if defined(HAS_ARGBTORGBAROW_SSSE3)
605   if (TestCpuFlag(kCpuHasSSSE3) &&
606       IS_ALIGNED(width, 4) &&
607       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16) &&
608       IS_ALIGNED(dst_rgba, 16) && IS_ALIGNED(dst_stride_rgba, 16)) {
609     ARGBToRGBARow = ARGBToRGBARow_SSSE3;
610   }
611 #endif
612 #if defined(HAS_ARGBTORGBAROW_NEON)
613   if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
614     ARGBToRGBARow = ARGBToRGBARow_NEON;
615   }
616 #endif
617 
618   for (int y = 0; y < height; ++y) {
619     ARGBToRGBARow(src_argb, dst_rgba, width);
620     src_argb += src_stride_argb;
621     dst_rgba += dst_stride_rgba;
622   }
623   return 0;
624 }
625 
626 // Convert ARGB To RGB24.
627 LIBYUV_API
ARGBToRGB24(const uint8 * src_argb,int src_stride_argb,uint8 * dst_rgb24,int dst_stride_rgb24,int width,int height)628 int ARGBToRGB24(const uint8* src_argb, int src_stride_argb,
629                 uint8* dst_rgb24, int dst_stride_rgb24,
630                 int width, int height) {
631   if (!src_argb || !dst_rgb24 || width <= 0 || height == 0) {
632     return -1;
633   }
634   if (height < 0) {
635     height = -height;
636     src_argb = src_argb + (height - 1) * src_stride_argb;
637     src_stride_argb = -src_stride_argb;
638   }
639   void (*ARGBToRGB24Row)(const uint8* src_argb, uint8* dst_rgb, int pix) =
640       ARGBToRGB24Row_C;
641 #if defined(HAS_ARGBTORGB24ROW_SSSE3)
642   if (TestCpuFlag(kCpuHasSSSE3) &&
643       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) {
644     if (width * 3 <= kMaxStride) {
645       ARGBToRGB24Row = ARGBToRGB24Row_Any_SSSE3;
646     }
647     if (IS_ALIGNED(width, 16) &&
648         IS_ALIGNED(dst_rgb24, 16) && IS_ALIGNED(dst_stride_rgb24, 16)) {
649       ARGBToRGB24Row = ARGBToRGB24Row_SSSE3;
650     }
651   }
652 #endif
653 #if defined(HAS_ARGBTORGB24ROW_NEON)
654   if (TestCpuFlag(kCpuHasNEON)) {
655     if (width * 3 <= kMaxStride) {
656       ARGBToRGB24Row = ARGBToRGB24Row_Any_NEON;
657     }
658     if (IS_ALIGNED(width, 8)) {
659       ARGBToRGB24Row = ARGBToRGB24Row_NEON;
660     }
661   }
662 #endif
663 
664   for (int y = 0; y < height; ++y) {
665     ARGBToRGB24Row(src_argb, dst_rgb24, width);
666     src_argb += src_stride_argb;
667     dst_rgb24 += dst_stride_rgb24;
668   }
669   return 0;
670 }
671 
672 // Convert ARGB To RAW.
673 LIBYUV_API
ARGBToRAW(const uint8 * src_argb,int src_stride_argb,uint8 * dst_raw,int dst_stride_raw,int width,int height)674 int ARGBToRAW(const uint8* src_argb, int src_stride_argb,
675               uint8* dst_raw, int dst_stride_raw,
676               int width, int height) {
677   if (!src_argb || !dst_raw || width <= 0 || height == 0) {
678     return -1;
679   }
680   if (height < 0) {
681     height = -height;
682     src_argb = src_argb + (height - 1) * src_stride_argb;
683     src_stride_argb = -src_stride_argb;
684   }
685   void (*ARGBToRAWRow)(const uint8* src_argb, uint8* dst_rgb, int pix) =
686       ARGBToRAWRow_C;
687 #if defined(HAS_ARGBTORAWROW_SSSE3)
688   if (TestCpuFlag(kCpuHasSSSE3) &&
689       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) {
690     if (width * 3 <= kMaxStride) {
691       ARGBToRAWRow = ARGBToRAWRow_Any_SSSE3;
692     }
693     if (IS_ALIGNED(width, 16) &&
694         IS_ALIGNED(dst_raw, 16) && IS_ALIGNED(dst_stride_raw, 16)) {
695       ARGBToRAWRow = ARGBToRAWRow_SSSE3;
696     }
697   }
698 #endif
699 #if defined(HAS_ARGBTORAWROW_NEON)
700   if (TestCpuFlag(kCpuHasNEON)) {
701     if (width * 3 <= kMaxStride) {
702       ARGBToRAWRow = ARGBToRAWRow_Any_NEON;
703     }
704     if (IS_ALIGNED(width, 8)) {
705       ARGBToRAWRow = ARGBToRAWRow_NEON;
706     }
707   }
708 #endif
709 
710   for (int y = 0; y < height; ++y) {
711     ARGBToRAWRow(src_argb, dst_raw, width);
712     src_argb += src_stride_argb;
713     dst_raw += dst_stride_raw;
714   }
715   return 0;
716 }
717 
718 // Convert ARGB To RGB565.
719 LIBYUV_API
ARGBToRGB565(const uint8 * src_argb,int src_stride_argb,uint8 * dst_rgb565,int dst_stride_rgb565,int width,int height)720 int ARGBToRGB565(const uint8* src_argb, int src_stride_argb,
721                  uint8* dst_rgb565, int dst_stride_rgb565,
722                  int width, int height) {
723   if (!src_argb || !dst_rgb565 || width <= 0 || height == 0) {
724     return -1;
725   }
726   if (height < 0) {
727     height = -height;
728     src_argb = src_argb + (height - 1) * src_stride_argb;
729     src_stride_argb = -src_stride_argb;
730   }
731   void (*ARGBToRGB565Row)(const uint8* src_argb, uint8* dst_rgb, int pix) =
732       ARGBToRGB565Row_C;
733 #if defined(HAS_ARGBTORGB565ROW_SSE2)
734   if (TestCpuFlag(kCpuHasSSE2) &&
735       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) {
736     if (width * 2 <= kMaxStride) {
737       ARGBToRGB565Row = ARGBToRGB565Row_Any_SSE2;
738     }
739     if (IS_ALIGNED(width, 4)) {
740       ARGBToRGB565Row = ARGBToRGB565Row_SSE2;
741     }
742   }
743 #endif
744 
745   for (int y = 0; y < height; ++y) {
746     ARGBToRGB565Row(src_argb, dst_rgb565, width);
747     src_argb += src_stride_argb;
748     dst_rgb565 += dst_stride_rgb565;
749   }
750   return 0;
751 }
752 
753 // Convert ARGB To ARGB1555.
754 LIBYUV_API
ARGBToARGB1555(const uint8 * src_argb,int src_stride_argb,uint8 * dst_argb1555,int dst_stride_argb1555,int width,int height)755 int ARGBToARGB1555(const uint8* src_argb, int src_stride_argb,
756                    uint8* dst_argb1555, int dst_stride_argb1555,
757                    int width, int height) {
758   if (!src_argb || !dst_argb1555 || width <= 0 || height == 0) {
759     return -1;
760   }
761   if (height < 0) {
762     height = -height;
763     src_argb = src_argb + (height - 1) * src_stride_argb;
764     src_stride_argb = -src_stride_argb;
765   }
766   void (*ARGBToARGB1555Row)(const uint8* src_argb, uint8* dst_rgb, int pix) =
767       ARGBToARGB1555Row_C;
768 #if defined(HAS_ARGBTOARGB1555ROW_SSE2)
769   if (TestCpuFlag(kCpuHasSSE2) &&
770       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) {
771     if (width * 2 <= kMaxStride) {
772       ARGBToARGB1555Row = ARGBToARGB1555Row_Any_SSE2;
773     }
774     if (IS_ALIGNED(width, 4)) {
775       ARGBToARGB1555Row = ARGBToARGB1555Row_SSE2;
776     }
777   }
778 #endif
779 
780   for (int y = 0; y < height; ++y) {
781     ARGBToARGB1555Row(src_argb, dst_argb1555, width);
782     src_argb += src_stride_argb;
783     dst_argb1555 += dst_stride_argb1555;
784   }
785   return 0;
786 }
787 
788 // Convert ARGB To ARGB4444.
789 LIBYUV_API
ARGBToARGB4444(const uint8 * src_argb,int src_stride_argb,uint8 * dst_argb4444,int dst_stride_argb4444,int width,int height)790 int ARGBToARGB4444(const uint8* src_argb, int src_stride_argb,
791                    uint8* dst_argb4444, int dst_stride_argb4444,
792                    int width, int height) {
793   if (!src_argb || !dst_argb4444 || width <= 0 || height == 0) {
794     return -1;
795   }
796   if (height < 0) {
797     height = -height;
798     src_argb = src_argb + (height - 1) * src_stride_argb;
799     src_stride_argb = -src_stride_argb;
800   }
801   void (*ARGBToARGB4444Row)(const uint8* src_argb, uint8* dst_rgb, int pix) =
802       ARGBToARGB4444Row_C;
803 #if defined(HAS_ARGBTOARGB4444ROW_SSE2)
804   if (TestCpuFlag(kCpuHasSSE2) &&
805       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) {
806     if (width * 2 <= kMaxStride) {
807       ARGBToARGB4444Row = ARGBToARGB4444Row_Any_SSE2;
808     }
809     if (IS_ALIGNED(width, 4)) {
810       ARGBToARGB4444Row = ARGBToARGB4444Row_SSE2;
811     }
812   }
813 #endif
814 
815   for (int y = 0; y < height; ++y) {
816     ARGBToARGB4444Row(src_argb, dst_argb4444, width);
817     src_argb += src_stride_argb;
818     dst_argb4444 += dst_stride_argb4444;
819   }
820   return 0;
821 }
822 
823 // Convert NV12 to RGB565.
824 // TODO(fbarchard): (Re) Optimize for Neon.
825 LIBYUV_API
NV12ToRGB565(const uint8 * src_y,int src_stride_y,const uint8 * src_uv,int src_stride_uv,uint8 * dst_rgb565,int dst_stride_rgb565,int width,int height)826 int NV12ToRGB565(const uint8* src_y, int src_stride_y,
827                  const uint8* src_uv, int src_stride_uv,
828                  uint8* dst_rgb565, int dst_stride_rgb565,
829                  int width, int height) {
830   if (!src_y || !src_uv || !dst_rgb565 || width <= 0 || height == 0) {
831     return -1;
832   }
833   // Negative height means invert the image.
834   if (height < 0) {
835     height = -height;
836     dst_rgb565 = dst_rgb565 + (height - 1) * dst_stride_rgb565;
837     dst_stride_rgb565 = -dst_stride_rgb565;
838   }
839   void (*NV12ToARGBRow)(const uint8* y_buf,
840                         const uint8* uv_buf,
841                         uint8* rgb_buf,
842                         int width) = NV12ToARGBRow_C;
843 #if defined(HAS_NV12TOARGBROW_SSSE3)
844   if (TestCpuFlag(kCpuHasSSSE3) && width * 4 <= kMaxStride) {
845     NV12ToARGBRow = NV12ToARGBRow_SSSE3;
846   }
847 #endif
848 #if defined(HAS_NV12TOARGBROW_NEON)
849   if (TestCpuFlag(kCpuHasNEON) && width * 4 <= kMaxStride) {
850     NV12ToARGBRow = NV12ToARGBRow_NEON;
851   }
852 #endif
853 
854   SIMD_ALIGNED(uint8 row[kMaxStride]);
855   void (*ARGBToRGB565Row)(const uint8* src_argb, uint8* dst_rgb, int pix) =
856       ARGBToRGB565Row_C;
857 #if defined(HAS_ARGBTORGB565ROW_SSE2)
858   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 4)) {
859     ARGBToRGB565Row = ARGBToRGB565Row_SSE2;
860   }
861 #endif
862 
863   for (int y = 0; y < height; ++y) {
864     NV12ToARGBRow(src_y, src_uv, row, width);
865     ARGBToRGB565Row(row, dst_rgb565, width);
866     dst_rgb565 += dst_stride_rgb565;
867     src_y += src_stride_y;
868     if (y & 1) {
869       src_uv += src_stride_uv;
870     }
871   }
872   return 0;
873 }
874 
875 // Convert NV21 to RGB565.
876 LIBYUV_API
NV21ToRGB565(const uint8 * src_y,int src_stride_y,const uint8 * src_vu,int src_stride_vu,uint8 * dst_rgb565,int dst_stride_rgb565,int width,int height)877 int NV21ToRGB565(const uint8* src_y, int src_stride_y,
878                  const uint8* src_vu, int src_stride_vu,
879                  uint8* dst_rgb565, int dst_stride_rgb565,
880                  int width, int height) {
881   if (!src_y || !src_vu || !dst_rgb565 || width <= 0 || height == 0) {
882     return -1;
883   }
884   // Negative height means invert the image.
885   if (height < 0) {
886     height = -height;
887     dst_rgb565 = dst_rgb565 + (height - 1) * dst_stride_rgb565;
888     dst_stride_rgb565 = -dst_stride_rgb565;
889   }
890   void (*NV21ToARGBRow)(const uint8* y_buf,
891                         const uint8* uv_buf,
892                         uint8* rgb_buf,
893                         int width) = NV21ToARGBRow_C;
894 #if defined(HAS_NV21TOARGBROW_SSSE3)
895   if (TestCpuFlag(kCpuHasSSSE3) && width * 4 <= kMaxStride) {
896     NV21ToARGBRow = NV21ToARGBRow_SSSE3;
897   }
898 #endif
899 
900   SIMD_ALIGNED(uint8 row[kMaxStride]);
901   void (*ARGBToRGB565Row)(const uint8* src_argb, uint8* dst_rgb, int pix) =
902       ARGBToRGB565Row_C;
903 #if defined(HAS_ARGBTORGB565ROW_SSE2)
904   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 4)) {
905     ARGBToRGB565Row = ARGBToRGB565Row_SSE2;
906   }
907 #endif
908 
909   for (int y = 0; y < height; ++y) {
910     NV21ToARGBRow(src_y, src_vu, row, width);
911     ARGBToRGB565Row(row, dst_rgb565, width);
912     dst_rgb565 += dst_stride_rgb565;
913     src_y += src_stride_y;
914     if (y & 1) {
915       src_vu += src_stride_vu;
916     }
917   }
918   return 0;
919 }
920 
921 LIBYUV_API
SetPlane(uint8 * dst_y,int dst_stride_y,int width,int height,uint32 value)922 void SetPlane(uint8* dst_y, int dst_stride_y,
923               int width, int height,
924               uint32 value) {
925   void (*SetRow)(uint8* dst, uint32 value, int pix) = SetRow8_C;
926 #if defined(HAS_SETROW_NEON)
927   if (TestCpuFlag(kCpuHasNEON) &&
928       IS_ALIGNED(width, 16) &&
929       IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
930     SetRow = SetRow8_NEON;
931   }
932 #endif
933 #if defined(HAS_SETROW_X86)
934   if (TestCpuFlag(kCpuHasX86) && IS_ALIGNED(width, 4)) {
935     SetRow = SetRow8_X86;
936   }
937 #endif
938 
939   uint32 v32 = value | (value << 8) | (value << 16) | (value << 24);
940   // Set plane
941   for (int y = 0; y < height; ++y) {
942     SetRow(dst_y, v32, width);
943     dst_y += dst_stride_y;
944   }
945 }
946 
947 // Draw a rectangle into I420
948 LIBYUV_API
I420Rect(uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int x,int y,int width,int height,int value_y,int value_u,int value_v)949 int I420Rect(uint8* dst_y, int dst_stride_y,
950              uint8* dst_u, int dst_stride_u,
951              uint8* dst_v, int dst_stride_v,
952              int x, int y,
953              int width, int height,
954              int value_y, int value_u, int value_v) {
955   if (!dst_y || !dst_u || !dst_v ||
956       width <= 0 || height <= 0 ||
957       x < 0 || y < 0 ||
958       value_y < 0 || value_y > 255 ||
959       value_u < 0 || value_u > 255 ||
960       value_v < 0 || value_v > 255) {
961     return -1;
962   }
963   int halfwidth = (width + 1) >> 1;
964   int halfheight = (height + 1) >> 1;
965   uint8* start_y = dst_y + y * dst_stride_y + x;
966   uint8* start_u = dst_u + (y / 2) * dst_stride_u + (x / 2);
967   uint8* start_v = dst_v + (y / 2) * dst_stride_v + (x / 2);
968 
969   SetPlane(start_y, dst_stride_y, width, height, value_y);
970   SetPlane(start_u, dst_stride_u, halfwidth, halfheight, value_u);
971   SetPlane(start_v, dst_stride_v, halfwidth, halfheight, value_v);
972   return 0;
973 }
974 
975 // Draw a rectangle into ARGB
976 LIBYUV_API
ARGBRect(uint8 * dst_argb,int dst_stride_argb,int dst_x,int dst_y,int width,int height,uint32 value)977 int ARGBRect(uint8* dst_argb, int dst_stride_argb,
978              int dst_x, int dst_y,
979              int width, int height,
980              uint32 value) {
981   if (!dst_argb ||
982       width <= 0 || height <= 0 ||
983       dst_x < 0 || dst_y < 0) {
984     return -1;
985   }
986   uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
987 #if defined(HAS_SETROW_NEON)
988   if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 16) &&
989       IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
990     SetRows32_NEON(dst, value, width, dst_stride_argb, height);
991     return 0;
992   }
993 #endif
994 #if defined(HAS_SETROW_X86)
995   if (TestCpuFlag(kCpuHasX86)) {
996     SetRows32_X86(dst, value, width, dst_stride_argb, height);
997     return 0;
998   }
999 #endif
1000   SetRows32_C(dst, value, width, dst_stride_argb, height);
1001   return 0;
1002 }
1003 
1004 // Convert unattentuated ARGB to preattenuated ARGB.
1005 // An unattenutated ARGB alpha blend uses the formula
1006 // p = a * f + (1 - a) * b
1007 // where
1008 //   p is output pixel
1009 //   f is foreground pixel
1010 //   b is background pixel
1011 //   a is alpha value from foreground pixel
1012 // An preattenutated ARGB alpha blend uses the formula
1013 // p = f + (1 - a) * b
1014 // where
1015 //   f is foreground pixel premultiplied by alpha
1016 
1017 LIBYUV_API
ARGBAttenuate(const uint8 * src_argb,int src_stride_argb,uint8 * dst_argb,int dst_stride_argb,int width,int height)1018 int ARGBAttenuate(const uint8* src_argb, int src_stride_argb,
1019                   uint8* dst_argb, int dst_stride_argb,
1020                   int width, int height) {
1021   if (!src_argb || !dst_argb || width <= 0 || height == 0) {
1022     return -1;
1023   }
1024   if (height < 0) {
1025     height = -height;
1026     src_argb = src_argb + (height - 1) * src_stride_argb;
1027     src_stride_argb = -src_stride_argb;
1028   }
1029   void (*ARGBAttenuateRow)(const uint8* src_argb, uint8* dst_argb,
1030                            int width) = ARGBAttenuateRow_C;
1031 #if defined(HAS_ARGBATTENUATE_SSE2)
1032   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 4) &&
1033       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16) &&
1034       IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
1035     ARGBAttenuateRow = ARGBAttenuateRow_SSE2;
1036   }
1037 #endif
1038 #if defined(HAS_ARGBATTENUATEROW_SSSE3)
1039   if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 4) &&
1040       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16) &&
1041       IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
1042     ARGBAttenuateRow = ARGBAttenuateRow_SSSE3;
1043   }
1044 #endif
1045 
1046   for (int y = 0; y < height; ++y) {
1047     ARGBAttenuateRow(src_argb, dst_argb, width);
1048     src_argb += src_stride_argb;
1049     dst_argb += dst_stride_argb;
1050   }
1051   return 0;
1052 }
1053 
1054 // Convert preattentuated ARGB to unattenuated ARGB.
1055 LIBYUV_API
ARGBUnattenuate(const uint8 * src_argb,int src_stride_argb,uint8 * dst_argb,int dst_stride_argb,int width,int height)1056 int ARGBUnattenuate(const uint8* src_argb, int src_stride_argb,
1057                     uint8* dst_argb, int dst_stride_argb,
1058                     int width, int height) {
1059   if (!src_argb || !dst_argb || width <= 0 || height == 0) {
1060     return -1;
1061   }
1062   if (height < 0) {
1063     height = -height;
1064     src_argb = src_argb + (height - 1) * src_stride_argb;
1065     src_stride_argb = -src_stride_argb;
1066   }
1067   void (*ARGBUnattenuateRow)(const uint8* src_argb, uint8* dst_argb,
1068                              int width) = ARGBUnattenuateRow_C;
1069 #if defined(HAS_ARGBUNATTENUATEROW_SSE2)
1070   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 4) &&
1071       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16) &&
1072       IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
1073     ARGBUnattenuateRow = ARGBUnattenuateRow_SSE2;
1074   }
1075 #endif
1076 
1077   for (int y = 0; y < height; ++y) {
1078     ARGBUnattenuateRow(src_argb, dst_argb, width);
1079     src_argb += src_stride_argb;
1080     dst_argb += dst_stride_argb;
1081   }
1082   return 0;
1083 }
1084 
1085 // Convert ARGB to Grayed ARGB.
1086 LIBYUV_API
ARGBGrayTo(const uint8 * src_argb,int src_stride_argb,uint8 * dst_argb,int dst_stride_argb,int width,int height)1087 int ARGBGrayTo(const uint8* src_argb, int src_stride_argb,
1088                uint8* dst_argb, int dst_stride_argb,
1089                int width, int height) {
1090   if (!src_argb || !dst_argb || width <= 0 || height == 0) {
1091     return -1;
1092   }
1093   if (height < 0) {
1094     height = -height;
1095     src_argb = src_argb + (height - 1) * src_stride_argb;
1096     src_stride_argb = -src_stride_argb;
1097   }
1098   void (*ARGBGrayRow)(const uint8* src_argb, uint8* dst_argb,
1099                       int width) = ARGBGrayRow_C;
1100 #if defined(HAS_ARGBGRAYROW_SSSE3)
1101   if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8) &&
1102       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16) &&
1103       IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
1104     ARGBGrayRow = ARGBGrayRow_SSSE3;
1105   }
1106 #endif
1107 
1108   for (int y = 0; y < height; ++y) {
1109     ARGBGrayRow(src_argb, dst_argb, width);
1110     src_argb += src_stride_argb;
1111     dst_argb += dst_stride_argb;
1112   }
1113   return 0;
1114 }
1115 
1116 // Make a rectangle of ARGB gray scale.
1117 LIBYUV_API
ARGBGray(uint8 * dst_argb,int dst_stride_argb,int dst_x,int dst_y,int width,int height)1118 int ARGBGray(uint8* dst_argb, int dst_stride_argb,
1119              int dst_x, int dst_y,
1120              int width, int height) {
1121   if (!dst_argb || width <= 0 || height <= 0 || dst_x < 0 || dst_y < 0) {
1122     return -1;
1123   }
1124   void (*ARGBGrayRow)(const uint8* src_argb, uint8* dst_argb,
1125                       int width) = ARGBGrayRow_C;
1126 #if defined(HAS_ARGBGRAYROW_SSSE3)
1127   if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8) &&
1128       IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
1129     ARGBGrayRow = ARGBGrayRow_SSSE3;
1130   }
1131 #endif
1132   uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
1133   for (int y = 0; y < height; ++y) {
1134     ARGBGrayRow(dst, dst, width);
1135     dst += dst_stride_argb;
1136   }
1137   return 0;
1138 }
1139 
1140 // Make a rectangle of ARGB Sepia tone.
1141 LIBYUV_API
ARGBSepia(uint8 * dst_argb,int dst_stride_argb,int dst_x,int dst_y,int width,int height)1142 int ARGBSepia(uint8* dst_argb, int dst_stride_argb,
1143               int dst_x, int dst_y, int width, int height) {
1144   if (!dst_argb || width <= 0 || height <= 0 || dst_x < 0 || dst_y < 0) {
1145     return -1;
1146   }
1147   void (*ARGBSepiaRow)(uint8* dst_argb, int width) = ARGBSepiaRow_C;
1148 #if defined(HAS_ARGBSEPIAROW_SSSE3)
1149   if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8) &&
1150       IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
1151     ARGBSepiaRow = ARGBSepiaRow_SSSE3;
1152   }
1153 #endif
1154   uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
1155   for (int y = 0; y < height; ++y) {
1156     ARGBSepiaRow(dst, width);
1157     dst += dst_stride_argb;
1158   }
1159   return 0;
1160 }
1161 
1162 // Apply a 4x3 matrix rotation to each ARGB pixel.
1163 LIBYUV_API
ARGBColorMatrix(uint8 * dst_argb,int dst_stride_argb,const int8 * matrix_argb,int dst_x,int dst_y,int width,int height)1164 int ARGBColorMatrix(uint8* dst_argb, int dst_stride_argb,
1165                     const int8* matrix_argb,
1166                     int dst_x, int dst_y, int width, int height) {
1167   if (!dst_argb || !matrix_argb || width <= 0 || height <= 0 ||
1168       dst_x < 0 || dst_y < 0) {
1169     return -1;
1170   }
1171   void (*ARGBColorMatrixRow)(uint8* dst_argb, const int8* matrix_argb,
1172                              int width) = ARGBColorMatrixRow_C;
1173 #if defined(HAS_ARGBCOLORMATRIXROW_SSSE3)
1174   if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8) &&
1175       IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
1176     ARGBColorMatrixRow = ARGBColorMatrixRow_SSSE3;
1177   }
1178 #endif
1179   uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
1180   for (int y = 0; y < height; ++y) {
1181     ARGBColorMatrixRow(dst, matrix_argb, width);
1182     dst += dst_stride_argb;
1183   }
1184   return 0;
1185 }
1186 
1187 // Apply a color table each ARGB pixel.
1188 // Table contains 256 ARGB values.
1189 LIBYUV_API
ARGBColorTable(uint8 * dst_argb,int dst_stride_argb,const uint8 * table_argb,int dst_x,int dst_y,int width,int height)1190 int ARGBColorTable(uint8* dst_argb, int dst_stride_argb,
1191                    const uint8* table_argb,
1192                    int dst_x, int dst_y, int width, int height) {
1193   if (!dst_argb || !table_argb || width <= 0 || height <= 0 ||
1194       dst_x < 0 || dst_y < 0) {
1195     return -1;
1196   }
1197   void (*ARGBColorTableRow)(uint8* dst_argb, const uint8* table_argb,
1198                             int width) = ARGBColorTableRow_C;
1199 #if defined(HAS_ARGBCOLORTABLEROW_X86)
1200   if (TestCpuFlag(kCpuHasX86)) {
1201     ARGBColorTableRow = ARGBColorTableRow_X86;
1202   }
1203 #endif
1204   uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
1205   for (int y = 0; y < height; ++y) {
1206     ARGBColorTableRow(dst, table_argb, width);
1207     dst += dst_stride_argb;
1208   }
1209   return 0;
1210 }
1211 
1212 // ARGBQuantize is used to posterize art.
1213 // e.g. rgb / qvalue * qvalue + qvalue / 2
1214 // But the low levels implement efficiently with 3 parameters, and could be
1215 // used for other high level operations.
1216 // The divide is replaces with a multiply by reciprocal fixed point multiply.
1217 // Caveat - although SSE2 saturates, the C function does not and should be used
1218 // with care if doing anything but quantization.
1219 LIBYUV_API
ARGBQuantize(uint8 * dst_argb,int dst_stride_argb,int scale,int interval_size,int interval_offset,int dst_x,int dst_y,int width,int height)1220 int ARGBQuantize(uint8* dst_argb, int dst_stride_argb,
1221                  int scale, int interval_size, int interval_offset,
1222                  int dst_x, int dst_y, int width, int height) {
1223   if (!dst_argb || width <= 0 || height <= 0 || dst_x < 0 || dst_y < 0 ||
1224       interval_size < 1 || interval_size > 255) {
1225     return -1;
1226   }
1227   void (*ARGBQuantizeRow)(uint8* dst_argb, int scale, int interval_size,
1228                           int interval_offset, int width) = ARGBQuantizeRow_C;
1229 #if defined(HAS_ARGBQUANTIZEROW_SSE2)
1230   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 4) &&
1231       IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
1232     ARGBQuantizeRow = ARGBQuantizeRow_SSE2;
1233   }
1234 #endif
1235   uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
1236   for (int y = 0; y < height; ++y) {
1237     ARGBQuantizeRow(dst, scale, interval_size, interval_offset, width);
1238     dst += dst_stride_argb;
1239   }
1240   return 0;
1241 }
1242 
1243 // Computes table of cumulative sum for image where the value is the sum
1244 // of all values above and to the left of the entry. Used by ARGBBlur.
1245 LIBYUV_API
ARGBComputeCumulativeSum(const uint8 * src_argb,int src_stride_argb,int32 * dst_cumsum,int dst_stride32_cumsum,int width,int height)1246 int ARGBComputeCumulativeSum(const uint8* src_argb, int src_stride_argb,
1247                              int32* dst_cumsum, int dst_stride32_cumsum,
1248                              int width, int height) {
1249   if (!dst_cumsum || !src_argb || width <= 0 || height <= 0) {
1250     return -1;
1251   }
1252   void (*ComputeCumulativeSumRow)(const uint8* row, int32* cumsum,
1253       const int32* previous_cumsum, int width) = ComputeCumulativeSumRow_C;
1254 #if defined(HAS_CUMULATIVESUMTOAVERAGE_SSE2)
1255   if (TestCpuFlag(kCpuHasSSE2)) {
1256     ComputeCumulativeSumRow = ComputeCumulativeSumRow_SSE2;
1257   }
1258 #endif
1259   memset(dst_cumsum, 0, width * sizeof(dst_cumsum[0]) * 4);  // 4 int per pixel.
1260   int32* previous_cumsum = dst_cumsum;
1261   for (int y = 0; y < height; ++y) {
1262     ComputeCumulativeSumRow(src_argb, dst_cumsum, previous_cumsum, width);
1263     previous_cumsum = dst_cumsum;
1264     dst_cumsum += dst_stride32_cumsum;
1265     src_argb += src_stride_argb;
1266   }
1267   return 0;
1268 }
1269 
1270 // Blur ARGB image.
1271 // Caller should allocate CumulativeSum table of width * height * 16 bytes
1272 // aligned to 16 byte boundary. height can be radius * 2 + 2 to save memory
1273 // as the buffer is treated as circular.
1274 LIBYUV_API
ARGBBlur(const uint8 * src_argb,int src_stride_argb,uint8 * dst_argb,int dst_stride_argb,int32 * dst_cumsum,int dst_stride32_cumsum,int width,int height,int radius)1275 int ARGBBlur(const uint8* src_argb, int src_stride_argb,
1276              uint8* dst_argb, int dst_stride_argb,
1277              int32* dst_cumsum, int dst_stride32_cumsum,
1278              int width, int height, int radius) {
1279   if (!src_argb || !dst_argb || width <= 0 || height == 0) {
1280     return -1;
1281   }
1282   void (*ComputeCumulativeSumRow)(const uint8* row, int32* cumsum,
1283       const int32* previous_cumsum, int width) = ComputeCumulativeSumRow_C;
1284   void (*CumulativeSumToAverage)(const int32* topleft, const int32* botleft,
1285       int width, int area, uint8* dst, int count) = CumulativeSumToAverage_C;
1286 #if defined(HAS_CUMULATIVESUMTOAVERAGE_SSE2)
1287   if (TestCpuFlag(kCpuHasSSE2)) {
1288     ComputeCumulativeSumRow = ComputeCumulativeSumRow_SSE2;
1289     CumulativeSumToAverage = CumulativeSumToAverage_SSE2;
1290   }
1291 #endif
1292   // Compute enough CumulativeSum for first row to be blurred. After this
1293   // one row of CumulativeSum is updated at a time.
1294   ARGBComputeCumulativeSum(src_argb, src_stride_argb,
1295                            dst_cumsum, dst_stride32_cumsum,
1296                            width, radius);
1297 
1298   src_argb = src_argb + radius * src_stride_argb;
1299   int32* cumsum_bot_row = &dst_cumsum[(radius - 1) * dst_stride32_cumsum];
1300 
1301   const int32* max_cumsum_bot_row =
1302       &dst_cumsum[(radius * 2 + 2) * dst_stride32_cumsum];
1303   const int32* cumsum_top_row = &dst_cumsum[0];
1304 
1305   for (int y = 0; y < height; ++y) {
1306     int top_y = ((y - radius - 1) >= 0) ? (y - radius - 1) : 0;
1307     int bot_y = ((y + radius) < height) ? (y + radius) : (height - 1);
1308     int area = radius * (bot_y - top_y);
1309 
1310     // Increment cumsum_top_row pointer with circular buffer wrap around.
1311     if (top_y) {
1312       cumsum_top_row += dst_stride32_cumsum;
1313       if (cumsum_top_row >= max_cumsum_bot_row) {
1314         cumsum_top_row = dst_cumsum;
1315       }
1316     }
1317     // Increment cumsum_bot_row pointer with circular buffer wrap around and
1318     // then fill in a row of CumulativeSum.
1319     if ((y + radius) < height) {
1320       const int32* prev_cumsum_bot_row = cumsum_bot_row;
1321       cumsum_bot_row += dst_stride32_cumsum;
1322       if (cumsum_bot_row >= max_cumsum_bot_row) {
1323         cumsum_bot_row = dst_cumsum;
1324       }
1325       ComputeCumulativeSumRow(src_argb, cumsum_bot_row, prev_cumsum_bot_row,
1326                               width);
1327       src_argb += src_stride_argb;
1328     }
1329 
1330     // Left clipped.
1331     int boxwidth = radius * 4;
1332     int x;
1333     for (x = 0; x < radius + 1; ++x) {
1334       CumulativeSumToAverage(cumsum_top_row, cumsum_bot_row,
1335                               boxwidth, area, &dst_argb[x * 4], 1);
1336       area += (bot_y - top_y);
1337       boxwidth += 4;
1338     }
1339 
1340     // Middle unclipped.
1341     int n = (width - 1) - radius - x + 1;
1342     CumulativeSumToAverage(cumsum_top_row, cumsum_bot_row,
1343                            boxwidth, area, &dst_argb[x * 4], n);
1344 
1345     // Right clipped.
1346     for (x += n; x <= width - 1; ++x) {
1347       area -= (bot_y - top_y);
1348       boxwidth -= 4;
1349       CumulativeSumToAverage(cumsum_top_row + (x - radius - 1) * 4,
1350                              cumsum_bot_row + (x - radius - 1) * 4,
1351                              boxwidth, area, &dst_argb[x * 4], 1);
1352     }
1353     dst_argb += dst_stride_argb;
1354   }
1355   return 0;
1356 }
1357 
1358 // Multiply ARGB image by a specified ARGB value.
1359 LIBYUV_API
ARGBShade(const uint8 * src_argb,int src_stride_argb,uint8 * dst_argb,int dst_stride_argb,int width,int height,uint32 value)1360 int ARGBShade(const uint8* src_argb, int src_stride_argb,
1361               uint8* dst_argb, int dst_stride_argb,
1362               int width, int height, uint32 value) {
1363   if (!src_argb || !dst_argb || width <= 0 || height == 0 || value == 0u) {
1364     return -1;
1365   }
1366   if (height < 0) {
1367     height = -height;
1368     src_argb = src_argb + (height - 1) * src_stride_argb;
1369     src_stride_argb = -src_stride_argb;
1370   }
1371   void (*ARGBShadeRow)(const uint8* src_argb, uint8* dst_argb,
1372                        int width, uint32 value) = ARGBShadeRow_C;
1373 #if defined(HAS_ARGBSHADE_SSE2)
1374   if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 4) &&
1375       IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16) &&
1376       IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
1377     ARGBShadeRow = ARGBShadeRow_SSE2;
1378   }
1379 #endif
1380 
1381   for (int y = 0; y < height; ++y) {
1382     ARGBShadeRow(src_argb, dst_argb, width, value);
1383     src_argb += src_stride_argb;
1384     dst_argb += dst_stride_argb;
1385   }
1386   return 0;
1387 }
1388 
1389 // Interpolate 2 ARGB images by specified amount (0 to 255).
1390 LIBYUV_API
ARGBInterpolate(const uint8 * src_argb0,int src_stride_argb0,const uint8 * src_argb1,int src_stride_argb1,uint8 * dst_argb,int dst_stride_argb,int width,int height,int interpolation)1391 int ARGBInterpolate(const uint8* src_argb0, int src_stride_argb0,
1392                     const uint8* src_argb1, int src_stride_argb1,
1393                     uint8* dst_argb, int dst_stride_argb,
1394                     int width, int height, int interpolation) {
1395   if (!src_argb0 || !src_argb1 || !dst_argb || width <= 0 || height == 0) {
1396     return -1;
1397   }
1398   // Negative height means invert the image.
1399   if (height < 0) {
1400     height = -height;
1401     dst_argb = dst_argb + (height - 1) * dst_stride_argb;
1402     dst_stride_argb = -dst_stride_argb;
1403   }
1404   void (*ARGBInterpolateRow)(uint8* dst_ptr, const uint8* src_ptr,
1405                               ptrdiff_t src_stride, int dst_width,
1406                               int source_y_fraction) = ARGBInterpolateRow_C;
1407 #if defined(HAS_ARGBINTERPOLATEROW_SSSE3)
1408   if (TestCpuFlag(kCpuHasSSSE3) &&
1409       IS_ALIGNED(src_argb0, 16) && IS_ALIGNED(src_stride_argb0, 16) &&
1410       IS_ALIGNED(src_argb1, 16) && IS_ALIGNED(src_stride_argb1, 16) &&
1411       IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
1412     ARGBInterpolateRow = ARGBInterpolateRow_SSSE3;
1413   }
1414 #endif
1415   for (int y = 0; y < height; ++y) {
1416     ARGBInterpolateRow(dst_argb, src_argb0, src_argb1 - src_argb0,
1417                        width, interpolation);
1418     src_argb0 += src_stride_argb0;
1419     src_argb1 += src_stride_argb1;
1420     dst_argb += dst_stride_argb;
1421   }
1422   return 0;
1423 }
1424 
1425 #ifdef __cplusplus
1426 }  // extern "C"
1427 }  // namespace libyuv
1428 #endif
1429