]> git.jsancho.org Git - lugaru.git/blob - Source/Utils/ImageIO.cpp
Rename all C++ headers with .hpp extension
[lugaru.git] / Source / Utils / ImageIO.cpp
1 /*
2 Copyright (C) 2003, 2010 - Wolfire Games
3 Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file)
4
5 This file is part of Lugaru.
6
7 Lugaru is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 Lugaru is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "Utils/ImageIO.hpp"
22
23 #include "Game.hpp"
24 #include "Utils/Folders.hpp"
25
26 #include <jpeglib.h>
27 #include <png.h>
28 #include <stdio.h>
29 #include <zlib.h>
30
31 extern bool visibleloading;
32
33 /* These two are needed for screenshot */
34 extern int kContextWidth;
35 extern int kContextHeight;
36
37 static bool load_png(const char * fname, ImageRec & tex);
38 static bool load_jpg(const char * fname, ImageRec & tex);
39 static bool save_screenshot_png(const char * fname);
40
41 ImageRec::ImageRec()
42 {
43     data = ( GLubyte* )malloc( 1024 * 1024 * 4 );
44 }
45
46 ImageRec::~ImageRec()
47 {
48     free(data);
49     data = NULL;
50 }
51
52 bool load_image(const char *file_name, ImageRec &tex)
53 {
54     if (visibleloading)
55         Game::LoadingScreen();
56
57     if ( tex.data == NULL )
58         return false;
59
60     const char *ptr = strrchr((char *)file_name, '.');
61     if (ptr) {
62         if (strcasecmp(ptr + 1, "png") == 0)
63             return load_png(file_name, tex);
64         else if (strcasecmp(ptr + 1, "jpg") == 0)
65             return load_jpg(file_name, tex);
66     }
67
68     STUBBED("Unsupported image type");
69     return false;
70 }
71
72 bool save_screenshot(const char *file_name)
73 {
74     const char *ptr = strrchr((char *)file_name, '.');
75     if (ptr) {
76         if (strcasecmp(ptr + 1, "png") == 0)
77             return save_screenshot_png((Folders::getScreenshotDir() + '/' + file_name).c_str());
78     }
79
80     STUBBED("Unsupported image type");
81     return false;
82 }
83
84 struct my_error_mgr {
85     struct jpeg_error_mgr pub; /* "public" fields */
86     jmp_buf setjmp_buffer; /* for return to caller */
87 };
88 typedef struct my_error_mgr * my_error_ptr;
89
90 static void my_error_exit(j_common_ptr cinfo)
91 {
92     struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err;
93     longjmp(err->setjmp_buffer, 1);
94 }
95
96 /* stolen from public domain example.c code in libjpg distribution. */
97 static bool load_jpg(const char *file_name, ImageRec &tex)
98 {
99     struct jpeg_decompress_struct cinfo;
100     struct my_error_mgr jerr;
101     JSAMPROW buffer[1]; /* Output row buffer */
102     int row_stride; /* physical row width in output buffer */
103     FILE *infile = fopen(file_name, "rb");
104
105     if (infile == NULL)
106         return false;
107
108     cinfo.err = jpeg_std_error(&jerr.pub);
109     jerr.pub.error_exit = my_error_exit;
110     if (setjmp(jerr.setjmp_buffer)) {
111         jpeg_destroy_decompress(&cinfo);
112         fclose(infile);
113         return false;
114     }
115
116     jpeg_create_decompress(&cinfo);
117     jpeg_stdio_src(&cinfo, infile);
118     (void) jpeg_read_header(&cinfo, TRUE);
119
120     cinfo.out_color_space = JCS_RGB;
121     cinfo.quantize_colors = 0;
122     (void) jpeg_calc_output_dimensions(&cinfo);
123     (void) jpeg_start_decompress(&cinfo);
124
125     row_stride = cinfo.output_width * cinfo.output_components;
126     tex.sizeX = cinfo.output_width;
127     tex.sizeY = cinfo.output_height;
128     tex.bpp = 24;
129
130     while (cinfo.output_scanline < cinfo.output_height) {
131         buffer[0] = (JSAMPROW)(char *)tex.data +
132                     ((cinfo.output_height - 1) - cinfo.output_scanline) * row_stride;
133         (void) jpeg_read_scanlines(&cinfo, buffer, 1);
134     }
135
136     (void) jpeg_finish_decompress(&cinfo);
137     jpeg_destroy_decompress(&cinfo);
138     fclose(infile);
139
140     return true;
141 }
142
143 /* stolen from public domain example.c code in libpng distribution. */
144 static bool load_png(const char *file_name, ImageRec &tex)
145 {
146     bool hasalpha = false;
147     png_structp png_ptr = NULL;
148     png_infop info_ptr = NULL;
149     png_uint_32 width, height;
150     int bit_depth, color_type, interlace_type;
151     bool retval = false;
152     png_byte **row_pointers = NULL;
153     FILE *fp = fopen(file_name, "rb");
154
155     if (fp == NULL) {
156         cerr << file_name << " not found" << endl;
157         return false;
158     }
159
160     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
161     if (png_ptr == NULL)
162         goto png_done;
163
164     info_ptr = png_create_info_struct(png_ptr);
165     if (info_ptr == NULL)
166         goto png_done;
167
168     if (setjmp(png_jmpbuf(png_ptr)))
169         goto png_done;
170
171     png_init_io(png_ptr, fp);
172     png_read_png(png_ptr, info_ptr,
173                  PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING,
174                  NULL);
175     png_get_IHDR(png_ptr, info_ptr, &width, &height,
176                  &bit_depth, &color_type, &interlace_type, NULL, NULL);
177
178     if (bit_depth != 8)  // transform SHOULD handle this...
179         goto png_done;
180
181     if (color_type & PNG_COLOR_MASK_PALETTE)  // !!! FIXME?
182         goto png_done;
183
184     if ((color_type & PNG_COLOR_MASK_COLOR) == 0)  // !!! FIXME?
185         goto png_done;
186
187     hasalpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0);
188     row_pointers = png_get_rows(png_ptr, info_ptr);
189     if (!row_pointers)
190         goto png_done;
191
192     if (!hasalpha) {
193         png_byte *dst = tex.data;
194         for (int i = height - 1; i >= 0; i--) {
195             png_byte *src = row_pointers[i];
196             for (unsigned j = 0; j < width; j++) {
197                 dst[0] = src[0];
198                 dst[1] = src[1];
199                 dst[2] = src[2];
200                 dst[3] = 0xFF;
201                 src += 3;
202                 dst += 4;
203             }
204         }
205     }
206
207     else {
208         png_byte *dst = tex.data;
209         int pitch = width * 4;
210         for (int i = height - 1; i >= 0; i--, dst += pitch)
211             memcpy(dst, row_pointers[i], pitch);
212     }
213
214     tex.sizeX = width;
215     tex.sizeY = height;
216     tex.bpp = 32;
217     retval = true;
218
219 png_done:
220     if (!retval) {
221         cerr << "There was a problem loading " << file_name << endl;
222     }
223     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
224     if (fp)
225         fclose(fp);
226     return (retval);
227 }
228
229 static bool save_screenshot_png(const char *file_name)
230 {
231     FILE *fp = NULL;
232     png_structp png_ptr = NULL;
233     png_infop info_ptr = NULL;
234     bool retval = false;
235
236     fp = fopen(file_name, "wb");
237     if (fp == NULL)
238         return false;
239
240     png_bytep *row_pointers = new png_bytep[kContextHeight];
241     png_bytep screenshot = new png_byte[kContextWidth * kContextHeight * 3];
242     if ((!screenshot) || (!row_pointers))
243         goto save_png_done;
244
245     glGetError();
246     glReadPixels(0, 0, kContextWidth, kContextHeight,
247                  GL_RGB, GL_UNSIGNED_BYTE, screenshot);
248     if (glGetError() != GL_NO_ERROR)
249         goto save_png_done;
250
251     for (int i = 0; i < kContextHeight; i++)
252         row_pointers[i] = screenshot + ((kContextWidth * ((kContextHeight - 1) - i)) * 3);
253
254     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
255     if (png_ptr == NULL)
256         goto save_png_done;
257
258     info_ptr = png_create_info_struct(png_ptr);
259     if (info_ptr == NULL)
260         goto save_png_done;
261
262     if (setjmp(png_jmpbuf(png_ptr)))
263         goto save_png_done;
264
265     png_init_io(png_ptr, fp);
266
267     if (setjmp(png_jmpbuf(png_ptr)))
268         goto save_png_done;
269
270     png_set_IHDR(png_ptr, info_ptr, kContextWidth, kContextHeight,
271                  8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
272                  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
273
274     png_write_info(png_ptr, info_ptr);
275
276     if (setjmp(png_jmpbuf(png_ptr)))
277         goto save_png_done;
278
279     png_write_image(png_ptr, row_pointers);
280
281     if (setjmp(png_jmpbuf(png_ptr)))
282         goto save_png_done;
283
284     png_write_end(png_ptr, NULL);
285     retval = true;
286
287 save_png_done:
288     png_destroy_write_struct(&png_ptr, &info_ptr);
289     delete[] screenshot;
290     delete[] row_pointers;
291     if (fp)
292         fclose(fp);
293     if (!retval)
294         unlink(file_name);
295     return retval;
296 }