LCOV - code coverage report
Current view: top level - src - png.c (source / functions) Coverage Total Hit
Test: colopresso Coverage Report Lines: 71.4 % 91 65
Test Date: 2026-02-16 05:23:27 Functions: 100.0 % 4 4
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 <stdbool.h>
      13              : #include <stdint.h>
      14              : #include <stdio.h>
      15              : #include <stdlib.h>
      16              : #include <string.h>
      17              : 
      18              : #include <png.h>
      19              : 
      20              : #include <colopresso.h>
      21              : 
      22              : #include "internal/log.h"
      23              : #include "internal/png.h"
      24              : 
      25              : typedef struct {
      26              :   const uint8_t *data;
      27              :   size_t size;
      28              :   size_t pos;
      29              : } png_memory_reader_t;
      30              : 
      31        18210 : static void png_read_from_memory(png_structp png_ptr, png_bytep data, png_size_t length) {
      32              :   png_memory_reader_t *reader;
      33              : 
      34        18210 :   reader = (png_memory_reader_t *)png_get_io_ptr(png_ptr);
      35        18210 :   if (reader->pos + length > reader->size) {
      36            0 :     colopresso_log(CPRES_LOG_LEVEL_ERROR, "Attempted to read past end of PNG data");
      37            0 :     png_error(png_ptr, "Read past end of data");
      38              :     return;
      39              :   }
      40              : 
      41        18210 :   memcpy(data, reader->data + reader->pos, length);
      42              : 
      43        18210 :   reader->pos += length;
      44              : }
      45              : 
      46           93 : static inline cpres_error_t read_png_common(png_structp png, png_infop info, uint8_t **rgba_data, png_uint_32 *width, png_uint_32 *height) {
      47              :   png_byte color_type, bit_depth;
      48              :   png_bytep *row_pointers;
      49              :   png_uint_32 y;
      50              :   size_t row_bytes, total_size;
      51              : 
      52           93 :   png_read_info(png, info);
      53              : 
      54           93 :   *width = png_get_image_width(png, info);
      55           93 :   *height = png_get_image_height(png, info);
      56           93 :   color_type = png_get_color_type(png, info);
      57           93 :   bit_depth = png_get_bit_depth(png, info);
      58              : 
      59           93 :   if (*width <= 0 || *height <= 0 || *width > 65536 || *height > 65536) {
      60            0 :     return CPRES_ERROR_INVALID_PNG;
      61              :   }
      62              : 
      63           93 :   if (bit_depth == 16) {
      64            9 :     png_set_strip_16(png);
      65              :   }
      66              : 
      67           93 :   if (color_type == PNG_COLOR_TYPE_PALETTE) {
      68            1 :     png_set_palette_to_rgb(png);
      69              :   }
      70              : 
      71           93 :   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
      72            0 :     png_set_expand_gray_1_2_4_to_8(png);
      73              :   }
      74              : 
      75           93 :   if (png_get_valid(png, info, PNG_INFO_tRNS)) {
      76            0 :     png_set_tRNS_to_alpha(png);
      77              :   }
      78              : 
      79           93 :   if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE) {
      80            1 :     png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
      81              :   }
      82              : 
      83           93 :   if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
      84            0 :     png_set_gray_to_rgb(png);
      85              :   }
      86              : 
      87           93 :   png_read_update_info(png, info);
      88              : 
      89           93 :   row_bytes = png_get_rowbytes(png, info);
      90              : 
      91           93 :   if (*height > 0 && row_bytes > SIZE_MAX / (*height)) {
      92            0 :     colopresso_log(CPRES_LOG_LEVEL_ERROR, "Integer overflow detected: image too large");
      93            0 :     return CPRES_ERROR_OUT_OF_MEMORY;
      94              :   }
      95              : 
      96           93 :   total_size = row_bytes * (*height);
      97              : 
      98           93 :   *rgba_data = (uint8_t *)malloc(total_size);
      99           93 :   if (!*rgba_data) {
     100            0 :     return CPRES_ERROR_OUT_OF_MEMORY;
     101              :   }
     102              : 
     103           93 :   row_pointers = (png_bytep *)malloc(sizeof(png_bytep) * (*height));
     104           93 :   if (!row_pointers) {
     105            0 :     free(*rgba_data);
     106            0 :     *rgba_data = NULL;
     107            0 :     return CPRES_ERROR_OUT_OF_MEMORY;
     108              :   }
     109              : 
     110        38310 :   for (y = 0; y < *height; y++) {
     111        38217 :     row_pointers[y] = *rgba_data + y * row_bytes;
     112              :   }
     113              : 
     114           93 :   png_read_image(png, row_pointers);
     115              : 
     116           93 :   free(row_pointers);
     117              : 
     118           93 :   return CPRES_OK;
     119              : }
     120              : 
     121           86 : extern cpres_error_t png_decode_from_memory(const uint8_t *png_data, size_t png_size, uint8_t **rgba_data, png_uint_32 *width, png_uint_32 *height) {
     122              :   png_structp png;
     123              :   png_infop info;
     124           86 :   png_memory_reader_t reader = {0};
     125              :   cpres_error_t result;
     126              : 
     127           86 :   if (!png_data || png_size < 8) {
     128            0 :     return CPRES_ERROR_INVALID_PARAMETER;
     129              :   }
     130              : 
     131           86 :   if (png_sig_cmp(png_data, 0, 8) != 0) {
     132            3 :     return CPRES_ERROR_INVALID_PNG;
     133              :   }
     134              : 
     135           83 :   png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
     136           83 :   if (!png) {
     137            0 :     return CPRES_ERROR_OUT_OF_MEMORY;
     138              :   }
     139              : 
     140           83 :   info = png_create_info_struct(png);
     141           83 :   if (!info) {
     142            0 :     png_destroy_read_struct(&png, NULL, NULL);
     143            0 :     return CPRES_ERROR_OUT_OF_MEMORY;
     144              :   }
     145              : 
     146           83 :   if (setjmp(png_jmpbuf(png))) {
     147            0 :     png_destroy_read_struct(&png, &info, NULL);
     148            0 :     return CPRES_ERROR_INVALID_PNG;
     149              :   }
     150              : 
     151           83 :   reader.data = png_data;
     152           83 :   reader.size = png_size;
     153           83 :   reader.pos = 0;
     154           83 :   png_set_read_fn(png, &reader, png_read_from_memory);
     155              : 
     156           83 :   result = read_png_common(png, info, rgba_data, width, height);
     157              : 
     158           83 :   png_destroy_read_struct(&png, &info, NULL);
     159              : 
     160           83 :   return result;
     161              : }
     162              : 
     163              : #if COLOPRESSO_WITH_FILE_OPS
     164           12 : extern cpres_error_t png_decode_from_file(const char *filename, uint8_t **rgba_data, png_uint_32 *width, png_uint_32 *height) {
     165              :   FILE *fp;
     166              :   png_structp png;
     167              :   png_infop info;
     168              :   cpres_error_t result;
     169              : 
     170           12 :   fp = fopen(filename, "rb");
     171           12 :   if (!fp) {
     172            2 :     return CPRES_ERROR_FILE_NOT_FOUND;
     173              :   }
     174              : 
     175           10 :   png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
     176           10 :   if (!png) {
     177            0 :     fclose(fp);
     178            0 :     return CPRES_ERROR_OUT_OF_MEMORY;
     179              :   }
     180              : 
     181           10 :   info = png_create_info_struct(png);
     182           10 :   if (!info) {
     183            0 :     png_destroy_read_struct(&png, NULL, NULL);
     184            0 :     fclose(fp);
     185            0 :     return CPRES_ERROR_OUT_OF_MEMORY;
     186              :   }
     187              : 
     188           10 :   if (setjmp(png_jmpbuf(png))) {
     189            0 :     png_destroy_read_struct(&png, &info, NULL);
     190            0 :     fclose(fp);
     191            0 :     return CPRES_ERROR_INVALID_PNG;
     192              :   }
     193              : 
     194           10 :   png_init_io(png, fp);
     195              : 
     196           10 :   result = read_png_common(png, info, rgba_data, width, height);
     197              : 
     198           10 :   png_destroy_read_struct(&png, &info, NULL);
     199           10 :   fclose(fp);
     200              : 
     201           10 :   return result;
     202              : }
     203              : 
     204              : #endif /* COLOPRESSO_WITH_FILE_OPS */
        

Generated by: LCOV version 2.0-1