OLD | NEW |
(Empty) | |
| 1 // Copyright 2011 Google Inc. All Rights Reserved. |
| 2 // |
| 3 // This code is licensed under the same terms as WebM: |
| 4 // Software License Agreement: http://www.webmproject.org/license/software/ |
| 5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/ |
| 6 // ----------------------------------------------------------------------------- |
| 7 // |
| 8 // Spatial prediction using various filters |
| 9 // |
| 10 // Author: Urvang (urvang@google.com) |
| 11 |
| 12 #include "./filters.h" |
| 13 #include <assert.h> |
| 14 #include <stdlib.h> |
| 15 #include <string.h> |
| 16 |
| 17 #if defined(__cplusplus) || defined(c_plusplus) |
| 18 extern "C" { |
| 19 #endif |
| 20 |
| 21 //------------------------------------------------------------------------------ |
| 22 // Helpful macro. |
| 23 |
| 24 # define SANITY_CHECK(in, out) \ |
| 25 assert(in != NULL); \ |
| 26 assert(out != NULL); \ |
| 27 assert(width > 0); \ |
| 28 assert(height > 0); \ |
| 29 assert(bpp > 0); \ |
| 30 assert(stride >= width * bpp); |
| 31 |
| 32 static WEBP_INLINE void PredictLine(const uint8_t* src, const uint8_t* pred, |
| 33 uint8_t* dst, int length, int inverse) { |
| 34 int i; |
| 35 if (inverse) { |
| 36 for (i = 0; i < length; ++i) dst[i] = src[i] + pred[i]; |
| 37 } else { |
| 38 for (i = 0; i < length; ++i) dst[i] = src[i] - pred[i]; |
| 39 } |
| 40 } |
| 41 |
| 42 //------------------------------------------------------------------------------ |
| 43 // Horizontal filter. |
| 44 |
| 45 static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in, |
| 46 int width, int height, int bpp, int stride, int inverse, uint8_t* out) { |
| 47 int h; |
| 48 const uint8_t* preds = (inverse ? out : in); |
| 49 SANITY_CHECK(in, out); |
| 50 |
| 51 // Filter line-by-line. |
| 52 for (h = 0; h < height; ++h) { |
| 53 // Leftmost pixel is predicted from above (except for topmost scanline). |
| 54 if (h == 0) { |
| 55 memcpy((void*)out, (const void*)in, bpp); |
| 56 } else { |
| 57 PredictLine(in, preds - stride, out, bpp, inverse); |
| 58 } |
| 59 PredictLine(in + bpp, preds, out + bpp, bpp * (width - 1), inverse); |
| 60 preds += stride; |
| 61 in += stride; |
| 62 out += stride; |
| 63 } |
| 64 } |
| 65 |
| 66 static void HorizontalFilter(const uint8_t* data, int width, int height, |
| 67 int bpp, int stride, uint8_t* filtered_data) { |
| 68 DoHorizontalFilter(data, width, height, bpp, stride, 0, filtered_data); |
| 69 } |
| 70 |
| 71 static void HorizontalUnfilter(const uint8_t* data, int width, int height, |
| 72 int bpp, int stride, uint8_t* recon_data) { |
| 73 DoHorizontalFilter(data, width, height, bpp, stride, 1, recon_data); |
| 74 } |
| 75 |
| 76 //------------------------------------------------------------------------------ |
| 77 // Vertical filter. |
| 78 |
| 79 static WEBP_INLINE void DoVerticalFilter(const uint8_t* in, |
| 80 int width, int height, int bpp, int stride, int inverse, uint8_t* out) { |
| 81 int h; |
| 82 const uint8_t* preds = (inverse ? out : in); |
| 83 SANITY_CHECK(in, out); |
| 84 |
| 85 // Very first top-left pixel is copied. |
| 86 memcpy((void*)out, (const void*)in, bpp); |
| 87 // Rest of top scan-line is left-predicted. |
| 88 PredictLine(in + bpp, preds, out + bpp, bpp * (width - 1), inverse); |
| 89 |
| 90 // Filter line-by-line. |
| 91 for (h = 1; h < height; ++h) { |
| 92 in += stride; |
| 93 out += stride; |
| 94 PredictLine(in, preds, out, bpp * width, inverse); |
| 95 preds += stride; |
| 96 } |
| 97 } |
| 98 |
| 99 static void VerticalFilter(const uint8_t* data, int width, int height, |
| 100 int bpp, int stride, uint8_t* filtered_data) { |
| 101 DoVerticalFilter(data, width, height, bpp, stride, 0, filtered_data); |
| 102 } |
| 103 |
| 104 static void VerticalUnfilter(const uint8_t* data, int width, int height, |
| 105 int bpp, int stride, uint8_t* recon_data) { |
| 106 DoVerticalFilter(data, width, height, bpp, stride, 1, recon_data); |
| 107 } |
| 108 |
| 109 //------------------------------------------------------------------------------ |
| 110 // Gradient filter. |
| 111 |
| 112 static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) { |
| 113 const int g = a + b - c; |
| 114 return (g < 0) ? 0 : (g > 255) ? 255 : g; |
| 115 } |
| 116 |
| 117 static WEBP_INLINE |
| 118 void DoGradientFilter(const uint8_t* in, int width, int height, |
| 119 int bpp, int stride, int inverse, uint8_t* out) { |
| 120 const uint8_t* preds = (inverse ? out : in); |
| 121 int h; |
| 122 SANITY_CHECK(in, out); |
| 123 |
| 124 // left prediction for top scan-line |
| 125 memcpy((void*)out, (const void*)in, bpp); |
| 126 PredictLine(in + bpp, preds, out + bpp, bpp * (width - 1), inverse); |
| 127 |
| 128 // Filter line-by-line. |
| 129 for (h = 1; h < height; ++h) { |
| 130 int w; |
| 131 preds += stride; |
| 132 in += stride; |
| 133 out += stride; |
| 134 // leftmost pixel: predict from above. |
| 135 PredictLine(in, preds - stride, out, bpp, inverse); |
| 136 for (w = bpp; w < width * bpp; ++w) { |
| 137 const int pred = GradientPredictor(preds[w - bpp], |
| 138 preds[w - stride], |
| 139 preds[w - stride - bpp]); |
| 140 out[w] = in[w] + (inverse ? pred : -pred); |
| 141 } |
| 142 } |
| 143 } |
| 144 |
| 145 static void GradientFilter(const uint8_t* data, int width, int height, |
| 146 int bpp, int stride, uint8_t* filtered_data) { |
| 147 DoGradientFilter(data, width, height, bpp, stride, 0, filtered_data); |
| 148 } |
| 149 |
| 150 static void GradientUnfilter(const uint8_t* data, int width, int height, |
| 151 int bpp, int stride, uint8_t* recon_data) { |
| 152 DoGradientFilter(data, width, height, bpp, stride, 1, recon_data); |
| 153 } |
| 154 |
| 155 #undef SANITY_CHECK |
| 156 |
| 157 // ----------------------------------------------------------------------------- |
| 158 // Quick estimate of a potentially interesting filter mode to try, in addition |
| 159 // to the default NONE. |
| 160 |
| 161 #define SMAX 16 |
| 162 #define SDIFF(a, b) (abs((a) - (b)) >> 4) // Scoring diff, in [0..SMAX) |
| 163 |
| 164 WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data, |
| 165 int width, int height, int stride) { |
| 166 int i, j; |
| 167 int bins[WEBP_FILTER_LAST][SMAX]; |
| 168 memset(bins, 0, sizeof(bins)); |
| 169 // We only sample every other pixels. That's enough. |
| 170 for (j = 2; j < height - 1; j += 2) { |
| 171 const uint8_t* const p = data + j * stride; |
| 172 int mean = p[0]; |
| 173 for (i = 2; i < width - 1; i += 2) { |
| 174 const int diff0 = SDIFF(p[i], mean); |
| 175 const int diff1 = SDIFF(p[i], p[i - 1]); |
| 176 const int diff2 = SDIFF(p[i], p[i - width]); |
| 177 const int grad_pred = |
| 178 GradientPredictor(p[i - 1], p[i - width], p[i - width - 1]); |
| 179 const int diff3 = SDIFF(p[i], grad_pred); |
| 180 bins[WEBP_FILTER_NONE][diff0] = 1; |
| 181 bins[WEBP_FILTER_HORIZONTAL][diff1] = 1; |
| 182 bins[WEBP_FILTER_VERTICAL][diff2] = 1; |
| 183 bins[WEBP_FILTER_GRADIENT][diff3] = 1; |
| 184 mean = (3 * mean + p[i] + 2) >> 2; |
| 185 } |
| 186 } |
| 187 { |
| 188 WEBP_FILTER_TYPE filter, best_filter = WEBP_FILTER_NONE; |
| 189 int best_score = 0x7fffffff; |
| 190 for (filter = WEBP_FILTER_NONE; filter < WEBP_FILTER_LAST; ++filter) { |
| 191 int score = 0; |
| 192 for (i = 0; i < SMAX; ++i) { |
| 193 if (bins[filter][i] > 0) { |
| 194 score += i; |
| 195 } |
| 196 } |
| 197 if (score < best_score) { |
| 198 best_score = score; |
| 199 best_filter = filter; |
| 200 } |
| 201 } |
| 202 return best_filter; |
| 203 } |
| 204 } |
| 205 |
| 206 #undef SMAX |
| 207 #undef SDIFF |
| 208 |
| 209 //------------------------------------------------------------------------------ |
| 210 |
| 211 const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = { |
| 212 NULL, // WEBP_FILTER_NONE |
| 213 HorizontalFilter, // WEBP_FILTER_HORIZONTAL |
| 214 VerticalFilter, // WEBP_FILTER_VERTICAL |
| 215 GradientFilter // WEBP_FILTER_GRADIENT |
| 216 }; |
| 217 |
| 218 const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST] = { |
| 219 NULL, // WEBP_FILTER_NONE |
| 220 HorizontalUnfilter, // WEBP_FILTER_HORIZONTAL |
| 221 VerticalUnfilter, // WEBP_FILTER_VERTICAL |
| 222 GradientUnfilter // WEBP_FILTER_GRADIENT |
| 223 }; |
| 224 |
| 225 //------------------------------------------------------------------------------ |
| 226 |
| 227 #if defined(__cplusplus) || defined(c_plusplus) |
| 228 } // extern "C" |
| 229 #endif |
OLD | NEW |