Home | History | Annotate | Download | only in dsp
      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 (at) imgtec.com)
     13 //            Djordje Pesut (djordje.pesut (at) 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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