/Avc/yuv420rgb8888c.cpp
C++ | 187 lines | 103 code | 10 blank | 74 comment | 15 complexity | 111c1d66dbb4323ba2eaba9345fc5f62 MD5 | raw file
Possible License(s): BSD-3-Clause
- /* YUV-> RGB conversion code.
- *
- * Copyright (C) 2011 Robin Watts (robin@wss.co.uk) for Pinknoise
- * Productions Ltd.
- *
- * Licensed under the BSD license. See 'COPYING' for details of
- * (non-)warranty.
- *
- *
- * The algorithm used here is based heavily on one created by Sophie Wilson
- * of Acorn/e-14/Broadcomm. Many thanks.
- *
- * Additional tweaks (in the fast fixup code) are from Paul Gardiner.
- *
- * The old implementation of YUV -> RGB did:
- *
- * R = CLAMP((Y-16)*1.164 + 1.596*V)
- * G = CLAMP((Y-16)*1.164 - 0.391*U - 0.813*V)
- * B = CLAMP((Y-16)*1.164 + 2.018*U )
- *
- * We're going to bend that here as follows:
- *
- * R = CLAMP(y + 1.596*V)
- * G = CLAMP(y - 0.383*U - 0.813*V)
- * B = CLAMP(y + 1.976*U )
- *
- * where y = 0 for Y <= 16,
- * y = ( Y-16)*1.164, for 16 < Y <= 239,
- * y = (239-16)*1.164, for 239 < Y
- *
- * i.e. We clamp Y to the 16 to 239 range (which it is supposed to be in
- * anyway). We then pick the B_U factor so that B never exceeds 511. We then
- * shrink the G_U factor in line with that to avoid a colour shift as much as
- * possible.
- *
- * We're going to use tables to do it faster, but rather than doing it using
- * 5 tables as as the above suggests, we're going to do it using just 3.
- *
- * We do this by working in parallel within a 32 bit word, and using one
- * table each for Y U and V.
- *
- * Source Y values are 0 to 255, so 0.. 260 after scaling
- * Source U values are -128 to 127, so -49.. 49(G), -253..251(B) after
- * Source V values are -128 to 127, so -204..203(R), -104..103(G) after
- *
- * So total summed values:
- * -223 <= R <= 481, -173 <= G <= 431, -253 <= B < 511
- *
- * We need to pack R G and B into a 32 bit word, and because of Bs range we
- * need 2 bits above the valid range of B to detect overflow, and another one
- * to detect the sense of the overflow. We therefore adopt the following
- * representation:
- *
- * osGGGGGgggggosBBBBBbbbosRRRRRrrr
- *
- * Each such word breaks down into 3 ranges.
- *
- * osGGGGGggggg osBBBBBbbb osRRRRRrrr
- *
- * Thus we have 8 bits for each B and R table entry, and 10 bits for G (good
- * as G is the most noticable one). The s bit for each represents the sign,
- * and o represents the overflow.
- *
- * For R and B we pack the table by taking the 11 bit representation of their
- * values, and toggling bit 10 in the U and V tables.
- *
- * For the green case we calculate 4*G (thus effectively using 10 bits for the
- * valid range) truncate to 12 bits. We toggle bit 11 in the Y table.
- */
- #include "yuv2rgb.h"
- enum
- {
- FLAGS = 0x40080100
- };
- #define READUV(U,V) (tables[256 + (U)] + tables[512 + (V)])
- #define READY(Y) tables[Y]
- #define FIXUP(Y) \
- do { \
- int tmp = (Y) & FLAGS; \
- if (tmp != 0) \
- { \
- tmp -= tmp>>8; \
- (Y) |= tmp; \
- tmp = FLAGS & ~(Y>>1); \
- (Y) += tmp>>8; \
- } \
- } while (0 == 1)
- #define STORE(Y,DSTPTR) \
- do { \
- (DSTPTR) = (Y & 0xFF) | (0xFF00 & (Y>>14)) | (0xFF0000 & (Y<<5)) ;\
- } while (0 == 1)
- void yuv420_2_rgb8888(uint8_t *dst_ptr_,
- const uint8_t *y_ptr,
- const uint8_t *u_ptr,
- const uint8_t *v_ptr,
- int32_t width,
- int32_t height,
- int32_t y_span,
- int32_t uv_span,
- int32_t dst_span,
- const uint32_t *tables,
- int32_t dither)
- {
- uint32_t *dst_ptr = (uint32_t *)(void *)dst_ptr_;
- dst_span >>= 2;
- height -= 1;
- while (height > 0)
- {
- height -= width<<16;
- height += 1<<16;
- while (height < 0)
- {
- /* Do 2 column pairs */
- uint32_t uv, y0, y1;
- uv = READUV(*u_ptr++,*v_ptr++);
- y1 = uv + READY(y_ptr[y_span]);
- y0 = uv + READY(*y_ptr++);
- FIXUP(y1);
- FIXUP(y0);
- STORE(y1, dst_ptr[dst_span]);
- STORE(y0, *dst_ptr++);
- y1 = uv + READY(y_ptr[y_span]);
- y0 = uv + READY(*y_ptr++);
- FIXUP(y1);
- FIXUP(y0);
- STORE(y1, dst_ptr[dst_span]);
- STORE(y0, *dst_ptr++);
- height += (2<<16);
- }
- if ((height>>16) == 0)
- {
- /* Trailing column pair */
- uint32_t uv, y0, y1;
- uv = READUV(*u_ptr,*v_ptr);
- y1 = uv + READY(y_ptr[y_span]);
- y0 = uv + READY(*y_ptr++);
- FIXUP(y1);
- FIXUP(y0);
- STORE(y0, dst_ptr[dst_span]);
- STORE(y1, *dst_ptr++);
- }
- dst_ptr += dst_span*2-width;
- y_ptr += y_span*2-width;
- u_ptr += uv_span-(width>>1);
- v_ptr += uv_span-(width>>1);
- height = (height<<16)>>16;
- height -= 2;
- }
- if (height == 0)
- {
- /* Trail row */
- height -= width<<16;
- height += 1<<16;
- while (height < 0)
- {
- /* Do a row pair */
- uint32_t uv, y0, y1;
- uv = READUV(*u_ptr++,*v_ptr++);
- y1 = uv + READY(*y_ptr++);
- y0 = uv + READY(*y_ptr++);
- FIXUP(y1);
- FIXUP(y0);
- STORE(y1, *dst_ptr++);
- STORE(y0, *dst_ptr++);
- height += (2<<16);
- }
- if ((height>>16) == 0)
- {
- /* Trailing pix */
- uint32_t uv, y0;
- uv = READUV(*u_ptr++,*v_ptr++);
- y0 = uv + READY(*y_ptr++);
- FIXUP(y0);
- STORE(y0, *dst_ptr++);
- }
- }
- }