1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include <stdlib.h>
7 #include <limits.h>
8 #include "nsDebug.h"
9 #include "ycbcr_to_rgb565.h"
10 #include "nsAlgorithm.h"
11 
12 
13 
14 #ifdef HAVE_YCBCR_TO_RGB565
15 
16 namespace mozilla {
17 
18 namespace gfx {
19 
20 /*This contains all of the parameters that are needed to convert a row.
21   Passing them in a struct instead of as individual parameters saves the need
22    to continually push onto the stack the ones that are fixed for every row.*/
23 struct yuv2rgb565_row_scale_bilinear_ctx{
24   uint16_t *rgb_row;
25   const uint8_t *y_row;
26   const uint8_t *u_row;
27   const uint8_t *v_row;
28   int y_yweight;
29   int y_pitch;
30   int width;
31   int source_x0_q16;
32   int source_dx_q16;
33   /*Not used for 4:4:4, except with chroma-nearest.*/
34   int source_uv_xoffs_q16;
35   /*Not used for 4:4:4 or chroma-nearest.*/
36   int uv_pitch;
37   /*Not used for 4:2:2, 4:4:4, or chroma-nearest.*/
38   int uv_yweight;
39 };
40 
41 
42 
43 /*This contains all of the parameters that are needed to convert a row.
44   Passing them in a struct instead of as individual parameters saves the need
45    to continually push onto the stack the ones that are fixed for every row.*/
46 struct yuv2rgb565_row_scale_nearest_ctx{
47   uint16_t *rgb_row;
48   const uint8_t *y_row;
49   const uint8_t *u_row;
50   const uint8_t *v_row;
51   int width;
52   int source_x0_q16;
53   int source_dx_q16;
54   /*Not used for 4:4:4.*/
55   int source_uv_xoffs_q16;
56 };
57 
58 
59 
60 typedef void (*yuv2rgb565_row_scale_bilinear_func)(
61  const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither);
62 
63 typedef void (*yuv2rgb565_row_scale_nearest_func)(
64  const yuv2rgb565_row_scale_nearest_ctx *ctx, int dither);
65 
66 
67 
68 //TODO: fix NEON asm for iOS
69 # if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__)
70 
71 extern "C" void ScaleYCbCr42xToRGB565_BilinearY_Row_NEON(
72  const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither);
73 
74 void __attribute((noinline)) yuv42x_to_rgb565_row_neon(uint16 *dst,
75                                                        const uint8 *y,
76                                                        const uint8 *u,
77                                                        const uint8 *v,
78                                                        int n,
79                                                        int oddflag);
80 
81 #endif
82 
83 
84 
85 /*Bilinear interpolation of a single value.
86   This uses the exact same formulas as the asm, even though it adds some extra
87    shifts that do nothing but reduce accuracy.*/
bislerp(const uint8_t * row,int pitch,int source_x,int xweight,int yweight)88 static int bislerp(const uint8_t *row,
89                    int pitch,
90                    int source_x,
91                    int xweight,
92                    int yweight) {
93   int a;
94   int b;
95   int c;
96   int d;
97   a = row[source_x];
98   b = row[source_x+1];
99   c = row[source_x+pitch];
100   d = row[source_x+pitch+1];
101   a = ((a<<8)+(c-a)*yweight+128)>>8;
102   b = ((b<<8)+(d-b)*yweight+128)>>8;
103   return ((a<<8)+(b-a)*xweight+128)>>8;
104 }
105 
106 /*Convert a single pixel from Y'CbCr to RGB565.
107   This uses the exact same formulas as the asm, even though we could make the
108    constants a lot more accurate with 32-bit wide registers.*/
yu2rgb565(int y,int u,int v,int dither)109 static uint16_t yu2rgb565(int y, int u, int v, int dither) {
110   /*This combines the constant offset that needs to be added during the Y'CbCr
111      conversion with a rounding offset that depends on the dither parameter.*/
112   static const int DITHER_BIAS[4][3]={
113     {-14240,    8704,    -17696},
114     {-14240+128,8704+64, -17696+128},
115     {-14240+256,8704+128,-17696+256},
116     {-14240+384,8704+192,-17696+384}
117   };
118   int r;
119   int g;
120   int b;
121   r = clamped((74*y+102*v+DITHER_BIAS[dither][0])>>9, 0, 31);
122   g = clamped((74*y-25*u-52*v+DITHER_BIAS[dither][1])>>8, 0, 63);
123   b = clamped((74*y+129*u+DITHER_BIAS[dither][2])>>9, 0, 31);
124   return (uint16_t)(r<<11 | g<<5 | b);
125 }
126 
ScaleYCbCr420ToRGB565_Bilinear_Row_C(const yuv2rgb565_row_scale_bilinear_ctx * ctx,int dither)127 static void ScaleYCbCr420ToRGB565_Bilinear_Row_C(
128  const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither){
129   int x;
130   int source_x_q16;
131   source_x_q16 = ctx->source_x0_q16;
132   for (x = 0; x < ctx->width; x++) {
133     int source_x;
134     int xweight;
135     int y;
136     int u;
137     int v;
138     xweight = ((source_x_q16&0xFFFF)+128)>>8;
139     source_x = source_x_q16>>16;
140     y = bislerp(ctx->y_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
141     xweight = (((source_x_q16+ctx->source_uv_xoffs_q16)&0x1FFFF)+256)>>9;
142     source_x = (source_x_q16+ctx->source_uv_xoffs_q16)>>17;
143     source_x_q16 += ctx->source_dx_q16;
144     u = bislerp(ctx->u_row, ctx->uv_pitch, source_x, xweight, ctx->uv_yweight);
145     v = bislerp(ctx->v_row, ctx->uv_pitch, source_x, xweight, ctx->uv_yweight);
146     ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
147     dither ^= 3;
148   }
149 }
150 
ScaleYCbCr422ToRGB565_Bilinear_Row_C(const yuv2rgb565_row_scale_bilinear_ctx * ctx,int dither)151 static void ScaleYCbCr422ToRGB565_Bilinear_Row_C(
152  const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither){
153   int x;
154   int source_x_q16;
155   source_x_q16 = ctx->source_x0_q16;
156   for (x = 0; x < ctx->width; x++) {
157     int source_x;
158     int xweight;
159     int y;
160     int u;
161     int v;
162     xweight = ((source_x_q16&0xFFFF)+128)>>8;
163     source_x = source_x_q16>>16;
164     y = bislerp(ctx->y_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
165     xweight = (((source_x_q16+ctx->source_uv_xoffs_q16)&0x1FFFF)+256)>>9;
166     source_x = (source_x_q16+ctx->source_uv_xoffs_q16)>>17;
167     source_x_q16 += ctx->source_dx_q16;
168     u = bislerp(ctx->u_row, ctx->uv_pitch, source_x, xweight, ctx->y_yweight);
169     v = bislerp(ctx->v_row, ctx->uv_pitch, source_x, xweight, ctx->y_yweight);
170     ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
171     dither ^= 3;
172   }
173 }
174 
ScaleYCbCr444ToRGB565_Bilinear_Row_C(const yuv2rgb565_row_scale_bilinear_ctx * ctx,int dither)175 static void ScaleYCbCr444ToRGB565_Bilinear_Row_C(
176  const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither){
177   int x;
178   int source_x_q16;
179   source_x_q16 = ctx->source_x0_q16;
180   for (x = 0; x < ctx->width; x++) {
181     int source_x;
182     int xweight;
183     int y;
184     int u;
185     int v;
186     xweight = ((source_x_q16&0xFFFF)+128)>>8;
187     source_x = source_x_q16>>16;
188     source_x_q16 += ctx->source_dx_q16;
189     y = bislerp(ctx->y_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
190     u = bislerp(ctx->u_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
191     v = bislerp(ctx->v_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
192     ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
193     dither ^= 3;
194   }
195 }
196 
ScaleYCbCr42xToRGB565_BilinearY_Row_C(const yuv2rgb565_row_scale_bilinear_ctx * ctx,int dither)197 static void ScaleYCbCr42xToRGB565_BilinearY_Row_C(
198  const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither){
199   int x;
200   int source_x_q16;
201   source_x_q16 = ctx->source_x0_q16;
202   for (x = 0; x < ctx->width; x++) {
203     int source_x;
204     int xweight;
205     int y;
206     int u;
207     int v;
208     xweight = ((source_x_q16&0xFFFF)+128)>>8;
209     source_x = source_x_q16>>16;
210     y = bislerp(ctx->y_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
211     source_x = (source_x_q16+ctx->source_uv_xoffs_q16)>>17;
212     source_x_q16 += ctx->source_dx_q16;
213     u = ctx->u_row[source_x];
214     v = ctx->v_row[source_x];
215     ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
216     dither ^= 3;
217   }
218 }
219 
ScaleYCbCr444ToRGB565_BilinearY_Row_C(const yuv2rgb565_row_scale_bilinear_ctx * ctx,int dither)220 static void ScaleYCbCr444ToRGB565_BilinearY_Row_C(
221  const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither){
222   int x;
223   int source_x_q16;
224   source_x_q16 = ctx->source_x0_q16;
225   for (x = 0; x < ctx->width; x++) {
226     int source_x;
227     int xweight;
228     int y;
229     int u;
230     int v;
231     xweight = ((source_x_q16&0xFFFF)+128)>>8;
232     source_x = source_x_q16>>16;
233     y = bislerp(ctx->y_row, ctx->y_pitch, source_x, xweight, ctx->y_yweight);
234     source_x = (source_x_q16+ctx->source_uv_xoffs_q16)>>16;
235     source_x_q16 += ctx->source_dx_q16;
236     u = ctx->u_row[source_x];
237     v = ctx->v_row[source_x];
238     ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
239     dither ^= 3;
240   }
241 }
242 
ScaleYCbCr42xToRGB565_Nearest_Row_C(const yuv2rgb565_row_scale_nearest_ctx * ctx,int dither)243 static void ScaleYCbCr42xToRGB565_Nearest_Row_C(
244  const yuv2rgb565_row_scale_nearest_ctx *ctx, int dither){
245   int y;
246   int u;
247   int v;
248   int x;
249   int source_x_q16;
250   int source_x;
251   source_x_q16 = ctx->source_x0_q16;
252   for (x = 0; x < ctx->width; x++) {
253     source_x = source_x_q16>>16;
254     y = ctx->y_row[source_x];
255     source_x = (source_x_q16+ctx->source_uv_xoffs_q16)>>17;
256     source_x_q16 += ctx->source_dx_q16;
257     u = ctx->u_row[source_x];
258     v = ctx->v_row[source_x];
259     ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
260     dither ^= 3;
261   }
262 }
263 
ScaleYCbCr444ToRGB565_Nearest_Row_C(const yuv2rgb565_row_scale_nearest_ctx * ctx,int dither)264 static void ScaleYCbCr444ToRGB565_Nearest_Row_C(
265  const yuv2rgb565_row_scale_nearest_ctx *ctx, int dither){
266   int y;
267   int u;
268   int v;
269   int x;
270   int source_x_q16;
271   int source_x;
272   source_x_q16 = ctx->source_x0_q16;
273   for (x = 0; x < ctx->width; x++) {
274     source_x = source_x_q16>>16;
275     source_x_q16 += ctx->source_dx_q16;
276     y = ctx->y_row[source_x];
277     u = ctx->u_row[source_x];
278     v = ctx->v_row[source_x];
279     ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
280     dither ^= 3;
281   }
282 }
283 
ScaleYCbCrToRGB565(const uint8_t * y_buf,const uint8_t * u_buf,const uint8_t * v_buf,uint8_t * rgb_buf,int source_x0,int source_y0,int source_width,int source_height,int width,int height,int y_pitch,int uv_pitch,int rgb_pitch,YUVType yuv_type,ScaleFilter filter)284 void ScaleYCbCrToRGB565(const uint8_t *y_buf,
285                                  const uint8_t *u_buf,
286                                  const uint8_t *v_buf,
287                                  uint8_t *rgb_buf,
288                                  int source_x0,
289                                  int source_y0,
290                                  int source_width,
291                                  int source_height,
292                                  int width,
293                                  int height,
294                                  int y_pitch,
295                                  int uv_pitch,
296                                  int rgb_pitch,
297                                  YUVType yuv_type,
298                                  ScaleFilter filter) {
299   int source_x0_q16;
300   int source_y0_q16;
301   int source_dx_q16;
302   int source_dy_q16;
303   int source_uv_xoffs_q16;
304   int source_uv_yoffs_q16;
305   int x_shift;
306   int y_shift;
307   int ymin;
308   int ymax;
309   int uvmin;
310   int uvmax;
311   int dither;
312   /*We don't support negative destination rectangles (just flip the source
313      instead), and for empty ones there's nothing to do.*/
314   if (width <= 0 || height <= 0)
315     return;
316   /*These bounds are required to avoid 16.16 fixed-point overflow.*/
317   NS_ASSERTION(source_x0 > (INT_MIN>>16) && source_x0 < (INT_MAX>>16),
318     "ScaleYCbCrToRGB565 source X offset out of bounds.");
319   NS_ASSERTION(source_x0+source_width > (INT_MIN>>16)
320             && source_x0+source_width < (INT_MAX>>16),
321     "ScaleYCbCrToRGB565 source width out of bounds.");
322   NS_ASSERTION(source_y0 > (INT_MIN>>16) && source_y0 < (INT_MAX>>16),
323     "ScaleYCbCrToRGB565 source Y offset out of bounds.");
324   NS_ASSERTION(source_y0+source_height > (INT_MIN>>16)
325             && source_y0+source_height < (INT_MAX>>16),
326     "ScaleYCbCrToRGB565 source height out of bounds.");
327   /*We require the same stride for Y' and Cb and Cr for 4:4:4 content.*/
328   NS_ASSERTION(yuv_type != YV24 || y_pitch == uv_pitch,
329     "ScaleYCbCrToRGB565 luma stride differs from chroma for 4:4:4 content.");
330   /*We assume we can read outside the bounds of the input, because it makes
331      the code much simpler (and in practice is true: both Theora and VP8 return
332      padded reference frames).
333     In practice, we do not even _have_ the actual bounds of the source, as
334      we are passed a crop rectangle from it, and not the dimensions of the full
335      image.
336     This assertion will not guarantee our out-of-bounds reads are safe, but it
337      should at least catch the simple case of passing in an unpadded buffer.*/
338   NS_ASSERTION(abs(y_pitch) >= abs(source_width)+16,
339     "ScaleYCbCrToRGB565 source image unpadded?");
340   /*The NEON code requires the pointers to be aligned to a 16-byte boundary at
341      the start of each row.
342     This should be true for all of our sources.
343     We could try to fix this up if it's not true by adjusting source_x0, but
344      that would require the mis-alignment to be the same for the U and V
345      planes.*/
346   NS_ASSERTION((y_pitch&15) == 0 && (uv_pitch&15) == 0 &&
347    ((y_buf-(uint8_t *)nullptr)&15) == 0 &&
348    ((u_buf-(uint8_t *)nullptr)&15) == 0 &&
349    ((v_buf-(uint8_t *)nullptr)&15) == 0,
350    "ScaleYCbCrToRGB565 source image unaligned");
351   /*We take an area-based approach to pixel coverage to avoid shifting by small
352      amounts (or not so small, when up-scaling or down-scaling by a large
353      factor).
354 
355     An illustrative example: scaling 4:2:0 up by 2, using JPEG chroma cositing^.
356 
357     + = RGB destination locations
358     * = Y' source locations
359     - = Cb, Cr source locations
360 
361     +   +   +   +  +   +   +   +
362       *       *      *       *
363     +   +   +   +  +   +   +   +
364           -              -
365     +   +   +   +  +   +   +   +
366       *       *      *       *
367     +   +   +   +  +   +   +   +
368 
369     +   +   +   +  +   +   +   +
370       *       *      *       *
371     +   +   +   +  +   +   +   +
372           -              -
373     +   +   +   +  +   +   +   +
374       *       *      *       *
375     +   +   +   +  +   +   +   +
376 
377     So, the coordinates of the upper-left + (first destination site) should
378      be (-0.25,-0.25) in the source Y' coordinate system.
379     Similarly, the coordinates should be (-0.375,-0.375) in the source Cb, Cr
380      coordinate system.
381     Note that the origin and scale of these two coordinate systems is not the
382      same!
383 
384     ^JPEG cositing is required for Theora; VP8 doesn't specify cositing rules,
385      but nearly all software converters in existence (at least those that are
386      open source, and many that are not) use JPEG cositing instead of MPEG.*/
387   source_dx_q16 = (source_width<<16) / width;
388   source_x0_q16 = (source_x0<<16)+(source_dx_q16>>1)-0x8000;
389   source_dy_q16 = (source_height<<16) / height;
390   source_y0_q16 = (source_y0<<16)+(source_dy_q16>>1)-0x8000;
391   x_shift = (yuv_type != YV24);
392   y_shift = (yuv_type == YV12);
393   /*These two variables hold the difference between the origins of the Y' and
394      the Cb, Cr coordinate systems, using the scale of the Y' coordinate
395      system.*/
396   source_uv_xoffs_q16 = -(x_shift<<15);
397   source_uv_yoffs_q16 = -(y_shift<<15);
398   /*Compute the range of source rows we'll actually use.
399     This doesn't guarantee we won't read outside this range.*/
400   ymin = source_height >= 0 ? source_y0 : source_y0+source_height-1;
401   ymax = source_height >= 0 ? source_y0+source_height-1 : source_y0;
402   uvmin = ymin>>y_shift;
403   uvmax = ((ymax+1+y_shift)>>y_shift)-1;
404   /*Pick a dithering pattern.
405     The "&3" at the end is just in case RAND_MAX is lying.*/
406   dither = (rand()/(RAND_MAX>>2))&3;
407   /*Nearest-neighbor scaling.*/
408   if (filter == FILTER_NONE) {
409     yuv2rgb565_row_scale_nearest_ctx ctx;
410     yuv2rgb565_row_scale_nearest_func scale_row;
411     int y;
412     /*Add rounding offsets once, in advance.*/
413     source_x0_q16 += 0x8000;
414     source_y0_q16 += 0x8000;
415     source_uv_xoffs_q16 += (x_shift<<15);
416     source_uv_yoffs_q16 += (y_shift<<15);
417     if (yuv_type == YV12)
418       scale_row = ScaleYCbCr42xToRGB565_Nearest_Row_C;
419     else
420       scale_row = ScaleYCbCr444ToRGB565_Nearest_Row_C;
421     ctx.width = width;
422     ctx.source_x0_q16 = source_x0_q16;
423     ctx.source_dx_q16 = source_dx_q16;
424     ctx.source_uv_xoffs_q16 = source_uv_xoffs_q16;
425     for (y=0; y<height; y++) {
426       int source_y;
427       ctx.rgb_row = (uint16_t *)(rgb_buf + y*rgb_pitch);
428       source_y = source_y0_q16>>16;
429       source_y = clamped(source_y, ymin, ymax);
430       ctx.y_row = y_buf + source_y*y_pitch;
431       source_y = (source_y0_q16+source_uv_yoffs_q16)>>(16+y_shift);
432       source_y = clamped(source_y, uvmin, uvmax);
433       source_y0_q16 += source_dy_q16;
434       ctx.u_row = u_buf + source_y*uv_pitch;
435       ctx.v_row = v_buf + source_y*uv_pitch;
436       (*scale_row)(&ctx, dither);
437       dither ^= 2;
438     }
439   }
440   /*Bilinear scaling.*/
441   else {
442     yuv2rgb565_row_scale_bilinear_ctx ctx;
443     yuv2rgb565_row_scale_bilinear_func scale_row;
444     int uvxscale_min;
445     int uvxscale_max;
446     int uvyscale_min;
447     int uvyscale_max;
448     int y;
449     /*Check how close the chroma scaling is to unity.
450       If it's close enough, we can get away with nearest-neighbor chroma
451        sub-sampling, and only doing bilinear on luma.
452       If a given axis is subsampled, we use bounds on the luma step of
453        [0.67...2], which is equivalent to scaling chroma by [1...3].
454       If it's not subsampled, we use bounds of [0.5...1.33], which is
455        equivalent to scaling chroma by [0.75...2].
456       The lower bound is chosen as a trade-off between speed and how terrible
457        nearest neighbor looks when upscaling.*/
458 # define CHROMA_NEAREST_SUBSAMP_STEP_MIN  0xAAAA
459 # define CHROMA_NEAREST_NORMAL_STEP_MIN   0x8000
460 # define CHROMA_NEAREST_SUBSAMP_STEP_MAX 0x20000
461 # define CHROMA_NEAREST_NORMAL_STEP_MAX  0x15555
462     uvxscale_min = yuv_type != YV24 ?
463      CHROMA_NEAREST_SUBSAMP_STEP_MIN : CHROMA_NEAREST_NORMAL_STEP_MIN;
464     uvxscale_max = yuv_type != YV24 ?
465      CHROMA_NEAREST_SUBSAMP_STEP_MAX : CHROMA_NEAREST_NORMAL_STEP_MAX;
466     uvyscale_min = yuv_type == YV12 ?
467      CHROMA_NEAREST_SUBSAMP_STEP_MIN : CHROMA_NEAREST_NORMAL_STEP_MIN;
468     uvyscale_max = yuv_type == YV12 ?
469      CHROMA_NEAREST_SUBSAMP_STEP_MAX : CHROMA_NEAREST_NORMAL_STEP_MAX;
470     if (uvxscale_min <= abs(source_dx_q16)
471      && abs(source_dx_q16) <= uvxscale_max
472      && uvyscale_min <= abs(source_dy_q16)
473      && abs(source_dy_q16) <= uvyscale_max) {
474       /*Add the rounding offsets now.*/
475       source_uv_xoffs_q16 += 1<<(15+x_shift);
476       source_uv_yoffs_q16 += 1<<(15+y_shift);
477       if (yuv_type != YV24) {
478         scale_row =
479 //TODO: fix NEON asm for iOS
480 #  if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__)
481          supports_neon() ? ScaleYCbCr42xToRGB565_BilinearY_Row_NEON :
482 #  endif
483          ScaleYCbCr42xToRGB565_BilinearY_Row_C;
484       }
485       else
486         scale_row = ScaleYCbCr444ToRGB565_BilinearY_Row_C;
487     }
488     else {
489       if (yuv_type == YV12)
490         scale_row = ScaleYCbCr420ToRGB565_Bilinear_Row_C;
491       else if (yuv_type == YV16)
492         scale_row = ScaleYCbCr422ToRGB565_Bilinear_Row_C;
493       else
494         scale_row = ScaleYCbCr444ToRGB565_Bilinear_Row_C;
495     }
496     ctx.width = width;
497     ctx.y_pitch = y_pitch;
498     ctx.source_x0_q16 = source_x0_q16;
499     ctx.source_dx_q16 = source_dx_q16;
500     ctx.source_uv_xoffs_q16 = source_uv_xoffs_q16;
501     ctx.uv_pitch = uv_pitch;
502     for (y=0; y<height; y++) {
503       int source_y;
504       int yweight;
505       int uvweight;
506       ctx.rgb_row = (uint16_t *)(rgb_buf + y*rgb_pitch);
507       source_y = (source_y0_q16+128)>>16;
508       yweight = ((source_y0_q16+128)>>8)&0xFF;
509       if (source_y < ymin) {
510         source_y = ymin;
511         yweight = 0;
512       }
513       if (source_y > ymax) {
514         source_y = ymax;
515         yweight = 0;
516       }
517       ctx.y_row = y_buf + source_y*y_pitch;
518       source_y = source_y0_q16+source_uv_yoffs_q16+(128<<y_shift);
519       source_y0_q16 += source_dy_q16;
520       uvweight = source_y>>(8+y_shift)&0xFF;
521       source_y >>= 16+y_shift;
522       if (source_y < uvmin) {
523         source_y = uvmin;
524         uvweight = 0;
525       }
526       if (source_y > uvmax) {
527         source_y = uvmax;
528         uvweight = 0;
529       }
530       ctx.u_row = u_buf + source_y*uv_pitch;
531       ctx.v_row = v_buf + source_y*uv_pitch;
532       ctx.y_yweight = yweight;
533       ctx.uv_yweight = uvweight;
534       (*scale_row)(&ctx, dither);
535       dither ^= 2;
536     }
537   }
538 }
539 
IsScaleYCbCrToRGB565Fast(int source_x0,int source_y0,int source_width,int source_height,int width,int height,YUVType yuv_type,ScaleFilter filter)540 bool IsScaleYCbCrToRGB565Fast(int source_x0,
541                                        int source_y0,
542                                        int source_width,
543                                        int source_height,
544                                        int width,
545                                        int height,
546                                        YUVType yuv_type,
547                                        ScaleFilter filter)
548 {
549   // Very fast.
550   if (width <= 0 || height <= 0)
551     return true;
552 #  if defined(MOZILLA_MAY_SUPPORT_NEON)
553   if (filter != FILTER_NONE) {
554     int source_dx_q16;
555     int source_dy_q16;
556     int uvxscale_min;
557     int uvxscale_max;
558     int uvyscale_min;
559     int uvyscale_max;
560     source_dx_q16 = (source_width<<16) / width;
561     source_dy_q16 = (source_height<<16) / height;
562     uvxscale_min = yuv_type != YV24 ?
563      CHROMA_NEAREST_SUBSAMP_STEP_MIN : CHROMA_NEAREST_NORMAL_STEP_MIN;
564     uvxscale_max = yuv_type != YV24 ?
565      CHROMA_NEAREST_SUBSAMP_STEP_MAX : CHROMA_NEAREST_NORMAL_STEP_MAX;
566     uvyscale_min = yuv_type == YV12 ?
567      CHROMA_NEAREST_SUBSAMP_STEP_MIN : CHROMA_NEAREST_NORMAL_STEP_MIN;
568     uvyscale_max = yuv_type == YV12 ?
569      CHROMA_NEAREST_SUBSAMP_STEP_MAX : CHROMA_NEAREST_NORMAL_STEP_MAX;
570     if (uvxscale_min <= abs(source_dx_q16)
571      && abs(source_dx_q16) <= uvxscale_max
572      && uvyscale_min <= abs(source_dy_q16)
573      && abs(source_dy_q16) <= uvyscale_max) {
574       if (yuv_type != YV24)
575         return supports_neon();
576     }
577   }
578 #  endif
579   return false;
580 }
581 
582 
583 
yuv_to_rgb565_row_c(uint16 * dst,const uint8 * y,const uint8 * u,const uint8 * v,int x_shift,int pic_x,int pic_width)584 void yuv_to_rgb565_row_c(uint16 *dst,
585                          const uint8 *y,
586                          const uint8 *u,
587                          const uint8 *v,
588                          int x_shift,
589                          int pic_x,
590                          int pic_width)
591 {
592   int x;
593   for (x = 0; x < pic_width; x++)
594   {
595     dst[x] = yu2rgb565(y[pic_x+x],
596                        u[(pic_x+x)>>x_shift],
597                        v[(pic_x+x)>>x_shift],
598                        2); // Disable dithering for now.
599   }
600 }
601 
ConvertYCbCrToRGB565(const uint8 * y_buf,const uint8 * u_buf,const uint8 * v_buf,uint8 * rgb_buf,int pic_x,int pic_y,int pic_width,int pic_height,int y_pitch,int uv_pitch,int rgb_pitch,YUVType yuv_type)602 void ConvertYCbCrToRGB565(const uint8* y_buf,
603                                    const uint8* u_buf,
604                                    const uint8* v_buf,
605                                    uint8* rgb_buf,
606                                    int pic_x,
607                                    int pic_y,
608                                    int pic_width,
609                                    int pic_height,
610                                    int y_pitch,
611                                    int uv_pitch,
612                                    int rgb_pitch,
613                                    YUVType yuv_type)
614 {
615   int x_shift;
616   int y_shift;
617   x_shift = yuv_type != YV24;
618   y_shift = yuv_type == YV12;
619 //TODO: fix NEON asm for iOS
620 #  if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__)
621   if (yuv_type != YV24 && supports_neon())
622   {
623     for (int i = 0; i < pic_height; i++) {
624       int yoffs;
625       int uvoffs;
626       yoffs = y_pitch * (pic_y+i) + pic_x;
627       uvoffs = uv_pitch * ((pic_y+i)>>y_shift) + (pic_x>>x_shift);
628       yuv42x_to_rgb565_row_neon((uint16*)(rgb_buf + rgb_pitch * i),
629                                 y_buf + yoffs,
630                                 u_buf + uvoffs,
631                                 v_buf + uvoffs,
632                                 pic_width,
633                                 pic_x&x_shift);
634     }
635   }
636   else
637 #  endif
638   {
639     for (int i = 0; i < pic_height; i++) {
640       int yoffs;
641       int uvoffs;
642       yoffs = y_pitch * (pic_y+i);
643       uvoffs = uv_pitch * ((pic_y+i)>>y_shift);
644       yuv_to_rgb565_row_c((uint16*)(rgb_buf + rgb_pitch * i),
645                           y_buf + yoffs,
646                           u_buf + uvoffs,
647                           v_buf + uvoffs,
648                           x_shift,
649                           pic_x,
650                           pic_width);
651     }
652   }
653 }
654 
IsConvertYCbCrToRGB565Fast(int pic_x,int pic_y,int pic_width,int pic_height,YUVType yuv_type)655 bool IsConvertYCbCrToRGB565Fast(int pic_x,
656                                          int pic_y,
657                                          int pic_width,
658                                          int pic_height,
659                                          YUVType yuv_type)
660 {
661 #  if defined(MOZILLA_MAY_SUPPORT_NEON)
662   return (yuv_type != YV24 && supports_neon());
663 #  else
664   return false;
665 #  endif
666 }
667 
668 } // namespace gfx
669 
670 } // namespace mozilla
671 
672 #endif // HAVE_YCBCR_TO_RGB565
673