|
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 #include "nsAlgorithm.h" |
|
11 |
|
12 |
|
13 |
|
14 #ifdef HAVE_YCBCR_TO_RGB565 |
|
15 |
|
16 namespace mozilla { |
|
17 |
|
18 namespace gfx { |
|
19 |
|
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; |
|
39 }; |
|
40 |
|
41 |
|
42 |
|
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; |
|
56 }; |
|
57 |
|
58 |
|
59 |
|
60 typedef void (*yuv2rgb565_row_scale_bilinear_func)( |
|
61 const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither); |
|
62 |
|
63 typedef void (*yuv2rgb565_row_scale_nearest_func)( |
|
64 const yuv2rgb565_row_scale_nearest_ctx *ctx, int dither); |
|
65 |
|
66 |
|
67 |
|
68 # if defined(MOZILLA_MAY_SUPPORT_NEON) |
|
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 *dst, |
|
74 const uint8 *y, |
|
75 const uint8 *u, |
|
76 const uint8 *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 = 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); |
|
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 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). |
|
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 = 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; |
|
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 # if defined(MOZILLA_MAY_SUPPORT_NEON) |
|
479 supports_neon() ? ScaleYCbCr42xToRGB565_BilinearY_Row_NEON : |
|
480 # endif |
|
481 ScaleYCbCr42xToRGB565_BilinearY_Row_C; |
|
482 } |
|
483 else |
|
484 scale_row = ScaleYCbCr444ToRGB565_BilinearY_Row_C; |
|
485 } |
|
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; |
|
493 } |
|
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; |
|
510 } |
|
511 if (source_y > ymax) { |
|
512 source_y = ymax; |
|
513 yweight = 0; |
|
514 } |
|
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; |
|
523 } |
|
524 if (source_y > uvmax) { |
|
525 source_y = uvmax; |
|
526 uvweight = 0; |
|
527 } |
|
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; |
|
534 } |
|
535 } |
|
536 } |
|
537 |
|
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) |
|
546 { |
|
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(); |
|
574 } |
|
575 } |
|
576 # endif |
|
577 return false; |
|
578 } |
|
579 |
|
580 |
|
581 |
|
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) |
|
589 { |
|
590 int x; |
|
591 for (x = 0; x < pic_width; x++) |
|
592 { |
|
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. |
|
597 } |
|
598 } |
|
599 |
|
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) |
|
612 { |
|
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()) |
|
619 { |
|
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); |
|
631 } |
|
632 } |
|
633 else |
|
634 # endif |
|
635 { |
|
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); |
|
648 } |
|
649 } |
|
650 } |
|
651 |
|
652 NS_GFX_(bool) IsConvertYCbCrToRGB565Fast(int pic_x, |
|
653 int pic_y, |
|
654 int pic_width, |
|
655 int pic_height, |
|
656 YUVType yuv_type) |
|
657 { |
|
658 # if defined(MOZILLA_MAY_SUPPORT_NEON) |
|
659 return (yuv_type != YV24 && supports_neon()); |
|
660 # else |
|
661 return false; |
|
662 # endif |
|
663 } |
|
664 |
|
665 } // namespace gfx |
|
666 |
|
667 } // namespace mozilla |
|
668 |
|
669 #endif // HAVE_YCBCR_TO_RGB565 |