summaryrefslogblamecommitdiffstats
path: root/src/loadpng.c
blob: ed12886bd5e4a4b7b04033b6720227cef87d84bd (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                              

                                                                                       


                                            
                                      
 

                         
 


                                    
 


                                                     
 

                                                   
 



                                                                                              
 


                                                   
 


                                                  
 

                                        
 

                                                   
 
                                         
 


                                                                                                    
 





                                                                                               
 
                                                
 


                                                                
 



                                                                                           
 

                                        
 

                                                                        
 

                                          
 

                             
 

                                                                        
 








                                                                                                  
 
/*
 * Copyright 2017 DiUS Computing Pty Ltd. All rights reserved.
 *
 * Released under GPLv3, see LICENSE for details.
 *
 * @author Johny Mattsson <jmattsson@dius.com.au>
 */
#include "loadpng.h"
#include <stdlib.h>
#include <assert.h>
#include <png.h>

static_assert(sizeof(png_byte) == sizeof(((ql_raster_image_t *) 0)->data[0]),
	      "Code relies on png_byte being compatible with ql_raster_image_t data ");

ql_raster_image_t *loadpng(const char *path)
{
	ql_raster_image_t *ret = NULL;

	if (!path)
		goto out;

	FILE *f = fopen(path, "rb");
	if (!f)
		goto out;

	uint8_t header[8];
	if (fread(header, 1, sizeof(header), f) != 8)
		goto close_out;

	if (!png_check_sig(header, sizeof(header)))
		goto close_out;

	png_infop info_ptr = NULL, end_ptr = NULL;
	png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (!png_ptr)
		goto close_out;

	info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr)
		goto destroy_read_out;

	end_ptr = png_create_info_struct(png_ptr);
	if (!end_ptr)
		goto destroy_read_out;

	if (setjmp(png_jmpbuf(png_ptr)))
		goto destroy_read_out;

	png_init_io(png_ptr, f);
	png_set_sig_bytes(png_ptr, sizeof(header));

	png_read_info(png_ptr, info_ptr);

	png_uint_32 width, height;
	int bit_depth, color_type;
	png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);

	if (color_type & PNG_COLOR_MASK_ALPHA)
		png_set_strip_alpha(png_ptr);
	if (color_type & (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE))
		png_set_rgb_to_gray_fixed(png_ptr, 1, -1, -1);	// force into grayscale
	if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
		png_set_expand_gray_1_2_4_to_8(png_ptr);	// get us a known output format

	png_read_update_info(png_ptr, info_ptr);

	png_bytepp row_ptrs = calloc(height, sizeof(png_bytep));
	if (!row_ptrs)
		goto destroy_read_out;

	const unsigned row_bytes = width * sizeof(png_byte);
	ql_raster_image_t *img = calloc(1, sizeof(ql_raster_image_t) + height * row_bytes);
	if (!img)
		goto free_image_out;

	if (setjmp(png_jmpbuf(png_ptr)))
		goto free_image_out;

	for (png_uint_32 i = 0; i < height; ++i)
		row_ptrs[i] = (png_bytep) (img->data + (i * row_bytes));

	png_read_image(png_ptr, row_ptrs);
	png_read_end(png_ptr, end_ptr);

	img->height = height;
	img->width = width;

	ret = img;
	img = NULL;		// don't free it, we're returning it now

 free_image_out:
	free(img);
	free(row_ptrs);
 destroy_read_out:
	png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : NULL, end_ptr ? &end_ptr : NULL);
 close_out:
	fclose(f);
 out:
	return ret;
}