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 ([email protected])
13
14 #include "src/dsp/dsp.h"
15 #include <assert.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 //------------------------------------------------------------------------------
20 // Helpful macro.
21
22 #define DCHECK(in, out) \
23 do { \
24 assert((in) != NULL); \
25 assert((out) != NULL); \
26 assert(width > 0); \
27 assert(height > 0); \
28 assert(stride >= width); \
29 assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
30 (void)height; /* Silence unused warning. */ \
31 } while (0)
32
33 #if !WEBP_NEON_OMIT_C_CODE
PredictLine_C(const uint8_t * src,const uint8_t * pred,uint8_t * dst,int length,int inverse)34 static WEBP_INLINE void PredictLine_C(const uint8_t* src, const uint8_t* pred,
35 uint8_t* dst, int length, int inverse) {
36 int i;
37 if (inverse) {
38 for (i = 0; i < length; ++i) dst[i] = (uint8_t)(src[i] + pred[i]);
39 } else {
40 for (i = 0; i < length; ++i) dst[i] = (uint8_t)(src[i] - pred[i]);
41 }
42 }
43
44 //------------------------------------------------------------------------------
45 // Horizontal filter.
46
DoHorizontalFilter_C(const uint8_t * in,int width,int height,int stride,int row,int num_rows,int inverse,uint8_t * out)47 static WEBP_INLINE void DoHorizontalFilter_C(const uint8_t* in,
48 int width, int height, int stride,
49 int row, int num_rows,
50 int inverse, uint8_t* out) {
51 const uint8_t* preds;
52 const size_t start_offset = row * stride;
53 const int last_row = row + num_rows;
54 DCHECK(in, out);
55 in += start_offset;
56 out += start_offset;
57 preds = inverse ? out : in;
58
59 if (row == 0) {
60 // Leftmost pixel is the same as input for topmost scanline.
61 out[0] = in[0];
62 PredictLine_C(in + 1, preds, out + 1, width - 1, inverse);
63 row = 1;
64 preds += stride;
65 in += stride;
66 out += stride;
67 }
68
69 // Filter line-by-line.
70 while (row < last_row) {
71 // Leftmost pixel is predicted from above.
72 PredictLine_C(in, preds - stride, out, 1, inverse);
73 PredictLine_C(in + 1, preds, out + 1, width - 1, inverse);
74 ++row;
75 preds += stride;
76 in += stride;
77 out += stride;
78 }
79 }
80
81 //------------------------------------------------------------------------------
82 // Vertical filter.
83
DoVerticalFilter_C(const uint8_t * in,int width,int height,int stride,int row,int num_rows,int inverse,uint8_t * out)84 static WEBP_INLINE void DoVerticalFilter_C(const uint8_t* in,
85 int width, int height, int stride,
86 int row, int num_rows,
87 int inverse, uint8_t* out) {
88 const uint8_t* preds;
89 const size_t start_offset = row * stride;
90 const int last_row = row + num_rows;
91 DCHECK(in, out);
92 in += start_offset;
93 out += start_offset;
94 preds = inverse ? out : in;
95
96 if (row == 0) {
97 // Very first top-left pixel is copied.
98 out[0] = in[0];
99 // Rest of top scan-line is left-predicted.
100 PredictLine_C(in + 1, preds, out + 1, width - 1, inverse);
101 row = 1;
102 in += stride;
103 out += stride;
104 } else {
105 // We are starting from in-between. Make sure 'preds' points to prev row.
106 preds -= stride;
107 }
108
109 // Filter line-by-line.
110 while (row < last_row) {
111 PredictLine_C(in, preds, out, width, inverse);
112 ++row;
113 preds += stride;
114 in += stride;
115 out += stride;
116 }
117 }
118 #endif // !WEBP_NEON_OMIT_C_CODE
119
120 //------------------------------------------------------------------------------
121 // Gradient filter.
122
GradientPredictor_C(uint8_t a,uint8_t b,uint8_t c)123 static WEBP_INLINE int GradientPredictor_C(uint8_t a, uint8_t b, uint8_t c) {
124 const int g = a + b - c;
125 return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
126 }
127
128 #if !WEBP_NEON_OMIT_C_CODE
DoGradientFilter_C(const uint8_t * in,int width,int height,int stride,int row,int num_rows,int inverse,uint8_t * out)129 static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in,
130 int width, int height, int stride,
131 int row, int num_rows,
132 int inverse, uint8_t* out) {
133 const uint8_t* preds;
134 const size_t start_offset = row * stride;
135 const int last_row = row + num_rows;
136 DCHECK(in, out);
137 in += start_offset;
138 out += start_offset;
139 preds = inverse ? out : in;
140
141 // left prediction for top scan-line
142 if (row == 0) {
143 out[0] = in[0];
144 PredictLine_C(in + 1, preds, out + 1, width - 1, inverse);
145 row = 1;
146 preds += stride;
147 in += stride;
148 out += stride;
149 }
150
151 // Filter line-by-line.
152 while (row < last_row) {
153 int w;
154 // leftmost pixel: predict from above.
155 PredictLine_C(in, preds - stride, out, 1, inverse);
156 for (w = 1; w < width; ++w) {
157 const int pred = GradientPredictor_C(preds[w - 1],
158 preds[w - stride],
159 preds[w - stride - 1]);
160 out[w] = (uint8_t)(in[w] + (inverse ? pred : -pred));
161 }
162 ++row;
163 preds += stride;
164 in += stride;
165 out += stride;
166 }
167 }
168 #endif // !WEBP_NEON_OMIT_C_CODE
169
170 #undef DCHECK
171
172 //------------------------------------------------------------------------------
173
174 #if !WEBP_NEON_OMIT_C_CODE
HorizontalFilter_C(const uint8_t * data,int width,int height,int stride,uint8_t * filtered_data)175 static void HorizontalFilter_C(const uint8_t* data, int width, int height,
176 int stride, uint8_t* filtered_data) {
177 DoHorizontalFilter_C(data, width, height, stride, 0, height, 0,
178 filtered_data);
179 }
180
VerticalFilter_C(const uint8_t * data,int width,int height,int stride,uint8_t * filtered_data)181 static void VerticalFilter_C(const uint8_t* data, int width, int height,
182 int stride, uint8_t* filtered_data) {
183 DoVerticalFilter_C(data, width, height, stride, 0, height, 0, filtered_data);
184 }
185
GradientFilter_C(const uint8_t * data,int width,int height,int stride,uint8_t * filtered_data)186 static void GradientFilter_C(const uint8_t* data, int width, int height,
187 int stride, uint8_t* filtered_data) {
188 DoGradientFilter_C(data, width, height, stride, 0, height, 0, filtered_data);
189 }
190 #endif // !WEBP_NEON_OMIT_C_CODE
191
192 //------------------------------------------------------------------------------
193
NoneUnfilter_C(const uint8_t * prev,const uint8_t * in,uint8_t * out,int width)194 static void NoneUnfilter_C(const uint8_t* prev, const uint8_t* in,
195 uint8_t* out, int width) {
196 (void)prev;
197 if (out != in) memcpy(out, in, width * sizeof(*out));
198 }
199
HorizontalUnfilter_C(const uint8_t * prev,const uint8_t * in,uint8_t * out,int width)200 static void HorizontalUnfilter_C(const uint8_t* prev, const uint8_t* in,
201 uint8_t* out, int width) {
202 uint8_t pred = (prev == NULL) ? 0 : prev[0];
203 int i;
204 for (i = 0; i < width; ++i) {
205 out[i] = (uint8_t)(pred + in[i]);
206 pred = out[i];
207 }
208 }
209
210 #if !WEBP_NEON_OMIT_C_CODE
VerticalUnfilter_C(const uint8_t * prev,const uint8_t * in,uint8_t * out,int width)211 static void VerticalUnfilter_C(const uint8_t* prev, const uint8_t* in,
212 uint8_t* out, int width) {
213 if (prev == NULL) {
214 HorizontalUnfilter_C(NULL, in, out, width);
215 } else {
216 int i;
217 for (i = 0; i < width; ++i) out[i] = (uint8_t)(prev[i] + in[i]);
218 }
219 }
220 #endif // !WEBP_NEON_OMIT_C_CODE
221
GradientUnfilter_C(const uint8_t * prev,const uint8_t * in,uint8_t * out,int width)222 static void GradientUnfilter_C(const uint8_t* prev, const uint8_t* in,
223 uint8_t* out, int width) {
224 if (prev == NULL) {
225 HorizontalUnfilter_C(NULL, in, out, width);
226 } else {
227 uint8_t top = prev[0], top_left = top, left = top;
228 int i;
229 for (i = 0; i < width; ++i) {
230 top = prev[i]; // need to read this first, in case prev==out
231 left = (uint8_t)(in[i] + GradientPredictor_C(left, top, top_left));
232 top_left = top;
233 out[i] = left;
234 }
235 }
236 }
237
238 //------------------------------------------------------------------------------
239 // Init function
240
241 WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
242 WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST];
243
244 extern VP8CPUInfo VP8GetCPUInfo;
245 extern void VP8FiltersInitMIPSdspR2(void);
246 extern void VP8FiltersInitMSA(void);
247 extern void VP8FiltersInitNEON(void);
248 extern void VP8FiltersInitSSE2(void);
249
WEBP_DSP_INIT_FUNC(VP8FiltersInit)250 WEBP_DSP_INIT_FUNC(VP8FiltersInit) {
251 WebPUnfilters[WEBP_FILTER_NONE] = NoneUnfilter_C;
252 #if !WEBP_NEON_OMIT_C_CODE
253 WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_C;
254 WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_C;
255 #endif
256 WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_C;
257
258 WebPFilters[WEBP_FILTER_NONE] = NULL;
259 #if !WEBP_NEON_OMIT_C_CODE
260 WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_C;
261 WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_C;
262 WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_C;
263 #endif
264
265 if (VP8GetCPUInfo != NULL) {
266 #if defined(WEBP_HAVE_SSE2)
267 if (VP8GetCPUInfo(kSSE2)) {
268 VP8FiltersInitSSE2();
269 }
270 #endif
271 #if defined(WEBP_USE_MIPS_DSP_R2)
272 if (VP8GetCPUInfo(kMIPSdspR2)) {
273 VP8FiltersInitMIPSdspR2();
274 }
275 #endif
276 #if defined(WEBP_USE_MSA)
277 if (VP8GetCPUInfo(kMSA)) {
278 VP8FiltersInitMSA();
279 }
280 #endif
281 }
282
283 #if defined(WEBP_HAVE_NEON)
284 if (WEBP_NEON_OMIT_C_CODE ||
285 (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
286 VP8FiltersInitNEON();
287 }
288 #endif
289
290 assert(WebPUnfilters[WEBP_FILTER_NONE] != NULL);
291 assert(WebPUnfilters[WEBP_FILTER_HORIZONTAL] != NULL);
292 assert(WebPUnfilters[WEBP_FILTER_VERTICAL] != NULL);
293 assert(WebPUnfilters[WEBP_FILTER_GRADIENT] != NULL);
294 assert(WebPFilters[WEBP_FILTER_HORIZONTAL] != NULL);
295 assert(WebPFilters[WEBP_FILTER_VERTICAL] != NULL);
296 assert(WebPFilters[WEBP_FILTER_GRADIENT] != NULL);
297 }
298