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