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 VerticalUnfilter(int width, int height, int stride, int row, 188 int num_rows, uint8_t* data) { 189 DoVerticalFilter(data, width, height, stride, row, num_rows, 1, data); 190 } 191 192 static void HorizontalUnfilter(int width, int height, int stride, int row, 193 int num_rows, uint8_t* data) { 194 DoHorizontalFilter(data, width, height, stride, row, num_rows, 1, data); 195 } 196 197 static void GradientUnfilter(int width, int height, int stride, int row, 198 int num_rows, uint8_t* data) { 199 DoGradientFilter(data, width, height, stride, row, num_rows, 1, data); 200 } 201 202 //------------------------------------------------------------------------------ 203 // Init function 204 205 WebPFilterFunc WebPFilters[WEBP_FILTER_LAST]; 206 WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST]; 207 208 extern void VP8FiltersInitMIPSdspR2(void); 209 extern void VP8FiltersInitSSE2(void); 210 211 static volatile VP8CPUInfo filters_last_cpuinfo_used = 212 (VP8CPUInfo)&filters_last_cpuinfo_used; 213 214 WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInit(void) { 215 if (filters_last_cpuinfo_used == VP8GetCPUInfo) return; 216 217 WebPUnfilters[WEBP_FILTER_NONE] = NULL; 218 WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter; 219 WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter; 220 WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter; 221 222 WebPFilters[WEBP_FILTER_NONE] = NULL; 223 WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter; 224 WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter; 225 WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter; 226 227 if (VP8GetCPUInfo != NULL) { 228 #if defined(WEBP_USE_SSE2) 229 if (VP8GetCPUInfo(kSSE2)) { 230 VP8FiltersInitSSE2(); 231 } 232 #endif 233 #if defined(WEBP_USE_MIPS_DSP_R2) 234 if (VP8GetCPUInfo(kMIPSdspR2)) { 235 VP8FiltersInitMIPSdspR2(); 236 } 237 #endif 238 } 239 filters_last_cpuinfo_used = VP8GetCPUInfo; 240 } 241