diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-07 15:10:42 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-07 15:10:42 +0000 |
commit | be2003515724ca3df351d414eb3ba85e9450afb5 (patch) | |
tree | 048e15ee89e47ad9278e0ad8a42d6e408496fad0 | |
parent | 18e9d8cab7ee5c40d69e5f8b5943472fc6432f72 (diff) | |
download | chromium_src-be2003515724ca3df351d414eb3ba85e9450afb5.zip chromium_src-be2003515724ca3df351d414eb3ba85e9450afb5.tar.gz chromium_src-be2003515724ca3df351d414eb3ba85e9450afb5.tar.bz2 |
Faster RGBA->YV12 conversion using hbono's algorithm
Thank you so much to hbono for working on a much faster RGB32 to YV12 conversion routine. Since his version is mainly for SSSE3 I updated the SSE2 version using his algorithm so that older processors can benefits too.
There are a couple things different than hbono's version:
1. 2x2 Subsampling for U and V.
In order to compute this subsampling faster, 2x2 RGB values
are subsampled first before doing the conversion for UV.
Mathematically this should generate the same results as
subsampling U and V after conversion.
2. Perform conversion for U and V on two pixels.
hbono's original version perform a multiply add on U and V
at the same time. This algorithm changes it so that
multiply add is performed on two Us and two Vs in separate
steps. This generate two subsampled Us and Vs.
3. Omit the addition of 0.5.
Instead of apply the rounding at each stage of calculation
the rounding is performed in the precomputed table. I tested that the 0.5 rounding doesn't have a significant effect.
4. Use movdqu instead of movdqa on source pixels.
Incoming RGB buffer doesn't gurantee alignment so movdqu
is used. To further enhance the speed we can do a
precomputation before each loop so that we can select a
version using movqdu or movdqa.
This color space conversion process is about 2 times faster than the existing
version.
BUG=None
TEST=Unit test and visual inspection.
Review URL: http://codereview.chromium.org/7791024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99956 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/base/yuv_convert_internal.h | 12 | ||||
-rw-r--r-- | media/base/yuv_convert_sse2.cc | 515 | ||||
-rw-r--r-- | media/base/yuv_convert_unittest.cc | 182 | ||||
-rw-r--r-- | media/base/yuv_row.h | 1 | ||||
-rw-r--r-- | media/base/yuv_row_table.cc | 224 |
5 files changed, 535 insertions, 399 deletions
diff --git a/media/base/yuv_convert_internal.h b/media/base/yuv_convert_internal.h index da41be3..80776aa 100644 --- a/media/base/yuv_convert_internal.h +++ b/media/base/yuv_convert_internal.h @@ -23,6 +23,18 @@ extern void ConvertRGB32ToYUV_SSE2(const uint8* rgbframe, int ystride, int uvstride); +// This is a C reference implementation of the above routine. +// This method should only be used in unit test. +void ConvertRGB32ToYUV_SSE2_Reference(const uint8* rgbframe, + uint8* yplane, + uint8* uplane, + uint8* vplane, + int width, + int height, + int rgbstride, + int ystride, + int uvstride); + // C version of converting RGBA to YV12. void ConvertRGB32ToYUV_C(const uint8* rgbframe, uint8* yplane, diff --git a/media/base/yuv_convert_sse2.cc b/media/base/yuv_convert_sse2.cc index 203b50e..9ecef5f 100644 --- a/media/base/yuv_convert_sse2.cc +++ b/media/base/yuv_convert_sse2.cc @@ -15,168 +15,278 @@ namespace media { +#define FIX_SHIFT 12 +#define FIX(x) ((x) * (1 << FIX_SHIFT)) + +SIMD_ALIGNED(const int16 ConvertRGBAToYUV_kTable[8 * 3]) = { + FIX(0.098), FIX(0.504), FIX(0.257), 0, + FIX(0.098), FIX(0.504), FIX(0.257), 0, + FIX(0.439), -FIX(0.291), -FIX(0.148), 0, + FIX(0.439), -FIX(0.291), -FIX(0.148), 0, + -FIX(0.071), -FIX(0.368), FIX(0.439), 0, + -FIX(0.071), -FIX(0.368), FIX(0.439), 0, +}; + // This is the final offset for the conversion from signed yuv values to // unsigned values. It is arranged so that offset of 16 is applied to Y // components and 128 is added to UV components for 2 pixels. -SIMD_ALIGNED(const int16 kYuvOffset[8]) = {16, 0, 128, 128, 16, 0, 128, 128}; - -void FastConvertRGB32ToYUVRow(const uint8* rgb_buf_1, - const uint8* rgb_buf_2, - uint8* y_buf_1, - uint8* y_buf_2, - uint8* u_buf, - uint8* v_buf, - int width) { -// 64-bits system has more registers and so saving the pointers to the tables -// will be faster. But on 32-bits system we want to save registers so calculate -// the offset each time. -#if defined(ARCH_CPU_X86_64) - const uint64* r_table = reinterpret_cast<uint64*>(kCoefficientsYuvR); - const uint64* g_table = reinterpret_cast<uint64*>(kCoefficientsYuvR + 256); - const uint64* b_table = reinterpret_cast<uint64*>(kCoefficientsYuvR + 512); -#define R_TABLE r_table -#define G_TABLE g_table -#define B_TABLE b_table -#else // On 32-bits system we compute the offset. - const uint64* r_table = reinterpret_cast<uint64*>(kCoefficientsYuvR); -#define R_TABLE r_table -#define G_TABLE (r_table + 256) -#define B_TABLE (r_table + 512) -#endif +SIMD_ALIGNED(const int32 kYOffset[4]) = {16, 16, 16, 16}; - uint16* y_row_1 = reinterpret_cast<uint16*>(y_buf_1); - uint16* y_row_2 = reinterpret_cast<uint16*>(y_buf_2); - __m128i offset = _mm_load_si128(reinterpret_cast<const __m128i*>(kYuvOffset)); - - for (; width > 1; width -= 2) { -// MSVC generates very different intrinsic code for reading that affects -// performance significantly so we define two different ways. -#if defined(COMPILER_MSVC) -uint32 pixel; -#define READ_RGB(buf) pixel = *reinterpret_cast<const uint32*>(buf); buf += 4; -#define READ_B(buf) (pixel & 0xFF) -#define READ_G(buf) ((pixel >> 8) & 0xFF) -#define READ_R(buf) ((pixel >> 16) & 0xFF) -#define READ_A(buf) -#else -#define READ_RGB(buf) -#define READ_B(buf) (*buf++) -#define READ_G(buf) (*buf++) -#define READ_R(buf) (*buf++) -#define READ_A(buf) (buf++) -#endif - // Load the first pixel (a) in the first row. - READ_RGB(rgb_buf_1); - __m128i b_comp_a = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(B_TABLE + READ_B(rgb_buf_1))); - __m128i g_comp_a = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(G_TABLE + READ_G(rgb_buf_1))); - __m128i r_comp_a = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(R_TABLE + READ_R(rgb_buf_1))); - READ_A(rgb_buf_1); - - // Load the second pixel (b) in the first row. - READ_RGB(rgb_buf_1); - __m128i b_comp_b = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(B_TABLE + READ_B(rgb_buf_1))); - __m128i g_comp_b = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(G_TABLE + READ_G(rgb_buf_1))); - __m128i r_comp_b = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(R_TABLE + READ_R(rgb_buf_1))); - READ_A(rgb_buf_1); - - // Pack two pixels into one register. - __m128i b_comp_ab = _mm_unpacklo_epi64(b_comp_a, b_comp_b); - __m128i g_comp_ab = _mm_unpacklo_epi64(g_comp_a, g_comp_b); - __m128i r_comp_ab = _mm_unpacklo_epi64(r_comp_a, r_comp_b); - - // Add the coefficients together. - // 127 0 - // |yuv_ab| will be (Vb Ub 0 Yb Va Ua 0 Ya). - __m128i yuv_ab = _mm_adds_epi16(r_comp_ab, - _mm_adds_epi16(g_comp_ab, b_comp_ab)); - - // Right shift 6 bits to perform divide by 64 and then add the offset. - yuv_ab = _mm_srai_epi16(yuv_ab, 6); - yuv_ab = _mm_adds_epi16(yuv_ab, offset); - - // Do a shuffle to obtain (Vb Ub Va Ua 0 Yb 0 Ya). - yuv_ab = _mm_shuffle_epi32(yuv_ab, 0xD8); - - // Load the first pixel (c) in the second row. - READ_RGB(rgb_buf_2); - __m128i b_comp_c = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(B_TABLE + READ_B(rgb_buf_2))); - __m128i g_comp_c = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(G_TABLE + READ_G(rgb_buf_2))); - __m128i r_comp_c = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(R_TABLE + READ_R(rgb_buf_2))); - READ_A(rgb_buf_2); - - // Load the second pixel (d) in the second row. - READ_RGB(rgb_buf_2); - __m128i b_comp_d = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(B_TABLE + READ_B(rgb_buf_2))); - __m128i g_comp_d = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(G_TABLE + READ_G(rgb_buf_2))); - __m128i r_comp_d = _mm_loadl_epi64( - reinterpret_cast<const __m128i*>(R_TABLE + READ_R(rgb_buf_2))); - READ_A(rgb_buf_2); - - // Pack two pixels into one register. - __m128i b_comp_cd = _mm_unpacklo_epi64(b_comp_c, b_comp_d); - __m128i g_comp_cd = _mm_unpacklo_epi64(g_comp_c, g_comp_d); - __m128i r_comp_cd = _mm_unpacklo_epi64(r_comp_c, r_comp_d); - - // Add the coefficients together. - // 127 0 - // |yuv_cd| will be (Vd Ud 0 Yd Vc Uc 0 Yc). - __m128i yuv_cd = _mm_adds_epi16(r_comp_cd, - _mm_adds_epi16(g_comp_cd, b_comp_cd)); - - // Right shift 6 bits to perform divide by 64 and then add the offset. - yuv_cd = _mm_srai_epi16(yuv_cd, 6); - yuv_cd = _mm_adds_epi16(yuv_cd, offset); - - // Do a shuffle to obtain (Vd Ud Vc Uc 0 Yd 0 Yc). - yuv_cd = _mm_shuffle_epi32(yuv_cd, 0xD8); - - // This will form (0 Yd 0 Yc 0 Yb 0 Ya). - __m128i y_comp = _mm_unpacklo_epi64(yuv_ab, yuv_cd); - - // Pack to 8-bits Y values. - y_comp = _mm_packs_epi32(y_comp, y_comp); - y_comp = _mm_packus_epi16(y_comp, y_comp); - - // And then store. - *y_row_1++ = _mm_extract_epi16(y_comp, 0); - *y_row_2++ = _mm_extract_epi16(y_comp, 1); - - // Find the average between pixel a and c then move to lower 64bit. - __m128i uv_comp = _mm_avg_epu16(yuv_ab, yuv_cd); - uv_comp = _mm_unpackhi_epi64(uv_comp, uv_comp); - - // Shuffle and then find average again. - __m128i uv_comp_2 = _mm_shuffle_epi32(uv_comp, 0x01); - uv_comp = _mm_avg_epu16(uv_comp, uv_comp_2); - - // Pack to 8-bits then store. - uv_comp = _mm_packus_epi16(uv_comp, uv_comp); - int uv = _mm_extract_epi16(uv_comp, 0); - - *u_buf++ = uv & 0xff; - *v_buf++ = uv >> 8; - } +static inline int Clamp(int value) { + if (value < 0) + return 0; + if (value > 255) + return 255; + return value; +} + +static inline int RGBToY(int r, int g, int b) { + int y = ConvertRGBAToYUV_kTable[0] * b + + ConvertRGBAToYUV_kTable[1] * g + + ConvertRGBAToYUV_kTable[2] * r; + y >>= FIX_SHIFT; + return Clamp(y + 16); +} + +static inline int RGBToU(int r, int g, int b, int shift) { + int u = ConvertRGBAToYUV_kTable[8] * b + + ConvertRGBAToYUV_kTable[9] * g + + ConvertRGBAToYUV_kTable[10] * r; + u >>= FIX_SHIFT + shift; + return Clamp(u + 128); +} + +static inline int RGBToV(int r, int g, int b, int shift) { + int v = ConvertRGBAToYUV_kTable[16] * b + + ConvertRGBAToYUV_kTable[17] * g + + ConvertRGBAToYUV_kTable[18] * r; + v >>= FIX_SHIFT + shift; + return Clamp(v + 128); +} + +#define CONVERT_Y(rgb_buf, y_buf) \ + b = *rgb_buf++; \ + g = *rgb_buf++; \ + r = *rgb_buf++; \ + ++rgb_buf; \ + sum_b += b; \ + sum_g += g; \ + sum_r += r; \ + *y_buf++ = RGBToY(r, g, b); + +static inline void ConvertRGBToYUV_V2H2(const uint8* rgb_buf_1, + const uint8* rgb_buf_2, + uint8* y_buf_1, + uint8* y_buf_2, + uint8* u_buf, + uint8* v_buf) { + int sum_b = 0; + int sum_g = 0; + int sum_r = 0; + int r, g, b; + + CONVERT_Y(rgb_buf_1, y_buf_1); + CONVERT_Y(rgb_buf_1, y_buf_1); + CONVERT_Y(rgb_buf_2, y_buf_2); + CONVERT_Y(rgb_buf_2, y_buf_2); + *u_buf++ = RGBToU(sum_r, sum_g, sum_b, 2); + *v_buf++ = RGBToV(sum_r, sum_g, sum_b, 2); } -#undef R_TABLE -#undef G_TABLE -#undef B_TABLE -#undef READ_RGB -#undef READ_R -#undef READ_G -#undef READ_B -#undef READ_A +static inline void ConvertRGBToYUV_V2H1(const uint8* rgb_buf_1, + const uint8* rgb_buf_2, + uint8* y_buf_1, + uint8* y_buf_2, + uint8* u_buf, + uint8* v_buf) { + int sum_b = 0; + int sum_g = 0; + int sum_r = 0; + int r, g, b; + + CONVERT_Y(rgb_buf_1, y_buf_1); + CONVERT_Y(rgb_buf_2, y_buf_2); + *u_buf++ = RGBToU(sum_r, sum_g, sum_b, 1); + *v_buf++ = RGBToV(sum_r, sum_g, sum_b, 1); +} + +static inline void ConvertRGBToYUV_V1H2(const uint8* rgb_buf, + uint8* y_buf, + uint8* u_buf, + uint8* v_buf) { + int sum_b = 0; + int sum_g = 0; + int sum_r = 0; + int r, g, b; + + CONVERT_Y(rgb_buf, y_buf); + CONVERT_Y(rgb_buf, y_buf); + *u_buf++ = RGBToU(sum_r, sum_g, sum_b, 1); + *v_buf++ = RGBToV(sum_r, sum_g, sum_b, 1); +} + +static inline void ConvertRGBToYUV_V1H1(const uint8* rgb_buf, + uint8* y_buf, + uint8* u_buf, + uint8* v_buf) { + int sum_b = 0; + int sum_g = 0; + int sum_r = 0; + int r, g, b; + + CONVERT_Y(rgb_buf, y_buf); + *u_buf++ = RGBToU(r, g, b, 0); + *v_buf++ = RGBToV(r, g, b, 0); +} + +static void ConvertRGB32ToYUVRow_SSE2(const uint8* rgb_buf_1, + const uint8* rgb_buf_2, + uint8* y_buf_1, + uint8* y_buf_2, + uint8* u_buf, + uint8* v_buf, + int width) { + while (width >= 4) { + // Name for the Y pixels: + // Row 1: a b c d + // Row 2: e f g h + // + // First row 4 pixels. + __m128i rgb_row_1 = _mm_loadu_si128( + reinterpret_cast<const __m128i*>(rgb_buf_1)); + __m128i zero_1 = _mm_xor_si128(rgb_row_1, rgb_row_1); + + __m128i y_table = _mm_load_si128( + reinterpret_cast<const __m128i*>(ConvertRGBAToYUV_kTable)); + + __m128i rgb_a_b = _mm_unpackhi_epi8(rgb_row_1, zero_1); + rgb_a_b = _mm_madd_epi16(rgb_a_b, y_table); + + __m128i rgb_c_d = _mm_unpacklo_epi8(rgb_row_1, zero_1); + rgb_c_d = _mm_madd_epi16(rgb_c_d, y_table); + + // Do a crazh shuffle so that we get: + // v------------ Multiply Add + // BG: a b c d + // A0: a b c d + __m128i bg_abcd = _mm_castps_si128( + _mm_shuffle_ps( + _mm_castsi128_ps(rgb_c_d), + _mm_castsi128_ps(rgb_a_b), + (3 << 6) | (1 << 4) | (3 << 2) | 1)); + __m128i r_abcd = _mm_castps_si128( + _mm_shuffle_ps( + _mm_castsi128_ps(rgb_c_d), + _mm_castsi128_ps(rgb_a_b), + (2 << 6) | (2 << 2))); + __m128i y_abcd = _mm_add_epi32(bg_abcd, r_abcd); + + // Down shift back to 8bits range. + __m128i y_offset = _mm_load_si128( + reinterpret_cast<const __m128i*>(kYOffset)); + y_abcd = _mm_srai_epi32(y_abcd, FIX_SHIFT); + y_abcd = _mm_add_epi32(y_abcd, y_offset); + y_abcd = _mm_packs_epi32(y_abcd, y_abcd); + y_abcd = _mm_packus_epi16(y_abcd, y_abcd); + *reinterpret_cast<uint32*>(y_buf_1) = _mm_cvtsi128_si32(y_abcd); + y_buf_1 += 4; + + // Second row 4 pixels. + __m128i rgb_row_2 = _mm_loadu_si128( + reinterpret_cast<const __m128i*>(rgb_buf_2)); + __m128i zero_2 = _mm_xor_si128(rgb_row_2, rgb_row_2); + __m128i rgb_e_f = _mm_unpackhi_epi8(rgb_row_2, zero_2); + __m128i rgb_g_h = _mm_unpacklo_epi8(rgb_row_2, zero_2); + + // Add two rows together. + __m128i rgb_ae_bf = + _mm_add_epi16(_mm_unpackhi_epi8(rgb_row_1, zero_2), rgb_e_f); + __m128i rgb_cg_dh = + _mm_add_epi16(_mm_unpacklo_epi8(rgb_row_1, zero_2), rgb_g_h); + + // Multiply add like the previous row. + rgb_e_f = _mm_madd_epi16(rgb_e_f, y_table); + rgb_g_h = _mm_madd_epi16(rgb_g_h, y_table); + + __m128i bg_efgh = _mm_castps_si128( + _mm_shuffle_ps(_mm_castsi128_ps(rgb_g_h), + _mm_castsi128_ps(rgb_e_f), + (3 << 6) | (1 << 4) | (3 << 2) | 1)); + __m128i r_efgh = _mm_castps_si128( + _mm_shuffle_ps(_mm_castsi128_ps(rgb_g_h), + _mm_castsi128_ps(rgb_e_f), + (2 << 6) | (2 << 2))); + __m128i y_efgh = _mm_add_epi32(bg_efgh, r_efgh); + y_efgh = _mm_srai_epi32(y_efgh, FIX_SHIFT); + y_efgh = _mm_add_epi32(y_efgh, y_offset); + y_efgh = _mm_packs_epi32(y_efgh, y_efgh); + y_efgh = _mm_packus_epi16(y_efgh, y_efgh); + *reinterpret_cast<uint32*>(y_buf_2) = _mm_cvtsi128_si32(y_efgh); + y_buf_2 += 4; + + __m128i rgb_ae_cg = _mm_castps_si128( + _mm_shuffle_ps(_mm_castsi128_ps(rgb_cg_dh), + _mm_castsi128_ps(rgb_ae_bf), + (3 << 6) | (2 << 4) | (3 << 2) | 2)); + __m128i rgb_bf_dh = _mm_castps_si128( + _mm_shuffle_ps(_mm_castsi128_ps(rgb_cg_dh), + _mm_castsi128_ps(rgb_ae_bf), + (1 << 6) | (1 << 2))); + + // This is a 2x2 subsampling for 2 pixels. + __m128i rgb_abef_cdgh = _mm_add_epi16(rgb_ae_cg, rgb_bf_dh); + + // Do a multiply add with U table. + __m128i u_a_b = _mm_madd_epi16( + rgb_abef_cdgh, + _mm_load_si128( + reinterpret_cast<const __m128i*>(ConvertRGBAToYUV_kTable + 8))); + u_a_b = _mm_add_epi32(_mm_shuffle_epi32(u_a_b, ((3 << 2) | 1)), + _mm_shuffle_epi32(u_a_b, (2 << 2))); + // Right shift 14 because of 12 from fixed point and 2 from subsampling. + u_a_b = _mm_srai_epi32(u_a_b, FIX_SHIFT + 2); + __m128i uv_offset = _mm_slli_epi32(y_offset, 3); + u_a_b = _mm_add_epi32(u_a_b, uv_offset); + u_a_b = _mm_packs_epi32(u_a_b, u_a_b); + u_a_b = _mm_packus_epi16(u_a_b, u_a_b); + *reinterpret_cast<uint16*>(u_buf) = _mm_extract_epi16(u_a_b, 0); + u_buf += 2; + + __m128i v_a_b = _mm_madd_epi16( + rgb_abef_cdgh, + _mm_load_si128( + reinterpret_cast<const __m128i*>(ConvertRGBAToYUV_kTable + 16))); + v_a_b = _mm_add_epi32(_mm_shuffle_epi32(v_a_b, ((3 << 2) | 1)), + _mm_shuffle_epi32(v_a_b, (2 << 2))); + v_a_b = _mm_srai_epi32(v_a_b, FIX_SHIFT + 2); + v_a_b = _mm_add_epi32(v_a_b, uv_offset); + v_a_b = _mm_packs_epi32(v_a_b, v_a_b); + v_a_b = _mm_packus_epi16(v_a_b, v_a_b); + *reinterpret_cast<uint16*>(v_buf) = _mm_extract_epi16(v_a_b, 0); + v_buf += 2; + + rgb_buf_1 += 16; + rgb_buf_2 += 16; + + // Move forward by 4 pixels. + width -= 4; + } + + // Just use C code to convert the remaining pixels. + if (width >= 2) { + ConvertRGBToYUV_V2H2(rgb_buf_1, rgb_buf_2, y_buf_1, y_buf_2, u_buf, v_buf); + rgb_buf_1 += 8; + rgb_buf_2 += 8; + y_buf_1 += 2; + y_buf_2 += 2; + ++u_buf; + ++v_buf; + width -= 2; + } + + if (width) + ConvertRGBToYUV_V2H1(rgb_buf_1, rgb_buf_2, y_buf_1, y_buf_2, u_buf, v_buf); +} extern void ConvertRGB32ToYUV_SSE2(const uint8* rgbframe, uint8* yplane, @@ -187,21 +297,94 @@ extern void ConvertRGB32ToYUV_SSE2(const uint8* rgbframe, int rgbstride, int ystride, int uvstride) { - // Make sure |width| is a multiple of 2. - width = (width / 2) * 2; - for (int i = 0; i < height; i += 2) { - FastConvertRGB32ToYUVRow(rgbframe, - rgbframe + rgbstride, - yplane, - yplane + ystride, - uplane, - vplane, - width); + while (height >= 2) { + ConvertRGB32ToYUVRow_SSE2(rgbframe, + rgbframe + rgbstride, + yplane, + yplane + ystride, + uplane, + vplane, + width); rgbframe += 2 * rgbstride; yplane += 2 * ystride; uplane += uvstride; vplane += uvstride; + height -= 2; + } + + if (!height) + return; + + // Handle the last row. + while (width >= 2) { + ConvertRGBToYUV_V1H2(rgbframe, yplane, uplane, vplane); + rgbframe += 8; + yplane += 2; + ++uplane; + ++vplane; + width -= 2; } + + if (width) + ConvertRGBToYUV_V1H1(rgbframe, yplane, uplane, vplane); +} + +void ConvertRGB32ToYUV_SSE2_Reference(const uint8* rgbframe, + uint8* yplane, + uint8* uplane, + uint8* vplane, + int width, + int height, + int rgbstride, + int ystride, + int uvstride) { + while (height >= 2) { + int i = 0; + + // Convert a 2x2 block. + while (i + 2 <= width) { + ConvertRGBToYUV_V2H2(rgbframe + i * 4, + rgbframe + rgbstride + i * 4, + yplane + i, + yplane + ystride + i, + uplane + i / 2, + vplane + i / 2); + i += 2; + } + + // Convert the last pixel of two rows. + if (i < width) { + ConvertRGBToYUV_V2H1(rgbframe + i * 4, + rgbframe + rgbstride + i * 4, + yplane + i, + yplane + ystride + i, + uplane + i / 2, + vplane + i / 2); + } + + rgbframe += 2 * rgbstride; + yplane += 2 * ystride; + uplane += uvstride; + vplane += uvstride; + height -= 2; + } + + if (!height) + return; + + // Handle the last row. + while (width >= 2) { + ConvertRGBToYUV_V1H2(rgbframe, yplane, uplane, vplane); + rgbframe += 8; + yplane += 2; + ++uplane; + ++vplane; + width -= 2; + } + + // Handle the last pixel in the last row. + if (width) + ConvertRGBToYUV_V1H1(rgbframe, yplane, uplane, vplane); } } // namespace media diff --git a/media/base/yuv_convert_unittest.cc b/media/base/yuv_convert_unittest.cc index 78c4f85..380897e 100644 --- a/media/base/yuv_convert_unittest.cc +++ b/media/base/yuv_convert_unittest.cc @@ -6,8 +6,10 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/path_service.h" +#include "media/base/cpu_features.h" #include "media/base/djb2.h" #include "media/base/yuv_convert.h" +#include "media/base/yuv_convert_internal.h" #include "media/base/yuv_row.h" #include "testing/gtest/include/gtest/gtest.h" @@ -15,6 +17,8 @@ static const int kSourceWidth = 640; static const int kSourceHeight = 360; static const int kSourceYSize = kSourceWidth * kSourceHeight; +static const int kSourceUOffset = kSourceYSize; +static const int kSourceVOffset = kSourceYSize * 5 / 4; static const int kScaledWidth = 1024; static const int kScaledHeight = 768; static const int kBpp = 4; @@ -79,8 +83,8 @@ TEST(YUVConvertTest, YV12) { // Convert a frame of YUV to 32 bit ARGB. media::ConvertYUVToRGB32(yuv_bytes.get(), - yuv_bytes.get() + kSourceYSize, - yuv_bytes.get() + kSourceYSize * 5 / 4, + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, rgb_converted_bytes.get(), // RGB output kSourceWidth, kSourceHeight, // Dimensions kSourceWidth, // YStride @@ -104,7 +108,7 @@ TEST(YUVConvertTest, YV16) { // Convert a frame of YUV to 32 bit ARGB. media::ConvertYUVToRGB32(yuv_bytes.get(), // Y - yuv_bytes.get() + kSourceYSize, // U + yuv_bytes.get() + kSourceUOffset, // U yuv_bytes.get() + kSourceYSize * 3 / 2, // V rgb_converted_bytes.get(), // RGB output kSourceWidth, kSourceHeight, // Dimensions @@ -151,7 +155,7 @@ class YUVScaleTest : public ::testing::TestWithParam<YUVScaleTestData> { uint8* v_plane() { switch (GetParam().yuv_type) { case media::YV12: - return yuv_bytes_.get() + kSourceYSize * 5 / 4; + return yuv_bytes_.get() + kSourceVOffset; case media::YV16: return yuv_bytes_.get() + kSourceYSize * 3 / 2; } @@ -263,8 +267,8 @@ TEST(YUVConvertTest, RGB24ToYUV) { // Convert to I420. media::ConvertRGB24ToYUV(rgb_bytes.get(), yuv_converted_bytes.get(), - yuv_converted_bytes.get() + kSourceYSize, - yuv_converted_bytes.get() + kSourceYSize * 5 / 4, + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, kSourceWidth, kSourceHeight, // Dimensions kSourceWidth * 3, // RGBStride kSourceWidth, // YStride @@ -275,6 +279,70 @@ TEST(YUVConvertTest, RGB24ToYUV) { EXPECT_EQ(320824432u, rgb_hash); } +TEST(YUVConvertTest, RGB32ToYUV) { + // Allocate all surfaces. + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes(new uint8[kRGBSize]); + scoped_array<uint8> yuv_converted_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_converted_bytes(new uint8[kRGBSize]); + + // Read YUV reference data from file. + FilePath yuv_url; + EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); + yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) + .Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("data")) + .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv")); + EXPECT_EQ(static_cast<int>(kYUV12Size), + file_util::ReadFile(yuv_url, + reinterpret_cast<char*>(yuv_bytes.get()), + static_cast<int>(kYUV12Size))); + + // Convert a frame of YUV to 32 bit ARGB. + media::ConvertYUVToRGB32(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UVStride + kSourceWidth * kBpp, // RGBStride + media::YV12); + + // Convert RGB32 to YV12. + media::ConvertRGB32ToYUV(rgb_bytes.get(), + yuv_converted_bytes.get(), + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth * 4, // RGBStride + kSourceWidth, // YStride + kSourceWidth / 2); // UVStride + + // Convert YV12 back to RGB32. + media::ConvertYUVToRGB32(yuv_converted_bytes.get(), + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, + rgb_converted_bytes.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UVStride + kSourceWidth * kBpp, // RGBStride + media::YV12); + + int error = 0; + for (int i = 0; i < kRGBSize; ++i) { + int diff = rgb_converted_bytes[i] - rgb_bytes[i]; + if (diff < 0) + diff = -diff; + error += diff; + } + + // Make sure error is within bound. + LOG(INFO) << "Average error per channel: " << error / kRGBSize; + EXPECT_GT(5, error / kRGBSize); +} + TEST(YUVConvertTest, YUY2ToYUV) { // Allocate all surfaces. scoped_array<uint8> yuy_bytes; @@ -286,11 +354,109 @@ TEST(YUVConvertTest, YUY2ToYUV) { // Convert to I420. media::ConvertYUY2ToYUV(yuy_bytes.get(), yuv_converted_bytes.get(), - yuv_converted_bytes.get() + kSourceYSize, - yuv_converted_bytes.get() + kSourceYSize * 5 / 4, + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, kSourceWidth, kSourceHeight); uint32 yuy_hash = DJB2Hash(yuv_converted_bytes.get(), kYUV12Size, kDJB2HashSeed); EXPECT_EQ(666823187u, yuy_hash); } + +#if !defined(ARCH_CPU_ARM_FAMILY) +TEST(YUVConvertTest, RGB32ToYUV_SSE2_MatchReference) { + if (!media::hasSSE2()) { + LOG(WARNING) << "System doesn't support SSE2, test not executed."; + return; + } + + // Allocate all surfaces. + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes(new uint8[kRGBSize]); + scoped_array<uint8> yuv_converted_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> yuv_reference_bytes(new uint8[kYUV12Size]); + + // Read YUV reference data from file. + FilePath yuv_url; + EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); + yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) + .Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("data")) + .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv")); + EXPECT_EQ(static_cast<int>(kYUV12Size), + file_util::ReadFile(yuv_url, + reinterpret_cast<char*>(yuv_bytes.get()), + static_cast<int>(kYUV12Size))); + + // Convert a frame of YUV to 32 bit ARGB. + media::ConvertYUVToRGB32( + yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UVStride + kSourceWidth * kBpp, // RGBStride + media::YV12); + + // Convert RGB32 to YV12 with SSE2 version. + media::ConvertRGB32ToYUV_SSE2( + rgb_bytes.get(), + yuv_converted_bytes.get(), + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth * 4, // RGBStride + kSourceWidth, // YStride + kSourceWidth / 2); // UVStride + + // Convert RGB32 to YV12 with reference version. + media::ConvertRGB32ToYUV_SSE2_Reference( + rgb_bytes.get(), + yuv_reference_bytes.get(), + yuv_reference_bytes.get() + kSourceUOffset, + yuv_reference_bytes.get() + kSourceVOffset, + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth * 4, // RGBStride + kSourceWidth, // YStride + kSourceWidth / 2); // UVStride + + // Now convert a odd width and height, this overrides part of the buffer + // generated above but that is fine because the point of this test is to + // match the result with the reference code. + + // Convert RGB32 to YV12 with SSE2 version. + media::ConvertRGB32ToYUV_SSE2( + rgb_bytes.get(), + yuv_converted_bytes.get(), + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, + 7, 7, // Dimensions + kSourceWidth * 4, // RGBStride + kSourceWidth, // YStride + kSourceWidth / 2); // UVStride + + // Convert RGB32 to YV12 with reference version. + media::ConvertRGB32ToYUV_SSE2_Reference( + rgb_bytes.get(), + yuv_reference_bytes.get(), + yuv_reference_bytes.get() + kSourceUOffset, + yuv_reference_bytes.get() + kSourceVOffset, + 7, 7, // Dimensions + kSourceWidth * 4, // RGBStride + kSourceWidth, // YStride + kSourceWidth / 2); // UVStride + + int error = 0; + for (int i = 0; i < kYUV12Size; ++i) { + int diff = yuv_reference_bytes[i] - yuv_converted_bytes[i]; + if (diff < 0) + diff = -diff; + error += diff; + } + + // Make sure there's no difference from the reference. + EXPECT_EQ(0, error); +} +#endif diff --git a/media/base/yuv_row.h b/media/base/yuv_row.h index 2414ccf..9a74d29 100644 --- a/media/base/yuv_row.h +++ b/media/base/yuv_row.h @@ -85,7 +85,6 @@ void FastConvertRGB32ToYUVRow(const uint8* rgb_buf_1, #define SIMD_ALIGNED(var) var __attribute__((aligned(16))) #endif extern SIMD_ALIGNED(int16 kCoefficientsRgbY[768][4]); -extern SIMD_ALIGNED(int16 kCoefficientsYuvR[768][4]); // Method to force C version. //#define USE_MMX 0 diff --git a/media/base/yuv_row_table.cc b/media/base/yuv_row_table.cc index 20d8c59..296380b 100644 --- a/media/base/yuv_row_table.cc +++ b/media/base/yuv_row_table.cc @@ -27,27 +27,6 @@ extern "C" { 0 \ } -#define YUVR(i) { \ - static_cast<int16>(0.254 * 64 * i + 0.5), \ - 0, \ - static_cast<int16>(-0.148 * 64 * i + 0.5), \ - static_cast<int16>(0.439 * 64 * i + 0.5), \ -} - -#define YUVG(i) { \ - static_cast<int16>(0.504 * 64 * i + 0.5), \ - 0, \ - static_cast<int16>(-0.291 * 64 * i + 0.5), \ - static_cast<int16>(-0.368 * 64 * i + 0.5), \ -} - -#define YUVB(i) { \ - static_cast<int16>(0.098 * 64 * i + 0.5), \ - 0, \ - static_cast<int16>(0.439 * 64 * i + 0.5), \ - static_cast<int16>(-0.071 * 64 * i + 0.5), \ -} - SIMD_ALIGNED(int16 kCoefficientsRgbY[256 * 3][4]) = { RGBY(0x00), RGBY(0x01), RGBY(0x02), RGBY(0x03), RGBY(0x04), RGBY(0x05), RGBY(0x06), RGBY(0x07), @@ -247,211 +226,8 @@ SIMD_ALIGNED(int16 kCoefficientsRgbY[256 * 3][4]) = { RGBV(0xFC), RGBV(0xFD), RGBV(0xFE), RGBV(0xFF), }; -SIMD_ALIGNED(int16 kCoefficientsYuvR[256 * 3][4]) = { - // R table. - YUVR(0x00), YUVR(0x01), YUVR(0x02), YUVR(0x03), - YUVR(0x04), YUVR(0x05), YUVR(0x06), YUVR(0x07), - YUVR(0x08), YUVR(0x09), YUVR(0x0A), YUVR(0x0B), - YUVR(0x0C), YUVR(0x0D), YUVR(0x0E), YUVR(0x0F), - YUVR(0x10), YUVR(0x11), YUVR(0x12), YUVR(0x13), - YUVR(0x14), YUVR(0x15), YUVR(0x16), YUVR(0x17), - YUVR(0x18), YUVR(0x19), YUVR(0x1A), YUVR(0x1B), - YUVR(0x1C), YUVR(0x1D), YUVR(0x1E), YUVR(0x1F), - YUVR(0x20), YUVR(0x21), YUVR(0x22), YUVR(0x23), - YUVR(0x24), YUVR(0x25), YUVR(0x26), YUVR(0x27), - YUVR(0x28), YUVR(0x29), YUVR(0x2A), YUVR(0x2B), - YUVR(0x2C), YUVR(0x2D), YUVR(0x2E), YUVR(0x2F), - YUVR(0x30), YUVR(0x31), YUVR(0x32), YUVR(0x33), - YUVR(0x34), YUVR(0x35), YUVR(0x36), YUVR(0x37), - YUVR(0x38), YUVR(0x39), YUVR(0x3A), YUVR(0x3B), - YUVR(0x3C), YUVR(0x3D), YUVR(0x3E), YUVR(0x3F), - YUVR(0x40), YUVR(0x41), YUVR(0x42), YUVR(0x43), - YUVR(0x44), YUVR(0x45), YUVR(0x46), YUVR(0x47), - YUVR(0x48), YUVR(0x49), YUVR(0x4A), YUVR(0x4B), - YUVR(0x4C), YUVR(0x4D), YUVR(0x4E), YUVR(0x4F), - YUVR(0x50), YUVR(0x51), YUVR(0x52), YUVR(0x53), - YUVR(0x54), YUVR(0x55), YUVR(0x56), YUVR(0x57), - YUVR(0x58), YUVR(0x59), YUVR(0x5A), YUVR(0x5B), - YUVR(0x5C), YUVR(0x5D), YUVR(0x5E), YUVR(0x5F), - YUVR(0x60), YUVR(0x61), YUVR(0x62), YUVR(0x63), - YUVR(0x64), YUVR(0x65), YUVR(0x66), YUVR(0x67), - YUVR(0x68), YUVR(0x69), YUVR(0x6A), YUVR(0x6B), - YUVR(0x6C), YUVR(0x6D), YUVR(0x6E), YUVR(0x6F), - YUVR(0x70), YUVR(0x71), YUVR(0x72), YUVR(0x73), - YUVR(0x74), YUVR(0x75), YUVR(0x76), YUVR(0x77), - YUVR(0x78), YUVR(0x79), YUVR(0x7A), YUVR(0x7B), - YUVR(0x7C), YUVR(0x7D), YUVR(0x7E), YUVR(0x7F), - YUVR(0x80), YUVR(0x81), YUVR(0x82), YUVR(0x83), - YUVR(0x84), YUVR(0x85), YUVR(0x86), YUVR(0x87), - YUVR(0x88), YUVR(0x89), YUVR(0x8A), YUVR(0x8B), - YUVR(0x8C), YUVR(0x8D), YUVR(0x8E), YUVR(0x8F), - YUVR(0x90), YUVR(0x91), YUVR(0x92), YUVR(0x93), - YUVR(0x94), YUVR(0x95), YUVR(0x96), YUVR(0x97), - YUVR(0x98), YUVR(0x99), YUVR(0x9A), YUVR(0x9B), - YUVR(0x9C), YUVR(0x9D), YUVR(0x9E), YUVR(0x9F), - YUVR(0xA0), YUVR(0xA1), YUVR(0xA2), YUVR(0xA3), - YUVR(0xA4), YUVR(0xA5), YUVR(0xA6), YUVR(0xA7), - YUVR(0xA8), YUVR(0xA9), YUVR(0xAA), YUVR(0xAB), - YUVR(0xAC), YUVR(0xAD), YUVR(0xAE), YUVR(0xAF), - YUVR(0xB0), YUVR(0xB1), YUVR(0xB2), YUVR(0xB3), - YUVR(0xB4), YUVR(0xB5), YUVR(0xB6), YUVR(0xB7), - YUVR(0xB8), YUVR(0xB9), YUVR(0xBA), YUVR(0xBB), - YUVR(0xBC), YUVR(0xBD), YUVR(0xBE), YUVR(0xBF), - YUVR(0xC0), YUVR(0xC1), YUVR(0xC2), YUVR(0xC3), - YUVR(0xC4), YUVR(0xC5), YUVR(0xC6), YUVR(0xC7), - YUVR(0xC8), YUVR(0xC9), YUVR(0xCA), YUVR(0xCB), - YUVR(0xCC), YUVR(0xCD), YUVR(0xCE), YUVR(0xCF), - YUVR(0xD0), YUVR(0xD1), YUVR(0xD2), YUVR(0xD3), - YUVR(0xD4), YUVR(0xD5), YUVR(0xD6), YUVR(0xD7), - YUVR(0xD8), YUVR(0xD9), YUVR(0xDA), YUVR(0xDB), - YUVR(0xDC), YUVR(0xDD), YUVR(0xDE), YUVR(0xDF), - YUVR(0xE0), YUVR(0xE1), YUVR(0xE2), YUVR(0xE3), - YUVR(0xE4), YUVR(0xE5), YUVR(0xE6), YUVR(0xE7), - YUVR(0xE8), YUVR(0xE9), YUVR(0xEA), YUVR(0xEB), - YUVR(0xEC), YUVR(0xED), YUVR(0xEE), YUVR(0xEF), - YUVR(0xF0), YUVR(0xF1), YUVR(0xF2), YUVR(0xF3), - YUVR(0xF4), YUVR(0xF5), YUVR(0xF6), YUVR(0xF7), - YUVR(0xF8), YUVR(0xF9), YUVR(0xFA), YUVR(0xFB), - YUVR(0xFC), YUVR(0xFD), YUVR(0xFE), YUVR(0xFF), - - // G table. - YUVG(0x00), YUVG(0x01), YUVG(0x02), YUVG(0x03), - YUVG(0x04), YUVG(0x05), YUVG(0x06), YUVG(0x07), - YUVG(0x08), YUVG(0x09), YUVG(0x0A), YUVG(0x0B), - YUVG(0x0C), YUVG(0x0D), YUVG(0x0E), YUVG(0x0F), - YUVG(0x10), YUVG(0x11), YUVG(0x12), YUVG(0x13), - YUVG(0x14), YUVG(0x15), YUVG(0x16), YUVG(0x17), - YUVG(0x18), YUVG(0x19), YUVG(0x1A), YUVG(0x1B), - YUVG(0x1C), YUVG(0x1D), YUVG(0x1E), YUVG(0x1F), - YUVG(0x20), YUVG(0x21), YUVG(0x22), YUVG(0x23), - YUVG(0x24), YUVG(0x25), YUVG(0x26), YUVG(0x27), - YUVG(0x28), YUVG(0x29), YUVG(0x2A), YUVG(0x2B), - YUVG(0x2C), YUVG(0x2D), YUVG(0x2E), YUVG(0x2F), - YUVG(0x30), YUVG(0x31), YUVG(0x32), YUVG(0x33), - YUVG(0x34), YUVG(0x35), YUVG(0x36), YUVG(0x37), - YUVG(0x38), YUVG(0x39), YUVG(0x3A), YUVG(0x3B), - YUVG(0x3C), YUVG(0x3D), YUVG(0x3E), YUVG(0x3F), - YUVG(0x40), YUVG(0x41), YUVG(0x42), YUVG(0x43), - YUVG(0x44), YUVG(0x45), YUVG(0x46), YUVG(0x47), - YUVG(0x48), YUVG(0x49), YUVG(0x4A), YUVG(0x4B), - YUVG(0x4C), YUVG(0x4D), YUVG(0x4E), YUVG(0x4F), - YUVG(0x50), YUVG(0x51), YUVG(0x52), YUVG(0x53), - YUVG(0x54), YUVG(0x55), YUVG(0x56), YUVG(0x57), - YUVG(0x58), YUVG(0x59), YUVG(0x5A), YUVG(0x5B), - YUVG(0x5C), YUVG(0x5D), YUVG(0x5E), YUVG(0x5F), - YUVG(0x60), YUVG(0x61), YUVG(0x62), YUVG(0x63), - YUVG(0x64), YUVG(0x65), YUVG(0x66), YUVG(0x67), - YUVG(0x68), YUVG(0x69), YUVG(0x6A), YUVG(0x6B), - YUVG(0x6C), YUVG(0x6D), YUVG(0x6E), YUVG(0x6F), - YUVG(0x70), YUVG(0x71), YUVG(0x72), YUVG(0x73), - YUVG(0x74), YUVG(0x75), YUVG(0x76), YUVG(0x77), - YUVG(0x78), YUVG(0x79), YUVG(0x7A), YUVG(0x7B), - YUVG(0x7C), YUVG(0x7D), YUVG(0x7E), YUVG(0x7F), - YUVG(0x80), YUVG(0x81), YUVG(0x82), YUVG(0x83), - YUVG(0x84), YUVG(0x85), YUVG(0x86), YUVG(0x87), - YUVG(0x88), YUVG(0x89), YUVG(0x8A), YUVG(0x8B), - YUVG(0x8C), YUVG(0x8D), YUVG(0x8E), YUVG(0x8F), - YUVG(0x90), YUVG(0x91), YUVG(0x92), YUVG(0x93), - YUVG(0x94), YUVG(0x95), YUVG(0x96), YUVG(0x97), - YUVG(0x98), YUVG(0x99), YUVG(0x9A), YUVG(0x9B), - YUVG(0x9C), YUVG(0x9D), YUVG(0x9E), YUVG(0x9F), - YUVG(0xA0), YUVG(0xA1), YUVG(0xA2), YUVG(0xA3), - YUVG(0xA4), YUVG(0xA5), YUVG(0xA6), YUVG(0xA7), - YUVG(0xA8), YUVG(0xA9), YUVG(0xAA), YUVG(0xAB), - YUVG(0xAC), YUVG(0xAD), YUVG(0xAE), YUVG(0xAF), - YUVG(0xB0), YUVG(0xB1), YUVG(0xB2), YUVG(0xB3), - YUVG(0xB4), YUVG(0xB5), YUVG(0xB6), YUVG(0xB7), - YUVG(0xB8), YUVG(0xB9), YUVG(0xBA), YUVG(0xBB), - YUVG(0xBC), YUVG(0xBD), YUVG(0xBE), YUVG(0xBF), - YUVG(0xC0), YUVG(0xC1), YUVG(0xC2), YUVG(0xC3), - YUVG(0xC4), YUVG(0xC5), YUVG(0xC6), YUVG(0xC7), - YUVG(0xC8), YUVG(0xC9), YUVG(0xCA), YUVG(0xCB), - YUVG(0xCC), YUVG(0xCD), YUVG(0xCE), YUVG(0xCF), - YUVG(0xD0), YUVG(0xD1), YUVG(0xD2), YUVG(0xD3), - YUVG(0xD4), YUVG(0xD5), YUVG(0xD6), YUVG(0xD7), - YUVG(0xD8), YUVG(0xD9), YUVG(0xDA), YUVG(0xDB), - YUVG(0xDC), YUVG(0xDD), YUVG(0xDE), YUVG(0xDF), - YUVG(0xE0), YUVG(0xE1), YUVG(0xE2), YUVG(0xE3), - YUVG(0xE4), YUVG(0xE5), YUVG(0xE6), YUVG(0xE7), - YUVG(0xE8), YUVG(0xE9), YUVG(0xEA), YUVG(0xEB), - YUVG(0xEC), YUVG(0xED), YUVG(0xEE), YUVG(0xEF), - YUVG(0xF0), YUVG(0xF1), YUVG(0xF2), YUVG(0xF3), - YUVG(0xF4), YUVG(0xF5), YUVG(0xF6), YUVG(0xF7), - YUVG(0xF8), YUVG(0xF9), YUVG(0xFA), YUVG(0xFB), - YUVG(0xFC), YUVG(0xFD), YUVG(0xFE), YUVG(0xFF), - - // B table. - YUVB(0x00), YUVB(0x01), YUVB(0x02), YUVB(0x03), - YUVB(0x04), YUVB(0x05), YUVB(0x06), YUVB(0x07), - YUVB(0x08), YUVB(0x09), YUVB(0x0A), YUVB(0x0B), - YUVB(0x0C), YUVB(0x0D), YUVB(0x0E), YUVB(0x0F), - YUVB(0x10), YUVB(0x11), YUVB(0x12), YUVB(0x13), - YUVB(0x14), YUVB(0x15), YUVB(0x16), YUVB(0x17), - YUVB(0x18), YUVB(0x19), YUVB(0x1A), YUVB(0x1B), - YUVB(0x1C), YUVB(0x1D), YUVB(0x1E), YUVB(0x1F), - YUVB(0x20), YUVB(0x21), YUVB(0x22), YUVB(0x23), - YUVB(0x24), YUVB(0x25), YUVB(0x26), YUVB(0x27), - YUVB(0x28), YUVB(0x29), YUVB(0x2A), YUVB(0x2B), - YUVB(0x2C), YUVB(0x2D), YUVB(0x2E), YUVB(0x2F), - YUVB(0x30), YUVB(0x31), YUVB(0x32), YUVB(0x33), - YUVB(0x34), YUVB(0x35), YUVB(0x36), YUVB(0x37), - YUVB(0x38), YUVB(0x39), YUVB(0x3A), YUVB(0x3B), - YUVB(0x3C), YUVB(0x3D), YUVB(0x3E), YUVB(0x3F), - YUVB(0x40), YUVB(0x41), YUVB(0x42), YUVB(0x43), - YUVB(0x44), YUVB(0x45), YUVB(0x46), YUVB(0x47), - YUVB(0x48), YUVB(0x49), YUVB(0x4A), YUVB(0x4B), - YUVB(0x4C), YUVB(0x4D), YUVB(0x4E), YUVB(0x4F), - YUVB(0x50), YUVB(0x51), YUVB(0x52), YUVB(0x53), - YUVB(0x54), YUVB(0x55), YUVB(0x56), YUVB(0x57), - YUVB(0x58), YUVB(0x59), YUVB(0x5A), YUVB(0x5B), - YUVB(0x5C), YUVB(0x5D), YUVB(0x5E), YUVB(0x5F), - YUVB(0x60), YUVB(0x61), YUVB(0x62), YUVB(0x63), - YUVB(0x64), YUVB(0x65), YUVB(0x66), YUVB(0x67), - YUVB(0x68), YUVB(0x69), YUVB(0x6A), YUVB(0x6B), - YUVB(0x6C), YUVB(0x6D), YUVB(0x6E), YUVB(0x6F), - YUVB(0x70), YUVB(0x71), YUVB(0x72), YUVB(0x73), - YUVB(0x74), YUVB(0x75), YUVB(0x76), YUVB(0x77), - YUVB(0x78), YUVB(0x79), YUVB(0x7A), YUVB(0x7B), - YUVB(0x7C), YUVB(0x7D), YUVB(0x7E), YUVB(0x7F), - YUVB(0x80), YUVB(0x81), YUVB(0x82), YUVB(0x83), - YUVB(0x84), YUVB(0x85), YUVB(0x86), YUVB(0x87), - YUVB(0x88), YUVB(0x89), YUVB(0x8A), YUVB(0x8B), - YUVB(0x8C), YUVB(0x8D), YUVB(0x8E), YUVB(0x8F), - YUVB(0x90), YUVB(0x91), YUVB(0x92), YUVB(0x93), - YUVB(0x94), YUVB(0x95), YUVB(0x96), YUVB(0x97), - YUVB(0x98), YUVB(0x99), YUVB(0x9A), YUVB(0x9B), - YUVB(0x9C), YUVB(0x9D), YUVB(0x9E), YUVB(0x9F), - YUVB(0xA0), YUVB(0xA1), YUVB(0xA2), YUVB(0xA3), - YUVB(0xA4), YUVB(0xA5), YUVB(0xA6), YUVB(0xA7), - YUVB(0xA8), YUVB(0xA9), YUVB(0xAA), YUVB(0xAB), - YUVB(0xAC), YUVB(0xAD), YUVB(0xAE), YUVB(0xAF), - YUVB(0xB0), YUVB(0xB1), YUVB(0xB2), YUVB(0xB3), - YUVB(0xB4), YUVB(0xB5), YUVB(0xB6), YUVB(0xB7), - YUVB(0xB8), YUVB(0xB9), YUVB(0xBA), YUVB(0xBB), - YUVB(0xBC), YUVB(0xBD), YUVB(0xBE), YUVB(0xBF), - YUVB(0xC0), YUVB(0xC1), YUVB(0xC2), YUVB(0xC3), - YUVB(0xC4), YUVB(0xC5), YUVB(0xC6), YUVB(0xC7), - YUVB(0xC8), YUVB(0xC9), YUVB(0xCA), YUVB(0xCB), - YUVB(0xCC), YUVB(0xCD), YUVB(0xCE), YUVB(0xCF), - YUVB(0xD0), YUVB(0xD1), YUVB(0xD2), YUVB(0xD3), - YUVB(0xD4), YUVB(0xD5), YUVB(0xD6), YUVB(0xD7), - YUVB(0xD8), YUVB(0xD9), YUVB(0xDA), YUVB(0xDB), - YUVB(0xDC), YUVB(0xDD), YUVB(0xDE), YUVB(0xDF), - YUVB(0xE0), YUVB(0xE1), YUVB(0xE2), YUVB(0xE3), - YUVB(0xE4), YUVB(0xE5), YUVB(0xE6), YUVB(0xE7), - YUVB(0xE8), YUVB(0xE9), YUVB(0xEA), YUVB(0xEB), - YUVB(0xEC), YUVB(0xED), YUVB(0xEE), YUVB(0xEF), - YUVB(0xF0), YUVB(0xF1), YUVB(0xF2), YUVB(0xF3), - YUVB(0xF4), YUVB(0xF5), YUVB(0xF6), YUVB(0xF7), - YUVB(0xF8), YUVB(0xF9), YUVB(0xFA), YUVB(0xFB), - YUVB(0xFC), YUVB(0xFD), YUVB(0xFE), YUVB(0xFF), -}; - #undef RGBY #undef RGBU #undef RGBV -#undef YUVR -#undef YUVG -#undef YUVB } // extern "C" |