go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

filters_neon.c (11048B)


      1 // Copyright 2017 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 // NEON variant of alpha filters
     11 //
     12 // Author: Skal (pascal.massimino@gmail.com)
     13 
     14 #include "src/dsp/dsp.h"
     15 
     16 #if defined(WEBP_USE_NEON)
     17 
     18 #include <assert.h>
     19 #include "src/dsp/neon.h"
     20 
     21 //------------------------------------------------------------------------------
     22 // Helpful macros.
     23 
     24 #define DCHECK(in, out)                                                        \
     25   do {                                                                         \
     26     assert((in) != NULL);                                                      \
     27     assert((out) != NULL);                                                     \
     28     assert((in) != (out));                                                     \
     29     assert(width > 0);                                                         \
     30     assert(height > 0);                                                        \
     31     assert(stride >= width);                                                   \
     32   } while (0)
     33 
     34 // load eight u8 and widen to s16
     35 #define U8_TO_S16(A) vreinterpretq_s16_u16(vmovl_u8(A))
     36 #define LOAD_U8_TO_S16(A) U8_TO_S16(vld1_u8(A))
     37 
     38 // shift left or right by N byte, inserting zeros
     39 #define SHIFT_RIGHT_N_Q(A, N) vextq_u8((A), zero, (N))
     40 #define SHIFT_LEFT_N_Q(A, N) vextq_u8(zero, (A), (16 - (N)) % 16)
     41 
     42 // rotate left by N bytes
     43 #define ROTATE_LEFT_N(A, N)   vext_u8((A), (A), (N))
     44 // rotate right by N bytes
     45 #define ROTATE_RIGHT_N(A, N)   vext_u8((A), (A), (8 - (N)) % 8)
     46 
     47 static void PredictLine_NEON(const uint8_t* src, const uint8_t* pred,
     48                              uint8_t* WEBP_RESTRICT dst, int length) {
     49   int i;
     50   assert(length >= 0);
     51   for (i = 0; i + 16 <= length; i += 16) {
     52     const uint8x16_t A = vld1q_u8(&src[i]);
     53     const uint8x16_t B = vld1q_u8(&pred[i]);
     54     const uint8x16_t C = vsubq_u8(A, B);
     55     vst1q_u8(&dst[i], C);
     56   }
     57   for (; i < length; ++i) dst[i] = src[i] - pred[i];
     58 }
     59 
     60 // Special case for left-based prediction (when preds==dst-1 or preds==src-1).
     61 static void PredictLineLeft_NEON(const uint8_t* WEBP_RESTRICT src,
     62                                  uint8_t* WEBP_RESTRICT dst, int length) {
     63   PredictLine_NEON(src, src - 1, dst, length);
     64 }
     65 
     66 //------------------------------------------------------------------------------
     67 // Horizontal filter.
     68 
     69 static WEBP_INLINE void DoHorizontalFilter_NEON(
     70     const uint8_t* WEBP_RESTRICT in, int width, int height, int stride,
     71     uint8_t* WEBP_RESTRICT out) {
     72   int row;
     73   DCHECK(in, out);
     74 
     75   // Leftmost pixel is the same as input for topmost scanline.
     76   out[0] = in[0];
     77   PredictLineLeft_NEON(in + 1, out + 1, width - 1);
     78   in += stride;
     79   out += stride;
     80 
     81   // Filter line-by-line.
     82   for (row = 1; row < height; ++row) {
     83     // Leftmost pixel is predicted from above.
     84     out[0] = in[0] - in[-stride];
     85     PredictLineLeft_NEON(in + 1, out + 1, width - 1);
     86     in += stride;
     87     out += stride;
     88   }
     89 }
     90 
     91 static void HorizontalFilter_NEON(const uint8_t* WEBP_RESTRICT data,
     92                                   int width, int height, int stride,
     93                                   uint8_t* WEBP_RESTRICT filtered_data) {
     94   DoHorizontalFilter_NEON(data, width, height, stride, filtered_data);
     95 }
     96 
     97 //------------------------------------------------------------------------------
     98 // Vertical filter.
     99 
    100 static WEBP_INLINE void DoVerticalFilter_NEON(const uint8_t* WEBP_RESTRICT in,
    101                                               int width, int height, int stride,
    102                                               uint8_t* WEBP_RESTRICT out) {
    103   int row;
    104   DCHECK(in, out);
    105 
    106   // Very first top-left pixel is copied.
    107   out[0] = in[0];
    108   // Rest of top scan-line is left-predicted.
    109   PredictLineLeft_NEON(in + 1, out + 1, width - 1);
    110   in += stride;
    111   out += stride;
    112 
    113   // Filter line-by-line.
    114   for (row = 1; row < height; ++row) {
    115     PredictLine_NEON(in, in - stride, out, width);
    116     in += stride;
    117     out += stride;
    118   }
    119 }
    120 
    121 static void VerticalFilter_NEON(const uint8_t* WEBP_RESTRICT data,
    122                                 int width, int height, int stride,
    123                                 uint8_t* WEBP_RESTRICT filtered_data) {
    124   DoVerticalFilter_NEON(data, width, height, stride, filtered_data);
    125 }
    126 
    127 //------------------------------------------------------------------------------
    128 // Gradient filter.
    129 
    130 static WEBP_INLINE int GradientPredictor_C(uint8_t a, uint8_t b, uint8_t c) {
    131   const int g = a + b - c;
    132   return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255;  // clip to 8bit
    133 }
    134 
    135 static void GradientPredictDirect_NEON(const uint8_t* const row,
    136                                        const uint8_t* const top,
    137                                        uint8_t* WEBP_RESTRICT const out,
    138                                        int length) {
    139   int i;
    140   for (i = 0; i + 8 <= length; i += 8) {
    141     const uint8x8_t A = vld1_u8(&row[i - 1]);
    142     const uint8x8_t B = vld1_u8(&top[i + 0]);
    143     const int16x8_t C = vreinterpretq_s16_u16(vaddl_u8(A, B));
    144     const int16x8_t D = LOAD_U8_TO_S16(&top[i - 1]);
    145     const uint8x8_t E = vqmovun_s16(vsubq_s16(C, D));
    146     const uint8x8_t F = vld1_u8(&row[i + 0]);
    147     vst1_u8(&out[i], vsub_u8(F, E));
    148   }
    149   for (; i < length; ++i) {
    150     out[i] = row[i] - GradientPredictor_C(row[i - 1], top[i], top[i - 1]);
    151   }
    152 }
    153 
    154 static WEBP_INLINE void DoGradientFilter_NEON(const uint8_t* WEBP_RESTRICT in,
    155                                               int width, int height, int stride,
    156                                               uint8_t* WEBP_RESTRICT out) {
    157   int row;
    158   DCHECK(in, out);
    159 
    160   // left prediction for top scan-line
    161   out[0] = in[0];
    162   PredictLineLeft_NEON(in + 1, out + 1, width - 1);
    163   in += stride;
    164   out += stride;
    165 
    166   // Filter line-by-line.
    167   for (row = 1; row < height; ++row) {
    168     out[0] = in[0] - in[-stride];
    169     GradientPredictDirect_NEON(in + 1, in + 1 - stride, out + 1, width - 1);
    170     in += stride;
    171     out += stride;
    172   }
    173 }
    174 
    175 static void GradientFilter_NEON(const uint8_t* WEBP_RESTRICT data,
    176                                 int width, int height, int stride,
    177                                 uint8_t* WEBP_RESTRICT filtered_data) {
    178   DoGradientFilter_NEON(data, width, height, stride, filtered_data);
    179 }
    180 
    181 #undef DCHECK
    182 
    183 //------------------------------------------------------------------------------
    184 // Inverse transforms
    185 
    186 static void HorizontalUnfilter_NEON(const uint8_t* prev, const uint8_t* in,
    187                                     uint8_t* out, int width) {
    188   int i;
    189   const uint8x16_t zero = vdupq_n_u8(0);
    190   uint8x16_t last;
    191   out[0] = in[0] + (prev == NULL ? 0 : prev[0]);
    192   if (width <= 1) return;
    193   last = vsetq_lane_u8(out[0], zero, 0);
    194   for (i = 1; i + 16 <= width; i += 16) {
    195     const uint8x16_t A0 = vld1q_u8(&in[i]);
    196     const uint8x16_t A1 = vaddq_u8(A0, last);
    197     const uint8x16_t A2 = SHIFT_LEFT_N_Q(A1, 1);
    198     const uint8x16_t A3 = vaddq_u8(A1, A2);
    199     const uint8x16_t A4 = SHIFT_LEFT_N_Q(A3, 2);
    200     const uint8x16_t A5 = vaddq_u8(A3, A4);
    201     const uint8x16_t A6 = SHIFT_LEFT_N_Q(A5, 4);
    202     const uint8x16_t A7 = vaddq_u8(A5, A6);
    203     const uint8x16_t A8 = SHIFT_LEFT_N_Q(A7, 8);
    204     const uint8x16_t A9 = vaddq_u8(A7, A8);
    205     vst1q_u8(&out[i], A9);
    206     last = SHIFT_RIGHT_N_Q(A9, 15);
    207   }
    208   for (; i < width; ++i) out[i] = in[i] + out[i - 1];
    209 }
    210 
    211 static void VerticalUnfilter_NEON(const uint8_t* prev, const uint8_t* in,
    212                                   uint8_t* out, int width) {
    213   if (prev == NULL) {
    214     HorizontalUnfilter_NEON(NULL, in, out, width);
    215   } else {
    216     int i;
    217     assert(width >= 0);
    218     for (i = 0; i + 16 <= width; i += 16) {
    219       const uint8x16_t A = vld1q_u8(&in[i]);
    220       const uint8x16_t B = vld1q_u8(&prev[i]);
    221       const uint8x16_t C = vaddq_u8(A, B);
    222       vst1q_u8(&out[i], C);
    223     }
    224     for (; i < width; ++i) out[i] = in[i] + prev[i];
    225   }
    226 }
    227 
    228 // GradientUnfilter_NEON is correct but slower than the C-version,
    229 // at least on ARM64. For armv7, it's a wash.
    230 // So best is to disable it for now, but keep the idea around...
    231 #if !defined(USE_GRADIENT_UNFILTER)
    232 #define USE_GRADIENT_UNFILTER 0   // ALTERNATE_CODE
    233 #endif
    234 
    235 #if (USE_GRADIENT_UNFILTER == 1)
    236 #define GRAD_PROCESS_LANE(L)  do {                                             \
    237   const uint8x8_t tmp1 = ROTATE_RIGHT_N(pred, 1);  /* rotate predictor in */   \
    238   const int16x8_t tmp2 = vaddq_s16(BC, U8_TO_S16(tmp1));                       \
    239   const uint8x8_t delta = vqmovun_s16(tmp2);                                   \
    240   pred = vadd_u8(D, delta);                                                    \
    241   out = vext_u8(out, ROTATE_LEFT_N(pred, (L)), 1);                             \
    242 } while (0)
    243 
    244 static void GradientPredictInverse_NEON(const uint8_t* const in,
    245                                         const uint8_t* const top,
    246                                         uint8_t* const row, int length) {
    247   if (length > 0) {
    248     int i;
    249     uint8x8_t pred = vdup_n_u8(row[-1]);   // left sample
    250     uint8x8_t out = vdup_n_u8(0);
    251     for (i = 0; i + 8 <= length; i += 8) {
    252       const int16x8_t B = LOAD_U8_TO_S16(&top[i + 0]);
    253       const int16x8_t C = LOAD_U8_TO_S16(&top[i - 1]);
    254       const int16x8_t BC = vsubq_s16(B, C);  // unclipped gradient basis B - C
    255       const uint8x8_t D = vld1_u8(&in[i]);   // base input
    256       GRAD_PROCESS_LANE(0);
    257       GRAD_PROCESS_LANE(1);
    258       GRAD_PROCESS_LANE(2);
    259       GRAD_PROCESS_LANE(3);
    260       GRAD_PROCESS_LANE(4);
    261       GRAD_PROCESS_LANE(5);
    262       GRAD_PROCESS_LANE(6);
    263       GRAD_PROCESS_LANE(7);
    264       vst1_u8(&row[i], out);
    265     }
    266     for (; i < length; ++i) {
    267       row[i] = in[i] + GradientPredictor_C(row[i - 1], top[i], top[i - 1]);
    268     }
    269   }
    270 }
    271 #undef GRAD_PROCESS_LANE
    272 
    273 static void GradientUnfilter_NEON(const uint8_t* prev, const uint8_t* in,
    274                                   uint8_t* out, int width) {
    275   if (prev == NULL) {
    276     HorizontalUnfilter_NEON(NULL, in, out, width);
    277   } else {
    278     out[0] = in[0] + prev[0];  // predict from above
    279     GradientPredictInverse_NEON(in + 1, prev + 1, out + 1, width - 1);
    280   }
    281 }
    282 
    283 #endif   // USE_GRADIENT_UNFILTER
    284 
    285 //------------------------------------------------------------------------------
    286 // Entry point
    287 
    288 extern void VP8FiltersInitNEON(void);
    289 
    290 WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitNEON(void) {
    291   WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_NEON;
    292   WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_NEON;
    293 #if (USE_GRADIENT_UNFILTER == 1)
    294   WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_NEON;
    295 #endif
    296 
    297   WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_NEON;
    298   WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_NEON;
    299   WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_NEON;
    300 }
    301 
    302 #else  // !WEBP_USE_NEON
    303 
    304 WEBP_DSP_INIT_STUB(VP8FiltersInitNEON)
    305 
    306 #endif  // WEBP_USE_NEON