From ec0d1be5771e0966c8e7cadd28e7d52c27a7c196 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Mon, 26 Jan 2015 14:55:54 -0800 Subject: [PATCH] Cleaup Near-lossless code. Cleaup Near-lossless code - Simplified and refactored the code. - Removed the requirement (TODO) to allocate the buffer of size WxH and work with buffer of size 3xW. - Disabled the Near-lossless prr-processing for small icon images (W < 64 and H < 64). Change-Id: Id7ee90c90622368d5528de4dd14fd5ead593bb1b --- src/enc/near_lossless.c | 120 ++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 53 deletions(-) diff --git a/src/enc/near_lossless.c b/src/enc/near_lossless.c index 98408ee0..20ac39f6 100644 --- a/src/enc/near_lossless.c +++ b/src/enc/near_lossless.c @@ -21,6 +21,10 @@ #include "./vp8enci.h" #ifdef WEBP_EXPERIMENTAL_FEATURES + +#define MIN_DIM_FOR_NEAR_LOSSLESS 64 +#define MAX_LIMIT_BITS 5 + // Computes quantized pixel value and distance from original value. static void GetValAndDistance(int a, int initial, int bits, int* const val, int* const distance) { @@ -29,20 +33,21 @@ static void GetValAndDistance(int a, int initial, int bits, *distance = 2 * abs(a - *val); } -// Quantizes values {a, a+(1< max_val) ? max_val : val; +} + +// Quantizes values {a, a+(1< 255) { - val = 255; - } + const int val = Clamp8b(a + i * (1 << bits)); GetValAndDistance(a, val, bits, &candidate, &distance); if (i != 0) { ++distance; @@ -50,7 +55,7 @@ static int FindClosestDiscretized(int a, int bits, int min, int max) { // Smallest distance but favor i == 0 over i == -1 and i == 1 // since that keeps the overall intensity more constant in the // images. - if (distance < min_distance && candidate >= min && candidate <= max) { + if (distance < min_distance) { min_distance = distance; best_val = candidate; } @@ -59,30 +64,37 @@ static int FindClosestDiscretized(int a, int bits, int min, int max) { } // Applies FindClosestDiscretized to all channels of pixel. -static uint32_t ClosestDiscretizedArgb(uint32_t a, int bits, - uint32_t min, uint32_t max) { - return (FindClosestDiscretized(a >> 24, bits, min >> 24, max >> 24) << 24) | - (FindClosestDiscretized((a >> 16) & 0xff, bits, - (min >> 16) & 0xff, - (max >> 16) & 0xff) << 16) | - (FindClosestDiscretized((a >> 8) & 0xff, bits, - (min >> 8) & 0xff, - (max >> 8) & 0xff) << 8) | - (FindClosestDiscretized(a & 0xff, bits, min & 0xff, max & 0xff)); +static uint32_t ClosestDiscretizedArgb(uint32_t a, int bits) { + return + (FindClosestDiscretized(a >> 24, bits) << 24) | + (FindClosestDiscretized((a >> 16) & 0xff, bits) << 16) | + (FindClosestDiscretized((a >> 8) & 0xff, bits) << 8) | + (FindClosestDiscretized(a & 0xff, bits)); } // Checks if distance between corresponding channel values of pixels a and b -// exceeds given limit. -static int IsFar(uint32_t a, uint32_t b, int limit) { +// is within the given limit. +static int IsNear(uint32_t a, uint32_t b, int limit) { int k; for (k = 0; k < 4; ++k) { - const int delta = (int)((a >> (k * 8)) & 0xff) - - (int)((b >> (k * 8)) & 0xff); + const int delta = + (int)((a >> (k * 8)) & 0xff) - (int)((b >> (k * 8)) & 0xff); if (delta >= limit || delta <= -limit) { - return 1; + return 0; } } - return 0; + return 1; +} + +static int IsSmooth(const uint32_t* const prev_row, + const uint32_t* const curr_row, + const uint32_t* const next_row, + int ix, int limit) { + // Check that all pixels in 4-connected neighborhood are smooth. + return (IsNear(curr_row[ix], curr_row[ix - 1], limit) && + IsNear(curr_row[ix], curr_row[ix + 1], limit) && + IsNear(curr_row[ix], prev_row[ix], limit) && + IsNear(curr_row[ix], next_row[ix], limit)); } // Adjusts pixel values of image with given maximum error. @@ -90,39 +102,37 @@ static void NearLossless(int xsize, int ysize, uint32_t* argb, int limit_bits, uint32_t* copy_buffer) { int x, y; const int limit = 1 << limit_bits; - memcpy(copy_buffer, argb, xsize * ysize * sizeof(argb[0])); + uint32_t* prev_row = copy_buffer; + uint32_t* curr_row = prev_row + xsize; + uint32_t* next_row = curr_row + xsize; + memcpy(copy_buffer, argb, xsize * 2 * sizeof(argb[0])); - for (y = 0; y < ysize; ++y) { - const int offset = y * xsize; - for (x = 0; x < xsize; ++x) { - const int ix = offset + x; - // Check that all pixels in 4-connected neighborhood are smooth. - int smooth_area = 1; - if (x != 0 && IsFar(copy_buffer[ix], copy_buffer[ix - 1], limit)) { - smooth_area = 0; - } else if (y != 0 && - IsFar(copy_buffer[ix], copy_buffer[ix - xsize], limit)) { - smooth_area = 0; - } else if (x != xsize - 1 && - IsFar(copy_buffer[ix], copy_buffer[ix + 1], limit)) { - smooth_area = 0; - } else if (y != ysize - 1 && - IsFar(copy_buffer[ix], copy_buffer[ix + xsize], limit)) { - smooth_area = 0; - } - if (!smooth_area) { - argb[ix] = ClosestDiscretizedArgb(argb[ix], limit_bits, 0, 0xffffffff); + for (y = 1; y < ysize - 1; ++y) { + uint32_t* const curr_argb_row = argb + y * xsize; + uint32_t* const next_argb_row = curr_argb_row + xsize; + memcpy(next_row, next_argb_row, xsize * sizeof(argb[0])); + for (x = 1; x < xsize - 1; ++x) { + if (!IsSmooth(prev_row, curr_row, next_row, x, limit)) { + curr_argb_row[x] = ClosestDiscretizedArgb(curr_row[x], limit_bits); } } + { + // Three-way swap. + uint32_t* const temp = prev_row; + prev_row = curr_row; + curr_row = next_row; + next_row = temp; + } } } static int QualityToLimitBits(int quality) { - return 5 - (quality + 12) / 25; + // quality mapping 0..12 -> 5 + // 13..100 -> 4..1 + return MAX_LIMIT_BITS - (quality + 12) / 25; } #endif // WEBP_EXPERIMENTAL_FEATURES -// TODO(vikasa): optimize memory to O(xsize) int VP8ApplyNearLossless(int xsize, int ysize, uint32_t* argb, int quality) { #ifndef WEBP_EXPERIMENTAL_FEATURES (void)xsize; @@ -132,16 +142,20 @@ int VP8ApplyNearLossless(int xsize, int ysize, uint32_t* argb, int quality) { #else int i; uint32_t* const copy_buffer = - (uint32_t *)WebPSafeMalloc(xsize * ysize, sizeof(*copy_buffer)); - // quality mapping 0..12 -> 5 - // 13..100 -> 4..1 + (uint32_t*)WebPSafeMalloc(xsize * 3, sizeof(*copy_buffer)); const int limit_bits = QualityToLimitBits(quality); assert(argb != NULL); assert(limit_bits >= 0); - assert(limit_bits < 31); + assert(limit_bits <= MAX_LIMIT_BITS); if (copy_buffer == NULL) { return 0; } + // For small icon images, don't attempt to apply near-lossless compression. + if (xsize < MIN_DIM_FOR_NEAR_LOSSLESS && ysize < MIN_DIM_FOR_NEAR_LOSSLESS) { + WebPSafeFree(copy_buffer); + return 1; + } + for (i = limit_bits; i != 0; --i) { NearLossless(xsize, ysize, argb, i, copy_buffer); }