1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom: a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2006 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 * Copyright 2005, 2006 by
12 * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 * DESCRIPTION:
30 * Screenshot functions, moved out of i_video.c
31 *
32 *-----------------------------------------------------------------------------
33 */
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #include <stdlib.h>
40
41 #include "SDL.h"
42
43 #ifdef HAVE_LIBPNG
44 #include <png.h>
45 #endif
46
47 #include "doomstat.h"
48 #include "doomdef.h"
49 #include "doomtype.h"
50 #include "v_video.h"
51 #include "i_video.h"
52 #include "z_zone.h"
53 #include "lprintf.h"
54
55 #ifdef HAVE_LIBPNG
56
57 #ifndef png_error_ptr_NULL
58 #define png_error_ptr_NULL NULL
59 #endif
60
61 #ifndef png_infopp_NULL
62 #define png_infopp_NULL NULL
63 #endif
64
65
66 //
67 // Error functions needed by libpng
68 //
69
error_fn(png_structp p,png_const_charp s)70 static void error_fn(png_structp p, png_const_charp s)
71 {
72 lprintf(LO_ERROR, "I_ScreenShot: %s\n", s);
73 }
74
warning_fn(png_structp p,png_const_charp s)75 static void warning_fn(png_structp p, png_const_charp s)
76 {
77 lprintf(LO_WARN, "I_ScreenShot: %s\n", s);
78 }
79
80 //
81 // Software mode screenshots
82 //
83
84 // Write the indexed palette of the passed surface to the PNG file
85 // Called from I_ScreenShot in 8bpp mode only
86
write_png_palette(png_struct * png_ptr,png_info * info_ptr,SDL_Surface * scr)87 static int write_png_palette(
88 png_struct *png_ptr, png_info *info_ptr, SDL_Surface *scr)
89 {
90 int i, result = -1;
91 png_color *palette;
92
93 palette = malloc(sizeof(*palette) * scr->format->palette->ncolors);
94
95 if (palette)
96 {
97 // Convert SDL palette to libpng
98 for (i = 0; i < scr->format->palette->ncolors; i++) {
99 palette[i].red = scr->format->palette->colors[i].r;
100 palette[i].green = scr->format->palette->colors[i].g;
101 palette[i].blue = scr->format->palette->colors[i].b;
102 }
103
104 png_set_PLTE(png_ptr, info_ptr,
105 palette, scr->format->palette->ncolors);
106
107 free(palette);
108 result = 0;
109 }
110 return result;
111 }
112
113 // Helper functions for screenshot_sdl
114 // Encapsulate differences between writing indexed palette and hicolor images
115
fill_buffer_indexed(SDL_Surface * scr,void * buffer)116 static void fill_buffer_indexed(SDL_Surface *scr, void *buffer)
117 {
118 memcpy(buffer, scr->pixels, SCREENWIDTH * SCREENHEIGHT);
119 }
120
fill_buffer_hicolor(SDL_Surface * scr,void * buffer)121 static void fill_buffer_hicolor(SDL_Surface *scr, void *buffer)
122 {
123 SDL_PixelFormat *fmt = scr->format;
124 png_color *pixel = buffer;
125 byte *source = scr->pixels;
126 int y;
127
128 // translate SDL pixel format into png_color
129 for (y = SCREENWIDTH * SCREENHEIGHT;
130 y > 0;
131 pixel++, source += fmt->BytesPerPixel, y--)
132 {
133 Uint32 p = *(Uint32 *)source;
134 pixel->red = (((p & fmt->Rmask)>>fmt->Rshift)<<fmt->Rloss);
135 pixel->green = (((p & fmt->Gmask)>>fmt->Gshift)<<fmt->Gloss);
136 pixel->blue = (((p & fmt->Bmask)>>fmt->Bshift)<<fmt->Bloss);
137 }
138 }
139
140 // screenshot_sdl is called with a pointer to one of these structs
141
142 enum {
143 SCREENSHOT_SDL_INDEXED,
144 SCREENSHOT_SDL_HICOLOR,
145 SCREENSHOT_SDL_MODES
146 };
147
148 static const struct screenshot_sdl_func {
149 const size_t pixel_size;
150 void (*fill_buffer)(SDL_Surface *scr, void *buffer);
151 } screenshot_sdl_funcs[SCREENSHOT_SDL_MODES] = {
152 {
153 sizeof(char),
154 fill_buffer_indexed
155 },
156 {
157 sizeof(png_color),
158 fill_buffer_hicolor
159 },
160 };
161
162 // Write the contents of the surface to the PNG file
163 // Called from I_ScreenShot when the PNG file is ready to write to
164
screenshot_sdl(png_struct * png_ptr,png_info * info_ptr,SDL_Surface * scr,const struct screenshot_sdl_func * mode)165 static int screenshot_sdl(
166 png_struct *png_ptr, png_info *info_ptr,
167 SDL_Surface *scr, const struct screenshot_sdl_func *mode)
168 {
169 int y, result = -1;
170 byte *pixel_data;
171
172 pixel_data = malloc(SCREENWIDTH * SCREENHEIGHT * mode->pixel_size);
173
174 if (pixel_data)
175 {
176 int lock_needed = SDL_MUSTLOCK(scr);
177 int lock_was_successful = 0;
178
179 if (!lock_needed || SDL_LockSurface(scr) >= 0)
180 {
181 // While the screen is locked write it into a buffer
182 lock_was_successful = 1;
183 mode->fill_buffer(scr, pixel_data);
184 if (lock_needed)
185 SDL_UnlockSurface(scr);
186 }
187
188 if (lock_was_successful)
189 {
190 // Write out the buffer
191 png_write_info(png_ptr, info_ptr);
192 for (y = 0; y < SCREENHEIGHT; y++)
193 png_write_row(png_ptr, pixel_data + y*SCREENWIDTH*mode->pixel_size);
194 png_write_end(png_ptr, info_ptr);
195
196 result = 0;
197 }
198
199 free(pixel_data);
200 }
201 return result;
202 }
203
204 //
205 // OpenGL mode screenshots
206 //
207 #ifdef GL_DOOM
screenshot_gl(png_struct * png_ptr,png_info * info_ptr)208 static int screenshot_gl(png_struct *png_ptr, png_info *info_ptr)
209 {
210 unsigned char *pixel_data = gld_ReadScreen();
211
212 if (pixel_data)
213 {
214 int y;
215
216 png_write_info(png_ptr, info_ptr);
217 for (y = 0; y < SCREENHEIGHT; y++)
218 png_write_row(png_ptr, pixel_data + y*SCREENWIDTH*3);
219 png_write_end(png_ptr, info_ptr);
220
221 //free(pixel_data); //static buffer
222 return 0;
223 }
224 else
225 return -1;
226 }
227 #endif
228
229 //
230 // I_ScreenShot - PNG version
231 //
232
233 // Open the PNG file, set it up to be written to, and call screenshot_sdl
234
I_ScreenShot(const char * fname)235 int I_ScreenShot (const char *fname)
236 {
237 int result = -1;
238 FILE *fp = fopen(fname, "wb");
239
240 if (fp)
241 {
242 png_struct *png_ptr = png_create_write_struct(
243 PNG_LIBPNG_VER_STRING, NULL, error_fn, warning_fn);
244
245 if (png_ptr)
246 {
247 png_info *info_ptr = png_create_info_struct(png_ptr);
248
249 if (info_ptr)
250 {
251 SDL_Surface *scr = SDL_GetVideoSurface();
252 png_time ptime;
253
254 png_set_compression_level(png_ptr, 2);
255 png_init_io(png_ptr, fp);
256 png_set_IHDR(
257 png_ptr, info_ptr, SCREENWIDTH, SCREENHEIGHT, 8,
258 (V_GetMode() == VID_MODE8)
259 ? PNG_COLOR_TYPE_PALETTE
260 : PNG_COLOR_TYPE_RGB,
261 PNG_INTERLACE_NONE,
262 PNG_COMPRESSION_TYPE_DEFAULT,
263 PNG_FILTER_TYPE_DEFAULT);
264
265 png_convert_from_time_t(&ptime, time(NULL));
266 png_set_tIME(png_ptr, info_ptr, &ptime);
267
268 switch (V_GetMode()) {
269 #ifdef GL_DOOM
270 case VID_MODEGL:
271 result = screenshot_gl(png_ptr, info_ptr);
272 break;
273 #endif
274 case VID_MODE8:
275 if (write_png_palette(png_ptr, info_ptr, scr) >= 0)
276 result = screenshot_sdl(png_ptr, info_ptr, scr,
277 &screenshot_sdl_funcs[SCREENSHOT_SDL_INDEXED]);
278 break;
279
280 case VID_MODE15:
281 case VID_MODE16:
282 case VID_MODE32:
283 result = screenshot_sdl(png_ptr, info_ptr, scr,
284 &screenshot_sdl_funcs[SCREENSHOT_SDL_HICOLOR]);
285 break;
286
287 default:
288 break;
289 }
290 }
291 png_destroy_write_struct(&png_ptr, NULL);
292 }
293 fclose(fp);
294 }
295 return result;
296 }
297
298 #else // !HAVE_LIBPNG
299
300 //
301 // I_ScreenShot - BMP version
302 //
303
I_ScreenShot(const char * fname)304 int I_ScreenShot (const char *fname)
305 {
306 #ifdef GL_DOOM
307 if (V_GetMode() == VID_MODEGL || vid_8ingl.enabled)
308 {
309 int result = -1;
310 unsigned char *pixel_data = gld_ReadScreen();
311
312 if (pixel_data)
313 {
314 SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(
315 pixel_data, REAL_SCREENWIDTH, REAL_SCREENHEIGHT, 24, REAL_SCREENWIDTH*3,
316 0x000000ff, 0x0000ff00, 0x00ff0000, 0);
317
318 if (surface)
319 {
320 result = SDL_SaveBMP(surface, fname);
321 SDL_FreeSurface(surface);
322 }
323 //free(pixel_data); // static buffer
324 }
325 return result;
326 } else
327 #endif
328 return SDL_SaveBMP(SDL_GetVideoSurface(), fname);
329 }
330
331 #endif // HAVE_LIBPNG
332
333 // NSM
334 // returns current screern contents as RGB24 (raw)
335 // returned pointer should be freed when done
336
I_GrabScreen(void)337 unsigned char *I_GrabScreen (void)
338 {
339 static unsigned char *pixel_data = NULL;
340 static int pixel_data_size = 0;
341 int size;
342
343 size = REAL_SCREENWIDTH * REAL_SCREENHEIGHT * 3;
344 if (!pixel_data || size > pixel_data_size)
345 {
346 pixel_data_size = size;
347 pixel_data = realloc(pixel_data, size);
348 }
349
350 #ifdef GL_DOOM
351 if (V_GetMode() == VID_MODEGL || vid_8ingl.enabled)
352 {
353 return gld_ReadScreen();
354 }
355 #endif
356
357 if (pixel_data)
358 {
359 SDL_Surface *scr = SDL_GetVideoSurface();
360 SDL_PixelFormat *fmt = scr->format;
361 SDL_Palette *pal = fmt->palette;
362 unsigned char *pixel_src = (unsigned char *) scr->pixels;
363
364 int lock_needed = SDL_MUSTLOCK(scr);
365 int lock_was_successful = 0;
366
367 if (!lock_needed || SDL_LockSurface(scr) >= 0)
368 {
369 // While the screen is locked write it into a buffer
370 unsigned char *s = pixel_data;
371 int i;
372
373 if (pal) // 8bpp
374 {
375 for (i = 0; i < REAL_SCREENWIDTH * REAL_SCREENHEIGHT; i++)
376 {
377 *s++ = pal->colors[pixel_src[i]].r;
378 *s++ = pal->colors[pixel_src[i]].g;
379 *s++ = pal->colors[pixel_src[i]].b;
380 }
381 }
382 else // high/truecolor
383 {
384 for (i = 0; i < REAL_SCREENWIDTH * REAL_SCREENHEIGHT; i++)
385 {
386 Uint32 p = *(Uint32 *) (pixel_src + i * fmt->BytesPerPixel);
387 *s++ = (unsigned char) (((p & fmt->Rmask)>>fmt->Rshift)<<fmt->Rloss);
388 *s++ = (unsigned char) (((p & fmt->Gmask)>>fmt->Gshift)<<fmt->Gloss);
389 *s++ = (unsigned char) (((p & fmt->Bmask)>>fmt->Bshift)<<fmt->Bloss);
390 }
391 }
392
393 if (lock_needed)
394 SDL_UnlockSurface(scr);
395 }
396 }
397 return pixel_data;
398 }
399
400
401
402