LCOV - code coverage report
Current view: top level - src - image.c (source / functions) Coverage Total Hit
Test: lierre Coverage Report Lines: 82.3 % 192 158
Test Date: 2026-03-06 08:40:14 Functions: 100.0 % 10 10
Legend: Lines: hit not hit

            Line data    Source code
       1              : /*
       2              :  * liblierre - image.c
       3              :  *
       4              :  * This file is part of liblierre.
       5              :  *
       6              :  * Author: Go Kudo <zeriyoshi@gmail.com>
       7              :  * SPDX-License-Identifier: MIT
       8              :  */
       9              : 
      10              : #include "internal/image.h"
      11              : 
      12              : #include <string.h>
      13              : 
      14              : #include "internal/memory.h"
      15              : 
      16              : #if LIERRE_USE_SIMD
      17              : #include "internal/simd.h"
      18              : #endif
      19              : 
      20              : #define LIERRE_PIXEL_VALUE_MIN     0
      21              : #define LIERRE_PIXEL_VALUE_MAX     255
      22              : #define LIERRE_MIN_QR_SIZE         21
      23              : #define LIERRE_CONTRAST_FACTOR     128
      24              : #define LIERRE_CONTRAST_DIVISOR    100
      25              : #define LIERRE_FILTER_KERNEL_SIZE  3
      26              : #define LIERRE_FILTER_KERNEL_ELEMS 9
      27              : #define LIERRE_SHARPEN_CENTER_COEF 5
      28              : 
      29              : typedef struct {
      30              :     const uint8_t *src;
      31              :     uint8_t *dst;
      32              :     size_t src_width;
      33              :     size_t dst_width;
      34              :     size_t start_row;
      35              :     size_t end_row;
      36              : } lierre_image_mt_minimize_ctx_t;
      37              : 
      38              : typedef struct {
      39              :     uint8_t *image;
      40              :     uint8_t *temp;
      41              :     size_t width;
      42              :     size_t height;
      43              :     size_t start_row;
      44              :     size_t end_row;
      45              : } lierre_image_mt_filter_ctx_t;
      46              : 
      47              : #define LIERRE_IMAGE_MT_MAX_THREADS          64
      48              : #define LIERRE_IMAGE_MINIMIZE_MAX_ITERATIONS 4
      49              : 
      50              : static inline void lierre_minimize_row(const uint8_t *src_row0, const uint8_t *src_row1, uint8_t *dst, size_t dst_width)
      51              : {
      52              : #if LIERRE_USE_SIMD && defined(LIERRE_SIMD_AVX2)
      53              :     __m256i data0, data1, row0_even, row0_odd, row1_even, row1_odd, sum, avg;
      54              :     __m128i packed;
      55              :     size_t x, simd_count, sx;
      56              :     int32_t sum_scalar;
      57              : 
      58              :     simd_count = dst_width & ~15ULL;
      59              : 
      60              :     for (x = 0; x < simd_count; x += 16) {
      61              :         data0 = _mm256_loadu_si256((const __m256i *)(src_row0 + x * 2));
      62              :         data1 = _mm256_loadu_si256((const __m256i *)(src_row1 + x * 2));
      63              : 
      64              :         row0_even = _mm256_and_si256(data0, _mm256_set1_epi16(0x00FF));
      65              :         row0_odd = _mm256_srli_epi16(data0, 8);
      66              :         row1_even = _mm256_and_si256(data1, _mm256_set1_epi16(0x00FF));
      67              :         row1_odd = _mm256_srli_epi16(data1, 8);
      68              : 
      69              :         sum = _mm256_add_epi16(row0_even, row0_odd);
      70              :         sum = _mm256_add_epi16(sum, row1_even);
      71              :         sum = _mm256_add_epi16(sum, row1_odd);
      72              : 
      73              :         avg = _mm256_srli_epi16(sum, 2);
      74              : 
      75              :         packed = _mm_packus_epi16(_mm256_castsi256_si128(avg), _mm256_extracti128_si256(avg, 1));
      76              :         _mm_storeu_si128((__m128i *)(dst + x), packed);
      77              :     }
      78              : 
      79              :     for (x = simd_count; x < dst_width; x++) {
      80              :         sx = x * 2;
      81              :         sum_scalar =
      82              :             (int32_t)src_row0[sx] + (int32_t)src_row0[sx + 1] + (int32_t)src_row1[sx] + (int32_t)src_row1[sx + 1];
      83              :         dst[x] = (uint8_t)(sum_scalar >> 2);
      84              :     }
      85              : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_NEON)
      86              :     uint8x16x2_t row0, row1;
      87              :     uint16x8_t sum_lo, sum_hi;
      88              :     uint8x8_t avg_lo, avg_hi;
      89              :     uint8x16_t result;
      90              :     size_t x, simd_count, sx;
      91              :     int32_t sum_scalar;
      92              : 
      93              :     simd_count = dst_width & ~15ULL;
      94              : 
      95              :     for (x = 0; x < simd_count; x += 16) {
      96              :         row0 = vld2q_u8(src_row0 + x * 2);
      97              :         row1 = vld2q_u8(src_row1 + x * 2);
      98              : 
      99              :         sum_lo = vaddl_u8(vget_low_u8(row0.val[0]), vget_low_u8(row0.val[1]));
     100              :         sum_lo = vaddw_u8(sum_lo, vget_low_u8(row1.val[0]));
     101              :         sum_lo = vaddw_u8(sum_lo, vget_low_u8(row1.val[1]));
     102              : 
     103              :         sum_hi = vaddl_u8(vget_high_u8(row0.val[0]), vget_high_u8(row0.val[1]));
     104              :         sum_hi = vaddw_u8(sum_hi, vget_high_u8(row1.val[0]));
     105              :         sum_hi = vaddw_u8(sum_hi, vget_high_u8(row1.val[1]));
     106              : 
     107              :         avg_lo = vshrn_n_u16(sum_lo, 2);
     108              :         avg_hi = vshrn_n_u16(sum_hi, 2);
     109              :         result = vcombine_u8(avg_lo, avg_hi);
     110              : 
     111              :         vst1q_u8(dst + x, result);
     112              :     }
     113              : 
     114              :     for (x = simd_count; x < dst_width; x++) {
     115              :         sx = x * 2;
     116              :         sum_scalar =
     117              :             (int32_t)src_row0[sx] + (int32_t)src_row0[sx + 1] + (int32_t)src_row1[sx] + (int32_t)src_row1[sx + 1];
     118              :         dst[x] = (uint8_t)(sum_scalar >> 2);
     119              :     }
     120              : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_WASM)
     121              :     v128_t data0, data1, row0_even, row0_odd, row1_even, row1_odd, sum, avg, mask = wasm_i16x8_splat(0x00FF);
     122              :     size_t x, simd_count = dst_width & ~15ULL;
     123              : 
     124              :     for (x = 0; x < simd_count; x += 16) {
     125              :         data0 = wasm_v128_load(src_row0 + x * 2);
     126              :         data1 = wasm_v128_load(src_row1 + x * 2);
     127              : 
     128              :         row0_even = wasm_v128_and(data0, mask);
     129              :         row0_odd = wasm_u16x8_shr(data0, 8);
     130              :         row1_even = wasm_v128_and(data1, mask);
     131              :         row1_odd = wasm_u16x8_shr(data1, 8);
     132              : 
     133              :         sum = wasm_i16x8_add(row0_even, row0_odd);
     134              :         sum = wasm_i16x8_add(sum, row1_even);
     135              :         sum = wasm_i16x8_add(sum, row1_odd);
     136              : 
     137              :         avg = wasm_u16x8_shr(sum, 2);
     138              : 
     139              :         data0 = wasm_v128_load(src_row0 + x * 2 + 16);
     140              :         data1 = wasm_v128_load(src_row1 + x * 2 + 16);
     141              : 
     142              :         row0_even = wasm_v128_and(data0, mask);
     143              :         row0_odd = wasm_u16x8_shr(data0, 8);
     144              :         row1_even = wasm_v128_and(data1, mask);
     145              :         row1_odd = wasm_u16x8_shr(data1, 8);
     146              : 
     147              :         sum = wasm_i16x8_add(row0_even, row0_odd);
     148              :         sum = wasm_i16x8_add(sum, row1_even);
     149              :         sum = wasm_i16x8_add(sum, row1_odd);
     150              :         sum = wasm_u16x8_shr(sum, 2);
     151              : 
     152              :         wasm_v128_store(dst + x, wasm_u8x16_narrow_i16x8(avg, sum));
     153              :     }
     154              : 
     155              :     {
     156              :         size_t sx;
     157              :         int32_t sum_scalar;
     158              :         for (x = simd_count; x < dst_width; x++) {
     159              :             sx = x * 2;
     160              :             sum_scalar =
     161              :                 (int32_t)src_row0[sx] + (int32_t)src_row0[sx + 1] + (int32_t)src_row1[sx] + (int32_t)src_row1[sx + 1];
     162              :             dst[x] = (uint8_t)(sum_scalar >> 2);
     163              :         }
     164              :     }
     165              : #else
     166              :     size_t x, sx;
     167              :     int32_t sum_scalar;
     168              : 
     169              :     for (x = 0; x < dst_width; x++) {
     170              :         sx = x * 2;
     171              :         sum_scalar =
     172              :             (int32_t)src_row0[sx] + (int32_t)src_row0[sx + 1] + (int32_t)src_row1[sx] + (int32_t)src_row1[sx + 1];
     173              :         dst[x] = (uint8_t)(sum_scalar >> 2);
     174              :     }
     175              : #endif
     176              : }
     177              : 
     178            1 : static inline void lierre_find_minmax(const uint8_t *image, size_t total, uint8_t *out_min, uint8_t *out_max)
     179              : {
     180              : #if LIERRE_USE_SIMD && defined(LIERRE_SIMD_AVX2)
     181            1 :     __m256i min_vec = _mm256_set1_epi8((char)255), max_vec = _mm256_setzero_si256(), data;
     182              :     __m128i min128, max128;
     183              :     uint8_t min_arr[32], max_arr[32], min_val, max_val;
     184            1 :     size_t i, simd_count = total & ~31ULL;
     185              : 
     186         1251 :     for (i = 0; i < simd_count; i += 32) {
     187         2500 :         data = _mm256_loadu_si256((const __m256i *)(image + i));
     188         1250 :         min_vec = _mm256_min_epu8(min_vec, data);
     189         1250 :         max_vec = _mm256_max_epu8(max_vec, data);
     190              :     }
     191              : 
     192            2 :     min128 = _mm_min_epu8(_mm256_castsi256_si128(min_vec), _mm256_extracti128_si256(min_vec, 1));
     193            3 :     max128 = _mm_max_epu8(_mm256_castsi256_si128(max_vec), _mm256_extracti128_si256(max_vec, 1));
     194              : 
     195              :     _mm_storeu_si128((__m128i *)min_arr, min128);
     196              :     _mm_storeu_si128((__m128i *)max_arr, max128);
     197              : 
     198            1 :     min_val = 255;
     199            1 :     max_val = 0;
     200           17 :     for (i = 0; i < 16; i++) {
     201           16 :         if (min_arr[i] < min_val) {
     202            1 :             min_val = min_arr[i];
     203              :         }
     204           16 :         if (max_arr[i] > max_val) {
     205            1 :             max_val = max_arr[i];
     206              :         }
     207              :     }
     208              : 
     209            1 :     for (i = simd_count; i < total; i++) {
     210            0 :         if (image[i] < min_val) {
     211            0 :             min_val = image[i];
     212              :         }
     213            0 :         if (image[i] > max_val) {
     214            0 :             max_val = image[i];
     215              :         }
     216              :     }
     217              : 
     218            1 :     *out_min = min_val;
     219            1 :     *out_max = max_val;
     220              : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_NEON)
     221              :     uint8x16_t min_vec, max_vec, data;
     222              :     uint8_t min_val, max_val;
     223              :     size_t i, simd_count;
     224              : 
     225              :     min_vec = vdupq_n_u8(255);
     226              :     max_vec = vdupq_n_u8(0);
     227              : 
     228              :     simd_count = total & ~15ULL;
     229              : 
     230              :     for (i = 0; i < simd_count; i += 16) {
     231              :         data = vld1q_u8(image + i);
     232              :         min_vec = vminq_u8(min_vec, data);
     233              :         max_vec = vmaxq_u8(max_vec, data);
     234              :     }
     235              : 
     236              :     min_val = vminvq_u8(min_vec);
     237              :     max_val = vmaxvq_u8(max_vec);
     238              : 
     239              :     for (i = simd_count; i < total; i++) {
     240              :         if (image[i] < min_val) {
     241              :             min_val = image[i];
     242              :         }
     243              :         if (image[i] > max_val) {
     244              :             max_val = image[i];
     245              :         }
     246              :     }
     247              : 
     248              :     *out_min = min_val;
     249              :     *out_max = max_val;
     250              : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_WASM)
     251              :     v128_t min_vec, max_vec, data;
     252              :     uint8_t min_arr[16], max_arr[16], min_val, max_val;
     253              :     size_t i, simd_count;
     254              : 
     255              :     min_vec = wasm_u8x16_splat(255);
     256              :     max_vec = wasm_u8x16_splat(0);
     257              : 
     258              :     simd_count = total & ~15ULL;
     259              : 
     260              :     for (i = 0; i < simd_count; i += 16) {
     261              :         data = wasm_v128_load(image + i);
     262              :         min_vec = wasm_u8x16_min(min_vec, data);
     263              :         max_vec = wasm_u8x16_max(max_vec, data);
     264              :     }
     265              : 
     266              :     wasm_v128_store(min_arr, min_vec);
     267              :     wasm_v128_store(max_arr, max_vec);
     268              : 
     269              :     min_val = 255;
     270              :     max_val = 0;
     271              :     for (i = 0; i < 16; i++) {
     272              :         if (min_arr[i] < min_val) {
     273              :             min_val = min_arr[i];
     274              :         }
     275              :         if (max_arr[i] > max_val) {
     276              :             max_val = max_arr[i];
     277              :         }
     278              :     }
     279              : 
     280              :     for (i = simd_count; i < total; i++) {
     281              :         if (image[i] < min_val) {
     282              :             min_val = image[i];
     283              :         }
     284              :         if (image[i] > max_val) {
     285              :             max_val = image[i];
     286              :         }
     287              :     }
     288              : 
     289              :     *out_min = min_val;
     290              :     *out_max = max_val;
     291              : #else
     292              :     uint8_t min_val, max_val;
     293              :     size_t i;
     294              : 
     295              :     min_val = 255;
     296              :     max_val = 0;
     297              : 
     298              :     for (i = 0; i < total; i++) {
     299              :         if (image[i] < min_val) {
     300              :             min_val = image[i];
     301              :         }
     302              :         if (image[i] > max_val) {
     303              :             max_val = image[i];
     304              :         }
     305              :     }
     306              : 
     307              :     *out_min = min_val;
     308              :     *out_max = max_val;
     309              : #endif
     310            1 : }
     311              : 
     312            1 : static inline int64_t lierre_sum(const uint8_t *image, size_t total)
     313              : {
     314              : #if LIERRE_USE_SIMD && defined(LIERRE_SIMD_AVX2)
     315              :     __m256i sum_vec, data, zero;
     316              :     int64_t sum;
     317              :     uint64_t partial[4];
     318              :     size_t i, simd_count;
     319              : 
     320            1 :     sum_vec = _mm256_setzero_si256();
     321            1 :     zero = _mm256_setzero_si256();
     322              : 
     323            1 :     simd_count = total & ~31ULL;
     324              : 
     325         1251 :     for (i = 0; i < simd_count; i += 32) {
     326         2500 :         data = _mm256_loadu_si256((const __m256i *)(image + i));
     327         2500 :         sum_vec = _mm256_add_epi64(sum_vec, _mm256_sad_epu8(data, zero));
     328              :     }
     329              : 
     330              :     _mm256_storeu_si256((__m256i *)partial, sum_vec);
     331            1 :     sum = (int64_t)(partial[0] + partial[1] + partial[2] + partial[3]);
     332              : 
     333            1 :     for (i = simd_count; i < total; i++) {
     334            0 :         sum += image[i];
     335              :     }
     336              : 
     337            1 :     return sum;
     338              : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_NEON)
     339              :     uint32x4_t sum_vec;
     340              :     uint8x16_t data;
     341              :     uint16x8_t sum16;
     342              :     int64_t sum;
     343              :     size_t i, simd_count;
     344              : 
     345              :     sum_vec = vdupq_n_u32(0);
     346              : 
     347              :     simd_count = total & ~15ULL;
     348              : 
     349              :     for (i = 0; i < simd_count; i += 16) {
     350              :         data = vld1q_u8(image + i);
     351              :         sum16 = vpaddlq_u8(data);
     352              :         sum_vec = vpadalq_u16(sum_vec, sum16);
     353              :     }
     354              : 
     355              :     sum = (int64_t)(vgetq_lane_u32(sum_vec, 0) + vgetq_lane_u32(sum_vec, 1) + vgetq_lane_u32(sum_vec, 2) +
     356              :                     vgetq_lane_u32(sum_vec, 3));
     357              : 
     358              :     for (i = simd_count; i < total; i++) {
     359              :         sum += image[i];
     360              :     }
     361              : 
     362              :     return sum;
     363              : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_WASM)
     364              :     v128_t sum_vec = wasm_i64x2_splat(0), data, lo, hi, sum16, lo32, hi32, sum32, lo64, hi64;
     365              :     int64_t sum;
     366              :     uint64_t partial[2];
     367              :     size_t i, simd_count = total & ~15ULL;
     368              : 
     369              :     for (i = 0; i < simd_count; i += 16) {
     370              :         data = wasm_v128_load(image + i);
     371              :         lo = wasm_u16x8_extend_low_u8x16(data);
     372              :         hi = wasm_u16x8_extend_high_u8x16(data);
     373              :         sum16 = wasm_i16x8_add(lo, hi);
     374              :         lo32 = wasm_u32x4_extend_low_u16x8(sum16);
     375              :         hi32 = wasm_u32x4_extend_high_u16x8(sum16);
     376              :         sum32 = wasm_i32x4_add(lo32, hi32);
     377              :         lo64 = wasm_u64x2_extend_low_u32x4(sum32);
     378              :         hi64 = wasm_u64x2_extend_high_u32x4(sum32);
     379              :         sum_vec = wasm_i64x2_add(sum_vec, wasm_i64x2_add(lo64, hi64));
     380              :     }
     381              : 
     382              :     wasm_v128_store(partial, sum_vec);
     383              :     sum = (int64_t)(partial[0] + partial[1]);
     384              : 
     385              :     for (i = simd_count; i < total; i++) {
     386              :         sum += image[i];
     387              :     }
     388              : 
     389              :     return sum;
     390              : #else
     391              :     int64_t sum;
     392              :     size_t i;
     393              : 
     394              :     sum = 0;
     395              :     for (i = 0; i < total; i++) {
     396              :         sum += image[i];
     397              :     }
     398              : 
     399              :     return sum;
     400              : #endif
     401              : }
     402              : 
     403              : static inline void *minimize_thread(void *arg)
     404              : {
     405              :     lierre_image_mt_minimize_ctx_t *ctx;
     406              :     size_t y, src_y;
     407              : 
     408              :     ctx = (lierre_image_mt_minimize_ctx_t *)arg;
     409              : 
     410              :     for (y = ctx->start_row; y < ctx->end_row; y++) {
     411              :         src_y = y * 2;
     412              :         lierre_minimize_row(ctx->src + src_y * ctx->src_width, ctx->src + (src_y + 1) * ctx->src_width,
     413              :                             ctx->dst + y * ctx->dst_width, ctx->dst_width);
     414              :     }
     415              : 
     416              :     return NULL;
     417              : }
     418              : 
     419              : static inline uint8_t *apply_minimize_once(const uint8_t *image, size_t width, size_t height, size_t *out_width,
     420              :                                            size_t *out_height)
     421              : {
     422              :     uint8_t *result;
     423              :     size_t new_width, new_height, y, src_y;
     424              : 
     425              :     new_width = width >> 1;
     426              :     new_height = height >> 1;
     427              : 
     428              :     if (new_width < 21 || new_height < 21) {
     429              :         *out_width = width;
     430              :         *out_height = height;
     431              :         return NULL;
     432              :     }
     433              : 
     434              :     result = lmalloc(new_width * new_height);
     435              :     if (!result) {
     436              :         return NULL;
     437              :     }
     438              : 
     439              :     for (y = 0; y < new_height; y++) {
     440              :         src_y = y * 2;
     441              :         lierre_minimize_row(image + src_y * width, image + (src_y + 1) * width, result + y * new_width, new_width);
     442              :     }
     443              : 
     444              :     *out_width = new_width;
     445              :     *out_height = new_height;
     446              : 
     447              :     return result;
     448              : }
     449              : 
     450              : static inline uint8_t *apply_minimize_once_mt(const uint8_t *image, size_t width, size_t height, size_t *out_width,
     451              :                                               size_t *out_height, uint32_t num_threads)
     452              : {
     453              :     lierre_thread_t *threads;
     454              :     lierre_image_mt_minimize_ctx_t *contexts;
     455              :     uint32_t i;
     456              :     uint8_t *result;
     457              :     size_t new_width, new_height, rows_per_thread;
     458              : 
     459              :     new_width = width >> 1;
     460              :     new_height = height >> 1;
     461              : 
     462              :     if (new_width < 21 || new_height < 21) {
     463              :         *out_width = width;
     464              :         *out_height = height;
     465              :         return NULL;
     466              :     }
     467              : 
     468              :     if (num_threads > LIERRE_IMAGE_MT_MAX_THREADS) {
     469              :         num_threads = LIERRE_IMAGE_MT_MAX_THREADS;
     470              :     }
     471              :     if (num_threads > new_height) {
     472              :         num_threads = (uint32_t)new_height;
     473              :     }
     474              :     if (num_threads < 1) {
     475              :         num_threads = 1;
     476              :     }
     477              : 
     478              :     result = lmalloc(new_width * new_height);
     479              :     if (!result) {
     480              :         return NULL;
     481              :     }
     482              : 
     483              :     threads = lmalloc(sizeof(lierre_thread_t) * num_threads);
     484              :     contexts = lmalloc(sizeof(lierre_image_mt_minimize_ctx_t) * num_threads);
     485              :     if (!threads || !contexts) {
     486              :         lfree(result);
     487              :         lfree(threads);
     488              :         lfree(contexts);
     489              :         return NULL;
     490              :     }
     491              : 
     492              :     rows_per_thread = new_height / num_threads;
     493              : 
     494              :     for (i = 0; i < num_threads; i++) {
     495              :         contexts[i].src = image;
     496              :         contexts[i].dst = result;
     497              :         contexts[i].src_width = width;
     498              :         contexts[i].dst_width = new_width;
     499              :         contexts[i].start_row = i * rows_per_thread;
     500              :         contexts[i].end_row = (i == num_threads - 1) ? new_height : (i + 1) * rows_per_thread;
     501              :         lierre_thread_create(&threads[i], minimize_thread, &contexts[i]);
     502              :     }
     503              : 
     504              :     for (i = 0; i < num_threads; i++) {
     505              :         lierre_thread_join(threads[i], NULL);
     506              :     }
     507              : 
     508              :     lfree(threads);
     509              :     lfree(contexts);
     510              : 
     511              :     *out_width = new_width;
     512              :     *out_height = new_height;
     513              : 
     514              :     return result;
     515              : }
     516              : 
     517              : static inline uint8_t *lierre_image_minimize(const uint8_t *image, size_t width, size_t height, size_t *out_width,
     518              :                                              size_t *out_height, bool use_mt, uint32_t num_threads)
     519              : {
     520              :     uint32_t iter;
     521              :     uint8_t *current, *next;
     522              :     size_t cur_width, cur_height, new_width, new_height;
     523              : 
     524              :     current = lmalloc(width * height);
     525              :     if (!current) {
     526              :         return NULL;
     527              :     }
     528              : 
     529              :     lmemcpy(current, image, width * height);
     530              :     cur_width = width;
     531              :     cur_height = height;
     532              : 
     533              :     for (iter = 0; iter < LIERRE_IMAGE_MINIMIZE_MAX_ITERATIONS; iter++) {
     534              :         if (use_mt) {
     535              :             next = apply_minimize_once_mt(current, cur_width, cur_height, &new_width, &new_height, num_threads);
     536              :         } else {
     537              :             next = apply_minimize_once(current, cur_width, cur_height, &new_width, &new_height);
     538              :         }
     539              : 
     540              :         if (!next) {
     541              :             break;
     542              :         }
     543              : 
     544              :         lfree(current);
     545              :         current = next;
     546              :         cur_width = new_width;
     547              :         cur_height = new_height;
     548              :     }
     549              : 
     550              :     *out_width = cur_width;
     551              :     *out_height = cur_height;
     552              : 
     553              :     return current;
     554              : }
     555              : 
     556            8 : static inline void *denoise_filter_thread(void *arg)
     557              : {
     558              :     lierre_image_mt_filter_ctx_t *ctx;
     559              :     int32_t sum;
     560              :     size_t x, y, i, j, idx;
     561              : 
     562            8 :     ctx = (lierre_image_mt_filter_ctx_t *)arg;
     563              : 
     564         1288 :     for (y = ctx->start_row; y < ctx->end_row; y++) {
     565         1280 :         if (y == 0 || y >= ctx->height - 1) {
     566            4 :             continue;
     567              :         }
     568      1533505 :         for (x = 1; x < ctx->width - 1; x++) {
     569      1532229 :             sum = 0;
     570      4702457 :             for (j = y - 1; j <= y + 1; j++) {
     571      8237723 :                 for (i = x - 1; i <= x + 1; i++) {
     572      5067495 :                     sum += ctx->temp[j * ctx->width + i];
     573              :                 }
     574              :             }
     575      1532229 :             idx = y * ctx->width + x;
     576      1532229 :             ctx->image[idx] = (uint8_t)(sum / LIERRE_FILTER_KERNEL_ELEMS);
     577              :         }
     578              :     }
     579              : 
     580            8 :     return NULL;
     581              : }
     582              : 
     583            8 : static inline void *sharpening_filter_thread(void *arg)
     584              : {
     585              :     lierre_image_mt_filter_ctx_t *ctx;
     586              :     int32_t val;
     587              :     size_t x, y, idx;
     588              : 
     589            8 :     ctx = (lierre_image_mt_filter_ctx_t *)arg;
     590              : 
     591         1286 :     for (y = ctx->start_row; y < ctx->end_row; y++) {
     592         1278 :         if (y == 0 || y >= ctx->height - 1) {
     593            4 :             continue;
     594              :         }
     595       536965 :         for (x = 1; x < ctx->width - 1; x++) {
     596       535691 :             idx = y * ctx->width + x;
     597       535691 :             val = LIERRE_SHARPEN_CENTER_COEF * (int32_t)ctx->temp[idx] - (int32_t)ctx->temp[(y - 1) * ctx->width + x] -
     598       535691 :                   (int32_t)ctx->temp[(y + 1) * ctx->width + x] - (int32_t)ctx->temp[y * ctx->width + (x - 1)] -
     599       535691 :                   (int32_t)ctx->temp[y * ctx->width + (x + 1)];
     600       535691 :             if (val < LIERRE_PIXEL_VALUE_MIN) {
     601        17554 :                 val = LIERRE_PIXEL_VALUE_MIN;
     602              :             }
     603       535691 :             if (val > LIERRE_PIXEL_VALUE_MAX) {
     604        43304 :                 val = LIERRE_PIXEL_VALUE_MAX;
     605              :             }
     606       535691 :             ctx->image[idx] = (uint8_t)val;
     607              :         }
     608              :     }
     609              : 
     610            8 :     return NULL;
     611              : }
     612              : 
     613            1 : extern void image_brightness_normalize(uint8_t *image, size_t width, size_t height)
     614              : {
     615              :     size_t i, total;
     616              :     int32_t range;
     617              :     uint8_t min_val, max_val;
     618              : 
     619            1 :     total = width * height;
     620            1 :     if (total == 0) {
     621            1 :         return;
     622              :     }
     623              : 
     624            1 :     lierre_find_minmax(image, total, &min_val, &max_val);
     625              : 
     626            1 :     range = (int32_t)max_val - (int32_t)min_val;
     627            1 :     if (range <= 0) {
     628            1 :         return;
     629              :     }
     630              : 
     631            0 :     for (i = 0; i < total; i++) {
     632            0 :         image[i] = (uint8_t)(((int32_t)(image[i] - min_val) * LIERRE_PIXEL_VALUE_MAX) / range);
     633              :     }
     634              : }
     635              : 
     636            1 : extern void image_contrast_normalize(uint8_t *image, size_t width, size_t height)
     637              : {
     638              :     size_t i, total;
     639              :     int64_t sum64;
     640              :     int32_t mean, val, new_val, factor;
     641              : 
     642            1 :     total = width * height;
     643            1 :     if (total == 0) {
     644            0 :         return;
     645              :     }
     646              : 
     647            1 :     sum64 = lierre_sum(image, total);
     648              : 
     649            1 :     mean = (int32_t)(sum64 / (int64_t)total);
     650            1 :     factor = LIERRE_CONTRAST_FACTOR;
     651              : 
     652        40001 :     for (i = 0; i < total; i++) {
     653        40000 :         val = (int32_t)image[i];
     654        40000 :         new_val = mean + ((val - mean) * factor) / LIERRE_CONTRAST_DIVISOR;
     655              : 
     656        40000 :         if (new_val < LIERRE_PIXEL_VALUE_MIN) {
     657            0 :             new_val = LIERRE_PIXEL_VALUE_MIN;
     658              :         }
     659              : 
     660        40000 :         if (new_val > LIERRE_PIXEL_VALUE_MAX) {
     661            0 :             new_val = LIERRE_PIXEL_VALUE_MAX;
     662              :         }
     663              : 
     664        40000 :         image[i] = (uint8_t)new_val;
     665              :     }
     666              : }
     667              : 
     668            2 : extern void image_denoise_mt(uint8_t *image, size_t width, size_t height, uint32_t num_threads)
     669              : {
     670              :     lierre_thread_t *threads;
     671              :     lierre_image_mt_filter_ctx_t *contexts;
     672              :     uint32_t i;
     673              :     uint8_t *temp;
     674              :     size_t rows_per_thread;
     675              : 
     676            2 :     if (width < 3 || height < 3) {
     677            0 :         return;
     678              :     }
     679              : 
     680            2 :     if (num_threads > LIERRE_IMAGE_MT_MAX_THREADS) {
     681            0 :         num_threads = LIERRE_IMAGE_MT_MAX_THREADS;
     682              :     }
     683            2 :     if (num_threads > height) {
     684            0 :         num_threads = (uint32_t)height;
     685              :     }
     686            2 :     if (num_threads < 1) {
     687            0 :         num_threads = 1;
     688              :     }
     689              : 
     690            2 :     temp = lmalloc(width * height);
     691            2 :     if (!temp) {
     692            0 :         return;
     693              :     }
     694              : 
     695            2 :     threads = lmalloc(sizeof(lierre_thread_t) * num_threads);
     696            2 :     contexts = lmalloc(sizeof(lierre_image_mt_filter_ctx_t) * num_threads);
     697            2 :     if (!threads || !contexts) {
     698            0 :         lfree(temp);
     699            0 :         lfree(threads);
     700            0 :         lfree(contexts);
     701            0 :         return;
     702              :     }
     703              : 
     704            2 :     lmemcpy(temp, image, width * height);
     705              : 
     706            2 :     rows_per_thread = height / num_threads;
     707              : 
     708           10 :     for (i = 0; i < num_threads; i++) {
     709            8 :         contexts[i].image = image;
     710            8 :         contexts[i].temp = temp;
     711            8 :         contexts[i].width = width;
     712            8 :         contexts[i].height = height;
     713            8 :         contexts[i].start_row = i * rows_per_thread;
     714            8 :         contexts[i].end_row = (i == num_threads - 1) ? height : (i + 1) * rows_per_thread;
     715            8 :         lierre_thread_create(&threads[i], denoise_filter_thread, &contexts[i]);
     716              :     }
     717              : 
     718           10 :     for (i = 0; i < num_threads; i++) {
     719            8 :         lierre_thread_join(threads[i], NULL);
     720              :     }
     721              : 
     722            2 :     lfree(threads);
     723            2 :     lfree(contexts);
     724            2 :     lfree(temp);
     725              : }
     726              : 
     727            2 : extern void image_denoise(uint8_t *image, size_t width, size_t height)
     728              : {
     729              :     int32_t sum;
     730              :     uint8_t *temp;
     731              :     size_t x, y, i, j, idx;
     732              : 
     733            2 :     if (width < 3 || height < 3) {
     734            0 :         return;
     735              :     }
     736              : 
     737            2 :     temp = lmalloc(width * height);
     738            2 :     if (!temp) {
     739            0 :         return;
     740              :     }
     741              : 
     742            2 :     lmemcpy(temp, image, width * height);
     743              : 
     744          798 :     for (y = 1; y < height - 1; y++) {
     745       517204 :         for (x = 1; x < width - 1; x++) {
     746       516408 :             sum = 0;
     747      2065632 :             for (j = y - 1; j <= y + 1; j++) {
     748      6196896 :                 for (i = x - 1; i <= x + 1; i++) {
     749      4647672 :                     sum += temp[j * width + i];
     750              :                 }
     751              :             }
     752       516408 :             idx = y * width + x;
     753       516408 :             image[idx] = (uint8_t)(sum / LIERRE_FILTER_KERNEL_ELEMS);
     754              :         }
     755              :     }
     756              : 
     757            2 :     lfree(temp);
     758              : }
     759              : 
     760            2 : extern void image_sharpen_mt(uint8_t *image, size_t width, size_t height, uint32_t num_threads)
     761              : {
     762              :     lierre_thread_t *threads;
     763              :     lierre_image_mt_filter_ctx_t *contexts;
     764              :     uint32_t i;
     765              :     uint8_t *temp;
     766              :     size_t rows_per_thread;
     767              : 
     768            2 :     if (width < 3 || height < 3) {
     769            0 :         return;
     770              :     }
     771              : 
     772            2 :     if (num_threads > LIERRE_IMAGE_MT_MAX_THREADS) {
     773            0 :         num_threads = LIERRE_IMAGE_MT_MAX_THREADS;
     774              :     }
     775            2 :     if (num_threads > height) {
     776            0 :         num_threads = (uint32_t)height;
     777              :     }
     778            2 :     if (num_threads < 1) {
     779            0 :         num_threads = 1;
     780              :     }
     781              : 
     782            2 :     temp = lmalloc(width * height);
     783            2 :     if (!temp) {
     784            0 :         return;
     785              :     }
     786              : 
     787            2 :     threads = lmalloc(sizeof(lierre_thread_t) * num_threads);
     788            2 :     contexts = lmalloc(sizeof(lierre_image_mt_filter_ctx_t) * num_threads);
     789            2 :     if (!threads || !contexts) {
     790            0 :         lfree(temp);
     791            0 :         lfree(threads);
     792            0 :         lfree(contexts);
     793            0 :         return;
     794              :     }
     795              : 
     796            2 :     lmemcpy(temp, image, width * height);
     797              : 
     798            2 :     rows_per_thread = height / num_threads;
     799              : 
     800           10 :     for (i = 0; i < num_threads; i++) {
     801            8 :         contexts[i].image = image;
     802            8 :         contexts[i].temp = temp;
     803            8 :         contexts[i].width = width;
     804            8 :         contexts[i].height = height;
     805            8 :         contexts[i].start_row = i * rows_per_thread;
     806            8 :         contexts[i].end_row = (i == num_threads - 1) ? height : (i + 1) * rows_per_thread;
     807            8 :         lierre_thread_create(&threads[i], sharpening_filter_thread, &contexts[i]);
     808              :     }
     809              : 
     810           10 :     for (i = 0; i < num_threads; i++) {
     811            8 :         lierre_thread_join(threads[i], NULL);
     812              :     }
     813              : 
     814            2 :     lfree(threads);
     815            2 :     lfree(contexts);
     816            2 :     lfree(temp);
     817              : }
     818              : 
     819            1 : extern void image_sharpen(uint8_t *image, size_t width, size_t height)
     820              : {
     821              :     int32_t val;
     822              :     uint8_t *temp;
     823              :     size_t x, y, idx;
     824              : 
     825            1 :     if (width < 3 || height < 3) {
     826            0 :         return;
     827              :     }
     828              : 
     829            1 :     temp = lmalloc(width * height);
     830            1 :     if (!temp) {
     831            0 :         return;
     832              :     }
     833              : 
     834            1 :     lmemcpy(temp, image, width * height);
     835              : 
     836          199 :     for (y = 1; y < height - 1; y++) {
     837        39402 :         for (x = 1; x < width - 1; x++) {
     838        39204 :             idx = y * width + x;
     839        39204 :             val = LIERRE_SHARPEN_CENTER_COEF * (int32_t)temp[idx] - (int32_t)temp[(y - 1) * width + x] -
     840        39204 :                   (int32_t)temp[(y + 1) * width + x] - (int32_t)temp[y * width + (x - 1)] -
     841        39204 :                   (int32_t)temp[y * width + (x + 1)];
     842        39204 :             if (val < LIERRE_PIXEL_VALUE_MIN) {
     843            0 :                 val = LIERRE_PIXEL_VALUE_MIN;
     844              :             }
     845        39204 :             if (val > LIERRE_PIXEL_VALUE_MAX) {
     846            0 :                 val = LIERRE_PIXEL_VALUE_MAX;
     847              :             }
     848        39204 :             image[idx] = (uint8_t)val;
     849              :         }
     850              :     }
     851              : 
     852            1 :     lfree(temp);
     853              : }
        

Generated by: LCOV version 2.0-1