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