Path: blob/master/thirdparty/libwebp/src/utils/quant_levels_dec_utils.c
21035 views
// Copyright 2013 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// Implement gradient smoothing: we replace a current alpha value by its10// surrounding average if it's close enough (that is: the change will be less11// than the minimum distance between two quantized level).12// We use sliding window for computing the 2d moving average.13//14// Author: Skal ([email protected])1516#include "src/utils/quant_levels_dec_utils.h"1718#include <string.h> // for memset1920#include "src/utils/utils.h"21#include "src/webp/types.h"2223// #define USE_DITHERING // uncomment to enable ordered dithering (not vital)2425#define FIX 16 // fix-point precision for averaging26#define LFIX 2 // extra precision for look-up table27#define LUT_SIZE ((1 << (8 + LFIX)) - 1) // look-up table size2829#if defined(USE_DITHERING)3031#define DFIX 4 // extra precision for ordered dithering32#define DSIZE 4 // dithering size (must be a power of two)33// cf. https://en.wikipedia.org/wiki/Ordered_dithering34static const uint8_t kOrderedDither[DSIZE][DSIZE] = {35{ 0, 8, 2, 10 }, // coefficients are in DFIX fixed-point precision36{ 12, 4, 14, 6 },37{ 3, 11, 1, 9 },38{ 15, 7, 13, 5 }39};4041#else42#define DFIX 043#endif4445typedef struct {46int width, height; // dimension47int stride; // stride in bytes48int row; // current input row being processed49uint8_t* src; // input pointer50uint8_t* dst; // output pointer5152int radius; // filter radius (=delay)53int scale; // normalization factor, in FIX bits precision5455void* mem; // all memory5657// various scratch buffers58uint16_t* start;59uint16_t* cur;60uint16_t* end;61uint16_t* top;62uint16_t* average;6364// input levels distribution65int num_levels; // number of quantized levels66int min, max; // min and max level values67int min_level_dist; // smallest distance between two consecutive levels6869int16_t* correction; // size = 1 + 2*LUT_SIZE -> ~4k memory70} SmoothParams;7172//------------------------------------------------------------------------------7374#define CLIP_8b_MASK (int)(~0U << (8 + DFIX))75static WEBP_INLINE uint8_t clip_8b(int v) {76return (!(v & CLIP_8b_MASK)) ? (uint8_t)(v >> DFIX) : (v < 0) ? 0u : 255u;77}78#undef CLIP_8b_MASK7980// vertical accumulation81static void VFilter(SmoothParams* const p) {82const uint8_t* src = p->src;83const int w = p->width;84uint16_t* const cur = p->cur;85const uint16_t* const top = p->top;86uint16_t* const out = p->end;87uint16_t sum = 0; // all arithmetic is modulo 16bit88int x;8990for (x = 0; x < w; ++x) {91uint16_t new_value;92sum += src[x];93new_value = top[x] + sum;94out[x] = new_value - cur[x]; // vertical sum of 'r' pixels.95cur[x] = new_value;96}97// move input pointers one row down98p->top = p->cur;99p->cur += w;100if (p->cur == p->end) p->cur = p->start; // roll-over101// We replicate edges, as it's somewhat easier as a boundary condition.102// That's why we don't update the 'src' pointer on top/bottom area:103if (p->row >= 0 && p->row < p->height - 1) {104p->src += p->stride;105}106}107108// horizontal accumulation. We use mirror replication of missing pixels, as it's109// a little easier to implement (surprisingly).110static void HFilter(SmoothParams* const p) {111const uint16_t* const in = p->end;112uint16_t* const out = p->average;113const uint32_t scale = p->scale;114const int w = p->width;115const int r = p->radius;116117int x;118for (x = 0; x <= r; ++x) { // left mirroring119const uint16_t delta = in[x + r - 1] + in[r - x];120out[x] = (delta * scale) >> FIX;121}122for (; x < w - r; ++x) { // bulk middle run123const uint16_t delta = in[x + r] - in[x - r - 1];124out[x] = (delta * scale) >> FIX;125}126for (; x < w; ++x) { // right mirroring127const uint16_t delta =1282 * in[w - 1] - in[2 * w - 2 - r - x] - in[x - r - 1];129out[x] = (delta * scale) >> FIX;130}131}132133// emit one filtered output row134static void ApplyFilter(SmoothParams* const p) {135const uint16_t* const average = p->average;136const int w = p->width;137const int16_t* const correction = p->correction;138#if defined(USE_DITHERING)139const uint8_t* const dither = kOrderedDither[p->row % DSIZE];140#endif141uint8_t* const dst = p->dst;142int x;143for (x = 0; x < w; ++x) {144const int v = dst[x];145if (v < p->max && v > p->min) {146const int c = (v << DFIX) + correction[average[x] - (v << LFIX)];147#if defined(USE_DITHERING)148dst[x] = clip_8b(c + dither[x % DSIZE]);149#else150dst[x] = clip_8b(c);151#endif152}153}154p->dst += p->stride; // advance output pointer155}156157//------------------------------------------------------------------------------158// Initialize correction table159160static void InitCorrectionLUT(int16_t* const lut, int min_dist) {161// The correction curve is:162// f(x) = x for x <= threshold2163// f(x) = 0 for x >= threshold1164// and a linear interpolation for range x=[threshold2, threshold1]165// (along with f(-x) = -f(x) symmetry).166// Note that: threshold2 = 3/4 * threshold1167const int threshold1 = min_dist << LFIX;168const int threshold2 = (3 * threshold1) >> 2;169const int max_threshold = threshold2 << DFIX;170const int delta = threshold1 - threshold2;171int i;172for (i = 1; i <= LUT_SIZE; ++i) {173int c = (i <= threshold2) ? (i << DFIX)174: (i < threshold1) ? max_threshold * (threshold1 - i) / delta175: 0;176c >>= LFIX;177lut[+i] = +c;178lut[-i] = -c;179}180lut[0] = 0;181}182183static void CountLevels(SmoothParams* const p) {184int i, j, last_level;185uint8_t used_levels[256] = { 0 };186const uint8_t* data = p->src;187p->min = 255;188p->max = 0;189for (j = 0; j < p->height; ++j) {190for (i = 0; i < p->width; ++i) {191const int v = data[i];192if (v < p->min) p->min = v;193if (v > p->max) p->max = v;194used_levels[v] = 1;195}196data += p->stride;197}198// Compute the mininum distance between two non-zero levels.199p->min_level_dist = p->max - p->min;200last_level = -1;201for (i = 0; i < 256; ++i) {202if (used_levels[i]) {203++p->num_levels;204if (last_level >= 0) {205const int level_dist = i - last_level;206if (level_dist < p->min_level_dist) {207p->min_level_dist = level_dist;208}209}210last_level = i;211}212}213}214215// Initialize all params.216static int InitParams(uint8_t* const data, int width, int height, int stride,217int radius, SmoothParams* const p) {218const int R = 2 * radius + 1; // total size of the kernel219220const size_t size_scratch_m = (R + 1) * width * sizeof(*p->start);221const size_t size_m = width * sizeof(*p->average);222const size_t size_lut = (1 + 2 * LUT_SIZE) * sizeof(*p->correction);223const size_t total_size = size_scratch_m + size_m + size_lut;224uint8_t* mem = (uint8_t*)WebPSafeMalloc(1U, total_size);225226if (mem == NULL) return 0;227p->mem = (void*)mem;228229p->start = (uint16_t*)mem;230p->cur = p->start;231p->end = p->start + R * width;232p->top = p->end - width;233memset(p->top, 0, width * sizeof(*p->top));234mem += size_scratch_m;235236p->average = (uint16_t*)mem;237mem += size_m;238239p->width = width;240p->height = height;241p->stride = stride;242p->src = data;243p->dst = data;244p->radius = radius;245p->scale = (1 << (FIX + LFIX)) / (R * R); // normalization constant246p->row = -radius;247248// analyze the input distribution so we can best-fit the threshold249CountLevels(p);250251// correction table252p->correction = ((int16_t*)mem) + LUT_SIZE;253InitCorrectionLUT(p->correction, p->min_level_dist);254255return 1;256}257258static void CleanupParams(SmoothParams* const p) {259WebPSafeFree(p->mem);260}261262int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride,263int strength) {264int radius = 4 * strength / 100;265266if (strength < 0 || strength > 100) return 0;267if (data == NULL || width <= 0 || height <= 0) return 0; // bad params268269// limit the filter size to not exceed the image dimensions270if (2 * radius + 1 > width) radius = (width - 1) >> 1;271if (2 * radius + 1 > height) radius = (height - 1) >> 1;272273if (radius > 0) {274SmoothParams p;275memset(&p, 0, sizeof(p));276if (!InitParams(data, width, height, stride, radius, &p)) return 0;277if (p.num_levels > 2) {278for (; p.row < p.height; ++p.row) {279VFilter(&p); // accumulate average of input280// Need to wait few rows in order to prime the filter,281// before emitting some output.282if (p.row >= p.radius) {283HFilter(&p);284ApplyFilter(&p);285}286}287}288CleanupParams(&p);289}290return 1;291}292293294