…aaand add PREFIX to the freebsd makefile too
[voxelands-alt.git] / src / graphics / image_png.c
blobe8e43fb1b070a3308bc31a24ce2daee725faaf6c
1 /************************************************************************
2 * image_png.c
3 * voxelands - 3d voxel world sandbox game
4 * Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>
18 ************************************************************************/
20 #include "common.h"
21 #include "graphics.h"
22 #include "file.h"
23 #include "path.h"
25 #include <string.h>
26 #include <png.h>
28 static void user_read_fn(png_structp png_ptr, png_bytep buffer, png_size_t size)
30 file_t *data = png_get_io_ptr(png_ptr);
31 if( data->len && (data->pos + size) > data->len )
33 png_error(png_ptr, "read error loading image");
34 return;
37 memcpy(buffer, data->data + data->pos, size);
38 data->pos += size;
41 /* is it a png image? */
42 int image_is_png(file_t *f)
44 return png_check_sig(f->data, 4);
47 /* load a png image to pixel data */
48 int image_load_png(file_t *f, image_t *p)
50 png_structp png_ptr;
51 png_infop info_ptr;
52 unsigned int sig_read = 0;
53 png_uint_32 width, height;
54 int bit_depth;
55 int color_type;
56 int interlace_type;
57 int i;
58 int rowbytes;
59 int bpp;
60 png_bytep *row_pointers;
62 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
63 if (png_ptr == NULL)
64 return 1;
66 png_set_error_fn(png_ptr, NULL, NULL, NULL);
67 png_set_read_fn(png_ptr, (png_voidp)f, user_read_fn);
68 info_ptr = png_create_info_struct(png_ptr);
69 if (!info_ptr) {
70 png_destroy_read_struct(&png_ptr, NULL, NULL);
71 return 1;
74 /* this is libpng's error handling, if something goes bad we'll
75 * end up jumping back here where we can exit gracefullyish */
76 if (setjmp(png_jmpbuf(png_ptr))) {
77 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
78 return 1;
81 png_init_io(png_ptr, (FILE*)f);
82 png_set_sig_bytes(png_ptr, sig_read);
83 png_read_info(png_ptr, info_ptr);
84 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
86 p->w = width;
87 p->h = height;
89 if (color_type == PNG_COLOR_TYPE_PALETTE)
90 png_set_expand(png_ptr);
91 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
92 png_set_expand(png_ptr);
93 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
94 png_set_expand(png_ptr);
96 png_read_update_info(png_ptr, info_ptr);
98 /* get the size of image rows in bytes */
99 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
100 bpp = rowbytes/width;
102 p->pixels = malloc(sizeof(png_byte)*(rowbytes * height));
103 if (!p->pixels) {
104 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
105 return 1;
108 /* libpng reads by row, not by chunk, so we need row pointers to load to */
109 row_pointers = alloca(sizeof(png_bytep)*height);
110 if (!row_pointers) {
111 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
112 free(p->pixels);
113 return 1;
116 /* and this ensures the rows point to the correct place in the pixel data */
117 for (i=0; i < height; ++i) {
118 row_pointers[i] = p->pixels + i * rowbytes;
121 /* finally let libpng read everything in */
122 png_read_image(png_ptr, row_pointers);
124 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
126 /* check for a 32bit RGBA image */
127 if (bpp != 4) {
128 /* convert 24bit RGB to 32bit RGBA */
129 if (bpp == 3) {
130 int j = 0;
131 int m = rowbytes*height;
132 unsigned char *px = p->pixels;
133 p->pixels = malloc(p->w*p->h*4);
134 for (i=0; i<m; i+=3) {
135 p->pixels[j++] = px[i];
136 p->pixels[j++] = px[i+1];
137 p->pixels[j++] = px[i+2];
138 p->pixels[j++] = 255;
140 free(px);
141 /* unsupported image */
142 }else{
143 vlprintf("PNG: unsupported image format: %dbpp",bpp);
144 free(p->pixels);
145 return 1;
149 return 0;
152 /* write pixel data to a png image */
153 int image_save_png(image_t *p, char* file)
155 char buff[2048];
156 FILE *f;
157 int i;
158 int rb;
159 png_structp png_ptr;
160 png_infop info_ptr;
161 png_bytep *row_pointers;
163 if (!path_get(NULL,file,0,buff,2048))
164 return 1;
165 f = fopen(buff,"wb");
166 if (!f)
167 return 1;
169 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
170 if (!png_ptr) {
171 fclose(f);
172 return 1;
175 /* Allocate/initialize the image information data. REQUIRED */
176 info_ptr = png_create_info_struct(png_ptr);
177 if (!info_ptr) {
178 fclose(f);
179 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
180 return 1;
183 /* this is libpng's error handling, if something goes bad we'll
184 * end up jumping back here where we can exit gracefullyish */
185 if (setjmp(png_jmpbuf(png_ptr))) {
186 fclose(f);
187 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
188 return 1;
191 png_init_io(png_ptr, f);
193 png_set_compression_level(png_ptr, 9);
195 png_set_IHDR(
196 png_ptr,
197 info_ptr,
198 p->w,
199 p->h,
201 PNG_COLOR_TYPE_RGB_ALPHA,
202 PNG_INTERLACE_NONE,
203 PNG_COMPRESSION_TYPE_BASE,
204 PNG_FILTER_TYPE_BASE
207 /* Write the file header information */
208 png_write_info(png_ptr, info_ptr);
210 /* create rows for later writing */
211 row_pointers = alloca(sizeof(png_bytep)*p->h);
212 rb = p->w*4;
213 if (!row_pointers) {
214 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
215 return 1;
218 /* and this ensures the rows point to the correct place in the pixel data */
219 for (i=0; i<p->h; ++i) {
220 row_pointers[i] = p->pixels + (i * rb);
222 png_write_image(png_ptr, row_pointers);
224 png_write_end(png_ptr, info_ptr);
226 /* clean up */
227 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
229 fclose(f);
231 return 0;