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