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