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 "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 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_MIPSdspR2(const uint8_t * src,uint8_t * dst,int length)104 static WEBP_INLINE void PredictLine_MIPSdspR2(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_MIPSdspR2(const uint8_t * in,int width,int height,int stride,int row,int num_rows,uint8_t * out)195 static WEBP_INLINE void DoHorizontalFilter_MIPSdspR2(const uint8_t* in,
196 int width, int height,
197 int stride,
198 int row, int num_rows,
199 uint8_t* out) {
200 const uint8_t* preds;
201 const size_t start_offset = row * stride;
202 const int last_row = row + num_rows;
203 SANITY_CHECK(in, out);
204 in += start_offset;
205 out += start_offset;
206 preds = in;
207
208 if (row == 0) {
209 // Leftmost pixel is the same as input for topmost scanline.
210 out[0] = in[0];
211 PredictLine_MIPSdspR2(in + 1, out + 1, width - 1);
212 row = 1;
213 preds += stride;
214 in += stride;
215 out += stride;
216 }
217
218 // Filter line-by-line.
219 FILTER_LINE_BY_LINE;
220 }
221 #undef FILTER_LINE_BY_LINE
222
HorizontalFilter_MIPSdspR2(const uint8_t * data,int width,int height,int stride,uint8_t * filtered_data)223 static void HorizontalFilter_MIPSdspR2(const uint8_t* data,
224 int width, int height,
225 int stride, uint8_t* filtered_data) {
226 DoHorizontalFilter_MIPSdspR2(data, width, height, stride, 0, height,
227 filtered_data);
228 }
229
230 //------------------------------------------------------------------------------
231 // Vertical filter.
232
233 #define FILTER_LINE_BY_LINE do { \
234 while (row < last_row) { \
235 DO_PREDICT_LINE_VERTICAL(in, preds, out, width, 0); \
236 ++row; \
237 preds += stride; \
238 in += stride; \
239 out += stride; \
240 } \
241 } while (0)
242
DoVerticalFilter_MIPSdspR2(const uint8_t * in,int width,int height,int stride,int row,int num_rows,uint8_t * out)243 static WEBP_INLINE void DoVerticalFilter_MIPSdspR2(const uint8_t* in,
244 int width, int height,
245 int stride,
246 int row, int num_rows,
247 uint8_t* out) {
248 const uint8_t* preds;
249 const size_t start_offset = row * stride;
250 const int last_row = row + num_rows;
251 SANITY_CHECK(in, out);
252 in += start_offset;
253 out += start_offset;
254 preds = in;
255
256 if (row == 0) {
257 // Very first top-left pixel is copied.
258 out[0] = in[0];
259 // Rest of top scan-line is left-predicted.
260 PredictLine_MIPSdspR2(in + 1, out + 1, width - 1);
261 row = 1;
262 in += stride;
263 out += stride;
264 } else {
265 // We are starting from in-between. Make sure 'preds' points to prev row.
266 preds -= stride;
267 }
268
269 // Filter line-by-line.
270 FILTER_LINE_BY_LINE;
271 }
272 #undef FILTER_LINE_BY_LINE
273
VerticalFilter_MIPSdspR2(const uint8_t * data,int width,int height,int stride,uint8_t * filtered_data)274 static void VerticalFilter_MIPSdspR2(const uint8_t* data, int width, int height,
275 int stride, uint8_t* filtered_data) {
276 DoVerticalFilter_MIPSdspR2(data, width, height, stride, 0, height,
277 filtered_data);
278 }
279
280 //------------------------------------------------------------------------------
281 // Gradient filter.
282
GradientPredictor_MIPSdspR2(uint8_t a,uint8_t b,uint8_t c)283 static int GradientPredictor_MIPSdspR2(uint8_t a, uint8_t b, uint8_t c) {
284 int temp0;
285 __asm__ volatile (
286 "addu %[temp0], %[a], %[b] \n\t"
287 "subu %[temp0], %[temp0], %[c] \n\t"
288 "shll_s.w %[temp0], %[temp0], 23 \n\t"
289 "precrqu_s.qb.ph %[temp0], %[temp0], $zero \n\t"
290 "srl %[temp0], %[temp0], 24 \n\t"
291 : [temp0]"=&r"(temp0)
292 : [a]"r"(a),[b]"r"(b),[c]"r"(c)
293 );
294 return temp0;
295 }
296
297 #define FILTER_LINE_BY_LINE(PREDS, OPERATION) do { \
298 while (row < last_row) { \
299 int w; \
300 PREDICT_LINE_ONE_PASS(in, PREDS - stride, out); \
301 for (w = 1; w < width; ++w) { \
302 const int pred = GradientPredictor_MIPSdspR2(PREDS[w - 1], \
303 PREDS[w - stride], \
304 PREDS[w - stride - 1]); \
305 out[w] = in[w] OPERATION pred; \
306 } \
307 ++row; \
308 in += stride; \
309 out += stride; \
310 } \
311 } while (0)
312
DoGradientFilter_MIPSdspR2(const uint8_t * in,int width,int height,int stride,int row,int num_rows,uint8_t * out)313 static void DoGradientFilter_MIPSdspR2(const uint8_t* in,
314 int width, int height, int stride,
315 int row, int num_rows, uint8_t* out) {
316 const uint8_t* preds;
317 const size_t start_offset = row * stride;
318 const int last_row = row + num_rows;
319 SANITY_CHECK(in, out);
320 in += start_offset;
321 out += start_offset;
322 preds = in;
323
324 // left prediction for top scan-line
325 if (row == 0) {
326 out[0] = in[0];
327 PredictLine_MIPSdspR2(in + 1, out + 1, width - 1);
328 row = 1;
329 preds += stride;
330 in += stride;
331 out += stride;
332 }
333
334 // Filter line-by-line.
335 FILTER_LINE_BY_LINE(in, -);
336 }
337 #undef FILTER_LINE_BY_LINE
338
GradientFilter_MIPSdspR2(const uint8_t * data,int width,int height,int stride,uint8_t * filtered_data)339 static void GradientFilter_MIPSdspR2(const uint8_t* data, int width, int height,
340 int stride, uint8_t* filtered_data) {
341 DoGradientFilter_MIPSdspR2(data, width, height, stride, 0, height,
342 filtered_data);
343 }
344
345 //------------------------------------------------------------------------------
346
HorizontalUnfilter_MIPSdspR2(const uint8_t * prev,const uint8_t * in,uint8_t * out,int width)347 static void HorizontalUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in,
348 uint8_t* out, int width) {
349 out[0] = in[0] + (prev == NULL ? 0 : prev[0]);
350 DO_PREDICT_LINE(in + 1, out + 1, width - 1, 1);
351 }
352
VerticalUnfilter_MIPSdspR2(const uint8_t * prev,const uint8_t * in,uint8_t * out,int width)353 static void VerticalUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in,
354 uint8_t* out, int width) {
355 if (prev == NULL) {
356 HorizontalUnfilter_MIPSdspR2(NULL, in, out, width);
357 } else {
358 DO_PREDICT_LINE_VERTICAL(in, prev, out, width, 1);
359 }
360 }
361
GradientUnfilter_MIPSdspR2(const uint8_t * prev,const uint8_t * in,uint8_t * out,int width)362 static void GradientUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in,
363 uint8_t* out, int width) {
364 if (prev == NULL) {
365 HorizontalUnfilter_MIPSdspR2(NULL, in, out, width);
366 } else {
367 uint8_t top = prev[0], top_left = top, left = top;
368 int i;
369 for (i = 0; i < width; ++i) {
370 top = prev[i]; // need to read this first, in case prev==dst
371 left = in[i] + GradientPredictor_MIPSdspR2(left, top, top_left);
372 top_left = top;
373 out[i] = left;
374 }
375 }
376 }
377
378 #undef DO_PREDICT_LINE_VERTICAL
379 #undef PREDICT_LINE_ONE_PASS
380 #undef DO_PREDICT_LINE
381 #undef SANITY_CHECK
382
383 //------------------------------------------------------------------------------
384 // Entry point
385
386 extern void VP8FiltersInitMIPSdspR2(void);
387
VP8FiltersInitMIPSdspR2(void)388 WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitMIPSdspR2(void) {
389 WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_MIPSdspR2;
390 WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_MIPSdspR2;
391 WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_MIPSdspR2;
392
393 WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_MIPSdspR2;
394 WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_MIPSdspR2;
395 WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_MIPSdspR2;
396 }
397
398 #else // !WEBP_USE_MIPS_DSP_R2
399
400 WEBP_DSP_INIT_STUB(VP8FiltersInitMIPSdspR2)
401
402 #endif // WEBP_USE_MIPS_DSP_R2
403