ycbcr_to_rgb565.cpp (23332B)
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 11 12 13 #ifdef HAVE_YCBCR_TO_RGB565 14 15 namespace mozilla { 16 17 namespace gfx { 18 19 /*This contains all of the parameters that are needed to convert a row. 20 Passing them in a struct instead of as individual parameters saves the need 21 to continually push onto the stack the ones that are fixed for every row.*/ 22 struct yuv2rgb565_row_scale_bilinear_ctx{ 23 uint16_t *rgb_row; 24 const uint8_t *y_row; 25 const uint8_t *u_row; 26 const uint8_t *v_row; 27 int y_yweight; 28 int y_pitch; 29 int width; 30 int source_x0_q16; 31 int source_dx_q16; 32 /*Not used for 4:4:4, except with chroma-nearest.*/ 33 int source_uv_xoffs_q16; 34 /*Not used for 4:4:4 or chroma-nearest.*/ 35 int uv_pitch; 36 /*Not used for 4:2:2, 4:4:4, or chroma-nearest.*/ 37 int uv_yweight; 38 }; 39 40 41 42 /*This contains all of the parameters that are needed to convert a row. 43 Passing them in a struct instead of as individual parameters saves the need 44 to continually push onto the stack the ones that are fixed for every row.*/ 45 struct yuv2rgb565_row_scale_nearest_ctx{ 46 uint16_t *rgb_row; 47 const uint8_t *y_row; 48 const uint8_t *u_row; 49 const uint8_t *v_row; 50 int width; 51 int source_x0_q16; 52 int source_dx_q16; 53 /*Not used for 4:4:4.*/ 54 int source_uv_xoffs_q16; 55 }; 56 57 58 59 typedef void (*yuv2rgb565_row_scale_bilinear_func)( 60 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither); 61 62 typedef void (*yuv2rgb565_row_scale_nearest_func)( 63 const yuv2rgb565_row_scale_nearest_ctx *ctx, int dither); 64 65 66 67 //TODO: fix NEON asm for iOS 68 # if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__) 69 70 extern "C" void ScaleYCbCr42xToRGB565_BilinearY_Row_NEON( 71 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither); 72 73 void __attribute((noinline)) yuv42x_to_rgb565_row_neon(uint16_t *dst, 74 const uint8_t *y, 75 const uint8_t *u, 76 const uint8_t *v, 77 int n, 78 int oddflag); 79 80 #endif 81 82 83 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; 103 } 104 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} 116 }; 117 int r; 118 int g; 119 int b; 120 r = std::clamp((74*y+102*v+DITHER_BIAS[dither][0])>>9, 0, 31); 121 g = std::clamp((74*y-25*u-52*v+DITHER_BIAS[dither][1])>>8, 0, 63); 122 b = std::clamp((74*y+129*u+DITHER_BIAS[dither][2])>>9, 0, 31); 123 return (uint16_t)(r<<11 | g<<5 | b); 124 } 125 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; 147 } 148 } 149 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; 171 } 172 } 173 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; 193 } 194 } 195 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; 216 } 217 } 218 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; 239 } 240 } 241 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; 260 } 261 } 262 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; 280 } 281 } 282 283 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). 353 354 An illustrative example: scaling 4:2:0 up by 2, using JPEG chroma cositing^. 355 356 + = RGB destination locations 357 * = Y' source locations 358 - = Cb, Cr source locations 359 360 + + + + + + + + 361 * * * * 362 + + + + + + + + 363 - - 364 + + + + + + + + 365 * * * * 366 + + + + + + + + 367 368 + + + + + + + + 369 * * * * 370 + + + + + + + + 371 - - 372 + + + + + + + + 373 * * * * 374 + + + + + + + + 375 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! 382 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 = std::clamp(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 = std::clamp(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; 437 } 438 } 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 //TODO: fix NEON asm for iOS 479 # if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__) 480 supports_neon() ? ScaleYCbCr42xToRGB565_BilinearY_Row_NEON : 481 # endif 482 ScaleYCbCr42xToRGB565_BilinearY_Row_C; 483 } 484 else 485 scale_row = ScaleYCbCr444ToRGB565_BilinearY_Row_C; 486 } 487 else { 488 if (yuv_type == YV12) 489 scale_row = ScaleYCbCr420ToRGB565_Bilinear_Row_C; 490 else if (yuv_type == YV16) 491 scale_row = ScaleYCbCr422ToRGB565_Bilinear_Row_C; 492 else 493 scale_row = ScaleYCbCr444ToRGB565_Bilinear_Row_C; 494 } 495 ctx.width = width; 496 ctx.y_pitch = y_pitch; 497 ctx.source_x0_q16 = source_x0_q16; 498 ctx.source_dx_q16 = source_dx_q16; 499 ctx.source_uv_xoffs_q16 = source_uv_xoffs_q16; 500 ctx.uv_pitch = uv_pitch; 501 for (y=0; y<height; y++) { 502 int source_y; 503 int yweight; 504 int uvweight; 505 ctx.rgb_row = (uint16_t *)(rgb_buf + y*rgb_pitch); 506 source_y = (source_y0_q16+128)>>16; 507 yweight = ((source_y0_q16+128)>>8)&0xFF; 508 if (source_y < ymin) { 509 source_y = ymin; 510 yweight = 0; 511 } 512 if (source_y > ymax) { 513 source_y = ymax; 514 yweight = 0; 515 } 516 ctx.y_row = y_buf + source_y*y_pitch; 517 source_y = source_y0_q16+source_uv_yoffs_q16+(128<<y_shift); 518 source_y0_q16 += source_dy_q16; 519 uvweight = source_y>>(8+y_shift)&0xFF; 520 source_y >>= 16+y_shift; 521 if (source_y < uvmin) { 522 source_y = uvmin; 523 uvweight = 0; 524 } 525 if (source_y > uvmax) { 526 source_y = uvmax; 527 uvweight = 0; 528 } 529 ctx.u_row = u_buf + source_y*uv_pitch; 530 ctx.v_row = v_buf + source_y*uv_pitch; 531 ctx.y_yweight = yweight; 532 ctx.uv_yweight = uvweight; 533 (*scale_row)(&ctx, dither); 534 dither ^= 2; 535 } 536 } 537 } 538 539 bool IsScaleYCbCrToRGB565Fast(int source_x0, 540 int source_y0, 541 int source_width, 542 int source_height, 543 int width, 544 int height, 545 YUVType yuv_type, 546 ScaleFilter filter) 547 { 548 // Very fast. 549 if (width <= 0 || height <= 0) 550 return true; 551 # if defined(MOZILLA_MAY_SUPPORT_NEON) 552 if (filter != FILTER_NONE) { 553 int source_dx_q16; 554 int source_dy_q16; 555 int uvxscale_min; 556 int uvxscale_max; 557 int uvyscale_min; 558 int uvyscale_max; 559 source_dx_q16 = (source_width<<16) / width; 560 source_dy_q16 = (source_height<<16) / height; 561 uvxscale_min = yuv_type != YV24 ? 562 CHROMA_NEAREST_SUBSAMP_STEP_MIN : CHROMA_NEAREST_NORMAL_STEP_MIN; 563 uvxscale_max = yuv_type != YV24 ? 564 CHROMA_NEAREST_SUBSAMP_STEP_MAX : CHROMA_NEAREST_NORMAL_STEP_MAX; 565 uvyscale_min = yuv_type == YV12 ? 566 CHROMA_NEAREST_SUBSAMP_STEP_MIN : CHROMA_NEAREST_NORMAL_STEP_MIN; 567 uvyscale_max = yuv_type == YV12 ? 568 CHROMA_NEAREST_SUBSAMP_STEP_MAX : CHROMA_NEAREST_NORMAL_STEP_MAX; 569 if (uvxscale_min <= abs(source_dx_q16) 570 && abs(source_dx_q16) <= uvxscale_max 571 && uvyscale_min <= abs(source_dy_q16) 572 && abs(source_dy_q16) <= uvyscale_max) { 573 if (yuv_type != YV24) 574 return supports_neon(); 575 } 576 } 577 # endif 578 return false; 579 } 580 581 582 583 void yuv_to_rgb565_row_c(uint16_t *dst, 584 const uint8_t *y, 585 const uint8_t *u, 586 const uint8_t *v, 587 int x_shift, 588 int pic_x, 589 int pic_width) 590 { 591 int x; 592 for (x = 0; x < pic_width; x++) 593 { 594 dst[x] = yu2rgb565(y[pic_x+x], 595 u[(pic_x+x)>>x_shift], 596 v[(pic_x+x)>>x_shift], 597 2); // Disable dithering for now. 598 } 599 } 600 601 void ConvertYCbCrToRGB565(const uint8_t* y_buf, 602 const uint8_t* u_buf, 603 const uint8_t* v_buf, 604 uint8_t* rgb_buf, 605 int pic_x, 606 int pic_y, 607 int pic_width, 608 int pic_height, 609 int y_pitch, 610 int uv_pitch, 611 int rgb_pitch, 612 YUVType yuv_type) 613 { 614 int x_shift; 615 int y_shift; 616 x_shift = yuv_type != YV24; 617 y_shift = yuv_type == YV12; 618 //TODO: fix NEON asm for iOS 619 # if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__) 620 if (yuv_type != YV24 && supports_neon()) 621 { 622 for (int i = 0; i < pic_height; i++) { 623 int yoffs; 624 int uvoffs; 625 yoffs = y_pitch * (pic_y+i) + pic_x; 626 uvoffs = uv_pitch * ((pic_y+i)>>y_shift) + (pic_x>>x_shift); 627 yuv42x_to_rgb565_row_neon((uint16_t*)(rgb_buf + rgb_pitch * i), 628 y_buf + yoffs, 629 u_buf + uvoffs, 630 v_buf + uvoffs, 631 pic_width, 632 pic_x&x_shift); 633 } 634 } 635 else 636 # endif 637 { 638 for (int i = 0; i < pic_height; i++) { 639 int yoffs; 640 int uvoffs; 641 yoffs = y_pitch * (pic_y+i); 642 uvoffs = uv_pitch * ((pic_y+i)>>y_shift); 643 yuv_to_rgb565_row_c((uint16_t*)(rgb_buf + rgb_pitch * i), 644 y_buf + yoffs, 645 u_buf + uvoffs, 646 v_buf + uvoffs, 647 x_shift, 648 pic_x, 649 pic_width); 650 } 651 } 652 } 653 654 bool IsConvertYCbCrToRGB565Fast(int pic_x, 655 int pic_y, 656 int pic_width, 657 int pic_height, 658 YUVType yuv_type) 659 { 660 # if defined(MOZILLA_MAY_SUPPORT_NEON) 661 return (yuv_type != YV24 && supports_neon()); 662 # else 663 return false; 664 # endif 665 } 666 667 } // namespace gfx 668 669 } // namespace mozilla 670 671 #endif // HAVE_YCBCR_TO_RGB565