LCOV - code coverage report
Current view: top level - src - avif.c (source / functions) Coverage Total Hit
Test: colopresso Coverage Report Lines: 77.2 % 101 78
Test Date: 2025-12-13 15:41:21 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 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 <stdbool.h>
      14              : #include <stdint.h>
      15              : #include <stdio.h>
      16              : #include <stdlib.h>
      17              : #include <string.h>
      18              : 
      19              : #include <avif/avif.h>
      20              : 
      21              : #include <colopresso.h>
      22              : 
      23              : #include "internal/avif.h"
      24              : #include "internal/log.h"
      25              : 
      26              : static int g_avif_last_error = 0;
      27              : 
      28            1 : int cpres_avif_get_last_error(void) { return g_avif_last_error; }
      29              : 
      30           20 : void cpres_avif_set_last_error(int error_code) { g_avif_last_error = error_code; }
      31              : 
      32           17 : static void apply_avif_config(avifEncoder *encoder, const cpres_config_t *config) {
      33              :   float quality;
      34              :   int alpha_quality, threads, speed;
      35              : 
      36           17 :   if (!encoder || !config) {
      37            0 :     return;
      38              :   }
      39              : 
      40           17 :   quality = config->avif_quality;
      41           17 :   alpha_quality = config->avif_alpha_quality;
      42           17 :   threads = config->avif_threads;
      43              : 
      44           17 :   if (quality < 0) {
      45            1 :     quality = 0;
      46           16 :   } else if (quality > 100) {
      47            1 :     quality = 100;
      48              :   }
      49           17 :   if (alpha_quality < 0) {
      50            1 :     alpha_quality = 0;
      51           16 :   } else if (alpha_quality > 100) {
      52            1 :     alpha_quality = 100;
      53              :   }
      54              : 
      55           17 :   encoder->quality = quality;
      56           17 :   encoder->qualityAlpha = alpha_quality;
      57              : 
      58           17 :   if (threads > 0) {
      59           17 :     encoder->maxThreads = threads;
      60              :   }
      61              : 
      62           17 :   speed = config->avif_speed;
      63           17 :   if (speed < 0 || speed > 10) {
      64            2 :     if (speed < 0) {
      65            1 :       speed = 0;
      66            1 :     } else if (speed > 10) {
      67            1 :       speed = 10;
      68              :     }
      69              :   }
      70           17 :   encoder->speed = speed;
      71              : 
      72           17 :   if (config->avif_lossless) {
      73            3 :     encoder->quality = AVIF_QUALITY_LOSSLESS;
      74            3 :     encoder->qualityAlpha = AVIF_QUALITY_LOSSLESS;
      75              :   }
      76              : }
      77              : 
      78           18 : static avifImage *create_avif_image_from_rgba(uint8_t *rgba_data, uint32_t width, uint32_t height) {
      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            0 :     return NULL;
     101              :   }
     102              : 
     103           17 :   return image;
     104              : }
     105              : 
     106           18 : static cpres_error_t encode_avif_common(uint8_t *rgba_data, uint32_t width, uint32_t height, avifRWData *output, const cpres_config_t *config) {
     107           18 :   avifImage *image = NULL;
     108           18 :   avifEncoder *encoder = NULL;
     109              :   avifResult result;
     110              : 
     111           18 :   if (!rgba_data || !output || !config) {
     112            0 :     return CPRES_ERROR_INVALID_PARAMETER;
     113              :   }
     114              : 
     115           18 :   image = create_avif_image_from_rgba(rgba_data, width, height);
     116           18 :   if (!image) {
     117            1 :     cpres_avif_set_last_error(AVIF_RESULT_OUT_OF_MEMORY);
     118            1 :     return CPRES_ERROR_OUT_OF_MEMORY;
     119              :   }
     120              : 
     121           17 :   encoder = avifEncoderCreate();
     122           17 :   if (!encoder) {
     123            0 :     avifImageDestroy(image);
     124            0 :     cpres_avif_set_last_error(AVIF_RESULT_OUT_OF_MEMORY);
     125            0 :     return CPRES_ERROR_OUT_OF_MEMORY;
     126              :   }
     127              : 
     128           17 :   apply_avif_config(encoder, config);
     129              : 
     130           17 :   result = avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE);
     131           17 :   if (result != AVIF_RESULT_OK) {
     132            0 :     cpres_avif_set_last_error(result);
     133            0 :     avifEncoderDestroy(encoder);
     134            0 :     avifImageDestroy(image);
     135            0 :     if (result == AVIF_RESULT_OUT_OF_MEMORY) {
     136            0 :       return CPRES_ERROR_OUT_OF_MEMORY;
     137              :     }
     138            0 :     return CPRES_ERROR_ENCODE_FAILED;
     139              :   }
     140              : 
     141           17 :   result = avifEncoderFinish(encoder, output);
     142              : 
     143           17 :   if (result != AVIF_RESULT_OK) {
     144            0 :     cpres_avif_set_last_error(result);
     145            0 :     avifEncoderDestroy(encoder);
     146            0 :     avifImageDestroy(image);
     147            0 :     if (result == AVIF_RESULT_OUT_OF_MEMORY) {
     148            0 :       return CPRES_ERROR_OUT_OF_MEMORY;
     149              :     }
     150            0 :     return CPRES_ERROR_ENCODE_FAILED;
     151              :   }
     152              : 
     153           17 :   cpres_avif_set_last_error(AVIF_RESULT_OK);
     154           17 :   avifEncoderDestroy(encoder);
     155           17 :   avifImageDestroy(image);
     156           17 :   return CPRES_OK;
     157              : }
     158              : 
     159           22 : cpres_error_t cpres_avif_encode_rgba_to_memory(uint8_t *rgba_data, uint32_t width, uint32_t height, uint8_t **avif_data, size_t *avif_size, const cpres_config_t *config) {
     160           22 :   avifRWData encoded = AVIF_DATA_EMPTY;
     161              :   cpres_error_t err;
     162              : 
     163           22 :   if (!rgba_data || !avif_data || !avif_size || !config) {
     164            4 :     return CPRES_ERROR_INVALID_PARAMETER;
     165              :   }
     166              : 
     167           18 :   *avif_data = NULL;
     168           18 :   *avif_size = 0;
     169              : 
     170           18 :   err = encode_avif_common(rgba_data, width, height, &encoded, config);
     171           18 :   if (err != CPRES_OK) {
     172            1 :     if (encoded.data) {
     173            0 :       avifRWDataFree(&encoded);
     174              :     }
     175            1 :     return err;
     176              :   }
     177              : 
     178           17 :   *avif_size = encoded.size;
     179           17 :   *avif_data = (uint8_t *)malloc(encoded.size);
     180           17 :   if (!*avif_data) {
     181            0 :     avifRWDataFree(&encoded);
     182            0 :     return CPRES_ERROR_OUT_OF_MEMORY;
     183              :   }
     184           17 :   memcpy(*avif_data, encoded.data, encoded.size);
     185           17 :   avifRWDataFree(&encoded);
     186              : 
     187           17 :   return CPRES_OK;
     188              : }
        

Generated by: LCOV version 2.0-1