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