11e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora// Copyright 2013 Google Inc. All Rights Reserved.
21e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora//
30406ce1417f76f2034833414dcecc9f56253640cVikas Arora// Use of this source code is governed by a BSD-style license
40406ce1417f76f2034833414dcecc9f56253640cVikas Arora// that can be found in the COPYING file in the root of the source
50406ce1417f76f2034833414dcecc9f56253640cVikas Arora// tree. An additional intellectual property rights grant can be found
60406ce1417f76f2034833414dcecc9f56253640cVikas Arora// in the file PATENTS. All contributing project authors may
70406ce1417f76f2034833414dcecc9f56253640cVikas Arora// be found in the AUTHORS file in the root of the source tree.
81e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora// -----------------------------------------------------------------------------
91e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora//
10af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// Implement gradient smoothing: we replace a current alpha value by its
11af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// surrounding average if it's close enough (that is: the change will be less
12af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// than the minimum distance between two quantized level).
13af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// We use sliding window for computing the 2d moving average.
141e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora//
151e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora// Author: Skal (pascal.massimino@gmail.com)
161e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora
171e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora#include "./quant_levels_dec.h"
181e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora
19af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#include <string.h>   // for memset
20af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
21af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#include "./utils.h"
22af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
23af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// #define USE_DITHERING   // uncomment to enable ordered dithering (not vital)
24af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
25af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#define FIX 16     // fix-point precision for averaging
26af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#define LFIX 2     // extra precision for look-up table
27af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#define LUT_SIZE ((1 << (8 + LFIX)) - 1)  // look-up table size
28af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
29af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#if defined(USE_DITHERING)
30af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
31af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#define DFIX 4           // extra precision for ordered dithering
32af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#define DSIZE 4          // dithering size (must be a power of two)
33af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// cf. http://en.wikipedia.org/wiki/Ordered_dithering
34af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arorastatic const uint8_t kOrderedDither[DSIZE][DSIZE] = {
35af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora {  0,  8,  2, 10 },     // coefficients are in DFIX fixed-point precision
36af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora { 12,  4, 14,  6 },
37af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora {  3, 11,  1,  9 },
38af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora { 15,  7, 13,  5 }
39af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora};
40af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
41af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#else
42af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#define DFIX 0
43af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#endif
44af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
45af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Aroratypedef struct {
46af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int width_, height_;  // dimension
47af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int row_;             // current input row being processed
48af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint8_t* src_;        // input pointer
49af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint8_t* dst_;        // output pointer
50af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
51af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int radius_;          // filter radius (=delay)
52af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int scale_;           // normalization factor, in FIX bits precision
53af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
54af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  void* mem_;           // all memory
55af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
56af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // various scratch buffers
57af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint16_t* start_;
58af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint16_t* cur_;
59af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint16_t* end_;
60af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint16_t* top_;
61af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint16_t* average_;
62af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
63af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // input levels distribution
64af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int num_levels_;       // number of quantized levels
65af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int min_, max_;        // min and max level values
66af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int min_level_dist_;   // smallest distance between two consecutive levels
67af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
68af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int16_t* correction_;  // size = 1 + 2*LUT_SIZE  -> ~4k memory
69af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora} SmoothParams;
70af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
71af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora//------------------------------------------------------------------------------
72af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
73af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#define CLIP_MASK (int)(~0U << (8 + DFIX))
74af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arorastatic WEBP_INLINE uint8_t clip_8b(int v) {
75af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  return (!(v & CLIP_MASK)) ? (uint8_t)(v >> DFIX) : (v < 0) ? 0u : 255u;
76af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora}
77af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
78af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// vertical accumulation
79af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arorastatic void VFilter(SmoothParams* const p) {
80af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const uint8_t* src = p->src_;
81af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const int w = p->width_;
82af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint16_t* const cur = p->cur_;
83af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const uint16_t* const top = p->top_;
84af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint16_t* const out = p->end_;
85af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint16_t sum = 0;               // all arithmetic is modulo 16bit
86af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int x;
87af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
88af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  for (x = 0; x < w; ++x) {
89af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    uint16_t new_value;
90af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    sum += src[x];
91af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    new_value = top[x] + sum;
92af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    out[x] = new_value - cur[x];  // vertical sum of 'r' pixels.
93af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    cur[x] = new_value;
94af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  }
95af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // move input pointers one row down
96af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->top_ = p->cur_;
97af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->cur_ += w;
98af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  if (p->cur_ == p->end_) p->cur_ = p->start_;  // roll-over
99af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // We replicate edges, as it's somewhat easier as a boundary condition.
100af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // That's why we don't update the 'src' pointer on top/bottom area:
101af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  if (p->row_ >= 0 && p->row_ < p->height_ - 1) {
102af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    p->src_ += p->width_;
103af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  }
104af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora}
105af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
106af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// horizontal accumulation. We use mirror replication of missing pixels, as it's
107af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// a little easier to implement (surprisingly).
108af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arorastatic void HFilter(SmoothParams* const p) {
109af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const uint16_t* const in = p->end_;
110af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint16_t* const out = p->average_;
111af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const uint32_t scale = p->scale_;
112af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const int w = p->width_;
113af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const int r = p->radius_;
114af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
115af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int x;
116af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  for (x = 0; x <= r; ++x) {   // left mirroring
117af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    const uint16_t delta = in[x + r - 1] + in[r - x];
118af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    out[x] = (delta * scale) >> FIX;
119af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  }
120af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  for (; x < w - r; ++x) {     // bulk middle run
121af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    const uint16_t delta = in[x + r] - in[x - r - 1];
122af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    out[x] = (delta * scale) >> FIX;
123af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  }
124af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  for (; x < w; ++x) {         // right mirroring
125af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    const uint16_t delta =
126af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora        2 * in[w - 1] - in[2 * w - 2 - r - x] - in[x - r - 1];
127af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    out[x] = (delta * scale) >> FIX;
128af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  }
129af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora}
130af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
131af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// emit one filtered output row
132af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arorastatic void ApplyFilter(SmoothParams* const p) {
133af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const uint16_t* const average = p->average_;
134af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const int w = p->width_;
135af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const int16_t* const correction = p->correction_;
136af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#if defined(USE_DITHERING)
137af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const uint8_t* const dither = kOrderedDither[p->row_ % DSIZE];
138af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#endif
139af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint8_t* const dst = p->dst_;
140af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int x;
141af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  for (x = 0; x < w; ++x) {
142af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    const int v = dst[x];
143af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    if (v < p->max_ && v > p->min_) {
144af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora      const int c = (v << DFIX) + correction[average[x] - (v << LFIX)];
145af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#if defined(USE_DITHERING)
146af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora      dst[x] = clip_8b(c + dither[x % DSIZE]);
147af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#else
148af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora      dst[x] = clip_8b(c);
149af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora#endif
150af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    }
1518b720228d581a84fd173b6dcb2fa295b59db489aVikas Arora  }
152af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->dst_ += w;  // advance output pointer
153af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora}
154af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
155af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora//------------------------------------------------------------------------------
156af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// Initialize correction table
157af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
158af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arorastatic void InitCorrectionLUT(int16_t* const lut, int min_dist) {
159af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // The correction curve is:
160af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  //   f(x) = x for x <= threshold2
161af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  //   f(x) = 0 for x >= threshold1
162af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // and a linear interpolation for range x=[threshold2, threshold1]
163af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // (along with f(-x) = -f(x) symmetry).
164af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // Note that: threshold2 = 3/4 * threshold1
165af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const int threshold1 = min_dist << LFIX;
166af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const int threshold2 = (3 * threshold1) >> 2;
167af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const int max_threshold = threshold2 << DFIX;
168af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const int delta = threshold1 - threshold2;
169af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int i;
170af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  for (i = 1; i <= LUT_SIZE; ++i) {
171af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    int c = (i <= threshold2) ? (i << DFIX)
172af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora          : (i < threshold1) ? max_threshold * (threshold1 - i) / delta
173af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora          : 0;
174af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    c >>= LFIX;
175af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    lut[+i] = +c;
176af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    lut[-i] = -c;
177af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  }
178af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  lut[0] = 0;
179af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora}
180af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
181af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arorastatic void CountLevels(const uint8_t* const data, int size,
182af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora                        SmoothParams* const p) {
183af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  int i, last_level;
184af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint8_t used_levels[256] = { 0 };
185af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->min_ = 255;
186af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->max_ = 0;
187af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  for (i = 0; i < size; ++i) {
188af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    const int v = data[i];
189af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    if (v < p->min_) p->min_ = v;
190af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    if (v > p->max_) p->max_ = v;
191af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    used_levels[v] = 1;
192af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  }
193af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // Compute the mininum distance between two non-zero levels.
194af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->min_level_dist_ = p->max_ - p->min_;
195af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  last_level = -1;
196af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  for (i = 0; i < 256; ++i) {
197af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    if (used_levels[i]) {
198af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora      ++p->num_levels_;
199af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora      if (last_level >= 0) {
200af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora        const int level_dist = i - last_level;
201af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora        if (level_dist < p->min_level_dist_) {
202af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora          p->min_level_dist_ = level_dist;
203af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora        }
204af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora      }
205af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora      last_level = i;
206af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    }
207af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  }
208af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora}
209af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
210af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora// Initialize all params.
211af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arorastatic int InitParams(uint8_t* const data, int width, int height,
212af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora                      int radius, SmoothParams* const p) {
213af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const int R = 2 * radius + 1;  // total size of the kernel
214af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
215af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const size_t size_scratch_m = (R + 1) * width * sizeof(*p->start_);
216af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const size_t size_m =  width * sizeof(*p->average_);
217af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const size_t size_lut = (1 + 2 * LUT_SIZE) * sizeof(*p->correction_);
218af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const size_t total_size = size_scratch_m + size_m + size_lut;
219af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  uint8_t* mem = (uint8_t*)WebPSafeMalloc(1U, total_size);
220af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
221af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  if (mem == NULL) return 0;
222af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->mem_ = (void*)mem;
223af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
224af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->start_ = (uint16_t*)mem;
225af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->cur_ = p->start_;
226af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->end_ = p->start_ + R * width;
227af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->top_ = p->end_ - width;
228af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  memset(p->top_, 0, width * sizeof(*p->top_));
229af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  mem += size_scratch_m;
230af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
231af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->average_ = (uint16_t*)mem;
232af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  mem += size_m;
233af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
234af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->width_ = width;
235af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->height_ = height;
236af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->src_ = data;
237af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->dst_ = data;
238af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->radius_ = radius;
239af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->scale_ = (1 << (FIX + LFIX)) / (R * R);  // normalization constant
240af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->row_ = -radius;
241af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
242af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // analyze the input distribution so we can best-fit the threshold
243af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  CountLevels(data, width * height, p);
244af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
245af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  // correction table
246af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  p->correction_ = ((int16_t*)mem) + LUT_SIZE;
247af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  InitCorrectionLUT(p->correction_, p->min_level_dist_);
248af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
2491e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora  return 1;
2501e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora}
2511e7bf8805bd030c19924a5306837ecd72c295751Vikas Arora
252af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arorastatic void CleanupParams(SmoothParams* const p) {
253af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  WebPSafeFree(p->mem_);
254af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora}
255af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora
256af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Aroraint WebPDequantizeLevels(uint8_t* const data, int width, int height,
257af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora                         int strength) {
258af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  const int radius = 4 * strength / 100;
259af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  if (strength < 0 || strength > 100) return 0;
260af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  if (data == NULL || width <= 0 || height <= 0) return 0;  // bad params
261af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  if (radius > 0) {
262af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    SmoothParams p;
263af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    memset(&p, 0, sizeof(p));
264af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    if (!InitParams(data, width, height, radius, &p)) return 0;
265af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    if (p.num_levels_ > 2) {
266af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora      for (; p.row_ < p.height_; ++p.row_) {
267af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora        VFilter(&p);  // accumulate average of input
268af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora        // Need to wait few rows in order to prime the filter,
269af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora        // before emitting some output.
270af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora        if (p.row_ >= p.radius_) {
271af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora          HFilter(&p);
272af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora          ApplyFilter(&p);
273af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora        }
274af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora      }
275af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    }
276af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora    CleanupParams(&p);
277af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  }
278af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora  return 1;
279af51b94a435132e9014c324e25fb686b3d07a8c8Vikas Arora}
280