LCOV - code coverage report
Current view: top level - src - pngx_common.c (source / functions) Coverage Total Hit
Test: colopresso Coverage Report Lines: 87.5 % 575 503
Test Date: 2026-11-09 10:50:48 Functions: 100.0 % 23 23
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-or-later
       3              :  *
       4              :  * This file is part of colopresso
       5              :  *
       6              :  * Copyright (C) 2025-2026 COLOPL, Inc.
       7              :  *
       8              :  * Author: Go Kudo <g-kudo@colopl.co.jp>
       9              :  * Developed with AI (LLM) code assistance. See `NOTICE` for details.
      10              :  */
      11              : 
      12              : #include <colopresso.h>
      13              : 
      14              : #include <stdbool.h>
      15              : #include <stdint.h>
      16              : #include <stdlib.h>
      17              : #include <string.h>
      18              : 
      19              : #include "internal/log.h"
      20              : #include "internal/png.h"
      21              : #include "internal/pngx_common.h"
      22              : #include "internal/simd.h"
      23              : #include "internal/threads.h"
      24              : 
      25              : typedef struct {
      26              :     uint64_t r_sum;
      27              :     uint64_t g_sum;
      28              :     uint64_t b_sum;
      29              :     uint64_t a_sum;
      30              :     uint32_t count;
      31              :     float score;
      32              :     float importance_accum;
      33              : } chroma_bucket_t;
      34              : 
      35              : typedef struct {
      36              :     uint8_t *rgba;
      37              :     size_t pixel_count;
      38              :     uint8_t bits_rgb;
      39              :     uint8_t bits_alpha;
      40              : } snap_rgba_parallel_ctx_t;
      41              : 
      42     19422486 : static inline float absf(float value)
      43              : {
      44     19422486 :     return (value < 0.0f) ? -value : value;
      45              : }
      46              : 
      47      1676655 : static inline uint32_t chroma_bucket_index(uint8_t r, uint8_t g, uint8_t b)
      48              : {
      49      1676655 :     return (((uint32_t)r >> PNGX_CHROMA_BUCKET_SHIFT) * PNGX_CHROMA_BUCKET_DIM *
      50      1676655 :             PNGX_CHROMA_BUCKET_DIM) +
      51      3353310 :            (((uint32_t)g >> PNGX_CHROMA_BUCKET_SHIFT) * PNGX_CHROMA_BUCKET_DIM) +
      52      1676655 :            ((uint32_t)b >> PNGX_CHROMA_BUCKET_SHIFT);
      53              : }
      54              : 
      55      9723722 : static inline float calc_saturation(uint8_t r, uint8_t g, uint8_t b)
      56              : {
      57      9723722 :     uint8_t max_v = r, min_v = r;
      58              : 
      59      9723722 :     if (g > max_v) {
      60      4982314 :         max_v = g;
      61              :     }
      62              : 
      63      9723722 :     if (b > max_v) {
      64      2479287 :         max_v = b;
      65              :     }
      66              : 
      67      9723722 :     if (g < min_v) {
      68      4606273 :         min_v = g;
      69              :     }
      70              : 
      71      9723722 :     if (b < min_v) {
      72      3447391 :         min_v = b;
      73              :     }
      74              : 
      75      9723722 :     if (max_v == 0) {
      76        36961 :         return 0.0f;
      77              :     }
      78              : 
      79      9686761 :     return (float)(max_v - min_v) / (float)max_v;
      80              : }
      81              : 
      82      9724027 : static inline float calc_luma(uint8_t r, uint8_t g, uint8_t b)
      83              : {
      84     19448054 :     return PNGX_COMMON_LUMA_R_COEFF * (float)r + PNGX_COMMON_LUMA_G_COEFF * (float)g +
      85      9724027 :            PNGX_COMMON_LUMA_B_COEFF * (float)b;
      86              : }
      87              : 
      88           43 : static inline bool decode_png_rgba(const uint8_t *png_data, size_t png_size, uint8_t **rgba,
      89              :                                    png_uint_32 *width, png_uint_32 *height)
      90              : {
      91              :     cpres_error_t status;
      92              : 
      93           43 :     if (!png_data || png_size == 0 || !rgba || !width || !height) {
      94            0 :         return false;
      95              :     }
      96              : 
      97           43 :     status = png_decode_from_memory(png_data, png_size, rgba, width, height);
      98           43 :     if (status != CPRES_OK) {
      99            1 :         colopresso_log(CPRES_LOG_LEVEL_WARNING, "PNGX: Failed to decode PNG (%d)", (int)status);
     100              : 
     101            1 :         return false;
     102              :     }
     103              : 
     104           42 :     return true;
     105              : }
     106              : 
     107           34 : static inline void extract_chroma_anchors(pngx_quant_support_t *support, chroma_bucket_t *buckets,
     108              :                                           size_t bucket_count, size_t pixel_count)
     109              : {
     110              :     cpres_rgba_color_t chosen[PNGX_MAX_DERIVED_COLORS];
     111              :     chroma_bucket_t *bucket;
     112              :     size_t selected, best_index, i, dst, auto_limit, scaled, check;
     113              :     bool duplicate;
     114              :     float best_score, score, importance_boost;
     115              : 
     116           34 :     if (!support || !buckets || bucket_count == 0) {
     117            4 :         return;
     118              :     }
     119              : 
     120           34 :     if (pixel_count > 0) {
     121           34 :         scaled = pixel_count / (size_t)PNGX_COMMON_ANCHOR_SCALE_DIVISOR;
     122           34 :         if (scaled < (size_t)PNGX_COMMON_ANCHOR_SCALE_MIN) {
     123           22 :             scaled = (size_t)PNGX_COMMON_ANCHOR_SCALE_MIN;
     124              :         }
     125              : 
     126           34 :         if (scaled > PNGX_MAX_DERIVED_COLORS) {
     127            9 :             scaled = PNGX_MAX_DERIVED_COLORS;
     128              :         }
     129              : 
     130           34 :         auto_limit = scaled;
     131              :     } else {
     132            0 :         auto_limit = (size_t)PNGX_COMMON_ANCHOR_AUTO_LIMIT_DEFAULT;
     133              :     }
     134              : 
     135           34 :     selected = 0;
     136          681 :     while (selected < auto_limit) {
     137          668 :         best_score = 0.0f;
     138          668 :         best_index = SIZE_MAX;
     139      2736796 :         for (i = 0; i < bucket_count; ++i) {
     140      2736128 :             bucket = &buckets[i];
     141      2736128 :             if (!bucket->count || bucket->score <= 0.0f) {
     142      2317833 :                 continue;
     143              :             }
     144              : 
     145       418295 :             importance_boost = bucket->importance_accum * PNGX_COMMON_ANCHOR_IMPORTANCE_FACTOR;
     146       418295 :             if (importance_boost > PNGX_COMMON_ANCHOR_IMPORTANCE_THRESHOLD) {
     147       367822 :                 importance_boost = PNGX_COMMON_ANCHOR_IMPORTANCE_BOOST_BASE +
     148       367822 :                                    (importance_boost - PNGX_COMMON_ANCHOR_IMPORTANCE_THRESHOLD) *
     149              :                                        PNGX_COMMON_ANCHOR_IMPORTANCE_BOOST_SCALE;
     150              :             }
     151              : 
     152       418295 :             score = bucket->score + importance_boost;
     153              : 
     154       418295 :             if (bucket->count < PNGX_COMMON_ANCHOR_LOW_COUNT_THRESHOLD) {
     155        67465 :                 score *= PNGX_COMMON_ANCHOR_LOW_COUNT_PENALTY;
     156              :             }
     157              : 
     158       418295 :             if (score > best_score) {
     159         7501 :                 best_score = score;
     160         7501 :                 best_index = i;
     161              :             }
     162              :         }
     163              : 
     164          668 :         if (best_index == SIZE_MAX || best_score < PNGX_COMMON_ANCHOR_SCORE_THRESHOLD) {
     165              :             break;
     166              :         }
     167              : 
     168          647 :         chosen[selected].r = (uint8_t)(buckets[best_index].r_sum / buckets[best_index].count);
     169          647 :         chosen[selected].g = (uint8_t)(buckets[best_index].g_sum / buckets[best_index].count);
     170          647 :         chosen[selected].b = (uint8_t)(buckets[best_index].b_sum / buckets[best_index].count);
     171          647 :         chosen[selected].a = (uint8_t)(buckets[best_index].a_sum / buckets[best_index].count);
     172          647 :         buckets[best_index].score = 0.0f;
     173          647 :         ++selected;
     174              :     }
     175              : 
     176           34 :     if (!selected) {
     177            4 :         return;
     178              :     }
     179              : 
     180           30 :     dst = 0;
     181          677 :     for (i = 0; i < selected; ++i) {
     182          647 :         duplicate = false;
     183         4573 :         for (check = 0; check < dst; ++check) {
     184         4282 :             if (color_distance_sq(&chosen[i], &chosen[check]) <
     185              :                 PNGX_COMMON_ANCHOR_DISTANCE_SQ_THRESHOLD) {
     186          356 :                 duplicate = true;
     187              : 
     188          356 :                 break;
     189              :             }
     190              :         }
     191              : 
     192          647 :         if (duplicate) {
     193          356 :             continue;
     194              :         }
     195              : 
     196          291 :         chosen[dst] = chosen[i];
     197          291 :         ++dst;
     198              :     }
     199              : 
     200           30 :     if (!dst) {
     201            0 :         return;
     202              :     }
     203              : 
     204           30 :     support->derived_colors = (cpres_rgba_color_t *)malloc(sizeof(cpres_rgba_color_t) * dst);
     205           30 :     if (!support->derived_colors) {
     206            0 :         return;
     207              :     }
     208              : 
     209           30 :     memcpy(support->derived_colors, chosen, sizeof(cpres_rgba_color_t) * dst);
     210           30 :     support->derived_colors_len = dst;
     211              : }
     212              : 
     213           10 : static void snap_rgba_parallel_worker(void *context, uint32_t start, uint32_t end)
     214              : {
     215           10 :     snap_rgba_parallel_ctx_t *ctx = (snap_rgba_parallel_ctx_t *)context;
     216              :     uint8_t r, g, b, a;
     217              :     size_t base, i;
     218              : 
     219           10 :     if (!ctx || !ctx->rgba) {
     220            0 :         return;
     221              :     }
     222              : 
     223      1181966 :     for (i = (size_t)start; i < (size_t)end && i < ctx->pixel_count; ++i) {
     224      1181956 :         base = i * 4;
     225      1181956 :         r = ctx->rgba[base + 0];
     226      1181956 :         g = ctx->rgba[base + 1];
     227      1181956 :         b = ctx->rgba[base + 2];
     228      1181956 :         a = ctx->rgba[base + 3];
     229              : 
     230      1181956 :         snap_rgba_to_bits(&r, &g, &b, &a, ctx->bits_rgb, ctx->bits_alpha);
     231              : 
     232      1181956 :         ctx->rgba[base + 0] = r;
     233      1181956 :         ctx->rgba[base + 1] = g;
     234      1181956 :         ctx->rgba[base + 2] = b;
     235      1181956 :         ctx->rgba[base + 3] = a;
     236              :     }
     237              : }
     238              : 
     239           39 : extern void image_stats_reset(pngx_image_stats_t *stats)
     240              : {
     241           39 :     if (!stats) {
     242            0 :         return;
     243              :     }
     244              : 
     245           39 :     stats->gradient_mean = 0.0f;
     246           39 :     stats->gradient_max = 0.0f;
     247           39 :     stats->saturation_mean = 0.0f;
     248           39 :     stats->opaque_ratio = 0.0f;
     249           39 :     stats->translucent_ratio = 0.0f;
     250           39 :     stats->vibrant_ratio = 0.0f;
     251              : }
     252              : 
     253           39 : extern void quant_support_reset(pngx_quant_support_t *support)
     254              : {
     255           39 :     if (!support) {
     256            0 :         return;
     257              :     }
     258              : 
     259           39 :     free(support->importance_map);
     260           39 :     support->importance_map = NULL;
     261           39 :     support->importance_map_len = 0;
     262              : 
     263           39 :     free(support->derived_colors);
     264           39 :     support->derived_colors = NULL;
     265           39 :     support->derived_colors_len = 0;
     266              : 
     267           39 :     free(support->combined_fixed_colors);
     268           39 :     support->combined_fixed_colors = NULL;
     269           39 :     support->combined_fixed_len = 0;
     270              : 
     271           39 :     free(support->bit_hint_map);
     272           39 :     support->bit_hint_map = NULL;
     273           39 :     support->bit_hint_len = 0;
     274              : }
     275              : 
     276            8 : extern const char *lossy_type_label(uint8_t lossy_type)
     277              : {
     278            8 :     switch (lossy_type) {
     279            3 :     case PNGX_LOSSY_TYPE_LIMITED_RGBA4444:
     280            3 :         return "Limited RGBA4444";
     281            5 :     case PNGX_LOSSY_TYPE_REDUCED_RGBA32:
     282            5 :         return "Reduced RGBA32";
     283            0 :     default:
     284            0 :         return "Palette256";
     285              :     }
     286              : }
     287              : 
     288           43 : extern void rgba_image_reset(pngx_rgba_image_t *image)
     289              : {
     290           43 :     if (!image) {
     291            0 :         return;
     292              :     }
     293              : 
     294           43 :     free(image->rgba);
     295           43 :     image->rgba = NULL;
     296           43 :     image->width = 0;
     297           43 :     image->height = 0;
     298           43 :     image->pixel_count = 0;
     299              : }
     300              : 
     301           43 : extern bool load_rgba_image(const uint8_t *png_data, size_t png_size, pngx_rgba_image_t *image)
     302              : {
     303           43 :     if (!png_data || png_size == 0 || !image) {
     304            0 :         return false;
     305              :     }
     306              : 
     307           43 :     image->rgba = NULL;
     308           43 :     image->width = 0;
     309           43 :     image->height = 0;
     310           43 :     image->pixel_count = 0;
     311              : 
     312           43 :     if (!decode_png_rgba(png_data, png_size, &image->rgba, &image->width, &image->height)) {
     313            1 :         rgba_image_reset(image);
     314              : 
     315            1 :         return false;
     316              :     }
     317              : 
     318           42 :     if (!image->rgba || image->width == 0 || image->height == 0) {
     319            0 :         rgba_image_reset(image);
     320              : 
     321            0 :         return false;
     322              :     }
     323              : 
     324           42 :     if ((size_t)image->width > SIZE_MAX / (size_t)image->height) {
     325            0 :         rgba_image_reset(image);
     326              : 
     327            0 :         return false;
     328              :     }
     329              : 
     330           42 :     image->pixel_count = (size_t)image->width * (size_t)image->height;
     331              : 
     332           42 :     return true;
     333              : }
     334              : 
     335      7411769 : extern uint8_t clamp_reduced_bits(uint8_t bits)
     336              : {
     337      7411769 :     const uint8_t min_bits = (uint8_t)COLOPRESSO_PNGX_REDUCED_BITS_MIN,
     338      7411769 :                   max_bits = (uint8_t)COLOPRESSO_PNGX_REDUCED_BITS_MAX;
     339              : 
     340      7411769 :     return bits < min_bits ? min_bits : (bits > max_bits ? max_bits : bits);
     341              : }
     342              : 
     343     30516067 : extern uint8_t quantize_channel_value(float value, uint8_t bits_per_channel)
     344              : {
     345              :     uint32_t levels;
     346              :     float clamped, scaled, rounded, quantized;
     347              : 
     348     30516067 :     if (bits_per_channel >= 8) {
     349            0 :         return value < 0.0f ? 0 : (value > 255.0f ? 255 : (uint8_t)(value + 0.5f));
     350              :     }
     351              : 
     352     30516067 :     if (bits_per_channel < 1) {
     353            0 :         bits_per_channel = 1;
     354              :     }
     355              : 
     356     30516067 :     levels = 1 << bits_per_channel;
     357              : 
     358     30516067 :     clamped = value;
     359     30516067 :     if (clamped < 0.0f) {
     360          539 :         clamped = 0.0f;
     361     30515528 :     } else if (clamped > 255.0f) {
     362         1243 :         clamped = 255.0f;
     363              :     }
     364              : 
     365     30516067 :     if (levels <= 1) {
     366            0 :         return 0;
     367              :     }
     368              : 
     369     30516067 :     scaled = clamped * (float)(levels - 1) / 255.0f;
     370     30516067 :     if (scaled < 0.0f) {
     371            0 :         scaled = 0.0f;
     372              :     }
     373              : 
     374     30516067 :     if (scaled > (float)(levels - 1)) {
     375            0 :         scaled = (float)(levels - 1);
     376              :     }
     377              : 
     378     30516067 :     rounded = (float)((int32_t)(scaled + 0.5f));
     379              : 
     380     30516067 :     quantized = rounded * 255.0f / (float)(levels - 1);
     381     30516067 :     if (quantized < 0.0f) {
     382            0 :         quantized = 0.0f;
     383     30516067 :     } else if (quantized > 255.0f) {
     384            0 :         quantized = 255.0f;
     385              :     }
     386              : 
     387     30516067 :     return (uint8_t)(quantized + 0.5f);
     388              : }
     389              : 
     390     14622960 : extern uint8_t quantize_bits(uint8_t value, uint8_t bits)
     391              : {
     392     14622960 :     if (bits >= 8) {
     393       407949 :         return value;
     394              :     }
     395              : 
     396     14215011 :     return quantize_channel_value((float)value, bits);
     397              : }
     398              : 
     399      1197400 : extern void snap_rgba_to_bits(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *a, uint8_t bits_rgb,
     400              :                               uint8_t bits_alpha)
     401              : {
     402      1197400 :     uint8_t rgb = clamp_reduced_bits(bits_rgb);
     403              : 
     404      1197400 :     if (r) {
     405      1197400 :         *r = quantize_bits(*r, rgb);
     406              :     }
     407              : 
     408      1197400 :     if (g) {
     409      1197400 :         *g = quantize_bits(*g, rgb);
     410              :     }
     411              : 
     412      1197400 :     if (b) {
     413      1197400 :         *b = quantize_bits(*b, rgb);
     414              :     }
     415              : 
     416      1197400 :     if (a) {
     417      1197400 :         *a = quantize_bits(*a, clamp_reduced_bits(bits_alpha));
     418              :     }
     419      1197400 : }
     420              : 
     421           10 : extern void snap_rgba_image_to_bits(uint32_t thread_count, uint8_t *rgba, size_t pixel_count,
     422              :                                     uint8_t bits_rgb, uint8_t bits_alpha)
     423              : {
     424              :     snap_rgba_parallel_ctx_t ctx;
     425              : 
     426           10 :     if (!rgba || pixel_count == 0) {
     427            0 :         return;
     428              :     }
     429              : 
     430           10 :     if (clamp_reduced_bits(bits_rgb) >= 8 && clamp_reduced_bits(bits_alpha) >= 8) {
     431            0 :         return;
     432              :     }
     433              : 
     434           10 :     ctx.rgba = rgba;
     435           10 :     ctx.pixel_count = pixel_count;
     436           10 :     ctx.bits_rgb = bits_rgb;
     437           10 :     ctx.bits_alpha = bits_alpha;
     438              : 
     439              : #if COLOPRESSO_ENABLE_THREADS
     440           10 :     colopresso_parallel_for(thread_count, (uint32_t)pixel_count, snap_rgba_parallel_worker, &ctx);
     441              : #else
     442              :     snap_rgba_parallel_worker(&ctx, 0, (uint32_t)pixel_count);
     443              : #endif
     444              : }
     445              : 
     446         6269 : extern uint32_t color_distance_sq(const cpres_rgba_color_t *lhs, const cpres_rgba_color_t *rhs)
     447              : {
     448              :     uint32_t lhs_packed, rhs_packed;
     449              : 
     450         6269 :     if (!lhs || !rhs) {
     451            0 :         return 0;
     452              :     }
     453              : 
     454         6269 :     lhs_packed = (uint32_t)lhs->r | ((uint32_t)lhs->g << 8) | ((uint32_t)lhs->b << 16) |
     455         6269 :                  ((uint32_t)lhs->a << 24);
     456         6269 :     rhs_packed = (uint32_t)rhs->r | ((uint32_t)rhs->g << 8) | ((uint32_t)rhs->b << 16) |
     457         6269 :                  ((uint32_t)rhs->a << 24);
     458              : 
     459         6269 :     return simd_color_distance_sq_u32(lhs_packed, rhs_packed);
     460              : }
     461              : 
     462            7 : extern float estimate_bitdepth_dither_level(const uint8_t *rgba, png_uint_32 width,
     463              :                                             png_uint_32 height, uint8_t bits_per_channel)
     464              : {
     465              :     png_uint_32 y, x;
     466              :     uint8_t r, g, b, a;
     467              :     size_t pixel_count, gradient_samples, opaque_pixels, translucent_pixels, base, right, below;
     468              :     double gradient_accum;
     469              :     float base_level, target, right_luma, below_luma, luma, normalized_gradient, opaque_ratio,
     470              :         translucent_ratio, min_luma, max_luma, coverage, gradient_span;
     471              : 
     472            7 :     if (!rgba || width == 0 || height == 0) {
     473            2 :         return clamp_float(COLOPRESSO_PNGX_DEFAULT_LOSSY_DITHER_LEVEL, 0.0f, 1.0f);
     474              :     }
     475              : 
     476            5 :     pixel_count = (size_t)width * (size_t)height;
     477            5 :     gradient_accum = 0.0;
     478            5 :     gradient_samples = 0;
     479            5 :     opaque_pixels = 0;
     480            5 :     translucent_pixels = 0;
     481            5 :     min_luma = 255.0f;
     482            5 :     max_luma = 0.0f;
     483              : 
     484           26 :     for (y = 0; y < height; ++y) {
     485          134 :         for (x = 0; x < width; ++x) {
     486          113 :             base = (((size_t)y * (size_t)width) + (size_t)x) * 4;
     487          113 :             r = rgba[base + 0];
     488          113 :             g = rgba[base + 1];
     489          113 :             b = rgba[base + 2];
     490          113 :             a = rgba[base + 3];
     491          113 :             luma = calc_luma(r, g, b);
     492              : 
     493          113 :             if (luma < min_luma) {
     494            6 :                 min_luma = luma;
     495              :             }
     496              : 
     497          113 :             if (luma > max_luma) {
     498           71 :                 max_luma = luma;
     499              :             }
     500              : 
     501          113 :             if (a > PNGX_COMMON_DITHER_ALPHA_OPAQUE_THRESHOLD) {
     502           97 :                 ++opaque_pixels;
     503           16 :             } else if (a > PNGX_COMMON_DITHER_ALPHA_TRANSLUCENT_THRESHOLD) {
     504           12 :                 ++translucent_pixels;
     505              :             }
     506              : 
     507          113 :             if (x + 1 < width) {
     508           92 :                 right = base + 4;
     509           92 :                 right_luma = calc_luma(rgba[right + 0], rgba[right + 1], rgba[right + 2]);
     510           92 :                 gradient_accum += absf(right_luma - luma);
     511           92 :                 ++gradient_samples;
     512              :             }
     513              : 
     514          113 :             if (y + 1 < height) {
     515           92 :                 below = (((size_t)(y + 1) * (size_t)width) + (size_t)x) * 4;
     516           92 :                 below_luma = calc_luma(rgba[below + 0], rgba[below + 1], rgba[below + 2]);
     517           92 :                 gradient_accum += absf(below_luma - luma);
     518           92 :                 ++gradient_samples;
     519              :             }
     520              :         }
     521              :     }
     522              : 
     523            5 :     if (gradient_samples == 0) {
     524            1 :         gradient_samples = 1;
     525              :     }
     526              : 
     527            5 :     normalized_gradient = (float)(gradient_accum / ((double)gradient_samples * 255.0));
     528            5 :     opaque_ratio = (pixel_count > 0) ? (float)((double)opaque_pixels / (double)pixel_count) : 0.0f;
     529            0 :     translucent_ratio =
     530            5 :         (pixel_count > 0) ? (float)((double)translucent_pixels / (double)pixel_count) : 0.0f;
     531              : 
     532            5 :     coverage = (max_luma - min_luma) / 255.0f;
     533            5 :     if (coverage < 0.0f) {
     534            0 :         coverage = 0.0f;
     535            5 :     } else if (coverage > 1.0f) {
     536            0 :         coverage = 1.0f;
     537              :     }
     538              : 
     539            5 :     gradient_span = coverage / ((normalized_gradient > PNGX_COMMON_DITHER_GRADIENT_MIN)
     540              :                                     ? normalized_gradient
     541            5 :                                     : PNGX_COMMON_DITHER_GRADIENT_MIN);
     542              : 
     543            5 :     base_level = PNGX_COMMON_DITHER_BASE_LEVEL;
     544            5 :     target = base_level;
     545              : 
     546            5 :     if (normalized_gradient > 0.35f) {
     547            0 :         target += PNGX_COMMON_DITHER_HIGH_GRADIENT_BOOST;
     548            5 :     } else if (normalized_gradient > 0.2f) {
     549            1 :         target += PNGX_COMMON_DITHER_MID_GRADIENT_BOOST;
     550            4 :     } else if (normalized_gradient < 0.08f) {
     551            4 :         target -= PNGX_COMMON_DITHER_LOW_GRADIENT_CUT;
     552            0 :     } else if (normalized_gradient < 0.15f) {
     553            0 :         target -= PNGX_COMMON_DITHER_MID_LOW_GRADIENT_CUT;
     554              :     }
     555              : 
     556            5 :     if (opaque_ratio < 0.35f) {
     557            1 :         target -= PNGX_COMMON_DITHER_OPAQUE_LOW_CUT;
     558            4 :     } else if (opaque_ratio > 0.9f) {
     559            4 :         target += PNGX_COMMON_DITHER_OPAQUE_HIGH_BOOST;
     560              :     }
     561              : 
     562            5 :     if (translucent_ratio > 0.3f) {
     563            1 :         target -= PNGX_COMMON_DITHER_TRANSLUCENT_CUT;
     564              :     }
     565              : 
     566            5 :     if (coverage > PNGX_COMMON_DITHER_COVERAGE_THRESHOLD &&
     567              :         gradient_span > PNGX_COMMON_DITHER_SPAN_THRESHOLD) {
     568            2 :         if (target < PNGX_COMMON_DITHER_TARGET_CAP) {
     569            2 :             target = PNGX_COMMON_DITHER_TARGET_CAP;
     570              :         }
     571              : 
     572            2 :         if (bits_per_channel <= 4 && target < PNGX_COMMON_DITHER_TARGET_CAP_LOW_BIT) {
     573            0 :             target = PNGX_COMMON_DITHER_TARGET_CAP_LOW_BIT;
     574              :         }
     575              :     }
     576              : 
     577            5 :     if (bits_per_channel <= 2) {
     578            1 :         target += PNGX_COMMON_DITHER_LOW_BIT_BOOST;
     579              : 
     580            1 :         if (normalized_gradient > 0.25f) {
     581            0 :             target += PNGX_COMMON_DITHER_LOW_BIT_GRADIENT_BOOST;
     582              :         }
     583              :     }
     584              : 
     585            5 :     return clamp_float(target, PNGX_COMMON_DITHER_MIN, PNGX_COMMON_DITHER_MAX);
     586              : }
     587              : 
     588            1 : extern float estimate_bitdepth_dither_level_limited4444(const uint8_t *rgba, png_uint_32 width,
     589              :                                                         png_uint_32 height)
     590              : {
     591              :     png_uint_32 y, x;
     592              :     uint8_t r, g, b, a;
     593              :     size_t pixel_count, gradient_samples, opaque_pixels, translucent_pixels, base, right, below;
     594              :     double gradient_accum;
     595              :     float right_luma, below_luma, luma, normalized_gradient, opaque_ratio, translucent_ratio,
     596              :         min_luma, max_luma, coverage, gradient_span, target;
     597              : 
     598            1 :     if (!rgba || width == 0 || height == 0) {
     599            0 :         return 0.0f;
     600              :     }
     601              : 
     602            1 :     pixel_count = (size_t)width * (size_t)height;
     603            1 :     gradient_accum = 0.0;
     604            1 :     gradient_samples = 0;
     605            1 :     opaque_pixels = 0;
     606            1 :     translucent_pixels = 0;
     607            1 :     min_luma = 255.0f;
     608            1 :     max_luma = 0.0f;
     609              : 
     610            3 :     for (y = 0; y < height; ++y) {
     611            6 :         for (x = 0; x < width; ++x) {
     612            4 :             base = (((size_t)y * (size_t)width) + (size_t)x) * 4;
     613            4 :             r = rgba[base + 0];
     614            4 :             g = rgba[base + 1];
     615            4 :             b = rgba[base + 2];
     616            4 :             a = rgba[base + 3];
     617            4 :             luma = calc_luma(r, g, b);
     618              : 
     619            4 :             if (luma < min_luma) {
     620            1 :                 min_luma = luma;
     621              :             }
     622              : 
     623            4 :             if (luma > max_luma) {
     624            0 :                 max_luma = luma;
     625              :             }
     626              : 
     627            4 :             if (a > PNGX_COMMON_DITHER_ALPHA_OPAQUE_THRESHOLD) {
     628            4 :                 ++opaque_pixels;
     629            0 :             } else if (a > PNGX_COMMON_DITHER_ALPHA_TRANSLUCENT_THRESHOLD) {
     630            0 :                 ++translucent_pixels;
     631              :             }
     632              : 
     633            4 :             if (x + 1 < width) {
     634            2 :                 right = base + 4;
     635            2 :                 right_luma = calc_luma(rgba[right + 0], rgba[right + 1], rgba[right + 2]);
     636            2 :                 gradient_accum += absf(right_luma - luma);
     637            2 :                 ++gradient_samples;
     638              :             }
     639              : 
     640            4 :             if (y + 1 < height) {
     641            2 :                 below = (((size_t)(y + 1) * (size_t)width) + (size_t)x) * 4;
     642            2 :                 below_luma = calc_luma(rgba[below + 0], rgba[below + 1], rgba[below + 2]);
     643            2 :                 gradient_accum += absf(below_luma - luma);
     644            2 :                 ++gradient_samples;
     645              :             }
     646              :         }
     647              :     }
     648              : 
     649            1 :     if (gradient_samples == 0) {
     650            0 :         gradient_samples = 1;
     651              :     }
     652              : 
     653            1 :     normalized_gradient = (float)(gradient_accum / ((double)gradient_samples * 255.0));
     654            1 :     opaque_ratio = (pixel_count > 0) ? (float)((double)opaque_pixels / (double)pixel_count) : 0.0f;
     655            0 :     translucent_ratio =
     656            1 :         (pixel_count > 0) ? (float)((double)translucent_pixels / (double)pixel_count) : 0.0f;
     657              : 
     658            1 :     coverage = (max_luma - min_luma) / 255.0f;
     659            1 :     if (coverage < 0.0f) {
     660            0 :         coverage = 0.0f;
     661            1 :     } else if (coverage > 1.0f) {
     662            0 :         coverage = 1.0f;
     663              :     }
     664              : 
     665            1 :     gradient_span = coverage / ((normalized_gradient > PNGX_COMMON_DITHER_GRADIENT_MIN)
     666              :                                     ? normalized_gradient
     667            1 :                                     : PNGX_COMMON_DITHER_GRADIENT_MIN);
     668              : 
     669            1 :     target = 0.0f;
     670              : 
     671            1 :     if (coverage > PNGX_COMMON_DITHER_COVERAGE_THRESHOLD &&
     672            0 :         gradient_span > PNGX_COMMON_DITHER_SPAN_THRESHOLD && normalized_gradient < 0.20f) {
     673            0 :         target = 0.55f;
     674              :     }
     675              : 
     676            1 :     if (translucent_ratio > 0.3f) {
     677            0 :         target *= 0.5f;
     678              :     }
     679              : 
     680            1 :     if (opaque_ratio > 0.9f) {
     681            1 :         target += 0.05f;
     682              :     }
     683              : 
     684            1 :     return clamp_float(target, 0.0f, 1.0f);
     685              : }
     686              : 
     687           28 : extern void build_fixed_palette(const pngx_options_t *source_opts, pngx_quant_support_t *support,
     688              :                                 pngx_options_t *patched_opts)
     689              : {
     690              :     size_t user_count, derived_count, total_cap, i, j;
     691              :     bool duplicate;
     692              : 
     693           28 :     if (!source_opts || !support || !patched_opts) {
     694            0 :         return;
     695              :     }
     696              : 
     697           28 :     *patched_opts = *source_opts;
     698           28 :     user_count = (source_opts->protected_colors && source_opts->protected_colors_count > 0)
     699            2 :                      ? (size_t)source_opts->protected_colors_count
     700           28 :                      : 0;
     701           28 :     derived_count = support->derived_colors_len;
     702           28 :     if (derived_count == 0) {
     703            3 :         return;
     704              :     }
     705              : 
     706           25 :     total_cap = user_count + derived_count;
     707           25 :     support->combined_fixed_colors =
     708           25 :         (cpres_rgba_color_t *)malloc(sizeof(cpres_rgba_color_t) * total_cap);
     709           25 :     if (!support->combined_fixed_colors) {
     710            0 :         return;
     711              :     }
     712              : 
     713           25 :     if (user_count > 0 && source_opts->protected_colors) {
     714            2 :         memcpy(support->combined_fixed_colors, source_opts->protected_colors,
     715            2 :                sizeof(cpres_rgba_color_t) * user_count);
     716              :     }
     717              : 
     718           25 :     support->combined_fixed_len = user_count;
     719          239 :     for (i = 0; i < derived_count; ++i) {
     720          215 :         duplicate = false;
     721         2201 :         for (j = 0; j < support->combined_fixed_len; ++j) {
     722         1986 :             if (color_distance_sq(&support->derived_colors[i], &support->combined_fixed_colors[j]) <
     723              :                 PNGX_COMMON_FIXED_PALETTE_DISTANCE_SQ) {
     724            0 :                 duplicate = true;
     725              : 
     726            0 :                 break;
     727              :             }
     728              :         }
     729              : 
     730          215 :         if (duplicate) {
     731            0 :             continue;
     732              :         }
     733              : 
     734          215 :         if (support->combined_fixed_len >= PNGX_COMMON_FIXED_PALETTE_MAX) {
     735            1 :             break;
     736              :         }
     737              : 
     738          214 :         if (support->combined_fixed_len < total_cap) {
     739          214 :             support->combined_fixed_colors[support->combined_fixed_len] =
     740          214 :                 support->derived_colors[i];
     741          214 :             ++support->combined_fixed_len;
     742              :         }
     743              :     }
     744              : 
     745           25 :     if (support->combined_fixed_len > user_count) {
     746           24 :         patched_opts->protected_colors = support->combined_fixed_colors;
     747           24 :         patched_opts->protected_colors_count = (int32_t)support->combined_fixed_len;
     748              :     }
     749              : }
     750              : 
     751           31 : extern float resolve_quant_dither(const pngx_options_t *opts, const pngx_image_stats_t *stats)
     752              : {
     753              :     float resolved, gradient_mean, saturation_mean, opaque_ratio, vibrant_ratio, gradient_max;
     754              : 
     755           31 :     if (!opts) {
     756            0 :         return 0.5f;
     757              :     }
     758              : 
     759           31 :     if (stats) {
     760           31 :         gradient_mean = stats->gradient_mean;
     761           31 :         saturation_mean = stats->saturation_mean;
     762           31 :         opaque_ratio = stats->opaque_ratio;
     763           31 :         vibrant_ratio = stats->vibrant_ratio;
     764           31 :         gradient_max = stats->gradient_max;
     765              :     } else {
     766            0 :         gradient_mean = PNGX_COMMON_RESOLVE_DEFAULT_GRADIENT;
     767            0 :         saturation_mean = PNGX_COMMON_RESOLVE_DEFAULT_SATURATION;
     768            0 :         opaque_ratio = PNGX_COMMON_RESOLVE_DEFAULT_OPAQUE;
     769            0 :         vibrant_ratio = PNGX_COMMON_RESOLVE_DEFAULT_VIBRANT;
     770            0 :         gradient_max = PNGX_COMMON_RESOLVE_DEFAULT_GRADIENT;
     771              :     }
     772              : 
     773           31 :     resolved = opts->lossy_dither_level;
     774              : 
     775           31 :     if (opts->lossy_dither_auto) {
     776            1 :         resolved = PNGX_COMMON_RESOLVE_AUTO_BASE +
     777            1 :                    gradient_mean * PNGX_COMMON_RESOLVE_AUTO_GRADIENT_WEIGHT +
     778            1 :                    saturation_mean * PNGX_COMMON_RESOLVE_AUTO_SATURATION_WEIGHT;
     779              : 
     780            1 :         if (opaque_ratio < 0.7f) {
     781            1 :             resolved -= PNGX_COMMON_RESOLVE_AUTO_OPAQUE_CUT;
     782              :         }
     783              :     }
     784              : 
     785           31 :     if (opts->adaptive_dither_enable) {
     786           30 :         if (gradient_mean < 0.10f) {
     787           30 :             resolved -= PNGX_COMMON_RESOLVE_ADAPTIVE_FLAT_CUT;
     788            0 :         } else if (gradient_mean > 0.30f) {
     789            0 :             resolved += PNGX_COMMON_RESOLVE_ADAPTIVE_GRADIENT_BOOST;
     790              :         }
     791              : 
     792           30 :         if (gradient_max > 0.5f && vibrant_ratio > 0.12f) {
     793            0 :             resolved -= PNGX_COMMON_RESOLVE_ADAPTIVE_VIBRANT_CUT;
     794              :         }
     795              : 
     796           30 :         if (saturation_mean > 0.38f) {
     797            7 :             resolved += PNGX_COMMON_RESOLVE_ADAPTIVE_SATURATION_BOOST;
     798           23 :         } else if (saturation_mean < 0.12f) {
     799            3 :             resolved -= PNGX_COMMON_RESOLVE_ADAPTIVE_SATURATION_CUT;
     800              :         }
     801              :     }
     802              : 
     803           31 :     if (resolved < PNGX_COMMON_RESOLVE_MIN) {
     804            7 :         resolved = PNGX_COMMON_RESOLVE_MIN;
     805           24 :     } else if (resolved > PNGX_COMMON_RESOLVE_MAX) {
     806            0 :         resolved = PNGX_COMMON_RESOLVE_MAX;
     807              :     }
     808              : 
     809           31 :     return resolved;
     810              : }
     811              : 
     812           39 : extern bool prepare_quant_support(const pngx_rgba_image_t *image, const pngx_options_t *opts,
     813              :                                   pngx_quant_support_t *support, pngx_image_stats_t *stats)
     814              : {
     815           39 :     chroma_bucket_t *buckets = NULL, *bucket_entry;
     816              :     uint32_t x, y, range, sample;
     817           39 :     uint16_t *importance_work = NULL, raw_min, raw_max;
     818              :     uint8_t r, g, b, a, value;
     819              :     size_t pixel_index, base, next_row_base, opaque_pixels, translucent_pixels, vibrant_pixels;
     820              :     bool need_map, need_buckets;
     821              :     float gradient_sum, saturation_sum, luma, gradient, saturation, importance, alpha_factor,
     822           39 :         anchor_score, right_luma, below_luma, importance_mix, *luma_row_curr = NULL,
     823           39 :                                                               *luma_row_next = NULL, *luma_row_tmp;
     824              : 
     825           39 :     if (!image || !opts || !support || !stats || image->pixel_count == 0) {
     826            0 :         return false;
     827              :     }
     828              : 
     829           39 :     raw_min = UINT16_MAX;
     830           39 :     raw_max = 0;
     831           39 :     need_map = opts->saliency_map_enable || opts->postprocess_smooth_enable;
     832           39 :     need_buckets = opts->chroma_anchor_enable;
     833           39 :     gradient_sum = 0.0f;
     834           39 :     saturation_sum = 0.0f;
     835           39 :     opaque_pixels = 0;
     836           39 :     translucent_pixels = 0;
     837           39 :     vibrant_pixels = 0;
     838              : 
     839           39 :     if (need_map) {
     840           34 :         importance_work = (uint16_t *)malloc(sizeof(uint16_t) * image->pixel_count);
     841           34 :         if (!importance_work) {
     842            0 :             return false;
     843              :         }
     844              :     }
     845              : 
     846           39 :     if (need_buckets) {
     847           34 :         buckets = (chroma_bucket_t *)calloc(PNGX_CHROMA_BUCKET_COUNT, sizeof(chroma_bucket_t));
     848           34 :         if (!buckets) {
     849            0 :             if (importance_work) {
     850            0 :                 free(importance_work);
     851              :             }
     852              : 
     853            0 :             return false;
     854              :         }
     855              :     }
     856              : 
     857           39 :     luma_row_curr = (float *)malloc(sizeof(float) * image->width);
     858           39 :     luma_row_next = (float *)malloc(sizeof(float) * image->width);
     859           39 :     if (!luma_row_curr || !luma_row_next) {
     860            0 :         free(importance_work);
     861            0 :         free(buckets);
     862            0 :         free(luma_row_curr);
     863            0 :         free(luma_row_next);
     864              : 
     865            0 :         return false;
     866              :     }
     867              : 
     868        13549 :     for (x = 0; x < image->width; ++x) {
     869        13510 :         base = (size_t)x * 4;
     870        13510 :         luma_row_curr[x] =
     871        13510 :             calc_luma(image->rgba[base + 0], image->rgba[base + 1], image->rgba[base + 2]) / 255.0f;
     872              :     }
     873              : 
     874        11675 :     for (y = 0; y < image->height; ++y) {
     875        11636 :         if (y + 1 < image->height) {
     876        11597 :             next_row_base = ((size_t)(y + 1) * (size_t)image->width) * 4;
     877      9721809 :             for (x = 0; x < image->width; ++x) {
     878      9710212 :                 base = next_row_base + (size_t)x * 4;
     879      9710212 :                 luma_row_next[x] =
     880      9710212 :                     calc_luma(image->rgba[base + 0], image->rgba[base + 1], image->rgba[base + 2]) /
     881              :                     255.0f;
     882              :             }
     883              :         }
     884              : 
     885      9735358 :         for (x = 0; x < image->width; ++x) {
     886      9723722 :             base = (((size_t)y * (size_t)image->width) + (size_t)x) * 4;
     887      9723722 :             r = image->rgba[base + 0];
     888      9723722 :             g = image->rgba[base + 1];
     889      9723722 :             b = image->rgba[base + 2];
     890      9723722 :             a = image->rgba[base + 3];
     891      9723722 :             luma = luma_row_curr[x];
     892      9723722 :             gradient = 0.0f;
     893      9723722 :             saturation = calc_saturation(r, g, b);
     894      9723722 :             alpha_factor = (float)a / 255.0f;
     895              : 
     896      9723722 :             if (x + 1 < image->width) {
     897      9712086 :                 right_luma = luma_row_curr[x + 1];
     898      9712086 :                 gradient += absf(right_luma - luma);
     899              :             }
     900              : 
     901      9723722 :             if (y + 1 < image->height) {
     902      9710212 :                 below_luma = luma_row_next[x];
     903      9710212 :                 gradient += absf(below_luma - luma);
     904              :             }
     905              : 
     906      9723722 :             gradient *= PNGX_COMMON_PREPARE_GRADIENT_SCALE;
     907      9723722 :             if (gradient > 1.0f) {
     908            0 :                 gradient = 1.0f;
     909              :             }
     910              : 
     911      9723722 :             gradient_sum += gradient;
     912              : 
     913      9723722 :             if (gradient > stats->gradient_max) {
     914          843 :                 stats->gradient_max = gradient;
     915              :             }
     916              : 
     917      9723722 :             saturation_sum += saturation;
     918              : 
     919      9723722 :             if (a > PNGX_COMMON_DITHER_ALPHA_OPAQUE_THRESHOLD) {
     920       280695 :                 ++opaque_pixels;
     921      9443027 :             } else if (a > PNGX_COMMON_DITHER_ALPHA_TRANSLUCENT_THRESHOLD) {
     922      9332720 :                 ++translucent_pixels;
     923              :             }
     924              : 
     925      9723722 :             if (saturation > PNGX_COMMON_PREPARE_VIBRANT_SATURATION &&
     926       197910 :                 gradient > PNGX_COMMON_PREPARE_VIBRANT_GRADIENT &&
     927       197910 :                 a > PNGX_COMMON_PREPARE_VIBRANT_ALPHA) {
     928       197890 :                 ++vibrant_pixels;
     929              :             }
     930              : 
     931      9723722 :             importance = gradient;
     932              : 
     933      9723722 :             if (opts->chroma_weight_enable) {
     934      9652802 :                 importance += saturation * PNGX_COMMON_PREPARE_CHROMA_WEIGHT;
     935              :             }
     936              : 
     937      9723722 :             if (opts->gradient_boost_enable) {
     938      9652802 :                 if (gradient > PNGX_COMMON_PREPARE_BOOST_THRESHOLD) {
     939        11960 :                     importance += PNGX_COMMON_PREPARE_BOOST_BASE +
     940        11960 :                                   (gradient * PNGX_COMMON_PREPARE_BOOST_FACTOR);
     941      9640842 :                 } else if (gradient < PNGX_COMMON_PREPARE_CUT_THRESHOLD) {
     942      8514427 :                     importance *= PNGX_COMMON_PREPARE_CUT_FACTOR;
     943              :                 }
     944              :             }
     945              : 
     946      9723722 :             if (alpha_factor < PNGX_COMMON_PREPARE_ALPHA_THRESHOLD) {
     947      8308625 :                 importance *= (PNGX_COMMON_PREPARE_ALPHA_BASE +
     948      8308625 :                                alpha_factor * PNGX_COMMON_PREPARE_ALPHA_MULTIPLIER);
     949              :             }
     950              : 
     951      9723722 :             if (importance < 0.0f) {
     952            0 :                 importance = 0.0f;
     953      9723722 :             } else if (importance > 1.0f) {
     954            0 :                 importance = 1.0f;
     955              :             }
     956              : 
     957      9723722 :             if (need_map && importance_work) {
     958      9656898 :                 pixel_index = ((size_t)y * (size_t)image->width) + (size_t)x;
     959      9656898 :                 importance_work[pixel_index] =
     960      9656898 :                     (uint16_t)(importance * PNGX_IMPORTANCE_SCALE + 0.5f);
     961              : 
     962      9656898 :                 if (importance_work[pixel_index] < raw_min) {
     963          963 :                     raw_min = importance_work[pixel_index];
     964              :                 }
     965              : 
     966      9656898 :                 if (importance_work[pixel_index] > raw_max) {
     967         1351 :                     raw_max = importance_work[pixel_index];
     968              :                 }
     969              :             }
     970              : 
     971      9723722 :             if (need_buckets && buckets) {
     972      9652806 :                 if ((saturation > PNGX_COMMON_PREPARE_BUCKET_SATURATION ||
     973      7458619 :                      importance > PNGX_COMMON_PREPARE_BUCKET_IMPORTANCE) &&
     974              :                     a > PNGX_COMMON_PREPARE_BUCKET_ALPHA) {
     975      2554335 :                     importance_mix = (importance * PNGX_COMMON_PREPARE_MIX_IMPORTANCE) +
     976      2554335 :                                      (gradient * PNGX_COMMON_PREPARE_MIX_GRADIENT);
     977      2554335 :                     anchor_score = (saturation * PNGX_COMMON_PREPARE_ANCHOR_SATURATION) +
     978      2554335 :                                    (importance_mix * PNGX_COMMON_PREPARE_ANCHOR_MIX);
     979              : 
     980      2554335 :                     if (importance > PNGX_COMMON_PREPARE_ANCHOR_IMPORTANCE_THRESHOLD) {
     981           30 :                         anchor_score += PNGX_COMMON_PREPARE_ANCHOR_IMPORTANCE_BONUS;
     982              :                     }
     983              : 
     984      2554335 :                     if (anchor_score > PNGX_COMMON_PREPARE_ANCHOR_SCORE_THRESHOLD) {
     985      1676655 :                         bucket_entry = &buckets[chroma_bucket_index(r, g, b)];
     986      1676655 :                         bucket_entry->r_sum += (uint64_t)r;
     987      1676655 :                         bucket_entry->g_sum += (uint64_t)g;
     988      1676655 :                         bucket_entry->b_sum += (uint64_t)b;
     989      1676655 :                         bucket_entry->a_sum += (uint64_t)a;
     990      1676655 :                         bucket_entry->count += 1;
     991      1676655 :                         bucket_entry->score += anchor_score;
     992      1676655 :                         bucket_entry->importance_accum += importance;
     993              :                     }
     994              :                 }
     995              :             }
     996              :         }
     997              : 
     998        11636 :         luma_row_tmp = luma_row_curr;
     999        11636 :         luma_row_curr = luma_row_next;
    1000        11636 :         luma_row_next = luma_row_tmp;
    1001              :     }
    1002              : 
    1003           39 :     free(luma_row_curr);
    1004           39 :     free(luma_row_next);
    1005              : 
    1006           39 :     if (image->pixel_count > 0) {
    1007           39 :         stats->gradient_mean = gradient_sum / (float)image->pixel_count;
    1008           39 :         stats->saturation_mean = saturation_sum / (float)image->pixel_count;
    1009           39 :         stats->opaque_ratio = (float)opaque_pixels / (float)image->pixel_count;
    1010           39 :         stats->translucent_ratio = (float)translucent_pixels / (float)image->pixel_count;
    1011           39 :         stats->vibrant_ratio = (float)vibrant_pixels / (float)image->pixel_count;
    1012              :     }
    1013              : 
    1014           39 :     if (need_map && importance_work && image->pixel_count > 0) {
    1015           34 :         range = (uint32_t)(raw_max - raw_min);
    1016           34 :         if (range == 0) {
    1017            3 :             range = 1;
    1018              :         }
    1019              : 
    1020           34 :         support->importance_map = (uint8_t *)malloc(image->pixel_count);
    1021           34 :         if (support->importance_map) {
    1022      9656932 :             for (pixel_index = 0; pixel_index < image->pixel_count; ++pixel_index) {
    1023      9656898 :                 sample = (uint32_t)(importance_work[pixel_index] - raw_min);
    1024      9656898 :                 value = (uint8_t)((sample * 255) / range);
    1025      9656898 :                 if (value < PNGX_COMMON_PREPARE_MAP_MIN_VALUE) {
    1026       103452 :                     value = (uint8_t)PNGX_COMMON_PREPARE_MAP_MIN_VALUE;
    1027              :                 }
    1028              : 
    1029      9656898 :                 support->importance_map[pixel_index] = value;
    1030              :             }
    1031              : 
    1032           34 :             support->importance_map_len = image->pixel_count;
    1033              :         }
    1034              :     }
    1035              : 
    1036           39 :     free(importance_work);
    1037              : 
    1038           39 :     if (need_buckets && buckets) {
    1039           34 :         extract_chroma_anchors(support, buckets, PNGX_CHROMA_BUCKET_COUNT, image->pixel_count);
    1040              :     }
    1041              : 
    1042           39 :     free(buckets);
    1043              : 
    1044           39 :     return true;
    1045              : }
        

Generated by: LCOV version 2.0-1