diff --git a/README.chromium b/README.chromium index 4ba0135de..0c0fc1381 100644 --- a/README.chromium +++ b/README.chromium @@ -1,6 +1,6 @@ Name: libyuv URL: http://code.google.com/p/libyuv/ -Version: 240 +Version: 241 License: BSD License File: LICENSE diff --git a/include/libyuv/version.h b/include/libyuv/version.h index cee7aebb0..1bf959c68 100644 --- a/include/libyuv/version.h +++ b/include/libyuv/version.h @@ -11,7 +11,7 @@ #ifndef INCLUDE_LIBYUV_VERSION_H_ #define INCLUDE_LIBYUV_VERSION_H_ -#define LIBYUV_VERSION 240 +#define LIBYUV_VERSION 241 #endif // INCLUDE_LIBYUV_VERSION_H_ diff --git a/source/compare.cc b/source/compare.cc index 05b0616a4..e07e4d1d1 100644 --- a/source/compare.cc +++ b/source/compare.cc @@ -86,7 +86,7 @@ static uint32 HashDjb2_SSE41(const uint8* src, int count, uint32 seed) { wloop: movdqu xmm1, [eax] // src[0-15] lea eax, [eax + 16] - pmulld(0xc6) // pmulld xmm0,xmm6 hash *= 33 ^ 8 + pmulld(0xc6) // pmulld xmm0,xmm6 hash *= 33 ^ 16 movdqa xmm5, kHashMul0 movdqa xmm2, xmm1 punpcklbw xmm2, xmm7 // src[0-7] diff --git a/source/planar_functions.cc b/source/planar_functions.cc index 24c72862f..0145fcf90 100644 --- a/source/planar_functions.cc +++ b/source/planar_functions.cc @@ -864,6 +864,7 @@ int ARGBRect(uint8* dst_argb, int dst_stride_argb, } // Multiply source RGB by alpha and store to destination. +// b = (b * a + 127) / 255; static void ARGBAttenuateRow_C(const uint8* src_argb, uint8* dst_argb, int width) { for (int i = 0; i < width; ++i) { @@ -871,17 +872,16 @@ static void ARGBAttenuateRow_C(const uint8* src_argb, uint8* dst_argb, const uint32 g = src_argb[1]; const uint32 r = src_argb[2]; const uint32 a = src_argb[3]; - dst_argb[0] = (b * a + 128) / 255; - dst_argb[1] = (g * a + 128) / 255; - dst_argb[2] = (r * a + 128) / 255; + dst_argb[0] = (b * a + 127) / 255; + dst_argb[1] = (g * a + 127) / 255; + dst_argb[2] = (r * a + 127) / 255; dst_argb[3] = a; src_argb += 4; dst_argb += 4; } } -// Convert unattentuated ARGB values to preattenuated ARGB by multiplying RGB by -// by alpha. +// Convert unattentuated ARGB values to preattenuated ARGB. // An unattenutated ARGB alpha blend uses the formula // p = a * f + (1 - a) * b // where @@ -912,6 +912,10 @@ int ARGBAttenuate(const uint8* src_argb, int src_stride_argb, } // Divide source RGB by alpha and store to destination. +// b = (b * 255 + (a / 2)) / a; +// g = (g * 255 + (a / 2)) / a; +// r = (r * 255 + (a / 2)) / a; +// Reciprocal method is off by 1 on some values. ie 125 static void ARGBUnattenuateRow_C(const uint8* src_argb, uint8* dst_argb, int width) { for (int i = 0; i < width; ++i) { @@ -920,15 +924,17 @@ static void ARGBUnattenuateRow_C(const uint8* src_argb, uint8* dst_argb, uint32 r = src_argb[2]; const uint32 a = src_argb[3]; if (a) { - b = (b * 255 + 127) / a; + const uint32 ia = (0x1000000 + (a >> 1)) / a; // 8.16 fixed point + b = (b * ia + 0x8000) >> 16; + g = (g * ia + 0x8000) >> 16; + r = (r * ia + 0x8000) >> 16; + // Clamping should not be necessary but is free in assembly. if (b > 255) { b = 255; } - g = (g * 255 + 127) / a; if (g > 255) { g = 255; } - r = (r * 255 + 127) / a; if (r > 255) { r = 255; } @@ -942,8 +948,7 @@ static void ARGBUnattenuateRow_C(const uint8* src_argb, uint8* dst_argb, } } -// Convert unattentuated ARGB values to preattenuated ARGB by multiplying RGB by -// by alpha. +// Convert unattentuated ARGB values to preattenuated ARGB. int ARGBUnattenuate(const uint8* src_argb, int src_stride_argb, uint8* dst_argb, int dst_stride_argb, int width, int height) { diff --git a/unit_test/planar_test.cc b/unit_test/planar_test.cc index 2e0135e98..78a15abfe 100644 --- a/unit_test/planar_test.cc +++ b/unit_test/planar_test.cc @@ -115,4 +115,51 @@ TESTI420TO(ARGB) TESTI420TO(BGRA) TESTI420TO(ABGR) +TEST_F (libyuvTest, TestAttenuate) { + uint8 orig_pixels[256][4]; + uint8 atten_pixels[256][4]; + uint8 unatten_pixels[256][4]; + uint8 atten2_pixels[256][4]; + for (int i = 0; i < 256; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i; + } + ARGBAttenuate(&orig_pixels[0][0], 0, &atten_pixels[0][0], 0, 256, 1); + ARGBUnattenuate(&atten_pixels[0][0], 0, &unatten_pixels[0][0], 0, 256, 1); + ARGBAttenuate(&unatten_pixels[0][0], 0, &atten2_pixels[0][0], 0, 256, 1); + + for (int i = 0; i < 256; ++i) { + EXPECT_NEAR(atten_pixels[i][0], atten2_pixels[i][0], 1); + EXPECT_NEAR(atten_pixels[i][1], atten2_pixels[i][1], 1); + EXPECT_NEAR(atten_pixels[i][2], atten2_pixels[i][2], 1); + EXPECT_NEAR(atten_pixels[i][3], atten2_pixels[i][3], 1); + } + // Make sure transparent, 50% and opaque are fully accurate. + EXPECT_EQ(0, atten_pixels[0][0]); + EXPECT_EQ(0, atten_pixels[0][1]); + EXPECT_EQ(0, atten_pixels[0][2]); + EXPECT_EQ(0, atten_pixels[0][3]); + EXPECT_EQ(64, atten_pixels[128][0]); + EXPECT_EQ(32, atten_pixels[128][1]); + EXPECT_EQ(21, atten_pixels[128][2]); + EXPECT_EQ(128, atten_pixels[128][3]); + EXPECT_EQ(255, atten_pixels[255][0]); + EXPECT_EQ(127, atten_pixels[255][1]); + EXPECT_EQ(85, atten_pixels[255][2]); + EXPECT_EQ(255, atten_pixels[255][3]); + + // Test unattenuation clamps + orig_pixels[0][0] = 200; + orig_pixels[0][1] = 129; + orig_pixels[0][2] = 127; + orig_pixels[0][3] = 128; + ARGBUnattenuate(&orig_pixels[0][0], 0, &unatten_pixels[0][0], 0, 1, 1); + EXPECT_EQ(255, unatten_pixels[0][0]); + EXPECT_EQ(255, unatten_pixels[0][1]); + EXPECT_EQ(254, unatten_pixels[0][2]); + EXPECT_EQ(128, unatten_pixels[0][3]); +} + }