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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |