| Index: third_party/libwebp/enc/picture_csp_enc.c
|
| diff --git a/third_party/libwebp/enc/picture_csp.c b/third_party/libwebp/enc/picture_csp_enc.c
|
| similarity index 87%
|
| rename from third_party/libwebp/enc/picture_csp.c
|
| rename to third_party/libwebp/enc/picture_csp_enc.c
|
| index 188a3ca55bfae2d6409f301f96a3887297b6233f..e5d1c75a66632782f16b4f383da09f13bfc112e8 100644
|
| --- a/third_party/libwebp/enc/picture_csp.c
|
| +++ b/third_party/libwebp/enc/picture_csp_enc.c
|
| @@ -15,8 +15,8 @@
|
| #include <stdlib.h>
|
| #include <math.h>
|
|
|
| -#include "./vp8enci.h"
|
| -#include "../utils/random.h"
|
| +#include "./vp8i_enc.h"
|
| +#include "../utils/random_utils.h"
|
| #include "../utils/utils.h"
|
| #include "../dsp/yuv.h"
|
|
|
| @@ -153,9 +153,9 @@ static int RGBToV(int r, int g, int b, VP8Random* const rg) {
|
| }
|
|
|
| //------------------------------------------------------------------------------
|
| -// Smart RGB->YUV conversion
|
| +// Sharp RGB->YUV conversion
|
|
|
| -static const int kNumIterations = 6;
|
| +static const int kNumIterations = 4;
|
| static const int kMinDimensionIterativeConversion = 4;
|
|
|
| // We could use SFIX=0 and only uint8_t for fixed_y_t, but it produces some
|
| @@ -171,9 +171,9 @@ typedef uint16_t fixed_y_t; // unsigned type with extra SFIX precision for W
|
| #if defined(USE_GAMMA_COMPRESSION)
|
|
|
| // float variant of gamma-correction
|
| -// We use tables of different size and precision, along with a 'real-world'
|
| -// Gamma value close to ~2.
|
| -#define kGammaF 2.2
|
| +// We use tables of different size and precision for the Rec709
|
| +// transfer function.
|
| +#define kGammaF (1./0.45)
|
| static float kGammaToLinearTabF[MAX_Y_T + 1]; // size scales with Y_FIX
|
| static float kLinearToGammaTabF[kGammaTabSize + 2];
|
| static volatile int kGammaTablesFOk = 0;
|
| @@ -183,11 +183,26 @@ static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) {
|
| int v;
|
| const double norm = 1. / MAX_Y_T;
|
| const double scale = 1. / kGammaTabSize;
|
| + const double a = 0.099;
|
| + const double thresh = 0.018;
|
| for (v = 0; v <= MAX_Y_T; ++v) {
|
| - kGammaToLinearTabF[v] = (float)pow(norm * v, kGammaF);
|
| + const double g = norm * v;
|
| + if (g <= thresh * 4.5) {
|
| + kGammaToLinearTabF[v] = (float)(g / 4.5);
|
| + } else {
|
| + const double a_rec = 1. / (1. + a);
|
| + kGammaToLinearTabF[v] = (float)pow(a_rec * (g + a), kGammaF);
|
| + }
|
| }
|
| for (v = 0; v <= kGammaTabSize; ++v) {
|
| - kLinearToGammaTabF[v] = (float)(MAX_Y_T * pow(scale * v, 1. / kGammaF));
|
| + const double g = scale * v;
|
| + double value;
|
| + if (g <= thresh) {
|
| + value = 4.5 * g;
|
| + } else {
|
| + value = (1. + a) * pow(g, 1. / kGammaF) - a;
|
| + }
|
| + kLinearToGammaTabF[v] = (float)(MAX_Y_T * value);
|
| }
|
| // to prevent small rounding errors to cause read-overflow:
|
| kLinearToGammaTabF[kGammaTabSize + 1] = kLinearToGammaTabF[kGammaTabSize];
|
| @@ -235,12 +250,12 @@ static fixed_y_t clip_y(int y) {
|
| //------------------------------------------------------------------------------
|
|
|
| static int RGBToGray(int r, int g, int b) {
|
| - const int luma = 19595 * r + 38470 * g + 7471 * b + YUV_HALF;
|
| + const int luma = 13933 * r + 46871 * g + 4732 * b + YUV_HALF;
|
| return (luma >> YUV_FIX);
|
| }
|
|
|
| static float RGBToGrayF(float r, float g, float b) {
|
| - return 0.299f * r + 0.587f * g + 0.114f * b;
|
| + return (float)(0.2126 * r + 0.7152 * g + 0.0722 * b);
|
| }
|
|
|
| static int ScaleDown(int a, int b, int c, int d) {
|
| @@ -251,58 +266,50 @@ static int ScaleDown(int a, int b, int c, int d) {
|
| return LinearToGammaF(0.25f * (A + B + C + D));
|
| }
|
|
|
| -static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int len) {
|
| - while (len-- > 0) {
|
| - const float R = GammaToLinearF(src[0]);
|
| - const float G = GammaToLinearF(src[1]);
|
| - const float B = GammaToLinearF(src[2]);
|
| +static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w) {
|
| + int i;
|
| + for (i = 0; i < w; ++i) {
|
| + const float R = GammaToLinearF(src[0 * w + i]);
|
| + const float G = GammaToLinearF(src[1 * w + i]);
|
| + const float B = GammaToLinearF(src[2 * w + i]);
|
| const float Y = RGBToGrayF(R, G, B);
|
| - *dst++ = (fixed_y_t)LinearToGammaF(Y);
|
| - src += 3;
|
| + dst[i] = (fixed_y_t)LinearToGammaF(Y);
|
| }
|
| }
|
|
|
| -static int UpdateChroma(const fixed_y_t* src1,
|
| - const fixed_y_t* src2,
|
| - fixed_t* dst, fixed_y_t* tmp, int len) {
|
| - int diff = 0;
|
| - while (len--> 0) {
|
| - const int r = ScaleDown(src1[0], src1[3], src2[0], src2[3]);
|
| - const int g = ScaleDown(src1[1], src1[4], src2[1], src2[4]);
|
| - const int b = ScaleDown(src1[2], src1[5], src2[2], src2[5]);
|
| +static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
|
| + fixed_t* dst, int uv_w) {
|
| + int i;
|
| + for (i = 0; i < uv_w; ++i) {
|
| + const int r = ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1],
|
| + src2[0 * uv_w + 0], src2[0 * uv_w + 1]);
|
| + const int g = ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1],
|
| + src2[2 * uv_w + 0], src2[2 * uv_w + 1]);
|
| + const int b = ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1],
|
| + src2[4 * uv_w + 0], src2[4 * uv_w + 1]);
|
| const int W = RGBToGray(r, g, b);
|
| - const int r_avg = (src1[0] + src1[3] + src2[0] + src2[3] + 2) >> 2;
|
| - const int g_avg = (src1[1] + src1[4] + src2[1] + src2[4] + 2) >> 2;
|
| - const int b_avg = (src1[2] + src1[5] + src2[2] + src2[5] + 2) >> 2;
|
| - dst[0] = (fixed_t)(r - W);
|
| - dst[1] = (fixed_t)(g - W);
|
| - dst[2] = (fixed_t)(b - W);
|
| - dst += 3;
|
| - src1 += 6;
|
| - src2 += 6;
|
| - if (tmp != NULL) {
|
| - tmp[0] = tmp[1] = clip_y(W);
|
| - tmp += 2;
|
| - }
|
| - diff += abs(RGBToGray(r_avg, g_avg, b_avg) - W);
|
| + dst[0 * uv_w] = (fixed_t)(r - W);
|
| + dst[1 * uv_w] = (fixed_t)(g - W);
|
| + dst[2 * uv_w] = (fixed_t)(b - W);
|
| + dst += 1;
|
| + src1 += 2;
|
| + src2 += 2;
|
| }
|
| - return diff;
|
| }
|
|
|
| -//------------------------------------------------------------------------------
|
| -
|
| -static WEBP_INLINE int Filter(const fixed_t* const A, const fixed_t* const B,
|
| - int rightwise) {
|
| - int v;
|
| - if (!rightwise) {
|
| - v = (A[0] * 9 + A[-3] * 3 + B[0] * 3 + B[-3]);
|
| - } else {
|
| - v = (A[0] * 9 + A[+3] * 3 + B[0] * 3 + B[+3]);
|
| +static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) {
|
| + int i;
|
| + for (i = 0; i < w; ++i) {
|
| + y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);
|
| }
|
| - return (v + 8) >> 4;
|
| }
|
|
|
| -static WEBP_INLINE int Filter2(int A, int B) { return (A * 3 + B + 2) >> 2; }
|
| +//------------------------------------------------------------------------------
|
| +
|
| +static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0) {
|
| + const int v0 = (A * 3 + B + 2) >> 2;
|
| + return clip_y(v0 + W0);
|
| +}
|
|
|
| //------------------------------------------------------------------------------
|
|
|
| @@ -317,52 +324,50 @@ static void ImportOneRow(const uint8_t* const r_ptr,
|
| int pic_width,
|
| fixed_y_t* const dst) {
|
| int i;
|
| + const int w = (pic_width + 1) & ~1;
|
| for (i = 0; i < pic_width; ++i) {
|
| const int off = i * step;
|
| - dst[3 * i + 0] = UpLift(r_ptr[off]);
|
| - dst[3 * i + 1] = UpLift(g_ptr[off]);
|
| - dst[3 * i + 2] = UpLift(b_ptr[off]);
|
| + dst[i + 0 * w] = UpLift(r_ptr[off]);
|
| + dst[i + 1 * w] = UpLift(g_ptr[off]);
|
| + dst[i + 2 * w] = UpLift(b_ptr[off]);
|
| }
|
| if (pic_width & 1) { // replicate rightmost pixel
|
| - memcpy(dst + 3 * pic_width, dst + 3 * (pic_width - 1), 3 * sizeof(*dst));
|
| + dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1];
|
| + dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1];
|
| + dst[pic_width + 2 * w] = dst[pic_width + 2 * w - 1];
|
| }
|
| }
|
|
|
| static void InterpolateTwoRows(const fixed_y_t* const best_y,
|
| - const fixed_t* const prev_uv,
|
| - const fixed_t* const cur_uv,
|
| - const fixed_t* const next_uv,
|
| + const fixed_t* prev_uv,
|
| + const fixed_t* cur_uv,
|
| + const fixed_t* next_uv,
|
| int w,
|
| - fixed_y_t* const out1,
|
| - fixed_y_t* const out2) {
|
| - int i, k;
|
| - { // special boundary case for i==0
|
| - const int W0 = best_y[0];
|
| - const int W1 = best_y[w];
|
| - for (k = 0; k <= 2; ++k) {
|
| - out1[k] = clip_y(Filter2(cur_uv[k], prev_uv[k]) + W0);
|
| - out2[k] = clip_y(Filter2(cur_uv[k], next_uv[k]) + W1);
|
| - }
|
| - }
|
| - for (i = 1; i < w - 1; ++i) {
|
| - const int W0 = best_y[i + 0];
|
| - const int W1 = best_y[i + w];
|
| - const int off = 3 * (i >> 1);
|
| - for (k = 0; k <= 2; ++k) {
|
| - const int tmp0 = Filter(cur_uv + off + k, prev_uv + off + k, i & 1);
|
| - const int tmp1 = Filter(cur_uv + off + k, next_uv + off + k, i & 1);
|
| - out1[3 * i + k] = clip_y(tmp0 + W0);
|
| - out2[3 * i + k] = clip_y(tmp1 + W1);
|
| - }
|
| - }
|
| - { // special boundary case for i == w - 1
|
| - const int W0 = best_y[i + 0];
|
| - const int W1 = best_y[i + w];
|
| - const int off = 3 * (i >> 1);
|
| - for (k = 0; k <= 2; ++k) {
|
| - out1[3 * i + k] = clip_y(Filter2(cur_uv[off + k], prev_uv[off + k]) + W0);
|
| - out2[3 * i + k] = clip_y(Filter2(cur_uv[off + k], next_uv[off + k]) + W1);
|
| + fixed_y_t* out1,
|
| + fixed_y_t* out2) {
|
| + const int uv_w = w >> 1;
|
| + const int len = (w - 1) >> 1; // length to filter
|
| + int k = 3;
|
| + while (k-- > 0) { // process each R/G/B segments in turn
|
| + // special boundary case for i==0
|
| + out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0]);
|
| + out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w]);
|
| +
|
| + WebPSharpYUVFilterRow(cur_uv, prev_uv, len, best_y + 0 + 1, out1 + 1);
|
| + WebPSharpYUVFilterRow(cur_uv, next_uv, len, best_y + w + 1, out2 + 1);
|
| +
|
| + // special boundary case for i == w - 1 when w is even
|
| + if (!(w & 1)) {
|
| + out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1],
|
| + best_y[w - 1 + 0]);
|
| + out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1],
|
| + best_y[w - 1 + w]);
|
| }
|
| + out1 += w;
|
| + out2 += w;
|
| + prev_uv += uv_w;
|
| + cur_uv += uv_w;
|
| + next_uv += uv_w;
|
| }
|
| }
|
|
|
| @@ -394,11 +399,11 @@ static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
|
| const int uv_h = h >> 1;
|
| for (best_uv = best_uv_base, j = 0; j < picture->height; ++j) {
|
| for (i = 0; i < picture->width; ++i) {
|
| - const int off = 3 * (i >> 1);
|
| + const int off = (i >> 1);
|
| const int W = best_y[i];
|
| - const int r = best_uv[off + 0] + W;
|
| - const int g = best_uv[off + 1] + W;
|
| - const int b = best_uv[off + 2] + W;
|
| + const int r = best_uv[off + 0 * uv_w] + W;
|
| + const int g = best_uv[off + 1 * uv_w] + W;
|
| + const int b = best_uv[off + 2 * uv_w] + W;
|
| dst_y[i] = ConvertRGBToY(r, g, b);
|
| }
|
| best_y += w;
|
| @@ -407,10 +412,10 @@ static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
|
| }
|
| for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) {
|
| for (i = 0; i < uv_w; ++i) {
|
| - const int off = 3 * i;
|
| - const int r = best_uv[off + 0];
|
| - const int g = best_uv[off + 1];
|
| - const int b = best_uv[off + 2];
|
| + const int off = i;
|
| + const int r = best_uv[off + 0 * uv_w];
|
| + const int g = best_uv[off + 1 * uv_w];
|
| + const int b = best_uv[off + 2 * uv_w];
|
| dst_u[i] = ConvertRGBToU(r, g, b);
|
| dst_v[i] = ConvertRGBToV(r, g, b);
|
| }
|
| @@ -436,7 +441,8 @@ static int PreprocessARGB(const uint8_t* r_ptr,
|
| const int h = (picture->height + 1) & ~1;
|
| const int uv_w = w >> 1;
|
| const int uv_h = h >> 1;
|
| - int i, j, iter;
|
| + uint64_t prev_diff_y_sum = ~0;
|
| + int j, iter;
|
|
|
| // TODO(skal): allocate one big memory chunk. But for now, it's easier
|
| // for valgrind debugging to have several chunks.
|
| @@ -451,11 +457,8 @@ static int PreprocessARGB(const uint8_t* r_ptr,
|
| fixed_y_t* target_y = target_y_base;
|
| fixed_t* best_uv = best_uv_base;
|
| fixed_t* target_uv = target_uv_base;
|
| + const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h);
|
| int ok;
|
| - int diff_sum = 0;
|
| - const int first_diff_threshold = (int)(2.5 * w * h);
|
| - const int min_improvement = 5; // stop if improvement is below this %
|
| - const int min_first_improvement = 80;
|
|
|
| if (best_y_base == NULL || best_uv_base == NULL ||
|
| target_y_base == NULL || target_uv_base == NULL ||
|
| @@ -467,10 +470,12 @@ static int PreprocessARGB(const uint8_t* r_ptr,
|
| assert(picture->width >= kMinDimensionIterativeConversion);
|
| assert(picture->height >= kMinDimensionIterativeConversion);
|
|
|
| + WebPInitConvertARGBToYUV();
|
| +
|
| // Import RGB samples to W/RGB representation.
|
| for (j = 0; j < picture->height; j += 2) {
|
| const int is_last_row = (j == picture->height - 1);
|
| - fixed_y_t* const src1 = tmp_buffer;
|
| + fixed_y_t* const src1 = tmp_buffer + 0 * w;
|
| fixed_y_t* const src2 = tmp_buffer + 3 * w;
|
|
|
| // prepare two rows of input
|
| @@ -481,11 +486,13 @@ static int PreprocessARGB(const uint8_t* r_ptr,
|
| } else {
|
| memcpy(src2, src1, 3 * w * sizeof(*src2));
|
| }
|
| + StoreGray(src1, best_y + 0, w);
|
| + StoreGray(src2, best_y + w, w);
|
| +
|
| UpdateW(src1, target_y, w);
|
| UpdateW(src2, target_y + w, w);
|
| - diff_sum += UpdateChroma(src1, src2, target_uv, best_y, uv_w);
|
| + UpdateChroma(src1, src2, target_uv, uv_w);
|
| memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
|
| - memcpy(best_y + w, best_y, w * sizeof(*best_y));
|
| best_y += 2 * w;
|
| best_uv += 3 * uv_w;
|
| target_y += 2 * w;
|
| @@ -497,18 +504,16 @@ static int PreprocessARGB(const uint8_t* r_ptr,
|
|
|
| // Iterate and resolve clipping conflicts.
|
| for (iter = 0; iter < kNumIterations; ++iter) {
|
| - int k;
|
| const fixed_t* cur_uv = best_uv_base;
|
| const fixed_t* prev_uv = best_uv_base;
|
| - const int old_diff_sum = diff_sum;
|
| - diff_sum = 0;
|
| + uint64_t diff_y_sum = 0;
|
|
|
| best_y = best_y_base;
|
| best_uv = best_uv_base;
|
| target_y = target_y_base;
|
| target_uv = target_uv_base;
|
| for (j = 0; j < h; j += 2) {
|
| - fixed_y_t* const src1 = tmp_buffer;
|
| + fixed_y_t* const src1 = tmp_buffer + 0 * w;
|
| fixed_y_t* const src2 = tmp_buffer + 3 * w;
|
| {
|
| const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0);
|
| @@ -519,50 +524,24 @@ static int PreprocessARGB(const uint8_t* r_ptr,
|
|
|
| UpdateW(src1, best_rgb_y + 0 * w, w);
|
| UpdateW(src2, best_rgb_y + 1 * w, w);
|
| - diff_sum += UpdateChroma(src1, src2, best_rgb_uv, NULL, uv_w);
|
| + UpdateChroma(src1, src2, best_rgb_uv, uv_w);
|
|
|
| // update two rows of Y and one row of RGB
|
| - for (i = 0; i < 2 * w; ++i) {
|
| - const int diff_y = target_y[i] - best_rgb_y[i];
|
| - const int new_y = (int)best_y[i] + diff_y;
|
| - best_y[i] = clip_y(new_y);
|
| - }
|
| - for (i = 0; i < uv_w; ++i) {
|
| - const int off = 3 * i;
|
| - int W;
|
| - for (k = 0; k <= 2; ++k) {
|
| - const int diff_uv = (int)target_uv[off + k] - best_rgb_uv[off + k];
|
| - best_uv[off + k] += diff_uv;
|
| - }
|
| - W = RGBToGray(best_uv[off + 0], best_uv[off + 1], best_uv[off + 2]);
|
| - for (k = 0; k <= 2; ++k) {
|
| - best_uv[off + k] -= W;
|
| - }
|
| - }
|
| + diff_y_sum += WebPSharpYUVUpdateY(target_y, best_rgb_y, best_y, 2 * w);
|
| + WebPSharpYUVUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w);
|
| +
|
| best_y += 2 * w;
|
| best_uv += 3 * uv_w;
|
| target_y += 2 * w;
|
| target_uv += 3 * uv_w;
|
| }
|
| // test exit condition
|
| - if (diff_sum > 0) {
|
| - const int improvement = 100 * abs(diff_sum - old_diff_sum) / diff_sum;
|
| - // Check if first iteration gave good result already, without a large
|
| - // jump of improvement (otherwise it means we need to try few extra
|
| - // iterations, just to be sure).
|
| - if (iter == 0 && diff_sum < first_diff_threshold &&
|
| - improvement < min_first_improvement) {
|
| - break;
|
| - }
|
| - // then, check if improvement is stalling.
|
| - if (improvement < min_improvement) {
|
| - break;
|
| - }
|
| - } else {
|
| - break;
|
| + if (iter > 0) {
|
| + if (diff_y_sum < diff_y_threshold) break;
|
| + if (diff_y_sum > prev_diff_y_sum) break;
|
| }
|
| + prev_diff_y_sum = diff_y_sum;
|
| }
|
| -
|
| // final reconstruction
|
| ok = ConvertWRGBToYUV(best_y_base, best_uv_base, picture);
|
|
|
| @@ -1032,9 +1011,13 @@ int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) {
|
| return PictureARGBToYUVA(picture, colorspace, 0.f, 0);
|
| }
|
|
|
| -int WebPPictureSmartARGBToYUVA(WebPPicture* picture) {
|
| +int WebPPictureSharpARGBToYUVA(WebPPicture* picture) {
|
| return PictureARGBToYUVA(picture, WEBP_YUV420, 0.f, 1);
|
| }
|
| +// for backward compatibility
|
| +int WebPPictureSmartARGBToYUVA(WebPPicture* picture) {
|
| + return WebPPictureSharpARGBToYUVA(picture);
|
| +}
|
|
|
| //------------------------------------------------------------------------------
|
| // call for YUVA -> ARGB conversion
|
|
|