1 /* $Id: screenshot.cpp 26209 2014-01-02 22:41:58Z rubidium $ */
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
10 /** @file screenshot.cpp The creation of screenshots! */
13 #include "fileio_func.h"
14 #include "viewport_func.h"
16 #include "screenshot.h"
17 #include "blitter/factory.hpp"
18 #include "zoom_func.h"
19 #include "core/endian_func.hpp"
20 #include "saveload/saveload.h"
21 #include "company_func.h"
22 #include "strings_func.h"
24 #include "window_gui.h"
25 #include "window_func.h"
27 #include "landscape.h"
28 #include "smallmap_gui.h"
29 #include "smallmap_colours.h"
32 #include "table/strings.h"
34 #include "safeguards.h"
36 static const char * const SCREENSHOT_NAME
= "screenshot"; ///< Default filename of a saved screenshot.
37 static const char * const HEIGHTMAP_NAME
= "heightmap"; ///< Default filename of a saved heightmap.
39 char _screenshot_format_name
[8]; ///< Extension of the current screenshot format (corresponds with #_cur_screenshot_format).
40 uint _num_screenshot_formats
; ///< Number of available screenshot formats.
41 uint _cur_screenshot_format
; ///< Index of the currently selected screenshot format in #_screenshot_formats.
42 static char _screenshot_name
[128]; ///< Filename of the screenshot file.
43 char _full_screenshot_name
[MAX_PATH
]; ///< Pathname of the screenshot file.
46 * Callback function signature for generating lines of pixel data to be written to the screenshot file.
47 * @param userdata Pointer to user data.
48 * @param buf Destination buffer.
49 * @param y Line number of the first line to write.
50 * @param pitch Number of pixels to write (1 byte for 8bpp, 4 bytes for 32bpp). @see Colour
51 * @param n Number of lines to write.
53 typedef void ScreenshotCallback(void *userdata
, void *buf
, uint y
, uint pitch
, uint n
);
56 * Function signature for a screenshot generation routine for one of the available formats.
57 * @param name Filename, including extension.
58 * @param callb Callback function for generating lines of pixels.
59 * @param userdata User data, passed on to \a callb.
60 * @param w Width of the image in pixels.
61 * @param h Height of the image in pixels.
62 * @param pixelformat Bits per pixel (bpp), either 8 or 32.
63 * @param palette %Colour palette (for 8bpp images).
64 * @return File was written successfully.
66 typedef bool ScreenshotHandlerProc(const char *name
, ScreenshotCallback
*callb
, void *userdata
, uint w
, uint h
, int pixelformat
, const Colour
*palette
);
68 /** Screenshot format information. */
69 struct ScreenshotFormat
{
70 const char *extension
; ///< File extension.
71 ScreenshotHandlerProc
*proc
; ///< Function for writing the screenshot.
74 #define MKCOLOUR(x) TO_LE32X(x)
76 /*************************************************
77 **** SCREENSHOT CODE FOR WINDOWS BITMAP (.BMP)
78 *************************************************/
79 #if defined(_MSC_VER) || defined(__WATCOMC__)
83 /** BMP File Header (stored in little endian) */
84 struct BitmapFileHeader
{
90 assert_compile(sizeof(BitmapFileHeader
) == 14);
92 #if defined(_MSC_VER) || defined(__WATCOMC__)
96 /** BMP Info Header (stored in little endian) */
97 struct BitmapInfoHeader
{
100 uint16 planes
, bitcount
;
101 uint32 compression
, sizeimage
, xpels
, ypels
, clrused
, clrimp
;
103 assert_compile(sizeof(BitmapInfoHeader
) == 40);
105 /** Format of palette data in BMP header */
107 byte blue
, green
, red
, reserved
;
109 assert_compile(sizeof(RgbQuad
) == 4);
112 * Generic .BMP writer
113 * @param name file name including extension
114 * @param callb callback used for gathering rendered image
115 * @param userdata parameters forwarded to \a callb
116 * @param w width in pixels
117 * @param h height in pixels
118 * @param pixelformat bits per pixel
119 * @param palette colour palette (for 8bpp mode)
120 * @return was everything ok?
121 * @see ScreenshotHandlerProc
123 static bool MakeBMPImage(const char *name
, ScreenshotCallback
*callb
, void *userdata
, uint w
, uint h
, int pixelformat
, const Colour
*palette
)
125 uint bpp
; // bytes per pixel
126 switch (pixelformat
) {
127 case 8: bpp
= 1; break;
128 /* 32bpp mode is saved as 24bpp BMP */
129 case 32: bpp
= 3; break;
130 /* Only implemented for 8bit and 32bit images so far */
131 default: return false;
134 FILE *f
= fopen(name
, "wb");
135 if (f
== nullptr) return false;
137 /* Each scanline must be aligned on a 32bit boundary */
138 uint bytewidth
= Align(w
* bpp
, 4); // bytes per line in file
140 /* Size of palette. Only present for 8bpp mode */
141 uint pal_size
= pixelformat
== 8 ? sizeof(RgbQuad
) * 256 : 0;
143 /* Setup the file header */
144 BitmapFileHeader bfh
;
145 bfh
.type
= TO_LE16('MB');
146 bfh
.size
= TO_LE32(sizeof(BitmapFileHeader
) + sizeof(BitmapInfoHeader
) + pal_size
+ bytewidth
* h
);
148 bfh
.off_bits
= TO_LE32(sizeof(BitmapFileHeader
) + sizeof(BitmapInfoHeader
) + pal_size
);
150 /* Setup the info header */
151 BitmapInfoHeader bih
;
152 bih
.size
= TO_LE32(sizeof(BitmapInfoHeader
));
153 bih
.width
= TO_LE32(w
);
154 bih
.height
= TO_LE32(h
);
155 bih
.planes
= TO_LE16(1);
156 bih
.bitcount
= TO_LE16(bpp
* 8);
164 /* Write file header and info header */
165 if (fwrite(&bfh
, sizeof(bfh
), 1, f
) != 1 || fwrite(&bih
, sizeof(bih
), 1, f
) != 1) {
170 if (pixelformat
== 8) {
171 /* Convert the palette to the windows format */
173 for (uint i
= 0; i
< 256; i
++) {
174 rq
[i
].red
= palette
[i
].r
;
175 rq
[i
].green
= palette
[i
].g
;
176 rq
[i
].blue
= palette
[i
].b
;
179 /* Write the palette */
180 if (fwrite(rq
, sizeof(rq
), 1, f
) != 1) {
186 /* Try to use 64k of memory, store between 16 and 128 lines */
187 uint maxlines
= Clamp(65536 / (w
* pixelformat
/ 8), 16, 128); // number of lines per iteration
189 uint8
*buff
= MallocT
<uint8
>(maxlines
* w
* pixelformat
/ 8); // buffer which is rendered to
190 uint8
*line
= AllocaM(uint8
, bytewidth
); // one line, stored to file
191 memset(line
, 0, bytewidth
);
193 /* Start at the bottom, since bitmaps are stored bottom up */
195 uint n
= min(h
, maxlines
);
198 /* Render the pixels */
199 callb(userdata
, buff
, h
, w
, n
);
201 /* Write each line */
203 if (pixelformat
== 8) {
204 /* Move to 'line', leave last few pixels in line zeroed */
205 memcpy(line
, buff
+ n
* w
, w
);
207 /* Convert from 'native' 32bpp to BMP-like 24bpp.
208 * Works for both big and little endian machines */
209 Colour
*src
= ((Colour
*)buff
) + n
* w
;
211 for (uint i
= 0; i
< w
; i
++) {
212 dst
[i
* 3 ] = src
[i
].b
;
213 dst
[i
* 3 + 1] = src
[i
].g
;
214 dst
[i
* 3 + 2] = src
[i
].r
;
218 if (fwrite(line
, bytewidth
, 1, f
) != 1) {
232 /*********************************************************
233 **** SCREENSHOT CODE FOR PORTABLE NETWORK GRAPHICS (.PNG)
234 *********************************************************/
235 #if defined(WITH_PNG)
238 #ifdef PNG_TEXT_SUPPORTED
240 #include "newgrf_config.h"
241 #include "ai/ai_info.hpp"
242 #include "company_base.h"
243 #include "base_media_base.h"
244 #endif /* PNG_TEXT_SUPPORTED */
246 static void PNGAPI
png_my_error(png_structp png_ptr
, png_const_charp message
)
248 DEBUG(misc
, 0, "[libpng] error: %s - %s", message
, (const char *)png_get_error_ptr(png_ptr
));
249 longjmp(png_jmpbuf(png_ptr
), 1);
252 static void PNGAPI
png_my_warning(png_structp png_ptr
, png_const_charp message
)
254 DEBUG(misc
, 1, "[libpng] warning: %s - %s", message
, (const char *)png_get_error_ptr(png_ptr
));
258 * Generic .PNG file image writer.
259 * @param name Filename, including extension.
260 * @param callb Callback function for generating lines of pixels.
261 * @param userdata User data, passed on to \a callb.
262 * @param w Width of the image in pixels.
263 * @param h Height of the image in pixels.
264 * @param pixelformat Bits per pixel (bpp), either 8 or 32.
265 * @param palette %Colour palette (for 8bpp images).
266 * @return File was written successfully.
267 * @see ScreenshotHandlerProc
269 static bool MakePNGImage(const char *name
, ScreenshotCallback
*callb
, void *userdata
, uint w
, uint h
, int pixelformat
, const Colour
*palette
)
275 uint bpp
= pixelformat
/ 8;
279 /* only implemented for 8bit and 32bit images so far. */
280 if (pixelformat
!= 8 && pixelformat
!= 32) return false;
282 f
= fopen(name
, "wb");
283 if (f
== nullptr) return false;
285 png_ptr
= png_create_write_struct(PNG_LIBPNG_VER_STRING
, const_cast<char *>(name
), png_my_error
, png_my_warning
);
287 if (png_ptr
== nullptr) {
292 info_ptr
= png_create_info_struct(png_ptr
);
293 if (info_ptr
== nullptr) {
294 png_destroy_write_struct(&png_ptr
, (png_infopp
)nullptr);
299 if (setjmp(png_jmpbuf(png_ptr
))) {
300 png_destroy_write_struct(&png_ptr
, &info_ptr
);
305 png_init_io(png_ptr
, f
);
307 png_set_filter(png_ptr
, 0, PNG_FILTER_NONE
);
309 png_set_IHDR(png_ptr
, info_ptr
, w
, h
, 8, pixelformat
== 8 ? PNG_COLOR_TYPE_PALETTE
: PNG_COLOR_TYPE_RGB
,
310 PNG_INTERLACE_NONE
, PNG_COMPRESSION_TYPE_DEFAULT
, PNG_FILTER_TYPE_DEFAULT
);
312 #ifdef PNG_TEXT_SUPPORTED
313 /* Try to add some game metadata to the PNG screenshot so
314 * it's more useful for debugging and archival purposes. */
315 png_text_struct text
[2];
316 memset(text
, 0, sizeof(text
));
317 text
[0].key
= const_cast<char *>("Software");
318 text
[0].text
= const_cast<char *>(_openttd_revision
);
319 text
[0].text_length
= strlen(_openttd_revision
);
320 text
[0].compression
= PNG_TEXT_COMPRESSION_NONE
;
324 p
+= seprintf(p
, lastof(buf
), "Graphics set: %s (%u)\n", BaseGraphics::GetUsedSet()->name
, BaseGraphics::GetUsedSet()->version
);
325 p
= strecpy(p
, "NewGRFs:\n", lastof(buf
));
326 for (const GRFConfig
*c
= _game_mode
== GM_MENU
? nullptr : _grfconfig
; c
!= nullptr; c
= c
->next
) {
327 p
+= seprintf(p
, lastof(buf
), "%08X ", BSWAP32(c
->ident
.grfid
));
328 p
= md5sumToString(p
, lastof(buf
), c
->ident
.md5sum
);
329 p
+= seprintf(p
, lastof(buf
), " %s\n", c
->filename
);
331 p
= strecpy(p
, "\nCompanies:\n", lastof(buf
));
333 FOR_ALL_COMPANIES(c
) {
334 if (c
->ai_info
== nullptr) {
335 p
+= seprintf(p
, lastof(buf
), "%2i: Human\n", (int)c
->index
);
337 p
+= seprintf(p
, lastof(buf
), "%2i: %s (v%d)\n", (int)c
->index
, c
->ai_info
->GetName(), c
->ai_info
->GetVersion());
340 text
[1].key
= const_cast<char *>("Description");
342 text
[1].text_length
= p
- buf
;
343 text
[1].compression
= PNG_TEXT_COMPRESSION_zTXt
;
344 png_set_text(png_ptr
, info_ptr
, text
, 2);
345 #endif /* PNG_TEXT_SUPPORTED */
347 if (pixelformat
== 8) {
348 /* convert the palette to the .PNG format. */
349 for (i
= 0; i
!= 256; i
++) {
350 rq
[i
].red
= palette
[i
].r
;
351 rq
[i
].green
= palette
[i
].g
;
352 rq
[i
].blue
= palette
[i
].b
;
355 png_set_PLTE(png_ptr
, info_ptr
, rq
, 256);
358 png_write_info(png_ptr
, info_ptr
);
359 png_set_flush(png_ptr
, 512);
361 if (pixelformat
== 32) {
364 /* Save exact colour/alpha resolution */
370 png_set_sBIT(png_ptr
, info_ptr
, &sig_bit
);
372 #if TTD_ENDIAN == TTD_LITTLE_ENDIAN
373 png_set_bgr(png_ptr
);
374 png_set_filler(png_ptr
, 0, PNG_FILLER_AFTER
);
376 png_set_filler(png_ptr
, 0, PNG_FILLER_BEFORE
);
377 #endif /* TTD_ENDIAN == TTD_LITTLE_ENDIAN */
380 /* use by default 64k temp memory */
381 maxlines
= Clamp(65536 / w
, 16, 128);
383 /* now generate the bitmap bits */
384 void *buff
= CallocT
<uint8
>(w
* maxlines
* bpp
); // by default generate 128 lines at a time.
388 /* determine # lines to write */
389 n
= min(h
- y
, maxlines
);
391 /* render the pixels into the buffer */
392 callb(userdata
, buff
, y
, w
, n
);
395 /* write them to png */
396 for (i
= 0; i
!= n
; i
++) {
397 png_write_row(png_ptr
, (png_bytep
)buff
+ i
* w
* bpp
);
401 png_write_end(png_ptr
, info_ptr
);
402 png_destroy_write_struct(&png_ptr
, &info_ptr
);
408 #endif /* WITH_PNG */
411 /*************************************************
412 **** SCREENSHOT CODE FOR ZSOFT PAINTBRUSH (.PCX)
413 *************************************************/
415 /** Definition of a PCX file header. */
424 byte pal_small
[16 * 3];
433 assert_compile(sizeof(PcxHeader
) == 128);
436 * Generic .PCX file image writer.
437 * @param name Filename, including extension.
438 * @param callb Callback function for generating lines of pixels.
439 * @param userdata User data, passed on to \a callb.
440 * @param w Width of the image in pixels.
441 * @param h Height of the image in pixels.
442 * @param pixelformat Bits per pixel (bpp), either 8 or 32.
443 * @param palette %Colour palette (for 8bpp images).
444 * @return File was written successfully.
445 * @see ScreenshotHandlerProc
447 static bool MakePCXImage(const char *name
, ScreenshotCallback
*callb
, void *userdata
, uint w
, uint h
, int pixelformat
, const Colour
*palette
)
455 if (pixelformat
== 32) {
456 DEBUG(misc
, 0, "Can't convert a 32bpp screenshot to PCX format. Please pick another format.");
459 if (pixelformat
!= 8 || w
== 0) return false;
461 f
= fopen(name
, "wb");
462 if (f
== nullptr) return false;
464 memset(&pcx
, 0, sizeof(pcx
));
466 /* setup pcx header */
467 pcx
.manufacturer
= 10;
471 pcx
.xmax
= TO_LE16(w
- 1);
472 pcx
.ymax
= TO_LE16(h
- 1);
473 pcx
.hdpi
= TO_LE16(320);
474 pcx
.vdpi
= TO_LE16(320);
477 pcx
.cpal
= TO_LE16(1);
478 pcx
.width
= pcx
.pitch
= TO_LE16(w
);
479 pcx
.height
= TO_LE16(h
);
481 /* write pcx header */
482 if (fwrite(&pcx
, sizeof(pcx
), 1, f
) != 1) {
487 /* use by default 64k temp memory */
488 maxlines
= Clamp(65536 / w
, 16, 128);
490 /* now generate the bitmap bits */
491 uint8
*buff
= CallocT
<uint8
>(w
* maxlines
); // by default generate 128 lines at a time.
495 /* determine # lines to write */
496 uint n
= min(h
- y
, maxlines
);
499 /* render the pixels into the buffer */
500 callb(userdata
, buff
, y
, w
, n
);
503 /* write them to pcx */
504 for (i
= 0; i
!= n
; i
++) {
505 const uint8
*bufp
= buff
+ i
* w
;
506 byte runchar
= bufp
[0];
510 /* for each pixel... */
511 for (j
= 1; j
< w
; j
++) {
514 if (ch
!= runchar
|| runcount
>= 0x3f) {
515 if (runcount
> 1 || (runchar
& 0xC0) == 0xC0) {
516 if (fputc(0xC0 | runcount
, f
) == EOF
) {
522 if (fputc(runchar
, f
) == EOF
) {
533 /* write remaining bytes.. */
534 if (runcount
> 1 || (runchar
& 0xC0) == 0xC0) {
535 if (fputc(0xC0 | runcount
, f
) == EOF
) {
541 if (fputc(runchar
, f
) == EOF
) {
551 /* write 8-bit colour palette */
552 if (fputc(12, f
) == EOF
) {
557 /* Palette is word-aligned, copy it to a temporary byte array */
560 for (uint i
= 0; i
< 256; i
++) {
561 tmp
[i
* 3 + 0] = palette
[i
].r
;
562 tmp
[i
* 3 + 1] = palette
[i
].g
;
563 tmp
[i
* 3 + 2] = palette
[i
].b
;
565 success
= fwrite(tmp
, sizeof(tmp
), 1, f
) == 1;
572 /*************************************************
573 **** GENERIC SCREENSHOT CODE
574 *************************************************/
576 /** Available screenshot formats. */
577 static const ScreenshotFormat _screenshot_formats
[] = {
578 #if defined(WITH_PNG)
579 {"png", &MakePNGImage
},
581 {"bmp", &MakeBMPImage
},
582 {"pcx", &MakePCXImage
},
585 /** Get filename extension of current screenshot file format. */
586 const char *GetCurrentScreenshotExtension()
588 return _screenshot_formats
[_cur_screenshot_format
].extension
;
591 /** Initialize screenshot format information on startup, with #_screenshot_format_name filled from the loadsave code. */
592 void InitializeScreenshotFormats()
595 for (uint i
= 0; i
< lengthof(_screenshot_formats
); i
++) {
596 if (!strcmp(_screenshot_format_name
, _screenshot_formats
[i
].extension
)) {
601 _cur_screenshot_format
= j
;
602 _num_screenshot_formats
= lengthof(_screenshot_formats
);
606 * Callback of the screenshot generator that dumps the current video buffer.
607 * @see ScreenshotCallback
609 static void CurrentScreenCallback(void *userdata
, void *buf
, uint y
, uint pitch
, uint n
)
611 Blitter
*blitter
= BlitterFactory::GetCurrentBlitter();
612 void *src
= blitter
->MoveTo(_screen
.dst_ptr
, 0, y
);
613 blitter
->CopyImageToBuffer(src
, buf
, _screen
.width
, n
, pitch
);
617 * generate a large piece of the world
618 * @param userdata Viewport area to draw
619 * @param buf Videobuffer with same bitdepth as current blitter
620 * @param y First line to render
621 * @param pitch Pitch of the videobuffer
622 * @param n Number of lines to render
624 static void LargeWorldCallback(void *userdata
, void *buf
, uint y
, uint pitch
, uint n
)
626 ViewPort
*vp
= (ViewPort
*)userdata
;
627 DrawPixelInfo dpi
, *old_dpi
;
630 /* We are no longer rendering to the screen */
631 DrawPixelInfo old_screen
= _screen
;
632 bool old_disable_anim
= _screen_disable_anim
;
634 _screen
.dst_ptr
= buf
;
635 _screen
.width
= pitch
;
637 _screen
.pitch
= pitch
;
638 _screen_disable_anim
= true;
645 dpi
.width
= vp
->width
;
647 dpi
.zoom
= ZOOM_LVL_WORLD_SCREENSHOT
;
651 /* Render viewport in blocks of 1600 pixels width */
653 while (vp
->width
- left
!= 0) {
654 wx
= min(vp
->width
- left
, 1600);
658 ScaleByZoom(left
- wx
- vp
->left
, vp
->zoom
) + vp
->virtual_left
,
659 ScaleByZoom(y
- vp
->top
, vp
->zoom
) + vp
->virtual_top
,
660 ScaleByZoom(left
- vp
->left
, vp
->zoom
) + vp
->virtual_left
,
661 ScaleByZoom((y
+ n
) - vp
->top
, vp
->zoom
) + vp
->virtual_top
667 /* Switch back to rendering to the screen */
668 _screen
= old_screen
;
669 _screen_disable_anim
= old_disable_anim
;
673 * Construct a pathname for a screenshot file.
674 * @param default_fn Default filename.
675 * @param ext Extension to use.
676 * @param crashlog Create path for crash.png
677 * @return Pathname for a screenshot file.
679 static const char *MakeScreenshotName(const char *default_fn
, const char *ext
, bool crashlog
= false)
681 bool generate
= StrEmpty(_screenshot_name
);
684 if (_game_mode
== GM_EDITOR
|| _game_mode
== GM_MENU
|| _local_company
== COMPANY_SPECTATOR
) {
685 strecpy(_screenshot_name
, default_fn
, lastof(_screenshot_name
));
687 GenerateDefaultSaveName(_screenshot_name
, lastof(_screenshot_name
));
691 /* Add extension to screenshot file */
692 size_t len
= strlen(_screenshot_name
);
693 seprintf(&_screenshot_name
[len
], lastof(_screenshot_name
), ".%s", ext
);
695 const char *screenshot_dir
= crashlog
? _personal_dir
: FiosGetScreenshotDir();
697 for (uint serial
= 1;; serial
++) {
698 if (seprintf(_full_screenshot_name
, lastof(_full_screenshot_name
), "%s%s", screenshot_dir
, _screenshot_name
) >= (int)lengthof(_full_screenshot_name
)) {
699 /* We need more characters than MAX_PATH -> end with error */
700 _full_screenshot_name
[0] = '\0';
703 if (!generate
) break; // allow overwriting of non-automatic filenames
704 if (!FileExists(_full_screenshot_name
)) break;
705 /* If file exists try another one with same name, but just with a higher index */
706 seprintf(&_screenshot_name
[len
], lastof(_screenshot_name
) - len
, "#%u.%s", serial
, ext
);
709 return _full_screenshot_name
;
712 /** Make a screenshot of the current screen. */
713 static bool MakeSmallScreenshot(bool crashlog
)
715 const ScreenshotFormat
*sf
= _screenshot_formats
+ _cur_screenshot_format
;
716 return sf
->proc(MakeScreenshotName(SCREENSHOT_NAME
, sf
->extension
, crashlog
), CurrentScreenCallback
, nullptr, _screen
.width
, _screen
.height
,
717 BlitterFactory::GetCurrentBlitter()->GetScreenDepth(), _cur_palette
.palette
);
721 * Configure a ViewPort for rendering (a part of) the map into a screenshot.
722 * @param t Screenshot type
723 * @param [out] vp Result viewport
725 void SetupScreenshotViewport(ScreenshotType t
, ViewPort
*vp
)
727 /* Determine world coordinates of screenshot */
729 vp
->zoom
= ZOOM_LVL_WORLD_SCREENSHOT
;
731 TileIndex north_tile
= _settings_game
.construction
.freeform_edges
? TileXY(1, 1) : TileXY(0, 0);
732 TileIndex south_tile
= MapSize() - 1;
734 /* We need to account for a hill or high building at tile 0,0. */
735 int extra_height_top
= TilePixelHeight(north_tile
) + 150;
736 /* If there is a hill at the bottom don't create a large black area. */
737 int reclaim_height_bottom
= TilePixelHeight(south_tile
);
739 vp
->virtual_left
= RemapCoords(TileX(south_tile
) * TILE_SIZE
, TileY(north_tile
) * TILE_SIZE
, 0).x
;
740 vp
->virtual_top
= RemapCoords(TileX(north_tile
) * TILE_SIZE
, TileY(north_tile
) * TILE_SIZE
, extra_height_top
).y
;
741 vp
->virtual_width
= RemapCoords(TileX(north_tile
) * TILE_SIZE
, TileY(south_tile
) * TILE_SIZE
, 0).x
- vp
->virtual_left
+ 1;
742 vp
->virtual_height
= RemapCoords(TileX(south_tile
) * TILE_SIZE
, TileY(south_tile
) * TILE_SIZE
, reclaim_height_bottom
).y
- vp
->virtual_top
+ 1;
744 vp
->zoom
= (t
== SC_ZOOMEDIN
) ? _settings_client
.gui
.zoom_min
: ZOOM_LVL_VIEWPORT
;
746 Window
*w
= FindWindowById(WC_MAIN_WINDOW
, 0);
747 vp
->virtual_left
= w
->viewport
->virtual_left
;
748 vp
->virtual_top
= w
->viewport
->virtual_top
;
749 vp
->virtual_width
= w
->viewport
->virtual_width
;
750 vp
->virtual_height
= w
->viewport
->virtual_height
;
753 /* Compute pixel coordinates */
756 vp
->width
= UnScaleByZoom(vp
->virtual_width
, vp
->zoom
);
757 vp
->height
= UnScaleByZoom(vp
->virtual_height
, vp
->zoom
);
758 vp
->overlay
= nullptr;
762 * Make a screenshot of the map.
763 * @param t Screenshot type: World or viewport screenshot
764 * @return true on success
766 static bool MakeLargeWorldScreenshot(ScreenshotType t
)
769 SetupScreenshotViewport(t
, &vp
);
771 const ScreenshotFormat
*sf
= _screenshot_formats
+ _cur_screenshot_format
;
772 return sf
->proc(MakeScreenshotName(SCREENSHOT_NAME
, sf
->extension
), LargeWorldCallback
, &vp
, vp
.width
, vp
.height
,
773 BlitterFactory::GetCurrentBlitter()->GetScreenDepth(), _cur_palette
.palette
);
777 * Callback for generating a heightmap. Supports 8bpp grayscale only.
778 * @param userdata Pointer to user data.
779 * @param buf Destination buffer.
780 * @param y Line number of the first line to write.
781 * @param pitch Number of pixels to write (1 byte for 8bpp, 4 bytes for 32bpp). @see Colour
782 * @param n Number of lines to write.
783 * @see ScreenshotCallback
785 static void HeightmapCallback(void *userdata
, void *buffer
, uint y
, uint pitch
, uint n
)
787 byte
*buf
= (byte
*)buffer
;
789 TileIndex ti
= TileXY(MapMaxX(), y
);
790 for (uint x
= MapMaxX(); true; x
--) {
791 *buf
= 256 * TileHeight(ti
) / (1 + _settings_game
.construction
.max_heightlevel
);
794 ti
= TILE_ADDXY(ti
, -1, 0);
802 * Make a heightmap of the current map.
803 * @param filename Filename to use for saving.
805 bool MakeHeightmapScreenshot(const char *filename
)
808 for (uint i
= 0; i
< lengthof(palette
); i
++) {
814 const ScreenshotFormat
*sf
= _screenshot_formats
+ _cur_screenshot_format
;
815 return sf
->proc(filename
, HeightmapCallback
, nullptr, MapSizeX(), MapSizeY(), 8, palette
);
819 * Show a a success or failure message indicating the result of a screenshot action
820 * @param ret whether the screenshot action was successful
822 static void ShowScreenshotResultMessage(bool ret
)
825 SetDParamStr(0, _screenshot_name
);
826 ShowErrorMessage(STR_MESSAGE_SCREENSHOT_SUCCESSFULLY
, INVALID_STRING_ID
, WL_WARNING
);
828 ShowErrorMessage(STR_ERROR_SCREENSHOT_FAILED
, INVALID_STRING_ID
, WL_ERROR
);
833 * Return the colour a tile would be displayed with in the small map in mode "Owner".
835 * @param tile The tile of which we would like to get the colour.
836 * @param screenshot_type The type of screenshot to create tile colors for.
837 * @return The colour of tile in the small map in mode "Owner"
839 static byte
GetMinimapPixels(TileIndex tile
, ScreenshotType screenshot_type
)
841 const auto tile_type
= GetTileType(tile
);
843 switch (screenshot_type
) {
845 if (tile_type
== MP_STATION
) {
846 switch (GetStationType(tile
)) {
848 return MKCOLOUR(PC_LIGHT_BLUE
);
849 case STATION_AIRPORT
:
850 return MKCOLOUR(0xCF);
852 return MKCOLOUR(PC_ORANGE
);
854 return MKCOLOUR(PC_YELLOW
);
855 case STATION_OILRIG
: // FALLTHROUGH
857 return MKCOLOUR(PC_WHITE
);
859 return MKCOLOUR(PC_WATER
);
860 case STATION_WAYPOINT
:
861 return MKCOLOUR(PC_GREY
);
862 default: NOT_REACHED();
866 if (IsBridgeAbove(tile
)) {
867 return MKCOLOUR(PC_DARK_GREY
);
871 case MP_TUNNELBRIDGE
:
872 return MKCOLOUR(PC_DARK_GREY
);
874 return MKCOLOUR(PC_GREY
);
876 return MKCOLOUR(PC_BLACK
);
878 return MKCOLOUR(0xB5);
880 return MKCOLOUR(PC_WATER
);
882 return MKCOLOUR(0xA2);
884 return MKCOLOUR(0x51);
889 case SC_MINI_HEIGHTMAP
: {
890 if (tile_type
== MP_STATION
) {
891 switch (GetStationType(tile
)) {
893 return MKCOLOUR(PC_GREY
);
894 case STATION_AIRPORT
:
895 return MKCOLOUR(PC_GREY
);
897 return MKCOLOUR(PC_BLACK
);
899 return MKCOLOUR(PC_BLACK
);
900 case STATION_OILRIG
: // FALLTHROUGH
902 return MKCOLOUR(PC_GREY
);
904 return MKCOLOUR(PC_WATER
);
905 case STATION_WAYPOINT
:
906 return MKCOLOUR(PC_GREY
);
907 default: NOT_REACHED();
911 if (IsBridgeAbove(tile
)) {
912 return MKCOLOUR(PC_DARK_GREY
);
916 case MP_TUNNELBRIDGE
:
917 return MKCOLOUR(PC_DARK_GREY
);
919 return MKCOLOUR(PC_GREY
);
921 return MKCOLOUR(PC_BLACK
);
923 return MKCOLOUR(0xB5);
925 return MKCOLOUR(PC_WATER
);
927 return MKCOLOUR(0xA2);
929 const auto tile_z
= GetTileZ(tile
);
930 const auto max_z
= _settings_game
.construction
.max_heightlevel
;
932 const auto color_index
= (tile_z
* 16) / max_z
;
934 switch (color_index
) {
936 return MKCOLOUR(0x50);
938 return MKCOLOUR(0x51);
940 return MKCOLOUR(0x52);
942 return MKCOLOUR(0x53);
944 return MKCOLOUR(0x54);
946 return MKCOLOUR(0x55);
948 return MKCOLOUR(0x56);
950 return MKCOLOUR(0x57);
952 return MKCOLOUR(0x3B);
954 return MKCOLOUR(0x3A);
956 return MKCOLOUR(0x39);
958 return MKCOLOUR(0x38);
960 return MKCOLOUR(0x37);
962 return MKCOLOUR(0x36);
964 return MKCOLOUR(0x35);
966 return MKCOLOUR(0x69);
968 return MKCOLOUR(0x46);
974 case SC_MINI_INDUSTRYMAP
: {
975 if (tile_type
== MP_STATION
) {
976 switch (GetStationType(tile
)) {
978 return MKCOLOUR(PC_DARK_GREY
);
979 case STATION_AIRPORT
:
980 return MKCOLOUR(GREY_SCALE(12));
982 return MKCOLOUR(PC_GREY
);
984 return MKCOLOUR(PC_GREY
);
985 case STATION_OILRIG
: // FALLTHROUGH
987 return MKCOLOUR(PC_GREY
);
989 return MKCOLOUR(PC_BLACK
);
990 case STATION_WAYPOINT
:
991 return MKCOLOUR(PC_GREY
);
992 default: NOT_REACHED();
996 if (IsBridgeAbove(tile
)) {
997 return MKCOLOUR(GREY_SCALE(12));
1000 switch (tile_type
) {
1001 case MP_TUNNELBRIDGE
:
1002 return MKCOLOUR(GREY_SCALE(12));
1004 return MKCOLOUR(PC_DARK_GREY
);
1006 return MKCOLOUR(PC_GREY
);
1008 return MKCOLOUR(GREY_SCALE(4));
1010 return MKCOLOUR(0x12);
1012 const IndustryType industry_type
= Industry::GetByTile(tile
)->type
;
1014 return GetIndustrySpec(industry_type
)->map_colour
* 0x01010101;
1017 return MKCOLOUR(GREY_SCALE(2));
1021 case SC_MINI_OWNERMAP
: {
1022 if (IsBridgeAbove(tile
)) return MKCOLOUR(PC_DARK_GREY
);
1024 // Setup owner table.
1025 uint32 _owner_colours
[OWNER_END
+ 1];
1027 // fill with some special colours.
1028 _owner_colours
[OWNER_TOWN
] = MKCOLOUR(0x00994433);
1029 _owner_colours
[OWNER_NONE
] = MKCOLOUR(0x54545454);
1030 _owner_colours
[OWNER_WATER
] = MKCOLOUR(0x00000066);
1031 _owner_colours
[OWNER_END
] = MKCOLOUR(0x20202020); // Industry
1033 // Now fill with the company colours.
1035 FOR_ALL_COMPANIES(c
) {
1036 _owner_colours
[c
->index
] =
1037 _colour_gradient
[c
->colour
][5];
1040 switch (GetTileType(tile
)) {
1042 return MKCOLOUR(GREY_SCALE(12));
1044 return MKCOLOUR(PC_GREY
);
1047 return _owner_colours
[GetTileOwner(tile
)];
1050 const Owner owner
= GetTileOwner(tile
);
1052 if (owner
== OWNER_TOWN
|| owner
== OWNER_NONE
|| owner
== OWNER_DEITY
) {
1053 return MKCOLOUR(PC_DARK_GREY
);
1056 return _owner_colours
[GetTileOwner(tile
)];
1060 const Owner owner
= GetTileOwner(tile
);
1062 if (owner
== OWNER_TOWN
|| owner
== OWNER_NONE
|| owner
== OWNER_DEITY
) {
1063 return MKCOLOUR(PC_DARK_GREY
);
1066 return _owner_colours
[GetTileOwner(tile
)];
1069 case MP_TUNNELBRIDGE
: {
1070 const Owner owner
= GetTileOwner(tile
);
1072 if (owner
== OWNER_TOWN
|| owner
== OWNER_NONE
|| owner
== OWNER_DEITY
) {
1073 return MKCOLOUR(PC_DARK_GREY
);
1076 return _owner_colours
[GetTileOwner(tile
)];
1079 case MP_OBJECT
: // FALLTHROUGH
1080 case MP_CLEAR
: // FALLTHROUGH
1082 return MKCOLOUR(GREY_SCALE(2));
1084 return MKCOLOUR(0x12);
1086 return MKCOLOUR(PC_BLACK
);
1087 default: NOT_REACHED();
1096 static void MinimapCallback(void *userdata
, void *buf
, uint y
, uint pitch
, uint n
)
1098 ScreenshotType type
= *(ScreenshotType
*)userdata
;
1099 uint8
*ubuf
= (uint8
*)buf
;
1101 uint num
= (pitch
* n
);
1105 for (uint i
= 0; i
< num
; i
++) {
1106 row
= y
+ (int)(i
/ pitch
);
1107 col
= (MapSizeX() - 1) - (i
% pitch
);
1109 TileIndex tile
= TileXY(col
, row
);
1111 if (IsTileType(tile
, MP_VOID
)) {
1115 val
= GetMinimapPixels(tile
, type
);
1118 *ubuf
= (uint8
)_cur_palette
.palette
[val
].b
;
1119 ubuf
+= sizeof(uint8
); *ubuf
= (uint8
)_cur_palette
.palette
[val
].g
;
1120 ubuf
+= sizeof(uint8
); *ubuf
= (uint8
)_cur_palette
.palette
[val
].r
;
1121 ubuf
+= sizeof(uint8
);
1122 ubuf
+= sizeof(uint8
);
1127 * Saves the complete savemap in a PNG-file.
1129 static bool MakeFlatMinimapScreenshot(ScreenshotType screenshotType
)
1131 _screenshot_name
[0] = '\0';
1133 const ScreenshotFormat
*sf
= _screenshot_formats
+ _cur_screenshot_format
;
1134 return sf
->proc(MakeScreenshotName("minimap", sf
->extension
), MinimapCallback
, &screenshotType
, MapSizeX(), MapSizeY(), 32, _cur_palette
.palette
);
1138 * Make an actual screenshot.
1139 * @param t the type of screenshot to make.
1140 * @param name the name to give to the screenshot.
1141 * @return true iff the screenshot was made successfully
1143 bool MakeScreenshot(ScreenshotType t
, const char *name
)
1145 if (t
== SC_VIEWPORT
) {
1146 /* First draw the dirty parts of the screen and only then change the name
1147 * of the screenshot. This way the screenshot will always show the name
1148 * of the previous screenshot in the 'successful' message instead of the
1149 * name of the new screenshot (or an empty name). */
1150 UndrawMouseCursor();
1154 _screenshot_name
[0] = '\0';
1155 if (name
!= nullptr) strecpy(_screenshot_name
, name
, lastof(_screenshot_name
));
1160 ret
= MakeSmallScreenshot(false);
1164 ret
= MakeSmallScreenshot(true);
1168 case SC_DEFAULTZOOM
:
1170 ret
= MakeLargeWorldScreenshot(t
);
1173 case SC_HEIGHTMAP
: {
1174 const ScreenshotFormat
*sf
= _screenshot_formats
+ _cur_screenshot_format
;
1175 ret
= MakeHeightmapScreenshot(MakeScreenshotName(HEIGHTMAP_NAME
, sf
->extension
));
1180 ret
= MakeFlatMinimapScreenshot(SC_MINIMAP
);
1184 case SC_MINI_HEIGHTMAP
: {
1185 ret
= MakeFlatMinimapScreenshot(SC_MINI_HEIGHTMAP
);
1189 case SC_MINI_INDUSTRYMAP
: {
1190 ret
= MakeFlatMinimapScreenshot(SC_MINI_INDUSTRYMAP
);
1194 case SC_MINI_OWNERMAP
: {
1195 ret
= MakeFlatMinimapScreenshot(SC_MINI_OWNERMAP
);
1203 ShowScreenshotResultMessage(ret
);
1209 * Callback for generating a smallmap screenshot.
1210 * @param userdata SmallMapWindow window pointer
1211 * @param buf Videobuffer with same bitdepth as current blitter
1212 * @param y First line to render
1213 * @param pitch Pitch of the videobuffer
1214 * @param n Number of lines to render
1216 static void SmallMapCallback(void *userdata
, void *buf
, uint y
, uint pitch
, uint n
)
1218 SmallMapWindow
*window
= static_cast<SmallMapWindow
*>(userdata
);
1219 window
->ScreenshotCallbackHandler(buf
, y
, pitch
, n
);
1223 * Make a screenshot of the smallmap
1224 * @param width the width of the screenshot
1225 * @param height the height of the screenshot
1226 * @param window a pointer to the smallmap window to use, the current mode and zoom status of the window is used for the screenshot
1227 * @return true iff the screenshot was made successfully
1229 bool MakeSmallMapScreenshot(unsigned int width
, unsigned int height
, SmallMapWindow
*window
)
1231 _screenshot_name
[0] = '\0';
1232 const ScreenshotFormat
*sf
= _screenshot_formats
+ _cur_screenshot_format
;
1233 bool ret
= sf
->proc(MakeScreenshotName(SCREENSHOT_NAME
, sf
->extension
), SmallMapCallback
, window
, width
, height
, BlitterFactory::GetCurrentBlitter()->GetScreenDepth(), _cur_palette
.palette
);
1234 ShowScreenshotResultMessage(ret
);