1 /*
2   Hatari - screenSnapShot.c
3 
4   This file is distributed under the GNU General Public License, version 2
5   or at your option any later version. Read the file gpl.txt for details.
6 
7   Screen Snapshots.
8 */
9 const char ScreenSnapShot_fileid[] = "Hatari screenSnapShot.c : " __DATE__ " " __TIME__;
10 
11 #include <SDL.h>
12 #include <dirent.h>
13 #include <string.h>
14 #include "main.h"
15 #include "configuration.h"
16 #include "log.h"
17 #include "paths.h"
18 #include "screen.h"
19 #include "screenSnapShot.h"
20 #include "statusbar.h"
21 #include "video.h"
22 /* after above that bring in config.h */
23 #if HAVE_LIBPNG
24 # include <png.h>
25 # include <assert.h>
26 # include "pixel_convert.h"				/* inline functions */
27 #endif
28 
29 
30 static int nScreenShots = 0;                /* Number of screen shots saved */
31 
32 
33 /*-----------------------------------------------------------------------*/
34 /**
35  * Scan working directory to get the screenshot number
36  */
ScreenSnapShot_GetNum(void)37 static void ScreenSnapShot_GetNum(void)
38 {
39 	char dummy[5];
40 	int i, num;
41 	DIR *workingdir = opendir(Paths_GetScreenShotDir());
42 	struct dirent *file;
43 
44 	nScreenShots = 0;
45 	if (workingdir == NULL)  return;
46 
47 	file = readdir(workingdir);
48 	while (file != NULL)
49 	{
50 		if ( strncmp("grab", file->d_name, 4) == 0 )
51 		{
52 			/* copy next 4 numbers */
53 			for (i = 0; i < 4; i++)
54 			{
55 				if (file->d_name[4+i] >= '0' && file->d_name[4+i] <= '9')
56 					dummy[i] = file->d_name[4+i];
57 				else
58 					break;
59 			}
60 
61 			dummy[i] = '\0'; /* null terminate */
62 			num = atoi(dummy);
63 			if (num > nScreenShots)  nScreenShots = num;
64 		}
65 		/* next file.. */
66 		file = readdir(workingdir);
67 	}
68 
69 	closedir(workingdir);
70 }
71 
72 
73 #if HAVE_LIBPNG
74 /**
75  * Save given SDL surface as PNG. Return png file size > 0 for success.
76  */
ScreenSnapShot_SavePNG(SDL_Surface * surface,const char * filename)77 static int ScreenSnapShot_SavePNG(SDL_Surface *surface, const char *filename)
78 {
79 	FILE *fp = NULL;
80 	int ret, bottom;
81 
82 	fp = fopen(filename, "wb");
83 	if (!fp)
84 		return -1;
85 
86 	if (ConfigureParams.Screen.bCrop)
87 		bottom = Statusbar_GetHeight();
88 	else
89 		bottom = 0;
90 
91 	/* default compression/filter and configured cropping */
92 	ret = ScreenSnapShot_SavePNG_ToFile(surface, 0, 0, fp, -1, -1, 0, 0, 0, bottom);
93 
94 	fclose (fp);
95 	return ret;					/* >0 if OK, -1 if error */
96 }
97 
98 
99 /**
100  * Save given SDL surface as PNG in an already opened FILE, eventually cropping some borders.
101  * Return png file size > 0 for success.
102  * This function is also used by avi_record.c to save individual frames as png images.
103  */
ScreenSnapShot_SavePNG_ToFile(SDL_Surface * surface,int dw,int dh,FILE * fp,int png_compression_level,int png_filter,int CropLeft,int CropRight,int CropTop,int CropBottom)104 int ScreenSnapShot_SavePNG_ToFile(SDL_Surface *surface, int dw, int dh,
105 		FILE *fp, int png_compression_level, int png_filter,
106 		int CropLeft , int CropRight , int CropTop , int CropBottom )
107 {
108 	bool do_lock;
109 	int y, ret;
110 	int sw = surface->w - CropLeft - CropRight;
111 	int sh = surface->h - CropTop - CropBottom;
112 	Uint8 *src_ptr;
113 	Uint8 *rowbuf;
114 	SDL_PixelFormat *fmt = surface->format;
115 	png_infop info_ptr = NULL;
116 	png_structp png_ptr;
117 	png_text pngtext;
118 	char key[] = "Title";
119 	char text[] = "Hatari screenshot";
120 	off_t start;;
121 
122 
123 	if (!dw)
124 		dw = sw;
125 	if (!dh)
126 		dh = sh;
127 
128 	rowbuf = alloca(3 * dw);
129 
130 	/* Create and initialize the png_struct with error handler functions. */
131 	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
132 	if (!png_ptr)
133 	{
134 		return -1;
135 	}
136 
137 	/* Allocate/initialize the image information data. */
138 	info_ptr = png_create_info_struct(png_ptr);
139 	if (!info_ptr) {
140 		ret = -1;
141 		goto png_cleanup;
142 	}
143 
144 	/* libpng ugliness: Set error handling when not supplying own
145 	 * error handling functions in the png_create_write_struct() call.
146 	 */
147 	if (setjmp(png_jmpbuf(png_ptr))) {
148 		ret = -1;
149 		goto png_cleanup;
150 	}
151 
152 	/* store current pos in fp (could be != 0 for avi recording) */
153 	start = ftello ( fp );
154 
155 	/* initialize the png structure */
156 	png_init_io(png_ptr, fp);
157 
158 	/* image data properties */
159 	png_set_IHDR(png_ptr, info_ptr, dw, dh, 8, PNG_COLOR_TYPE_RGB,
160 		     PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
161 		     PNG_FILTER_TYPE_DEFAULT);
162 
163 	if ( png_compression_level >= 0 )
164 		png_set_compression_level ( png_ptr , png_compression_level );
165 	if ( png_filter >= 0 )
166 		png_set_filter ( png_ptr , 0 , png_filter );
167 
168 	/* image info */
169 	pngtext.key = key;
170 	pngtext.text = text;
171 	pngtext.compression = PNG_TEXT_COMPRESSION_NONE;
172 #ifdef PNG_iTXt_SUPPORTED
173 	pngtext.lang = NULL;
174 #endif
175 	png_set_text(png_ptr, info_ptr, &pngtext, 1);
176 
177 	/* write the file header information */
178 	png_write_info(png_ptr, info_ptr);
179 
180 	/* write surface data rows one at a time (after cropping if necessary) */
181 	do_lock = SDL_MUSTLOCK(surface);
182 	for (y = 0; y < dh; y++)
183 	{
184 		/* need to lock the surface while accessing it directly */
185 		if (do_lock)
186 			SDL_LockSurface(surface);
187 
188 
189 		src_ptr = (Uint8 *)surface->pixels
190 		          + (CropTop + (y * sh + dh/2) / dh) * surface->pitch
191 		          + CropLeft * surface->format->BytesPerPixel;
192 
193 		switch (fmt->BytesPerPixel)
194 		{
195 		 case 2:
196 			/* unpack 16-bit RGB pixels */
197 			PixelConvert_16to24Bits(rowbuf, (Uint16*)src_ptr, dw, surface);
198 			break;
199 		 case 4:
200 			/* unpack 32-bit RGBA pixels */
201 			PixelConvert_32to24Bits(rowbuf, (Uint32*)src_ptr, dw, surface);
202 			break;
203 		 default:
204 			abort();
205 		}
206 		/* and unlock surface before syscalls */
207 		if (do_lock)
208 			SDL_UnlockSurface(surface);
209 		png_write_row(png_ptr, rowbuf);
210 	}
211 
212 	/* write the additional chuncks to the PNG file */
213 	png_write_end(png_ptr, info_ptr);
214 
215 	ret = (int)( ftello ( fp ) - start );			/* size of the png image */
216 png_cleanup:
217 	if (png_ptr)
218 		png_destroy_write_struct(&png_ptr, NULL);
219 	return ret;
220 }
221 #endif
222 
223 
224 /*-----------------------------------------------------------------------*/
225 /**
226  * Save screen shot file with filename like 'grab0000.[png|bmp]',
227  * 'grab0001.[png|bmp]', etc... Whether screen shots are saved as BMP
228  * or PNG depends on Hatari configuration.
229  */
ScreenSnapShot_SaveScreen(void)230 void ScreenSnapShot_SaveScreen(void)
231 {
232 	char *szFileName = malloc(FILENAME_MAX);
233 
234 	if (!szFileName)  return;
235 
236 	ScreenSnapShot_GetNum();
237 	/* Create our filename */
238 	nScreenShots++;
239 #if HAVE_LIBPNG
240 	/* try first PNG */
241 	sprintf(szFileName,"%s/grab%4.4d.png", Paths_GetScreenShotDir(), nScreenShots);
242 	if (ScreenSnapShot_SavePNG(sdlscrn, szFileName) > 0)
243 	{
244 		fprintf(stderr, "Screen dump saved to: %s\n", szFileName);
245 		free(szFileName);
246 		return;
247 	}
248 #endif
249 	sprintf(szFileName,"%s/grab%4.4d.bmp", Paths_GetScreenShotDir(), nScreenShots);
250 	if (SDL_SaveBMP(sdlscrn, szFileName))
251 		fprintf(stderr, "Screen dump failed!\n");
252 	else
253 		fprintf(stderr, "Screen dump saved to: %s\n", szFileName);
254 
255 	free(szFileName);
256 }
257 
258