6 #include "functions.h" |
6 #include "functions.h" |
7 #include "strings.h" |
7 #include "strings.h" |
8 #include "table/strings.h" |
8 #include "table/strings.h" |
9 #include "gfx.h" |
9 #include "gfx.h" |
10 #include "hal.h" |
10 #include "hal.h" |
|
11 #include "fileio.h" |
11 #include "viewport.h" |
12 #include "viewport.h" |
12 #include "player.h" |
13 #include "player.h" |
13 #include "screenshot.h" |
14 #include "screenshot.h" |
14 #include "variables.h" |
15 #include "variables.h" |
15 #include "date.h" |
16 #include "date.h" |
|
17 #include "string.h" |
16 #include "helpers.hpp" |
18 #include "helpers.hpp" |
|
19 #include "blitter/factory.hpp" |
|
20 #include "fileio.h" |
17 |
21 |
18 char _screenshot_format_name[8]; |
22 char _screenshot_format_name[8]; |
19 uint _num_screenshot_formats; |
23 uint _num_screenshot_formats; |
20 uint _cur_screenshot_format; |
24 uint _cur_screenshot_format; |
21 ScreenshotType current_screenshot_type; |
25 ScreenshotType current_screenshot_type; |
22 |
26 |
23 /* called by the ScreenShot proc to generate screenshot lines. */ |
27 /* called by the ScreenShot proc to generate screenshot lines. */ |
24 typedef void ScreenshotCallback(void *userdata, Pixel *buf, uint y, uint pitch, uint n); |
28 typedef void ScreenshotCallback(void *userdata, void *buf, uint y, uint pitch, uint n); |
25 typedef bool ScreenshotHandlerProc(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette); |
29 typedef bool ScreenshotHandlerProc(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette); |
26 |
30 |
27 struct ScreenshotFormat { |
31 struct ScreenshotFormat { |
28 const char *name; |
32 const char *name; |
29 const char *extension; |
33 const char *extension; |
69 BitmapInfoHeader bih; |
73 BitmapInfoHeader bih; |
70 RgbQuad rq[256]; |
74 RgbQuad rq[256]; |
71 FILE *f; |
75 FILE *f; |
72 uint i, padw; |
76 uint i, padw; |
73 uint n, maxlines; |
77 uint n, maxlines; |
74 |
78 uint pal_size = 0; |
75 /* only implemented for 8bit images so far. */ |
79 uint bpp = pixelformat / 8; |
76 if (pixelformat != 8) |
80 |
77 return false; |
81 /* only implemented for 8bit and 32bit images so far. */ |
|
82 if (pixelformat != 8 && pixelformat != 32) return false; |
78 |
83 |
79 f = fopen(name, "wb"); |
84 f = fopen(name, "wb"); |
80 if (f == NULL) return false; |
85 if (f == NULL) return false; |
81 |
86 |
82 /* each scanline must be aligned on a 32bit boundary */ |
87 /* each scanline must be aligned on a 32bit boundary */ |
83 padw = ALIGN(w, 4); |
88 padw = ALIGN(w, 4); |
84 |
89 |
|
90 if (pixelformat == 8) pal_size = sizeof(RgbQuad) * 256; |
|
91 |
85 /* setup the file header */ |
92 /* setup the file header */ |
86 bfh.type = TO_LE16('MB'); |
93 bfh.type = TO_LE16('MB'); |
87 bfh.size = TO_LE32(sizeof(bfh) + sizeof(bih) + sizeof(RgbQuad) * 256 + padw * h); |
94 bfh.size = TO_LE32(sizeof(bfh) + sizeof(bih) + pal_size + padw * h * bpp); |
88 bfh.reserved = 0; |
95 bfh.reserved = 0; |
89 bfh.off_bits = TO_LE32(sizeof(bfh) + sizeof(bih) + sizeof(RgbQuad) * 256); |
96 bfh.off_bits = TO_LE32(sizeof(bfh) + sizeof(bih) + pal_size); |
90 |
97 |
91 /* setup the info header */ |
98 /* setup the info header */ |
92 bih.size = TO_LE32(sizeof(BitmapInfoHeader)); |
99 bih.size = TO_LE32(sizeof(BitmapInfoHeader)); |
93 bih.width = TO_LE32(w); |
100 bih.width = TO_LE32(w); |
94 bih.height = TO_LE32(h); |
101 bih.height = TO_LE32(h); |
95 bih.planes = TO_LE16(1); |
102 bih.planes = TO_LE16(1); |
96 bih.bitcount = TO_LE16(8); |
103 bih.bitcount = TO_LE16(pixelformat); |
97 bih.compression = 0; |
104 bih.compression = 0; |
98 bih.sizeimage = 0; |
105 bih.sizeimage = 0; |
99 bih.xpels = 0; |
106 bih.xpels = 0; |
100 bih.ypels = 0; |
107 bih.ypels = 0; |
101 bih.clrused = 0; |
108 bih.clrused = 0; |
102 bih.clrimp = 0; |
109 bih.clrimp = 0; |
103 |
110 |
104 /* convert the palette to the windows format */ |
111 if (pixelformat == 8) { |
105 for (i = 0; i != 256; i++) { |
112 /* convert the palette to the windows format */ |
106 rq[i].red = palette[i].r; |
113 for (i = 0; i != 256; i++) { |
107 rq[i].green = palette[i].g; |
114 rq[i].red = palette[i].r; |
108 rq[i].blue = palette[i].b; |
115 rq[i].green = palette[i].g; |
109 rq[i].reserved = 0; |
116 rq[i].blue = palette[i].b; |
|
117 rq[i].reserved = 0; |
|
118 } |
110 } |
119 } |
111 |
120 |
112 /* write file header and info header and palette */ |
121 /* write file header and info header and palette */ |
113 if (fwrite(&bfh, sizeof(bfh), 1, f) != 1) return false; |
122 if (fwrite(&bfh, sizeof(bfh), 1, f) != 1) return false; |
114 if (fwrite(&bih, sizeof(bih), 1, f) != 1) return false; |
123 if (fwrite(&bih, sizeof(bih), 1, f) != 1) return false; |
115 if (fwrite(rq, sizeof(rq), 1, f) != 1) return false; |
124 if (pixelformat == 8) if (fwrite(rq, sizeof(rq), 1, f) != 1) return false; |
116 |
125 |
117 /* use by default 64k temp memory */ |
126 /* use by default 64k temp memory */ |
118 maxlines = clamp(65536 / padw, 16, 128); |
127 maxlines = clamp(65536 / padw, 16, 128); |
119 |
128 |
120 /* now generate the bitmap bits */ |
129 /* now generate the bitmap bits */ |
121 Pixel *buff = MallocT<Pixel>(padw * maxlines); // by default generate 128 lines at a time. |
130 void *buff = MallocT<uint8>(padw * maxlines * bpp); // by default generate 128 lines at a time. |
122 if (buff == NULL) { |
131 if (buff == NULL) { |
123 fclose(f); |
132 fclose(f); |
124 return false; |
133 return false; |
125 } |
134 } |
126 memset(buff, 0, padw * maxlines); // zero the buffer to have the padding bytes set to 0 |
135 memset(buff, 0, padw * maxlines); // zero the buffer to have the padding bytes set to 0 |
204 |
213 |
205 png_init_io(png_ptr, f); |
214 png_init_io(png_ptr, f); |
206 |
215 |
207 png_set_filter(png_ptr, 0, PNG_FILTER_NONE); |
216 png_set_filter(png_ptr, 0, PNG_FILTER_NONE); |
208 |
217 |
209 png_set_IHDR(png_ptr, info_ptr, w, h, pixelformat, PNG_COLOR_TYPE_PALETTE, |
218 png_set_IHDR(png_ptr, info_ptr, w, h, 8, pixelformat == 8 ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB, |
210 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); |
219 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); |
211 |
220 |
212 /* convert the palette to the .PNG format. */ |
221 if (pixelformat == 8) { |
213 for (i = 0; i != 256; i++) { |
222 /* convert the palette to the .PNG format. */ |
214 rq[i].red = palette[i].r; |
223 for (i = 0; i != 256; i++) { |
215 rq[i].green = palette[i].g; |
224 rq[i].red = palette[i].r; |
216 rq[i].blue = palette[i].b; |
225 rq[i].green = palette[i].g; |
217 } |
226 rq[i].blue = palette[i].b; |
218 |
227 } |
219 png_set_PLTE(png_ptr, info_ptr, rq, 256); |
228 |
|
229 png_set_PLTE(png_ptr, info_ptr, rq, 256); |
|
230 } |
|
231 |
220 png_write_info(png_ptr, info_ptr); |
232 png_write_info(png_ptr, info_ptr); |
221 png_set_flush(png_ptr, 512); |
233 png_set_flush(png_ptr, 512); |
222 |
234 |
|
235 if (pixelformat == 32) { |
|
236 png_color_8 sig_bit; |
|
237 |
|
238 /* Save exact color/alpha resolution */ |
|
239 sig_bit.alpha = 0; |
|
240 sig_bit.blue = 8; |
|
241 sig_bit.green = 8; |
|
242 sig_bit.red = 8; |
|
243 sig_bit.gray = 8; |
|
244 png_set_sBIT(png_ptr, info_ptr, &sig_bit); |
|
245 |
|
246 #ifdef TTD_LITTLE_ENDIAN |
|
247 png_set_bgr(png_ptr); |
|
248 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); |
|
249 #else |
|
250 png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); |
|
251 #endif |
|
252 } |
|
253 |
223 /* use by default 64k temp memory */ |
254 /* use by default 64k temp memory */ |
224 maxlines = clamp(65536 / w, 16, 128); |
255 maxlines = clamp(65536 / w, 16, 128); |
225 |
256 |
226 /* now generate the bitmap bits */ |
257 /* now generate the bitmap bits */ |
227 Pixel *buff = MallocT<Pixel>(w * maxlines); // by default generate 128 lines at a time. |
258 void *buff = MallocT<uint8>(w * maxlines * bpp); // by default generate 128 lines at a time. |
228 if (buff == NULL) { |
259 if (buff == NULL) { |
229 png_destroy_write_struct(&png_ptr, &info_ptr); |
260 png_destroy_write_struct(&png_ptr, &info_ptr); |
230 fclose(f); |
261 fclose(f); |
231 return false; |
262 return false; |
232 } |
263 } |
233 memset(buff, 0, w * maxlines); // zero the buffer to have the padding bytes set to 0 |
264 memset(buff, 0, w * maxlines * bpp); |
234 |
265 |
235 y = 0; |
266 y = 0; |
236 do { |
267 do { |
237 /* determine # lines to write */ |
268 /* determine # lines to write */ |
238 n = min(h - y, maxlines); |
269 n = min(h - y, maxlines); |
241 callb(userdata, buff, y, w, n); |
272 callb(userdata, buff, y, w, n); |
242 y += n; |
273 y += n; |
243 |
274 |
244 /* write them to png */ |
275 /* write them to png */ |
245 for (i = 0; i != n; i++) |
276 for (i = 0; i != n; i++) |
246 png_write_row(png_ptr, buff + i * w); |
277 png_write_row(png_ptr, (png_bytep)buff + i * w * bpp); |
247 } while (y != h); |
278 } while (y != h); |
248 |
279 |
249 png_write_end(png_ptr, info_ptr); |
280 png_write_end(png_ptr, info_ptr); |
250 png_destroy_write_struct(&png_ptr, &info_ptr); |
281 png_destroy_write_struct(&png_ptr, &info_ptr); |
251 |
282 |
337 callb(userdata, buff, y, w, n); |
372 callb(userdata, buff, y, w, n); |
338 y += n; |
373 y += n; |
339 |
374 |
340 /* write them to pcx */ |
375 /* write them to pcx */ |
341 for (i = 0; i != n; i++) { |
376 for (i = 0; i != n; i++) { |
342 const Pixel* bufp = buff + i * w; |
377 const uint8 *bufp = buff + i * w; |
343 byte runchar = bufp[0]; |
378 byte runchar = bufp[0]; |
344 uint runcount = 1; |
379 uint runcount = 1; |
345 uint j; |
380 uint j; |
346 |
381 |
347 /* for each pixel... */ |
382 /* for each pixel... */ |
348 for (j = 1; j < w; j++) { |
383 for (j = 1; j < w; j++) { |
349 Pixel ch = bufp[j]; |
384 uint8 ch = bufp[j]; |
350 |
385 |
351 if (ch != runchar || runcount >= 0x3f) { |
386 if (ch != runchar || runcount >= 0x3f) { |
352 if (runcount > 1 || (runchar & 0xC0) == 0xC0) |
387 if (runcount > 1 || (runchar & 0xC0) == 0xC0) |
353 if (fputc(0xC0 | runcount, f) == EOF) { |
388 if (fputc(0xC0 | runcount, f) == EOF) { |
354 free(buff); |
389 free(buff); |
444 _cur_screenshot_format = i; |
479 _cur_screenshot_format = i; |
445 strcpy(_screenshot_format_name, _screenshot_formats[i].extension); |
480 strcpy(_screenshot_format_name, _screenshot_formats[i].extension); |
446 } |
481 } |
447 |
482 |
448 /* screenshot generator that dumps the current video buffer */ |
483 /* screenshot generator that dumps the current video buffer */ |
449 static void CurrentScreenCallback(void *userdata, Pixel *buf, uint y, uint pitch, uint n) |
484 static void CurrentScreenCallback(void *userdata, void *buf, uint y, uint pitch, uint n) |
450 { |
485 { |
451 for (; n > 0; --n) { |
486 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter(); |
452 memcpy(buf, _screen.dst_ptr + y * _screen.pitch, _screen.width * sizeof(Pixel)); |
487 void *src = blitter->MoveTo(_screen.dst_ptr, 0, y); |
453 ++y; |
488 blitter->CopyToBuffer(src, buf, _screen.width, n, pitch); |
454 buf += pitch; |
|
455 } |
|
456 } |
489 } |
457 |
490 |
458 /* generate a large piece of the world */ |
491 /* generate a large piece of the world */ |
459 static void LargeWorldCallback(void *userdata, Pixel *buf, uint y, uint pitch, uint n) |
492 static void LargeWorldCallback(void *userdata, void *buf, uint y, uint pitch, uint n) |
460 { |
493 { |
461 ViewPort *vp = (ViewPort *)userdata; |
494 ViewPort *vp = (ViewPort *)userdata; |
462 DrawPixelInfo dpi, *old_dpi; |
495 DrawPixelInfo dpi, *old_dpi; |
463 int wx, left; |
496 int wx, left; |
464 |
497 |
489 _cur_dpi = old_dpi; |
522 _cur_dpi = old_dpi; |
490 } |
523 } |
491 |
524 |
492 static char *MakeScreenshotName(const char *ext) |
525 static char *MakeScreenshotName(const char *ext) |
493 { |
526 { |
494 static char filename[256]; |
527 static char filename[MAX_PATH]; |
495 char *base; |
|
496 int serial; |
528 int serial; |
|
529 size_t len; |
497 |
530 |
498 if (_game_mode == GM_EDITOR || _game_mode == GM_MENU || _local_player == PLAYER_SPECTATOR) { |
531 if (_game_mode == GM_EDITOR || _game_mode == GM_MENU || _local_player == PLAYER_SPECTATOR) { |
499 sprintf(_screenshot_name, "screenshot"); |
532 ttd_strlcpy(_screenshot_name, "screenshot", lengthof(_screenshot_name)); |
500 } else { |
533 } else { |
501 const Player* p = GetPlayer(_local_player); |
534 const Player* p = GetPlayer(_local_player); |
502 SetDParam(0, p->name_1); |
535 SetDParam(0, p->name_1); |
503 SetDParam(1, p->name_2); |
536 SetDParam(1, p->name_2); |
504 SetDParam(2, _date); |
537 SetDParam(2, _date); |
505 GetString(_screenshot_name, STR_4004, lastof(_screenshot_name)); |
538 GetString(_screenshot_name, STR_4004, lastof(_screenshot_name)); |
506 } |
539 } |
507 |
540 |
508 base = strchr(_screenshot_name, 0); |
541 /* Add extension to screenshot file */ |
509 base[0] = '.'; strcpy(base + 1, ext); |
542 SanitizeFilename(_screenshot_name); |
510 |
543 len = strlen(_screenshot_name); |
511 serial = 0; |
544 snprintf(&_screenshot_name[len], lengthof(_screenshot_name) - len, ".%s", ext); |
512 for (;;) { |
545 |
513 snprintf(filename, sizeof(filename), "%s%s", _paths.personal_dir, _screenshot_name); |
546 for (serial = 1;; serial++) { |
514 if (!FileExists(filename)) |
547 snprintf(filename, lengthof(filename), "%s%s", _personal_dir, _screenshot_name); |
515 break; |
548 if (!FileExists(filename)) break; |
516 sprintf(base, " #%d.%s", ++serial, ext); |
549 /* If file exists try another one with same name, but just with a higher index */ |
|
550 snprintf(&_screenshot_name[len], lengthof(_screenshot_name) - len, "#%d.%s", serial, ext); |
517 } |
551 } |
518 |
552 |
519 return filename; |
553 return filename; |
520 } |
554 } |
521 |
555 |
549 vp.width = vp.virtual_width; |
583 vp.width = vp.virtual_width; |
550 vp.virtual_height = (MapMaxX() + MapMaxY()) * TILE_PIXELS >> 1; |
584 vp.virtual_height = (MapMaxX() + MapMaxY()) * TILE_PIXELS >> 1; |
551 vp.height = vp.virtual_height; |
585 vp.height = vp.virtual_height; |
552 |
586 |
553 sf = _screenshot_formats + _cur_screenshot_format; |
587 sf = _screenshot_formats + _cur_screenshot_format; |
554 return sf->proc(MakeScreenshotName(sf->extension), LargeWorldCallback, &vp, vp.width, vp.height, 8, _cur_palette); |
588 return sf->proc(MakeScreenshotName(sf->extension), LargeWorldCallback, &vp, vp.width, vp.height, BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(), _cur_palette); |
555 } |
589 } |
556 |
590 |
557 bool MakeScreenshot() |
591 bool MakeScreenshot() |
558 { |
592 { |
559 switch (current_screenshot_type) { |
593 switch (current_screenshot_type) { |