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