i3lock-color

The world's most popular non-default computer lockscreen.
Index Commits Files Refs README LICENSE
blur_simd.c (3280B)
   1 /*
   2  * vim:ts=4:sw=4:expandtab
   3  *
   4  * © 2016 Sebastian Frysztak
   5  *
   6  * See LICENSE for licensing information
   7  *
   8  */
   9 
  10 
  11 // number of xmm registers needed to store input pixels for given kernel size
  12 #ifdef __SSE2__
  13 #include "blur.h"
  14 #define REGISTERS_CNT (KERNEL_SIZE + 4/2) / 4
  15 #include <xmmintrin.h>
  16 void blur_impl_horizontal_pass_sse2(uint32_t *src, uint32_t *dst, int width, int height) {
  17     uint32_t* o_src = src;
  18     for (int row = 0; row < height; row++) {
  19         for (int column = 0; column < width; column++, src++) {
  20             __m128i rgbaIn[REGISTERS_CNT];
  21 
  22             // handle borders
  23             int leftBorder = column < HALF_KERNEL;
  24             int rightBorder = column > width - HALF_KERNEL;
  25             uint32_t _rgbaIn[KERNEL_SIZE + 1] __attribute__((aligned(16)));
  26             int i = 0;
  27             if (leftBorder) {
  28                 // for kernel size 7x7 and column == 0, we have:
  29                 // x x x P0 P1 P2 P3
  30                 // first loop mirrors P{0..3} to fill x's,
  31                 // second one loads P{0..3}
  32                 for (; i < HALF_KERNEL - column; i++)
  33                     _rgbaIn[i] = *(src + (HALF_KERNEL - i));
  34                 for (; i < KERNEL_SIZE; i++)
  35                     _rgbaIn[i] = *(src - (HALF_KERNEL - i));
  36 
  37                 for (int k = 0; k < REGISTERS_CNT; k++)
  38                     rgbaIn[k] = _mm_load_si128((__m128i*)(_rgbaIn + 4*k));
  39             } else if (rightBorder) {
  40                 for (; i < width - column; i++)
  41                     _rgbaIn[i] = *(src + i);
  42                 for (int k = 0; i < KERNEL_SIZE; i++, k++)
  43                     _rgbaIn[i] = *(src - k);
  44 
  45                 for (int k = 0; k < REGISTERS_CNT; k++)
  46                     rgbaIn[k] = _mm_load_si128((__m128i*)(_rgbaIn + 4*k));
  47             } else {
  48                 for (int k = 0; k < REGISTERS_CNT; k++) {
  49                     if ((uintptr_t) (((__m128i*) src + 4*k - HALF_KERNEL) + 1)
  50                             > (uintptr_t) (o_src + (height * width)))
  51                         break;
  52                     rgbaIn[k] = _mm_loadu_si128((__m128i*)(src + 4*k - HALF_KERNEL));
  53                 }
  54             }
  55 
  56             __m128i zero = _mm_setzero_si128();
  57             __m128i acc = _mm_setzero_si128();
  58 
  59             acc = _mm_add_epi16(acc, _mm_unpacklo_epi8(rgbaIn[0], zero));
  60             acc = _mm_add_epi16(acc, _mm_unpackhi_epi8(rgbaIn[0], zero));
  61             acc = _mm_add_epi16(acc, _mm_unpacklo_epi8(rgbaIn[1], zero));
  62 
  63             // kernel size equals to 7, but we can only load multiples of 4 pixels
  64             // we have to set 8th pixel to zero
  65             acc = _mm_add_epi16(acc, _mm_andnot_si128(_mm_set_epi32(0xFFFFFFFF, 0xFFFFFFFF, 0, 0),
  66                                                       _mm_unpackhi_epi8(rgbaIn[1], zero)));
  67             acc = _mm_add_epi32(_mm_unpacklo_epi16(acc, zero),
  68                                 _mm_unpackhi_epi16(acc, zero));
  69 
  70             // multiplication is significantly faster than division
  71             acc = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(acc),
  72                                              _mm_set1_ps(1.0/KERNEL_SIZE)));
  73 
  74             *(dst + height * column + row) =
  75                 _mm_cvtsi128_si32(_mm_packus_epi16(_mm_packs_epi32(acc, zero), zero));
  76         }
  77     }
  78 }
  79 #endif