gfx/ycbcr/ycbcr_to_rgb565.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial