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