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

Generated by: LCOV version 2.0-1