1 // Copyright 2014 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(s): Branimir Vasic (branimir.vasic@imgtec.com)
13 // Djordje Pesut (djordje.pesut@imgtec.com)
14
15 #include "./dsp.h"
16
17 #if defined(WEBP_USE_MIPS_DSP_R2)
18
19 #include "../dsp/dsp.h"
20 #include <assert.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 //------------------------------------------------------------------------------
25 // Helpful macro.
26
27 # define SANITY_CHECK(in, out) \
28 assert(in != NULL); \
29 assert(out != NULL); \
30 assert(width > 0); \
31 assert(height > 0); \
32 assert(stride >= width); \
33 assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
34 (void)height; // Silence unused warning.
35
36 #define DO_PREDICT_LINE(SRC, DST, LENGTH, INVERSE) do { \
37 const uint8_t* psrc = (uint8_t*)(SRC); \
38 uint8_t* pdst = (uint8_t*)(DST); \
39 const int ilength = (int)(LENGTH); \
40 int temp0, temp1, temp2, temp3, temp4, temp5, temp6; \
41 __asm__ volatile ( \
42 ".set push \n\t" \
43 ".set noreorder \n\t" \
44 "srl %[temp0], %[length], 2 \n\t" \
45 "beqz %[temp0], 4f \n\t" \
46 " andi %[temp6], %[length], 3 \n\t" \
47 ".if " #INVERSE " \n\t" \
48 "1: \n\t" \
49 "lbu %[temp1], -1(%[dst]) \n\t" \
50 "lbu %[temp2], 0(%[src]) \n\t" \
51 "lbu %[temp3], 1(%[src]) \n\t" \
52 "lbu %[temp4], 2(%[src]) \n\t" \
53 "lbu %[temp5], 3(%[src]) \n\t" \
54 "addu %[temp1], %[temp1], %[temp2] \n\t" \
55 "addu %[temp2], %[temp1], %[temp3] \n\t" \
56 "addu %[temp3], %[temp2], %[temp4] \n\t" \
57 "addu %[temp4], %[temp3], %[temp5] \n\t" \
58 "sb %[temp1], 0(%[dst]) \n\t" \
59 "sb %[temp2], 1(%[dst]) \n\t" \
60 "sb %[temp3], 2(%[dst]) \n\t" \
61 "sb %[temp4], 3(%[dst]) \n\t" \
62 "addiu %[src], %[src], 4 \n\t" \
63 "addiu %[temp0], %[temp0], -1 \n\t" \
64 "bnez %[temp0], 1b \n\t" \
65 " addiu %[dst], %[dst], 4 \n\t" \
66 ".else \n\t" \
67 "1: \n\t" \
68 "ulw %[temp1], -1(%[src]) \n\t" \
69 "ulw %[temp2], 0(%[src]) \n\t" \
70 "addiu %[src], %[src], 4 \n\t" \
71 "addiu %[temp0], %[temp0], -1 \n\t" \
72 "subu.qb %[temp3], %[temp2], %[temp1] \n\t" \
73 "usw %[temp3], 0(%[dst]) \n\t" \
74 "bnez %[temp0], 1b \n\t" \
75 " addiu %[dst], %[dst], 4 \n\t" \
76 ".endif \n\t" \
77 "4: \n\t" \
78 "beqz %[temp6], 3f \n\t" \
79 " nop \n\t" \
80 "2: \n\t" \
81 "lbu %[temp2], 0(%[src]) \n\t" \
82 ".if " #INVERSE " \n\t" \
83 "lbu %[temp1], -1(%[dst]) \n\t" \
84 "addu %[temp3], %[temp1], %[temp2] \n\t" \
85 ".else \n\t" \
86 "lbu %[temp1], -1(%[src]) \n\t" \
87 "subu %[temp3], %[temp1], %[temp2] \n\t" \
88 ".endif \n\t" \
89 "addiu %[src], %[src], 1 \n\t" \
90 "sb %[temp3], 0(%[dst]) \n\t" \
91 "addiu %[temp6], %[temp6], -1 \n\t" \
92 "bnez %[temp6], 2b \n\t" \
93 " addiu %[dst], %[dst], 1 \n\t" \
94 "3: \n\t" \
95 ".set pop \n\t" \
96 : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \
97 [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \
98 [temp6]"=&r"(temp6), [dst]"+&r"(pdst), [src]"+&r"(psrc) \
99 : [length]"r"(ilength) \
100 : "memory" \
101 ); \
102 } while (0)
103
PredictLine(const uint8_t * src,uint8_t * dst,int length)104 static WEBP_INLINE void PredictLine(const uint8_t* src, uint8_t* dst,
105 int length) {
106 DO_PREDICT_LINE(src, dst, length, 0);
107 }
108
109 #define DO_PREDICT_LINE_VERTICAL(SRC, PRED, DST, LENGTH, INVERSE) do { \
110 const uint8_t* psrc = (uint8_t*)(SRC); \
111 const uint8_t* ppred = (uint8_t*)(PRED); \
112 uint8_t* pdst = (uint8_t*)(DST); \
113 const int ilength = (int)(LENGTH); \
114 int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; \
115 __asm__ volatile ( \
116 ".set push \n\t" \
117 ".set noreorder \n\t" \
118 "srl %[temp0], %[length], 0x3 \n\t" \
119 "beqz %[temp0], 4f \n\t" \
120 " andi %[temp7], %[length], 0x7 \n\t" \
121 "1: \n\t" \
122 "ulw %[temp1], 0(%[src]) \n\t" \
123 "ulw %[temp2], 0(%[pred]) \n\t" \
124 "ulw %[temp3], 4(%[src]) \n\t" \
125 "ulw %[temp4], 4(%[pred]) \n\t" \
126 "addiu %[src], %[src], 8 \n\t" \
127 ".if " #INVERSE " \n\t" \
128 "addu.qb %[temp5], %[temp1], %[temp2] \n\t" \
129 "addu.qb %[temp6], %[temp3], %[temp4] \n\t" \
130 ".else \n\t" \
131 "subu.qb %[temp5], %[temp1], %[temp2] \n\t" \
132 "subu.qb %[temp6], %[temp3], %[temp4] \n\t" \
133 ".endif \n\t" \
134 "addiu %[pred], %[pred], 8 \n\t" \
135 "usw %[temp5], 0(%[dst]) \n\t" \
136 "usw %[temp6], 4(%[dst]) \n\t" \
137 "addiu %[temp0], %[temp0], -1 \n\t" \
138 "bnez %[temp0], 1b \n\t" \
139 " addiu %[dst], %[dst], 8 \n\t" \
140 "4: \n\t" \
141 "beqz %[temp7], 3f \n\t" \
142 " nop \n\t" \
143 "2: \n\t" \
144 "lbu %[temp1], 0(%[src]) \n\t" \
145 "lbu %[temp2], 0(%[pred]) \n\t" \
146 "addiu %[src], %[src], 1 \n\t" \
147 "addiu %[pred], %[pred], 1 \n\t" \
148 ".if " #INVERSE " \n\t" \
149 "addu %[temp3], %[temp1], %[temp2] \n\t" \
150 ".else \n\t" \
151 "subu %[temp3], %[temp1], %[temp2] \n\t" \
152 ".endif \n\t" \
153 "sb %[temp3], 0(%[dst]) \n\t" \
154 "addiu %[temp7], %[temp7], -1 \n\t" \
155 "bnez %[temp7], 2b \n\t" \
156 " addiu %[dst], %[dst], 1 \n\t" \
157 "3: \n\t" \
158 ".set pop \n\t" \
159 : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \
160 [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \
161 [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [pred]"+&r"(ppred), \
162 [dst]"+&r"(pdst), [src]"+&r"(psrc) \
163 : [length]"r"(ilength) \
164 : "memory" \
165 ); \
166 } while (0)
167
168 #define PREDICT_LINE_ONE_PASS(SRC, PRED, DST) do { \
169 int temp1, temp2, temp3; \
170 __asm__ volatile ( \
171 "lbu %[temp1], 0(%[src]) \n\t" \
172 "lbu %[temp2], 0(%[pred]) \n\t" \
173 "subu %[temp3], %[temp1], %[temp2] \n\t" \
174 "sb %[temp3], 0(%[dst]) \n\t" \
175 : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3) \
176 : [pred]"r"((PRED)), [dst]"r"((DST)), [src]"r"((SRC)) \
177 : "memory" \
178 ); \
179 } while (0)
180
181 //------------------------------------------------------------------------------
182 // Horizontal filter.
183
184 #define FILTER_LINE_BY_LINE do { \
185 while (row < last_row) { \
186 PREDICT_LINE_ONE_PASS(in, preds - stride, out); \
187 DO_PREDICT_LINE(in + 1, out + 1, width - 1, 0); \
188 ++row; \
189 preds += stride; \
190 in += stride; \
191 out += stride; \
192 } \
193 } while (0)
194
DoHorizontalFilter(const uint8_t * in,int width,int height,int stride,int row,int num_rows,uint8_t * out)195 static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in,
196 int width, int height, int stride,
197 int row, int num_rows,
198 uint8_t* out) {
199 const uint8_t* preds;
200 const size_t start_offset = row * stride;
201 const int last_row = row + num_rows;
202 SANITY_CHECK(in, out);
203 in += start_offset;
204 out += start_offset;
205 preds = in;
206
207 if (row == 0) {
208 // Leftmost pixel is the same as input for topmost scanline.
209 out[0] = in[0];
210 PredictLine(in + 1, out + 1, width - 1);
211 row = 1;
212 preds += stride;
213 in += stride;
214 out += stride;
215 }
216
217 // Filter line-by-line.
218 FILTER_LINE_BY_LINE;
219 }
220 #undef FILTER_LINE_BY_LINE
221
HorizontalFilter(const uint8_t * data,int width,int height,int stride,uint8_t * filtered_data)222 static void HorizontalFilter(const uint8_t* data, int width, int height,
223 int stride, uint8_t* filtered_data) {
224 DoHorizontalFilter(data, width, height, stride, 0, height, filtered_data);
225 }
226
227 //------------------------------------------------------------------------------
228 // Vertical filter.
229
230 #define FILTER_LINE_BY_LINE do { \
231 while (row < last_row) { \
232 DO_PREDICT_LINE_VERTICAL(in, preds, out, width, 0); \
233 ++row; \
234 preds += stride; \
235 in += stride; \
236 out += stride; \
237 } \
238 } while (0)
239
DoVerticalFilter(const uint8_t * in,int width,int height,int stride,int row,int num_rows,uint8_t * out)240 static WEBP_INLINE void DoVerticalFilter(const uint8_t* in,
241 int width, int height, int stride,
242 int row, int num_rows, uint8_t* out) {
243 const uint8_t* preds;
244 const size_t start_offset = row * stride;
245 const int last_row = row + num_rows;
246 SANITY_CHECK(in, out);
247 in += start_offset;
248 out += start_offset;
249 preds = in;
250
251 if (row == 0) {
252 // Very first top-left pixel is copied.
253 out[0] = in[0];
254 // Rest of top scan-line is left-predicted.
255 PredictLine(in + 1, out + 1, width - 1);
256 row = 1;
257 in += stride;
258 out += stride;
259 } else {
260 // We are starting from in-between. Make sure 'preds' points to prev row.
261 preds -= stride;
262 }
263
264 // Filter line-by-line.
265 FILTER_LINE_BY_LINE;
266 }
267 #undef FILTER_LINE_BY_LINE
268
VerticalFilter(const uint8_t * data,int width,int height,int stride,uint8_t * filtered_data)269 static void VerticalFilter(const uint8_t* data, int width, int height,
270 int stride, uint8_t* filtered_data) {
271 DoVerticalFilter(data, width, height, stride, 0, height, filtered_data);
272 }
273
274 //------------------------------------------------------------------------------
275 // Gradient filter.
276
GradientPredictor(uint8_t a,uint8_t b,uint8_t c)277 static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) {
278 int temp0;
279 __asm__ volatile (
280 "addu %[temp0], %[a], %[b] \n\t"
281 "subu %[temp0], %[temp0], %[c] \n\t"
282 "shll_s.w %[temp0], %[temp0], 23 \n\t"
283 "precrqu_s.qb.ph %[temp0], %[temp0], $zero \n\t"
284 "srl %[temp0], %[temp0], 24 \n\t"
285 : [temp0]"=&r"(temp0)
286 : [a]"r"(a),[b]"r"(b),[c]"r"(c)
287 );
288 return temp0;
289 }
290
291 #define FILTER_LINE_BY_LINE(PREDS, OPERATION) do { \
292 while (row < last_row) { \
293 int w; \
294 PREDICT_LINE_ONE_PASS(in, PREDS - stride, out); \
295 for (w = 1; w < width; ++w) { \
296 const int pred = GradientPredictor(PREDS[w - 1], \
297 PREDS[w - stride], \
298 PREDS[w - stride - 1]); \
299 out[w] = in[w] OPERATION pred; \
300 } \
301 ++row; \
302 in += stride; \
303 out += stride; \
304 } \
305 } while (0)
306
DoGradientFilter(const uint8_t * in,int width,int height,int stride,int row,int num_rows,uint8_t * out)307 static WEBP_INLINE void DoGradientFilter(const uint8_t* in,
308 int width, int height, int stride,
309 int row, int num_rows, uint8_t* out) {
310 const uint8_t* preds;
311 const size_t start_offset = row * stride;
312 const int last_row = row + num_rows;
313 SANITY_CHECK(in, out);
314 in += start_offset;
315 out += start_offset;
316 preds = in;
317
318 // left prediction for top scan-line
319 if (row == 0) {
320 out[0] = in[0];
321 PredictLine(in + 1, out + 1, width - 1);
322 row = 1;
323 preds += stride;
324 in += stride;
325 out += stride;
326 }
327
328 // Filter line-by-line.
329 FILTER_LINE_BY_LINE(in, -);
330 }
331 #undef FILTER_LINE_BY_LINE
332
GradientFilter(const uint8_t * data,int width,int height,int stride,uint8_t * filtered_data)333 static void GradientFilter(const uint8_t* data, int width, int height,
334 int stride, uint8_t* filtered_data) {
335 DoGradientFilter(data, width, height, stride, 0, height, filtered_data);
336 }
337
338 //------------------------------------------------------------------------------
339
HorizontalUnfilter(const uint8_t * prev,const uint8_t * in,uint8_t * out,int width)340 static void HorizontalUnfilter(const uint8_t* prev, const uint8_t* in,
341 uint8_t* out, int width) {
342 out[0] = in[0] + (prev == NULL ? 0 : prev[0]);
343 DO_PREDICT_LINE(in + 1, out + 1, width - 1, 1);
344 }
345
VerticalUnfilter(const uint8_t * prev,const uint8_t * in,uint8_t * out,int width)346 static void VerticalUnfilter(const uint8_t* prev, const uint8_t* in,
347 uint8_t* out, int width) {
348 if (prev == NULL) {
349 HorizontalUnfilter(NULL, in, out, width);
350 } else {
351 DO_PREDICT_LINE_VERTICAL(in, prev, out, width, 1);
352 }
353 }
354
GradientUnfilter(const uint8_t * prev,const uint8_t * in,uint8_t * out,int width)355 static void GradientUnfilter(const uint8_t* prev, const uint8_t* in,
356 uint8_t* out, int width) {
357 if (prev == NULL) {
358 HorizontalUnfilter(NULL, in, out, width);
359 } else {
360 uint8_t top = prev[0], top_left = top, left = top;
361 int i;
362 for (i = 0; i < width; ++i) {
363 top = prev[i]; // need to read this first, in case prev==dst
364 left = in[i] + GradientPredictor(left, top, top_left);
365 top_left = top;
366 out[i] = left;
367 }
368 }
369 }
370
371 #undef DO_PREDICT_LINE_VERTICAL
372 #undef PREDICT_LINE_ONE_PASS
373 #undef DO_PREDICT_LINE
374 #undef SANITY_CHECK
375
376 //------------------------------------------------------------------------------
377 // Entry point
378
379 extern void VP8FiltersInitMIPSdspR2(void);
380
VP8FiltersInitMIPSdspR2(void)381 WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitMIPSdspR2(void) {
382 WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter;
383 WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter;
384 WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter;
385
386 WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter;
387 WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter;
388 WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter;
389 }
390
391 #else // !WEBP_USE_MIPS_DSP_R2
392
393 WEBP_DSP_INIT_STUB(VP8FiltersInitMIPSdspR2)
394
395 #endif // WEBP_USE_MIPS_DSP_R2
396