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