]> git.jsancho.org Git - lugaru.git/blob - Source/Utils/ImageIO.cpp
Improved error reporting of screenshot saving, fixed screenshot path.
[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
58     if ( tex.data == NULL ) {
59         return false;
60     }
61
62     const char *ptr = strrchr((char *)file_name, '.');
63     if (ptr) {
64         if (strcasecmp(ptr + 1, "png") == 0) {
65             return load_png(file_name, tex);
66         } else if (strcasecmp(ptr + 1, "jpg") == 0) {
67             return load_jpg(file_name, tex);
68         }
69     }
70
71     std::cerr << "Unsupported image type" << std::endl;
72     return false;
73 }
74
75 bool save_screenshot(const char *file_name)
76 {
77     const char *ptr = strrchr((char *)file_name, '.');
78     if (ptr) {
79         if (strcasecmp(ptr + 1, "png") == 0) {
80             return save_screenshot_png((Folders::getScreenshotDir() + '/' + file_name).c_str());
81         }
82     }
83
84     std::cerr << "Unsupported image type" << std::endl;
85     return false;
86 }
87
88 struct my_error_mgr {
89     struct jpeg_error_mgr pub; /* "public" fields */
90     jmp_buf setjmp_buffer; /* for return to caller */
91 };
92 typedef struct my_error_mgr * my_error_ptr;
93
94 static void my_error_exit(j_common_ptr cinfo)
95 {
96     struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err;
97     longjmp(err->setjmp_buffer, 1);
98 }
99
100 /* stolen from public domain example.c code in libjpg distribution. */
101 static bool load_jpg(const char *file_name, ImageRec &tex)
102 {
103     struct jpeg_decompress_struct cinfo;
104     struct my_error_mgr jerr;
105     JSAMPROW buffer[1]; /* Output row buffer */
106     int row_stride; /* physical row width in output buffer */
107     errno = 0;
108     FILE *infile = fopen(file_name, "rb");
109
110     if (infile == NULL) {
111         perror((std::string("Couldn't open file ") + file_name).c_str());
112         return false;
113     }
114
115     cinfo.err = jpeg_std_error(&jerr.pub);
116     jerr.pub.error_exit = my_error_exit;
117     if (setjmp(jerr.setjmp_buffer)) {
118         jpeg_destroy_decompress(&cinfo);
119         fclose(infile);
120         return false;
121     }
122
123     jpeg_create_decompress(&cinfo);
124     jpeg_stdio_src(&cinfo, infile);
125     (void) jpeg_read_header(&cinfo, TRUE);
126
127     cinfo.out_color_space = JCS_RGB;
128     cinfo.quantize_colors = 0;
129     (void) jpeg_calc_output_dimensions(&cinfo);
130     (void) jpeg_start_decompress(&cinfo);
131
132     row_stride = cinfo.output_width * cinfo.output_components;
133     tex.sizeX = cinfo.output_width;
134     tex.sizeY = cinfo.output_height;
135     tex.bpp = 24;
136
137     while (cinfo.output_scanline < cinfo.output_height) {
138         buffer[0] = (JSAMPROW)(char *)tex.data +
139                     ((cinfo.output_height - 1) - cinfo.output_scanline) * row_stride;
140         (void) jpeg_read_scanlines(&cinfo, buffer, 1);
141     }
142
143     (void) jpeg_finish_decompress(&cinfo);
144     jpeg_destroy_decompress(&cinfo);
145     fclose(infile);
146
147     return true;
148 }
149
150 /* stolen from public domain example.c code in libpng distribution. */
151 static bool load_png(const char *file_name, ImageRec &tex)
152 {
153     bool hasalpha = false;
154     png_structp png_ptr = NULL;
155     png_infop info_ptr = NULL;
156     png_uint_32 width, height;
157     int bit_depth, color_type, interlace_type;
158     bool retval = false;
159     png_byte **row_pointers = NULL;
160     errno = 0;
161     FILE *fp = fopen(file_name, "rb");
162
163     if (fp == NULL) {
164         perror((std::string("Couldn't open file ") + file_name).c_str());
165         return false;
166     }
167
168     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
169     if (png_ptr == NULL)
170         goto png_done;
171
172     info_ptr = png_create_info_struct(png_ptr);
173     if (info_ptr == NULL)
174         goto png_done;
175
176     if (setjmp(png_jmpbuf(png_ptr)))
177         goto png_done;
178
179     png_init_io(png_ptr, fp);
180     png_read_png(png_ptr, info_ptr,
181                  PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING,
182                  NULL);
183     png_get_IHDR(png_ptr, info_ptr, &width, &height,
184                  &bit_depth, &color_type, &interlace_type, NULL, NULL);
185
186     if (bit_depth != 8)  // transform SHOULD handle this...
187         goto png_done;
188
189     if (color_type & PNG_COLOR_MASK_PALETTE)  // !!! FIXME?
190         goto png_done;
191
192     if ((color_type & PNG_COLOR_MASK_COLOR) == 0)  // !!! FIXME?
193         goto png_done;
194
195     hasalpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0);
196     row_pointers = png_get_rows(png_ptr, info_ptr);
197     if (!row_pointers)
198         goto png_done;
199
200     if (!hasalpha) {
201         png_byte *dst = tex.data;
202         for (int i = height - 1; i >= 0; i--) {
203             png_byte *src = row_pointers[i];
204             for (unsigned j = 0; j < width; j++) {
205                 dst[0] = src[0];
206                 dst[1] = src[1];
207                 dst[2] = src[2];
208                 dst[3] = 0xFF;
209                 src += 3;
210                 dst += 4;
211             }
212         }
213     }
214
215     else {
216         png_byte *dst = tex.data;
217         int pitch = width * 4;
218         for (int i = height - 1; i >= 0; i--, dst += pitch)
219             memcpy(dst, row_pointers[i], pitch);
220     }
221
222     tex.sizeX = width;
223     tex.sizeY = height;
224     tex.bpp = 32;
225     retval = true;
226
227 png_done:
228     if (!retval) {
229         cerr << "There was a problem loading " << file_name << endl;
230     }
231     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
232     if (fp)
233         fclose(fp);
234     return (retval);
235 }
236
237 static bool save_screenshot_png(const char *file_name)
238 {
239     FILE *fp = NULL;
240     png_structp png_ptr = NULL;
241     png_infop info_ptr = NULL;
242     bool retval = false;
243
244     errno = 0;
245     fp = fopen(file_name, "wb");
246     if (fp == NULL) {
247         perror((std::string("Couldn't open file ") + file_name).c_str());
248         return false;
249     }
250
251     png_bytep *row_pointers = new png_bytep[kContextHeight];
252     png_bytep screenshot = new png_byte[kContextWidth * kContextHeight * 3];
253     if ((!screenshot) || (!row_pointers))
254         goto save_png_done;
255
256     glGetError();
257     glReadPixels(0, 0, kContextWidth, kContextHeight,
258                  GL_RGB, GL_UNSIGNED_BYTE, screenshot);
259     if (glGetError() != GL_NO_ERROR)
260         goto save_png_done;
261
262     for (int i = 0; i < kContextHeight; i++)
263         row_pointers[i] = screenshot + ((kContextWidth * ((kContextHeight - 1) - i)) * 3);
264
265     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
266     if (png_ptr == NULL)
267         goto save_png_done;
268
269     info_ptr = png_create_info_struct(png_ptr);
270     if (info_ptr == NULL)
271         goto save_png_done;
272
273     if (setjmp(png_jmpbuf(png_ptr)))
274         goto save_png_done;
275
276     png_init_io(png_ptr, fp);
277
278     if (setjmp(png_jmpbuf(png_ptr)))
279         goto save_png_done;
280
281     png_set_IHDR(png_ptr, info_ptr, kContextWidth, kContextHeight,
282                  8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
283                  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
284
285     png_write_info(png_ptr, info_ptr);
286
287     if (setjmp(png_jmpbuf(png_ptr)))
288         goto save_png_done;
289
290     png_write_image(png_ptr, row_pointers);
291
292     if (setjmp(png_jmpbuf(png_ptr)))
293         goto save_png_done;
294
295     png_write_end(png_ptr, NULL);
296     retval = true;
297
298 save_png_done:
299     png_destroy_write_struct(&png_ptr, &info_ptr);
300     delete[] screenshot;
301     delete[] row_pointers;
302     if (fp)
303         fclose(fp);
304     if (!retval)
305         unlink(file_name);
306     return retval;
307 }