Wed, 31 Dec 2014 06:09:35 +0100
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 |