Bug 1669129 - [devtools] Enable devtools.overflow.debugging.enabled. r=jdescottes
[gecko.git] / gfx / ycbcr / ycbcr_to_rgb565.cpp
blob0572e3e094aec79c7fb970f0b73a9584a3ca977c
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/. */
6 #include <stdlib.h>
7 #include <limits.h>
8 #include "nsDebug.h"
9 #include "ycbcr_to_rgb565.h"
10 #include "nsAlgorithm.h"
14 #ifdef HAVE_YCBCR_TO_RGB565
16 namespace mozilla {
18 namespace gfx {
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;
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;
60 typedef void (*yuv2rgb565_row_scale_bilinear_func)(
61 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither);
63 typedef void (*yuv2rgb565_row_scale_nearest_func)(
64 const yuv2rgb565_row_scale_nearest_ctx *ctx, int dither);
68 //TODO: fix NEON asm for iOS
69 # if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__)
71 extern "C" void ScaleYCbCr42xToRGB565_BilinearY_Row_NEON(
72 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither);
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);
81 #endif
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.*/
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;
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.*/
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}
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);
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;
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;
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;
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;
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;
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;
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;
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).
355 An illustrative example: scaling 4:2:0 up by 2, using JPEG chroma cositing^.
357 + = RGB destination locations
358 * = Y' source locations
359 - = Cb, Cr source locations
361 + + + + + + + +
362 * * * *
363 + + + + + + + +
365 + + + + + + + +
366 * * * *
367 + + + + + + + +
369 + + + + + + + +
370 * * * *
371 + + + + + + + +
373 + + + + + + + +
374 * * * *
375 + + + + + + + +
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!
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;
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;
485 else
486 scale_row = ScaleYCbCr444ToRGB565_BilinearY_Row_C;
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;
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;
513 if (source_y > ymax) {
514 source_y = ymax;
515 yweight = 0;
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;
526 if (source_y > uvmax) {
527 source_y = uvmax;
528 uvweight = 0;
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;
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)
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();
578 # endif
579 return false;
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)
592 int x;
593 for (x = 0; x < pic_width; x++)
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.
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)
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())
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);
636 else
637 # endif
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);
655 bool IsConvertYCbCrToRGB565Fast(int pic_x,
656 int pic_y,
657 int pic_width,
658 int pic_height,
659 YUVType yuv_type)
661 # if defined(MOZILLA_MAY_SUPPORT_NEON)
662 return (yuv_type != YV24 && supports_neon());
663 # else
664 return false;
665 # endif
668 } // namespace gfx
670 } // namespace mozilla
672 #endif // HAVE_YCBCR_TO_RGB565