1 // Copyright 2011 Google Inc. All Rights Reserved. 2 // 3 // Use of this source code is governed by a BSD-style license 4 // that can be found in the COPYING file in the root of the source 5 // tree. An additional intellectual property rights grant can be found 6 // in the file PATENTS. All contributing project authors may 7 // be found in the AUTHORS file in the root of the source tree. 8 // ----------------------------------------------------------------------------- 9 // 10 // Spatial prediction using various filters 11 // 12 // Author: Urvang (urvang (at) google.com) 13 14 #include "./dsp.h" 15 #include <assert.h> 16 #include <stdlib.h> 17 #include <string.h> 18 19 //------------------------------------------------------------------------------ 20 // Helpful macro. 21 22 # define SANITY_CHECK(in, out) \ 23 assert(in != NULL); \ 24 assert(out != NULL); \ 25 assert(width > 0); \ 26 assert(height > 0); \ 27 assert(stride >= width); \ 28 assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ 29 (void)height; // Silence unused warning. 30 31 static WEBP_INLINE void PredictLine(const uint8_t* src, const uint8_t* pred, 32 uint8_t* dst, int length, int inverse) { 33 int i; 34 if (inverse) { 35 for (i = 0; i < length; ++i) dst[i] = src[i] + pred[i]; 36 } else { 37 for (i = 0; i < length; ++i) dst[i] = src[i] - pred[i]; 38 } 39 } 40 41 //------------------------------------------------------------------------------ 42 // Horizontal filter. 43 44 static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in, 45 int width, int height, int stride, 46 int row, int num_rows, 47 int inverse, uint8_t* out) { 48 const uint8_t* preds; 49 const size_t start_offset = row * stride; 50 const int last_row = row + num_rows; 51 SANITY_CHECK(in, out); 52 in += start_offset; 53 out += start_offset; 54 preds = inverse ? out : in; 55 56 if (row == 0) { 57 // Leftmost pixel is the same as input for topmost scanline. 58 out[0] = in[0]; 59 PredictLine(in + 1, preds, out + 1, width - 1, inverse); 60 row = 1; 61 preds += stride; 62 in += stride; 63 out += stride; 64 } 65 66 // Filter line-by-line. 67 while (row < last_row) { 68 // Leftmost pixel is predicted from above. 69 PredictLine(in, preds - stride, out, 1, inverse); 70 PredictLine(in + 1, preds, out + 1, width - 1, inverse); 71 ++row; 72 preds += stride; 73 in += stride; 74 out += stride; 75 } 76 } 77 78 //------------------------------------------------------------------------------ 79 // Vertical filter. 80 81 static WEBP_INLINE void DoVerticalFilter(const uint8_t* in, 82 int width, int height, int stride, 83 int row, int num_rows, 84 int inverse, uint8_t* out) { 85 const uint8_t* preds; 86 const size_t start_offset = row * stride; 87 const int last_row = row + num_rows; 88 SANITY_CHECK(in, out); 89 in += start_offset; 90 out += start_offset; 91 preds = inverse ? out : in; 92 93 if (row == 0) { 94 // Very first top-left pixel is copied. 95 out[0] = in[0]; 96 // Rest of top scan-line is left-predicted. 97 PredictLine(in + 1, preds, out + 1, width - 1, inverse); 98 row = 1; 99 in += stride; 100 out += stride; 101 } else { 102 // We are starting from in-between. Make sure 'preds' points to prev row. 103 preds -= stride; 104 } 105 106 // Filter line-by-line. 107 while (row < last_row) { 108 PredictLine(in, preds, out, width, inverse); 109 ++row; 110 preds += stride; 111 in += stride; 112 out += stride; 113 } 114 } 115 116 //------------------------------------------------------------------------------ 117 // Gradient filter. 118 119 static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) { 120 const int g = a + b - c; 121 return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit 122 } 123 124 static WEBP_INLINE void DoGradientFilter(const uint8_t* in, 125 int width, int height, int stride, 126 int row, int num_rows, 127 int inverse, uint8_t* out) { 128 const uint8_t* preds; 129 const size_t start_offset = row * stride; 130 const int last_row = row + num_rows; 131 SANITY_CHECK(in, out); 132 in += start_offset; 133 out += start_offset; 134 preds = inverse ? out : in; 135 136 // left prediction for top scan-line 137 if (row == 0) { 138 out[0] = in[0]; 139 PredictLine(in + 1, preds, out + 1, width - 1, inverse); 140 row = 1; 141 preds += stride; 142 in += stride; 143 out += stride; 144 } 145 146 // Filter line-by-line. 147 while (row < last_row) { 148 int w; 149 // leftmost pixel: predict from above. 150 PredictLine(in, preds - stride, out, 1, inverse); 151 for (w = 1; w < width; ++w) { 152 const int pred = GradientPredictor(preds[w - 1], 153 preds[w - stride], 154 preds[w - stride - 1]); 155 out[w] = in[w] + (inverse ? pred : -pred); 156 } 157 ++row; 158 preds += stride; 159 in += stride; 160 out += stride; 161 } 162 } 163 164 #undef SANITY_CHECK 165 166 //------------------------------------------------------------------------------ 167 168 static void HorizontalFilter(const uint8_t* data, int width, int height, 169 int stride, uint8_t* filtered_data) { 170 DoHorizontalFilter(data, width, height, stride, 0, height, 0, filtered_data); 171 } 172 173 static void VerticalFilter(const uint8_t* data, int width, int height, 174 int stride, uint8_t* filtered_data) { 175 DoVerticalFilter(data, width, height, stride, 0, height, 0, filtered_data); 176 } 177 178 179 static void GradientFilter(const uint8_t* data, int width, int height, 180 int stride, uint8_t* filtered_data) { 181 DoGradientFilter(data, width, height, stride, 0, height, 0, filtered_data); 182 } 183 184 185 //------------------------------------------------------------------------------ 186 187 static void HorizontalUnfilter(const uint8_t* prev, const uint8_t* in, 188 uint8_t* out, int width) { 189 uint8_t pred = (prev == NULL) ? 0 : prev[0]; 190 int i; 191 for (i = 0; i < width; ++i) { 192 out[i] = pred + in[i]; 193 pred = out[i]; 194 } 195 } 196 197 static void VerticalUnfilter(const uint8_t* prev, const uint8_t* in, 198 uint8_t* out, int width) { 199 if (prev == NULL) { 200 HorizontalUnfilter(NULL, in, out, width); 201 } else { 202 int i; 203 for (i = 0; i < width; ++i) out[i] = prev[i] + in[i]; 204 } 205 } 206 207 static void GradientUnfilter(const uint8_t* prev, const uint8_t* in, 208 uint8_t* out, int width) { 209 if (prev == NULL) { 210 HorizontalUnfilter(NULL, in, out, width); 211 } else { 212 uint8_t top = prev[0], top_left = top, left = top; 213 int i; 214 for (i = 0; i < width; ++i) { 215 top = prev[i]; // need to read this first, in case prev==out 216 left = in[i] + GradientPredictor(left, top, top_left); 217 top_left = top; 218 out[i] = left; 219 } 220 } 221 } 222 223 //------------------------------------------------------------------------------ 224 // Init function 225 226 WebPFilterFunc WebPFilters[WEBP_FILTER_LAST]; 227 WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST]; 228 229 extern void VP8FiltersInitMIPSdspR2(void); 230 extern void VP8FiltersInitMSA(void); 231 extern void VP8FiltersInitNEON(void); 232 extern void VP8FiltersInitSSE2(void); 233 234 static volatile VP8CPUInfo filters_last_cpuinfo_used = 235 (VP8CPUInfo)&filters_last_cpuinfo_used; 236 237 WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInit(void) { 238 if (filters_last_cpuinfo_used == VP8GetCPUInfo) return; 239 240 WebPUnfilters[WEBP_FILTER_NONE] = NULL; 241 WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter; 242 WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter; 243 WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter; 244 245 WebPFilters[WEBP_FILTER_NONE] = NULL; 246 WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter; 247 WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter; 248 WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter; 249 250 if (VP8GetCPUInfo != NULL) { 251 #if defined(WEBP_USE_SSE2) 252 if (VP8GetCPUInfo(kSSE2)) { 253 VP8FiltersInitSSE2(); 254 } 255 #endif 256 #if defined(WEBP_USE_NEON) 257 if (VP8GetCPUInfo(kNEON)) { 258 VP8FiltersInitNEON(); 259 } 260 #endif 261 #if defined(WEBP_USE_MIPS_DSP_R2) 262 if (VP8GetCPUInfo(kMIPSdspR2)) { 263 VP8FiltersInitMIPSdspR2(); 264 } 265 #endif 266 #if defined(WEBP_USE_MSA) 267 if (VP8GetCPUInfo(kMSA)) { 268 VP8FiltersInitMSA(); 269 } 270 #endif 271 } 272 filters_last_cpuinfo_used = VP8GetCPUInfo; 273 } 274