screenshot.c
changeset 0 29654efe3188
child 1 0ea1e0a5c4df
equal deleted inserted replaced
-1:000000000000 0:29654efe3188
       
     1 #include "stdafx.h"
       
     2 #include "ttd.h"
       
     3 #include "gfx.h"
       
     4 #include "viewport.h"
       
     5 #include "player.h"
       
     6 #include "gui.h"
       
     7 
       
     8 // called by the ScreenShot proc to generate screenshot lines.
       
     9 typedef void ScreenshotCallback(void *userdata, byte *buf, uint y, uint pitch, uint n);
       
    10 typedef bool ScreenshotHandlerProc(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const byte *palette);
       
    11 
       
    12 typedef struct {
       
    13 	const char *name;
       
    14 	const char *extension;
       
    15 	ScreenshotHandlerProc *proc;
       
    16 	byte id;
       
    17 } ScreenshotFormat;
       
    18 
       
    19 //************************************************
       
    20 //*** SCREENSHOT CODE FOR WINDOWS BITMAP (.BMP)
       
    21 //************************************************
       
    22 #if defined(_MSC_VER)
       
    23 #pragma pack(push, 1)
       
    24 #endif
       
    25 
       
    26 typedef struct BitmapFileHeader { 
       
    27 	uint16 type;
       
    28 	uint32 size;
       
    29 	uint32 reserved;
       
    30 	uint32 off_bits;
       
    31 } GCC_PACK BitmapFileHeader;
       
    32 assert_compile(sizeof(BitmapFileHeader) == 14);
       
    33 
       
    34 #if defined(_MSC_VER)
       
    35 #pragma pack(pop)
       
    36 #endif
       
    37 
       
    38 typedef struct BitmapInfoHeader {
       
    39 	uint32 size;
       
    40 	int32 width, height;
       
    41 	uint16 planes, bitcount;
       
    42 	uint32 compression, sizeimage, xpels, ypels, clrused, clrimp;
       
    43 } BitmapInfoHeader;
       
    44 assert_compile(sizeof(BitmapInfoHeader) == 40);
       
    45 
       
    46 typedef struct RgbQuad {
       
    47 	byte blue, green, red, reserved;
       
    48 } RgbQuad;
       
    49 assert_compile(sizeof(RgbQuad) == 4);
       
    50 
       
    51 // generic .BMP writer
       
    52 static bool MakeBmpImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const byte *palette)
       
    53 {
       
    54 	BitmapFileHeader bfh;
       
    55 	BitmapInfoHeader bih;
       
    56 	RgbQuad *rq = alloca(sizeof(RgbQuad) * 256);
       
    57 	byte *buff;
       
    58 	FILE *f;
       
    59 	uint i, padw;
       
    60 	uint n, maxlines;
       
    61 
       
    62 	// only implemented for 8bit images so far.
       
    63 	if (pixelformat != 8)
       
    64 		return false;
       
    65 
       
    66 	f = fopen(name, "wb");
       
    67 	if (f == NULL) return false;
       
    68 
       
    69 	// each scanline must be aligned on a 32bit boundary
       
    70 	padw = (w + 3) & ~3;
       
    71 
       
    72 	// setup the file header
       
    73 	bfh.type = TO_LE16('MB');
       
    74 	bfh.size = TO_LE32(sizeof(bfh) + sizeof(bih) + sizeof(RgbQuad) * 256 + padw * h);
       
    75 	bfh.reserved = 0;
       
    76 	bfh.off_bits = TO_LE32(sizeof(bfh) + sizeof(bih) + sizeof(RgbQuad) * 256);
       
    77 
       
    78 	// setup the info header
       
    79 	bih.size = TO_LE32(sizeof(BitmapInfoHeader));
       
    80 	bih.width = TO_LE32(w);
       
    81 	bih.height = TO_LE32(h);
       
    82 	bih.planes = TO_LE16(1);
       
    83 	bih.bitcount = TO_LE16(8);
       
    84 	bih.compression = 0;
       
    85 	bih.sizeimage = 0;
       
    86 	bih.xpels = 0;
       
    87 	bih.ypels = 0;
       
    88 	bih.clrused = 0;
       
    89 	bih.clrimp = 0;
       
    90 
       
    91 	// convert the palette to the windows format
       
    92 	for(i=0; i!=256; i++) {
       
    93 		rq[i].red = *palette++;
       
    94 		rq[i].green = *palette++;
       
    95 		rq[i].blue = *palette++;
       
    96 		rq[i].reserved = 0;
       
    97 	}
       
    98 
       
    99 	// write file header and info header and palette
       
   100 	fwrite(&bfh, 1, sizeof(bfh), f);
       
   101 	fwrite(&bih, 1, sizeof(bih), f);
       
   102 	fwrite(rq, 1, sizeof(RgbQuad) * 256, f);
       
   103 
       
   104 	// use by default 64k temp memory
       
   105 	maxlines = clamp(65536 / padw, 16, 128);
       
   106 
       
   107 	// now generate the bitmap bits
       
   108 	buff = (byte*)alloca(padw * maxlines); // by default generate 128 lines at a time.
       
   109 	memset(buff, 0, padw * maxlines); // zero the buffer to have the padding bytes set to 0
       
   110 
       
   111 	// start at the bottom, since bitmaps are stored bottom up.
       
   112 	do {
       
   113 		// determine # lines
       
   114 		n = min(h, maxlines);
       
   115 		h -= n;
       
   116 		
       
   117 		// render the pixels
       
   118 		callb(userdata, buff, h, padw, n);
       
   119 		
       
   120 		// write each line 
       
   121 		while (n) 
       
   122 			fwrite(buff + (--n) * padw, 1, padw, f);
       
   123 	} while (h);
       
   124 
       
   125 	fclose(f);
       
   126 
       
   127 	return true;
       
   128 }
       
   129 
       
   130 //********************************************************
       
   131 //*** SCREENSHOT CODE FOR PORTABLE NETWORK GRAPHICS (.PNG)
       
   132 //********************************************************
       
   133 #if defined(WITH_PNG)
       
   134 #include <png.h>
       
   135 
       
   136 static void PNGAPI png_my_error(png_structp png_ptr, png_const_charp message)
       
   137 {
       
   138 	DEBUG(misc, 0) ("ERROR(libpng): %s - %s\n", message,(char *)png_get_error_ptr(png_ptr));
       
   139 	longjmp(png_ptr->jmpbuf, 1);
       
   140 }
       
   141 
       
   142 static void PNGAPI png_my_warning(png_structp png_ptr, png_const_charp message)
       
   143 {
       
   144 	DEBUG(misc, 0) ("WARNING(libpng): %s - %s\n", message, (char *)png_get_error_ptr(png_ptr));
       
   145 }
       
   146 
       
   147 static bool MakePNGImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const byte *palette)
       
   148 {
       
   149 	png_colorp rq = alloca(sizeof(png_color) * 256);
       
   150 	byte *buff;
       
   151 	FILE *f;
       
   152 	uint i, y, n;
       
   153 	uint maxlines;
       
   154 	png_structp png_ptr;
       
   155 	png_infop info_ptr;
       
   156 
       
   157 	// only implemented for 8bit images so far.
       
   158 	if (pixelformat != 8)
       
   159 		return false;
       
   160 
       
   161 	f = fopen(name, "wb");
       
   162 	if (f == NULL) return false;
       
   163 
       
   164 	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (char *) name, png_my_error, png_my_warning);
       
   165 
       
   166 	if (!png_ptr) {
       
   167 		fclose(f);
       
   168 		return false;
       
   169 	}
       
   170 
       
   171 	info_ptr = png_create_info_struct(png_ptr);
       
   172 	if (!info_ptr) {
       
   173 		png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
       
   174 		fclose(f);
       
   175 		return false;
       
   176 	}
       
   177 
       
   178 	if (setjmp(png_jmpbuf(png_ptr))) {
       
   179 		png_destroy_write_struct(&png_ptr, &info_ptr);
       
   180 		fclose(f);
       
   181 		return false;
       
   182 	}
       
   183 
       
   184 	png_init_io(png_ptr, f);
       
   185 	
       
   186 	png_set_filter(png_ptr, 0, PNG_FILTER_NONE);
       
   187 
       
   188 	png_set_IHDR(png_ptr, info_ptr, w, h, pixelformat, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, 
       
   189 		PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
       
   190 
       
   191 	// convert the palette to the .PNG format.
       
   192 	{
       
   193     // avoids "might be clobbered" warning of argument "palette"
       
   194 		const byte *pal = palette;
       
   195 
       
   196 		for(i=0; i!=256; i++) {
       
   197 			rq[i].red = *pal++;
       
   198 			rq[i].green = *pal++;
       
   199 			rq[i].blue = *pal++;
       
   200 		}
       
   201 	}
       
   202 
       
   203 	png_set_PLTE(png_ptr, info_ptr, rq, 256);
       
   204 	png_write_info(png_ptr, info_ptr);
       
   205 	png_set_flush(png_ptr, 512);
       
   206 
       
   207 	// use by default 64k temp memory
       
   208 	maxlines = clamp(65536 / w, 16, 128);
       
   209 
       
   210 	// now generate the bitmap bits
       
   211 	buff = (byte*)alloca(w * maxlines); // by default generate 128 lines at a time.
       
   212 	memset(buff, 0, w * maxlines); // zero the buffer to have the padding bytes set to 0
       
   213 
       
   214 	y = 0;
       
   215 	do {
       
   216 		// determine # lines to write
       
   217 		n = min(h - y, maxlines);
       
   218 		
       
   219 		// render the pixels into the buffer
       
   220 		callb(userdata, buff, y, w, n);
       
   221 		y += n;
       
   222 
       
   223 		// write them to png
       
   224 		for(i=0; i!=n; i++)
       
   225 			png_write_row(png_ptr, buff + i * w);
       
   226 	} while (y != h);
       
   227 
       
   228 	png_write_end(png_ptr, info_ptr);
       
   229 	png_destroy_write_struct(&png_ptr, &info_ptr);
       
   230 
       
   231 	fclose(f);
       
   232 	return true;
       
   233 }
       
   234 #endif // WITH_PNG
       
   235 
       
   236 
       
   237 //************************************************
       
   238 //*** SCREENSHOT CODE FOR ZSOFT PAINTBRUSH (.PCX)
       
   239 //************************************************
       
   240 
       
   241 typedef struct {
       
   242 	byte manufacturer;
       
   243 	byte version;
       
   244 	byte rle;
       
   245 	byte bpp;
       
   246 	uint32 unused;
       
   247 	uint16 xmax, ymax;
       
   248 	uint16 hdpi, vdpi;
       
   249 	byte pal_small[16*3];
       
   250 	byte reserved;
       
   251 	byte planes;
       
   252 	uint16 pitch;
       
   253 	uint16 cpal;
       
   254 	uint16 width;
       
   255 	uint16 height;
       
   256 	byte filler[54];
       
   257 } PcxHeader;
       
   258 assert_compile(sizeof(PcxHeader) == 128);
       
   259 
       
   260 static bool MakePCXImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const byte *palette)
       
   261 {
       
   262 	byte *buff;
       
   263 	FILE *f;
       
   264 	uint maxlines;
       
   265 	uint y;
       
   266 	PcxHeader pcx;
       
   267 
       
   268 	if (pixelformat != 8 || w == 0)
       
   269 		return false;
       
   270 
       
   271 	f = fopen(name, "wb");
       
   272 	if (f == NULL) return false;
       
   273 
       
   274 	memset(&pcx, 0, sizeof(pcx));
       
   275 	
       
   276 	// setup pcx header
       
   277 	pcx.manufacturer = 10;
       
   278 	pcx.version = 5;
       
   279 	pcx.rle = 1;
       
   280 	pcx.bpp = 8;
       
   281 	pcx.xmax = TO_LE16(w-1);
       
   282 	pcx.ymax = TO_LE16(h-1);
       
   283 	pcx.hdpi = TO_LE16(320);
       
   284 	pcx.vdpi = TO_LE16(320);
       
   285 
       
   286 	pcx.planes = 1;
       
   287 	pcx.cpal = TO_LE16(1);
       
   288 	pcx.width = pcx.pitch = TO_LE16(w);
       
   289 	pcx.height = TO_LE16(h);
       
   290 	
       
   291 	// write pcx header
       
   292 	fwrite(&pcx, sizeof(pcx), 1, f);
       
   293 
       
   294 	// use by default 64k temp memory
       
   295 	maxlines = clamp(65536 / w, 16, 128);
       
   296 
       
   297 	// now generate the bitmap bits
       
   298 	buff = (byte*)alloca(w * maxlines);				// by default generate 128 lines at a time.
       
   299 	memset(buff, 0, w * maxlines);			// zero the buffer to have the padding bytes set to 0
       
   300 
       
   301 	y = 0;
       
   302 	do {
       
   303 		// determine # lines to write
       
   304 		uint n = min(h - y, maxlines), i;
       
   305 		
       
   306 		// render the pixels into the buffer
       
   307 		callb(userdata, buff, y, w, n);
       
   308 		y += n;
       
   309 
       
   310 		// write them to pcx
       
   311 		for(i=0; i!=n; i++) {
       
   312 			int runcount = 1;
       
   313 			byte *bufp = buff + i * w;
       
   314 			byte runchar = buff[0];
       
   315 			uint left = w - 1;
       
   316 
       
   317 			// for each pixel... 
       
   318 			while (left) {
       
   319 				byte ch = *bufp++;
       
   320 				if (ch != runchar || runcount >= 0x3f) {
       
   321 					if (runcount > 1 || (runchar & 0xC0) == 0xC0) fputc(0xC0 | runcount, f);
       
   322 					fputc(runchar,f);
       
   323 					runcount = 0;
       
   324 					runchar = ch;
       
   325 				}
       
   326 				runcount++;
       
   327 				left--;
       
   328 			}
       
   329 
       
   330 			// write remaining bytes..
       
   331 			if (runcount > 1 || (runchar & 0xC0) == 0xC0) fputc(0xC0 | runcount, f);
       
   332 			fputc(runchar,f);
       
   333 		}
       
   334 	} while (y != h);
       
   335 
       
   336 	// write 8-bit color palette
       
   337 	fputc(12, f); 
       
   338 	fwrite(palette, 256*3, 1, f);
       
   339 	fclose(f);
       
   340 
       
   341 	return true;
       
   342 }
       
   343 
       
   344 //************************************************
       
   345 //*** GENERIC SCREENSHOT CODE
       
   346 //************************************************
       
   347 
       
   348 static const ScreenshotFormat _screenshot_formats[] = {
       
   349 	{"BMP", "bmp", &MakeBmpImage},
       
   350 #if defined(WITH_PNG)
       
   351 	{"PNG", "png", &MakePNGImage},
       
   352 #endif
       
   353 	{"PCX", "pcx", &MakePCXImage},
       
   354 };
       
   355 
       
   356 void InitializeScreenshotFormats()
       
   357 {
       
   358 	int i,j;
       
   359 	for (i=0,j=0; i!=lengthof(_screenshot_formats); i++)
       
   360 		if (!strcmp(_screenshot_format_name, _screenshot_formats[i].extension)) { j=i; break; }
       
   361 	_cur_screenshot_format = j;
       
   362 	_num_screenshot_formats = lengthof(_screenshot_formats);
       
   363 }
       
   364 
       
   365 const char *GetScreenshotFormatDesc(int i)
       
   366 {
       
   367 	return _screenshot_formats[i].name;
       
   368 }
       
   369 
       
   370 void SetScreenshotFormat(int i)
       
   371 {
       
   372 	_cur_screenshot_format = i;
       
   373 	strcpy(_screenshot_format_name, _screenshot_formats[i].extension);
       
   374 }
       
   375 
       
   376 // screenshot generator that dumps the current video buffer
       
   377 void CurrentScreenCallback(void *userdata, byte *buf, uint y, uint pitch, uint n)
       
   378 {
       
   379 	assert(_screen.pitch == (int)pitch);
       
   380 	memcpy(buf, _screen.dst_ptr + y * _screen.pitch, n * _screen.pitch);
       
   381 }
       
   382 
       
   383 extern void ViewportDoDraw(ViewPort *vp, int left, int top, int right, int bottom);
       
   384 
       
   385 // generate a large piece of the world
       
   386 void LargeWorldCallback(void *userdata, byte *buf, uint y, uint pitch, uint n)
       
   387 {
       
   388 	ViewPort *vp = (ViewPort *)userdata;
       
   389 	DrawPixelInfo dpi, *old_dpi;
       
   390 	int wx, left;
       
   391 
       
   392 	old_dpi = _cur_dpi;
       
   393 	_cur_dpi = &dpi;
       
   394 
       
   395 	dpi.dst_ptr = buf;
       
   396 	dpi.height = n;
       
   397 	dpi.width = vp->width;
       
   398 	dpi.pitch = pitch;
       
   399 	dpi.zoom = 0;
       
   400 	dpi.left = 0;
       
   401 	dpi.top = y;
       
   402 
       
   403 	left = 0;
       
   404 	while (vp->width - left != 0) {
       
   405 		wx = min(vp->width - left, 1600);
       
   406 		left += wx;
       
   407 		
       
   408 		ViewportDoDraw(vp, 
       
   409 			((left - wx - vp->left) << vp->zoom) + vp->virtual_left,
       
   410 			((y - vp->top) << vp->zoom) + vp->virtual_top,
       
   411 			((left - vp->left) << vp->zoom) + vp->virtual_left,
       
   412 			(((y+n) - vp->top) << vp->zoom) + vp->virtual_top
       
   413 		);
       
   414 	}
       
   415 
       
   416 	_cur_dpi = old_dpi;
       
   417 }
       
   418 
       
   419 static char *MakeScreenshotName(const char *ext)
       
   420 {
       
   421 	static char filename[256];
       
   422 	char *base;
       
   423 	int serial;
       
   424 
       
   425 	if (_game_mode == GM_EDITOR || _local_player == 0xff) {
       
   426 		sprintf(_screenshot_name, "screenshot");
       
   427 	} else {
       
   428 		Player *p = &_players[_local_player];
       
   429 		SET_DPARAM16(0, p->name_1);
       
   430 		SET_DPARAM32(1, p->name_2);
       
   431 		SET_DPARAM16(2, _date);
       
   432 		GetString(_screenshot_name, STR_4004);
       
   433 	}
       
   434 
       
   435 	base = strchr(_screenshot_name, 0);
       
   436 	base[0] = '.'; strcpy(base + 1, ext);
       
   437 
       
   438 	serial = 0;
       
   439 	for(;;) {
       
   440 		snprintf(filename, sizeof(filename), "%s%s", _path.personal_dir, _screenshot_name);	
       
   441 		if (!FileExists(filename))
       
   442 			break;
       
   443 		sprintf(base, " #%d.%s", ++serial, ext);
       
   444 	}
       
   445 
       
   446 	return filename;
       
   447 }
       
   448 
       
   449 extern byte _cur_palette[768];
       
   450 
       
   451 bool MakeScreenshot()
       
   452 {
       
   453 	const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format;
       
   454 	return sf->proc(MakeScreenshotName(sf->extension), CurrentScreenCallback, NULL, _screen.width, _screen.height, 8, _cur_palette); 
       
   455 }
       
   456 
       
   457 bool MakeWorldScreenshot(int left, int top, int width, int height, int zoom)
       
   458 {
       
   459 	ViewPort vp;
       
   460 	const ScreenshotFormat *sf;
       
   461 
       
   462 	vp.zoom = zoom;
       
   463 	vp.left = 0;
       
   464 	vp.top = 0;
       
   465 	vp.virtual_width = width;
       
   466 	vp.width = width >> zoom;
       
   467 	vp.virtual_height = height;
       
   468 	vp.height = height >> zoom;
       
   469 	vp.virtual_left = left;
       
   470 	vp.virtual_top = top;
       
   471 
       
   472 	sf = _screenshot_formats + _cur_screenshot_format;
       
   473 	return sf->proc(MakeScreenshotName(sf->extension), LargeWorldCallback, &vp, vp.width, vp.height, 8, _cur_palette);
       
   474 }