LCOV - code coverage report
Current view: top level - src - avif.c (source / functions) Coverage Total Hit
Test: colopresso Coverage Report Lines: 78.1 % 105 82
Test Date: 2026-11-09 10:50:48 Functions: 100.0 % 6 6
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 <errno.h>
      13              : #include <math.h>
      14              : #include <stdbool.h>
      15              : #include <stdint.h>
      16              : #include <stdio.h>
      17              : #include <stdlib.h>
      18              : #include <string.h>
      19              : 
      20              : #include <avif/avif.h>
      21              : 
      22              : #include <colopresso.h>
      23              : 
      24              : #include "internal/avif.h"
      25              : #include "internal/log.h"
      26              : 
      27              : static int g_avif_last_error = 0;
      28              : 
      29           17 : static inline void apply_avif_config(avifEncoder *encoder, const cpres_config_t *config)
      30              : {
      31              :     int alpha_quality, threads, speed;
      32              :     float quality;
      33              : 
      34           17 :     if (!encoder || !config) {
      35            0 :         return;
      36              :     }
      37              : 
      38           17 :     quality = config->avif_quality;
      39           17 :     alpha_quality = config->avif_alpha_quality;
      40           17 :     threads = config->avif_threads;
      41              : 
      42           17 :     if (quality < 0) {
      43            1 :         quality = 0;
      44           16 :     } else if (quality > 100) {
      45            1 :         quality = 100;
      46              :     }
      47              : 
      48           17 :     if (alpha_quality < 0) {
      49            1 :         alpha_quality = 0;
      50           16 :     } else if (alpha_quality > 100) {
      51            1 :         alpha_quality = 100;
      52              :     }
      53              : 
      54           17 :     encoder->quality = quality;
      55           17 :     encoder->qualityAlpha = alpha_quality;
      56              : 
      57           17 :     if (threads > 0) {
      58           17 :         encoder->maxThreads = threads;
      59              :     }
      60              : 
      61           17 :     speed = config->avif_speed;
      62           17 :     if (speed < 0) {
      63            1 :         speed = 0;
      64           16 :     } else if (speed > 10) {
      65            1 :         speed = 10;
      66              :     }
      67              : 
      68           17 :     encoder->speed = speed;
      69              : 
      70           17 :     if (config->avif_lossless) {
      71            3 :         encoder->quality = AVIF_QUALITY_LOSSLESS;
      72            3 :         encoder->qualityAlpha = AVIF_QUALITY_LOSSLESS;
      73              :     }
      74              : }
      75              : 
      76           18 : static inline avifImage *create_avif_image_from_rgba(uint8_t *rgba_data, uint32_t width,
      77              :                                                      uint32_t height)
      78              : {
      79           18 :     avifImage *image = NULL;
      80              :     avifRGBImage rgb;
      81              : 
      82           18 :     if (!rgba_data || width == 0 || height == 0) {
      83            1 :         return image;
      84              :     }
      85              : 
      86           17 :     image = avifImageCreate((uint32_t)width, (uint32_t)height, 8, AVIF_PIXEL_FORMAT_YUV444);
      87           17 :     if (!image) {
      88            0 :         return NULL;
      89              :     }
      90              : 
      91           17 :     avifRGBImageSetDefaults(&rgb, image);
      92           17 :     rgb.format = AVIF_RGB_FORMAT_RGBA;
      93           17 :     rgb.depth = 8;
      94           17 :     rgb.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_AUTOMATIC;
      95           17 :     rgb.pixels = rgba_data;
      96           17 :     rgb.rowBytes = width * 4;
      97              : 
      98           17 :     if (avifImageRGBToYUV(image, &rgb) != AVIF_RESULT_OK) {
      99            0 :         avifImageDestroy(image);
     100              : 
     101            0 :         return NULL;
     102              :     }
     103              : 
     104           17 :     return image;
     105              : }
     106              : 
     107           18 : static inline cpres_error_t encode_avif_common(uint8_t *rgba_data, uint32_t width, uint32_t height,
     108              :                                                avifRWData *output, const cpres_config_t *config)
     109              : {
     110           18 :     avifImage *image = NULL;
     111           18 :     avifEncoder *encoder = NULL;
     112              :     avifResult result;
     113              : 
     114           18 :     if (!rgba_data || !output || !config) {
     115            0 :         return CPRES_ERROR_INVALID_PARAMETER;
     116              :     }
     117              : 
     118           18 :     image = create_avif_image_from_rgba(rgba_data, width, height);
     119           18 :     if (!image) {
     120            1 :         avif_set_last_error(AVIF_RESULT_OUT_OF_MEMORY);
     121              : 
     122            1 :         return CPRES_ERROR_OUT_OF_MEMORY;
     123              :     }
     124              : 
     125           17 :     encoder = avifEncoderCreate();
     126           17 :     if (!encoder) {
     127            0 :         avifImageDestroy(image);
     128            0 :         avif_set_last_error(AVIF_RESULT_OUT_OF_MEMORY);
     129              : 
     130            0 :         return CPRES_ERROR_OUT_OF_MEMORY;
     131              :     }
     132              : 
     133           17 :     apply_avif_config(encoder, config);
     134              : 
     135           17 :     result = avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE);
     136           17 :     if (result != AVIF_RESULT_OK) {
     137            0 :         avif_set_last_error(result);
     138            0 :         avifEncoderDestroy(encoder);
     139            0 :         avifImageDestroy(image);
     140              : 
     141            0 :         if (result == AVIF_RESULT_OUT_OF_MEMORY) {
     142            0 :             return CPRES_ERROR_OUT_OF_MEMORY;
     143              :         }
     144              : 
     145            0 :         return CPRES_ERROR_ENCODE_FAILED;
     146              :     }
     147              : 
     148           17 :     result = avifEncoderFinish(encoder, output);
     149              : 
     150           17 :     if (result != AVIF_RESULT_OK) {
     151            0 :         avif_set_last_error(result);
     152            0 :         avifEncoderDestroy(encoder);
     153            0 :         avifImageDestroy(image);
     154              : 
     155            0 :         if (result == AVIF_RESULT_OUT_OF_MEMORY) {
     156            0 :             return CPRES_ERROR_OUT_OF_MEMORY;
     157              :         }
     158              : 
     159            0 :         return CPRES_ERROR_ENCODE_FAILED;
     160              :     }
     161              : 
     162           17 :     avif_set_last_error(AVIF_RESULT_OK);
     163           17 :     avifEncoderDestroy(encoder);
     164           17 :     avifImageDestroy(image);
     165              : 
     166           17 :     return CPRES_OK;
     167              : }
     168              : 
     169            1 : extern int avif_get_last_error(void)
     170              : {
     171            1 :     return g_avif_last_error;
     172              : }
     173              : 
     174           20 : extern void avif_set_last_error(int error_code)
     175              : {
     176           20 :     g_avif_last_error = error_code;
     177           20 : }
     178              : 
     179           23 : extern cpres_error_t avif_encode_rgba_to_memory(uint8_t *rgba_data, uint32_t width, uint32_t height,
     180              :                                                 uint8_t **avif_data, size_t *avif_size,
     181              :                                                 const cpres_config_t *config)
     182              : {
     183           23 :     avifRWData encoded = AVIF_DATA_EMPTY;
     184              :     cpres_error_t err;
     185              : 
     186           23 :     if (!rgba_data || !avif_data || !avif_size || !config) {
     187            4 :         return CPRES_ERROR_INVALID_PARAMETER;
     188              :     }
     189              : 
     190           19 :     *avif_data = NULL;
     191           19 :     *avif_size = 0;
     192              : 
     193           19 :     if (!isfinite(config->avif_quality)) {
     194            1 :         return CPRES_ERROR_INVALID_PARAMETER;
     195              :     }
     196              : 
     197           18 :     err = encode_avif_common(rgba_data, width, height, &encoded, config);
     198           18 :     if (err != CPRES_OK) {
     199            1 :         if (encoded.data) {
     200            0 :             avifRWDataFree(&encoded);
     201              :         }
     202              : 
     203            1 :         return err;
     204              :     }
     205              : 
     206           17 :     *avif_size = encoded.size;
     207           17 :     *avif_data = (uint8_t *)malloc(encoded.size);
     208           17 :     if (!*avif_data) {
     209            0 :         avifRWDataFree(&encoded);
     210              : 
     211            0 :         return CPRES_ERROR_OUT_OF_MEMORY;
     212              :     }
     213              : 
     214           17 :     memcpy(*avif_data, encoded.data, encoded.size);
     215           17 :     avifRWDataFree(&encoded);
     216              : 
     217           17 :     return CPRES_OK;
     218              : }
        

Generated by: LCOV version 2.0-1