|
@@ -0,0 +1,184 @@
|
|
|
+/* YUV-> RGB conversion code.
|
|
|
+ *
|
|
|
+ * Copyright (C) 2011 Robin Watts ([email protected]) 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); \
|
|
|
+ *(DSTPTR)++ = (Y)>>22; \
|
|
|
+ *(DSTPTR)++ = (Y)>>11; \
|
|
|
+ *(DSTPTR)++ = 255; \
|
|
|
+} while (0 == 1)
|
|
|
+
|
|
|
+void yuv444_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)
|
|
|
+{
|
|
|
+ height -= 1;
|
|
|
+ while (height > 0)
|
|
|
+ {
|
|
|
+ height -= width<<16;
|
|
|
+ height += 1<<16;
|
|
|
+ while (height < 0)
|
|
|
+ {
|
|
|
+ /* Do top row pair */
|
|
|
+ uint32_t uv, y0, y1;
|
|
|
+
|
|
|
+ uv = READUV(*u_ptr++,*v_ptr++);
|
|
|
+ y0 = uv + READY(*y_ptr++);
|
|
|
+ FIXUP(y0);
|
|
|
+ STORE(y0, dst_ptr);
|
|
|
+ uv = READUV(*u_ptr++,*v_ptr++);
|
|
|
+ y1 = uv + READY(*y_ptr++);
|
|
|
+ FIXUP(y1);
|
|
|
+ STORE(y1, dst_ptr);
|
|
|
+ height += (2<<16);
|
|
|
+ }
|
|
|
+ if ((height>>16) == 0)
|
|
|
+ {
|
|
|
+ /* Trailing top row pix */
|
|
|
+ uint32_t uv, y0;
|
|
|
+
|
|
|
+ uv = READUV(*u_ptr++,*v_ptr++);
|
|
|
+ y0 = uv + READY(*y_ptr++);
|
|
|
+ FIXUP(y0);
|
|
|
+ STORE(y0, dst_ptr);
|
|
|
+ }
|
|
|
+ dst_ptr += dst_span-width*4;
|
|
|
+ y_ptr += y_span-width;
|
|
|
+ u_ptr += uv_span-width;
|
|
|
+ v_ptr += uv_span-width;
|
|
|
+ height = (height<<16)>>16;
|
|
|
+ height -= 1;
|
|
|
+ if (height == 0)
|
|
|
+ break;
|
|
|
+ height -= width<<16;
|
|
|
+ height += 1<<16;
|
|
|
+ while (height < 0)
|
|
|
+ {
|
|
|
+ /* Do second row pair */
|
|
|
+ uint32_t uv, y0, y1;
|
|
|
+
|
|
|
+ uv = READUV(*u_ptr++,*v_ptr++);
|
|
|
+ y0 = uv + READY(*y_ptr++);
|
|
|
+ FIXUP(y0);
|
|
|
+ STORE(y0, dst_ptr);
|
|
|
+ uv = READUV(*u_ptr++,*v_ptr++);
|
|
|
+ y1 = uv + READY(*y_ptr++);
|
|
|
+ FIXUP(y1);
|
|
|
+ STORE(y1, dst_ptr);
|
|
|
+ height += (2<<16);
|
|
|
+ }
|
|
|
+ if ((height>>16) == 0)
|
|
|
+ {
|
|
|
+ /* Trailing bottom row pix */
|
|
|
+ uint32_t uv, y0;
|
|
|
+
|
|
|
+ uv = READUV(*u_ptr++,*v_ptr++);
|
|
|
+ y0 = uv + READY(*y_ptr++);
|
|
|
+ FIXUP(y0);
|
|
|
+ STORE(y0, dst_ptr);
|
|
|
+ }
|
|
|
+ dst_ptr += dst_span-width*4;
|
|
|
+ y_ptr += y_span-width;
|
|
|
+ u_ptr += uv_span-width;
|
|
|
+ v_ptr += uv_span-width;
|
|
|
+ height = (height<<16)>>16;
|
|
|
+ height -= 1;
|
|
|
+ }
|
|
|
+}
|