OpenTTD Source 20241224-master-gf74b0cf984
screenshot.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
6 */
7
10#include "stdafx.h"
11#include "core/backup_type.hpp"
12#include "fileio_func.h"
13#include "viewport_func.h"
14#include "gfx_func.h"
15#include "screenshot.h"
16#include "screenshot_gui.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_base.h"
22#include "company_func.h"
23#include "strings_func.h"
24#include "error.h"
25#include "textbuf_gui.h"
26#include "window_gui.h"
27#include "window_func.h"
28#include "tile_map.h"
29#include "landscape.h"
31#include "smallmap_gui.h"
32
33#include "table/strings.h"
34
35#include "safeguards.h"
36
37static const char * const SCREENSHOT_NAME = "screenshot";
38static const char * const HEIGHTMAP_NAME = "heightmap";
39
41static std::string _screenshot_name;
44
53typedef void ScreenshotCallback(void *userdata, void *buf, uint y, uint pitch, uint n);
54
66typedef bool ScreenshotHandlerProc(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette);
67
73
74#define MKCOLOUR(x) TO_LE32(x)
75
76/*************************************************
77 **** SCREENSHOT CODE FOR WINDOWS BITMAP (.BMP)
78 *************************************************/
79
81PACK(struct BitmapFileHeader {
82 uint16_t type;
83 uint32_t size;
84 uint32_t reserved;
85 uint32_t off_bits;
86});
87static_assert(sizeof(BitmapFileHeader) == 14);
88
91 uint32_t size;
92 int32_t width, height;
93 uint16_t planes, bitcount;
94 uint32_t compression, sizeimage, xpels, ypels, clrused, clrimp;
95};
96static_assert(sizeof(BitmapInfoHeader) == 40);
97
99struct RgbQuad {
100 uint8_t blue, green, red, reserved;
101};
102static_assert(sizeof(RgbQuad) == 4);
103
116static bool MakeBMPImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
117{
118 uint bpp; // bytes per pixel
119 switch (pixelformat) {
120 case 8: bpp = 1; break;
121 /* 32bpp mode is saved as 24bpp BMP */
122 case 32: bpp = 3; break;
123 /* Only implemented for 8bit and 32bit images so far */
124 default: return false;
125 }
126
127 auto of = FileHandle::Open(name, "wb");
128 if (!of.has_value()) return false;
129 auto &f = *of;
130
131 /* Each scanline must be aligned on a 32bit boundary */
132 uint bytewidth = Align(w * bpp, 4); // bytes per line in file
133
134 /* Size of palette. Only present for 8bpp mode */
135 uint pal_size = pixelformat == 8 ? sizeof(RgbQuad) * 256 : 0;
136
137 /* Setup the file header */
138 BitmapFileHeader bfh;
139 bfh.type = TO_LE16('MB');
140 bfh.size = TO_LE32(sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + pal_size + static_cast<size_t>(bytewidth) * h);
141 bfh.reserved = 0;
142 bfh.off_bits = TO_LE32(sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + pal_size);
143
144 /* Setup the info header */
146 bih.size = TO_LE32(sizeof(BitmapInfoHeader));
147 bih.width = TO_LE32(w);
148 bih.height = TO_LE32(h);
149 bih.planes = TO_LE16(1);
150 bih.bitcount = TO_LE16(bpp * 8);
151 bih.compression = 0;
152 bih.sizeimage = 0;
153 bih.xpels = 0;
154 bih.ypels = 0;
155 bih.clrused = 0;
156 bih.clrimp = 0;
157
158 /* Write file header and info header */
159 if (fwrite(&bfh, sizeof(bfh), 1, f) != 1 || fwrite(&bih, sizeof(bih), 1, f) != 1) {
160 return false;
161 }
162
163 if (pixelformat == 8) {
164 /* Convert the palette to the windows format */
165 RgbQuad rq[256];
166 for (uint i = 0; i < 256; i++) {
167 rq[i].red = palette[i].r;
168 rq[i].green = palette[i].g;
169 rq[i].blue = palette[i].b;
170 rq[i].reserved = 0;
171 }
172 /* Write the palette */
173 if (fwrite(rq, sizeof(rq), 1, f) != 1) {
174 return false;
175 }
176 }
177
178 /* Try to use 64k of memory, store between 16 and 128 lines */
179 uint maxlines = Clamp(65536 / (w * pixelformat / 8), 16, 128); // number of lines per iteration
180
181 std::vector<uint8_t> buff(maxlines * w * pixelformat / 8); // buffer which is rendered to
182 std::vector<uint8_t> line(bytewidth); // one line, stored to file
183
184 /* Start at the bottom, since bitmaps are stored bottom up */
185 do {
186 uint n = std::min(h, maxlines);
187 h -= n;
188
189 /* Render the pixels */
190 callb(userdata, buff.data(), h, w, n);
191
192 /* Write each line */
193 while (n-- != 0) {
194 if (pixelformat == 8) {
195 /* Move to 'line', leave last few pixels in line zeroed */
196 memcpy(line.data(), buff.data() + n * w, w);
197 } else {
198 /* Convert from 'native' 32bpp to BMP-like 24bpp.
199 * Works for both big and little endian machines */
200 Colour *src = ((Colour *)buff.data()) + n * w;
201 uint8_t *dst = line.data();
202 for (uint i = 0; i < w; i++) {
203 dst[i * 3 ] = src[i].b;
204 dst[i * 3 + 1] = src[i].g;
205 dst[i * 3 + 2] = src[i].r;
206 }
207 }
208 /* Write to file */
209 if (fwrite(line.data(), bytewidth, 1, f) != 1) {
210 return false;
211 }
212 }
213 } while (h != 0);
214
215
216 return true;
217}
218
219/*********************************************************
220 **** SCREENSHOT CODE FOR PORTABLE NETWORK GRAPHICS (.PNG)
221 *********************************************************/
222#if defined(WITH_PNG)
223#include <png.h>
224
225#ifdef PNG_TEXT_SUPPORTED
226#include "rev.h"
227#include "newgrf_config.h"
228#include "ai/ai_info.hpp"
229#include "company_base.h"
230#include "base_media_base.h"
231#endif /* PNG_TEXT_SUPPORTED */
232
233static void PNGAPI png_my_error(png_structp png_ptr, png_const_charp message)
234{
235 Debug(misc, 0, "[libpng] error: {} - {}", message, (const char *)png_get_error_ptr(png_ptr));
236 longjmp(png_jmpbuf(png_ptr), 1);
237}
238
239static void PNGAPI png_my_warning(png_structp png_ptr, png_const_charp message)
240{
241 Debug(misc, 1, "[libpng] warning: {} - {}", message, (const char *)png_get_error_ptr(png_ptr));
242}
243
256static bool MakePNGImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
257{
258 png_color rq[256];
259 uint i, y, n;
260 uint maxlines;
261 uint bpp = pixelformat / 8;
262 png_structp png_ptr;
263 png_infop info_ptr;
264
265 /* only implemented for 8bit and 32bit images so far. */
266 if (pixelformat != 8 && pixelformat != 32) return false;
267
268 auto of = FileHandle::Open(name, "wb");
269 if (!of.has_value()) return false;
270 auto &f = *of;
271
272 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, const_cast<char *>(name), png_my_error, png_my_warning);
273
274 if (png_ptr == nullptr) {
275 return false;
276 }
277
278 info_ptr = png_create_info_struct(png_ptr);
279 if (info_ptr == nullptr) {
280 png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
281 return false;
282 }
283
284 if (setjmp(png_jmpbuf(png_ptr))) {
285 png_destroy_write_struct(&png_ptr, &info_ptr);
286 return false;
287 }
288
289 png_init_io(png_ptr, f);
290
291 png_set_filter(png_ptr, 0, PNG_FILTER_NONE);
292
293 png_set_IHDR(png_ptr, info_ptr, w, h, 8, pixelformat == 8 ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB,
294 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
295
296#ifdef PNG_TEXT_SUPPORTED
297 /* Try to add some game metadata to the PNG screenshot so
298 * it's more useful for debugging and archival purposes. */
299 png_text_struct text[2];
300 memset(text, 0, sizeof(text));
301 text[0].key = const_cast<char *>("Software");
302 text[0].text = const_cast<char *>(_openttd_revision);
303 text[0].text_length = strlen(_openttd_revision);
304 text[0].compression = PNG_TEXT_COMPRESSION_NONE;
305
306 std::string message;
307 message.reserve(1024);
308 fmt::format_to(std::back_inserter(message), "Graphics set: {} ({})\n", BaseGraphics::GetUsedSet()->name, BaseGraphics::GetUsedSet()->version);
309 message += "NewGRFs:\n";
310 for (const GRFConfig *c = _game_mode == GM_MENU ? nullptr : _grfconfig; c != nullptr; c = c->next) {
311 fmt::format_to(std::back_inserter(message), "{:08X} {} {}\n", BSWAP32(c->ident.grfid), FormatArrayAsHex(c->ident.md5sum), c->filename);
312 }
313 message += "\nCompanies:\n";
314 for (const Company *c : Company::Iterate()) {
315 if (c->ai_info == nullptr) {
316 fmt::format_to(std::back_inserter(message), "{:2d}: Human\n", (int)c->index);
317 } else {
318 fmt::format_to(std::back_inserter(message), "{:2d}: {} (v{})\n", (int)c->index, c->ai_info->GetName(), c->ai_info->GetVersion());
319 }
320 }
321 text[1].key = const_cast<char *>("Description");
322 text[1].text = const_cast<char *>(message.c_str());
323 text[1].text_length = message.size();
324 text[1].compression = PNG_TEXT_COMPRESSION_zTXt;
325 png_set_text(png_ptr, info_ptr, text, 2);
326#endif /* PNG_TEXT_SUPPORTED */
327
328 if (pixelformat == 8) {
329 /* convert the palette to the .PNG format. */
330 for (i = 0; i != 256; i++) {
331 rq[i].red = palette[i].r;
332 rq[i].green = palette[i].g;
333 rq[i].blue = palette[i].b;
334 }
335
336 png_set_PLTE(png_ptr, info_ptr, rq, 256);
337 }
338
339 png_write_info(png_ptr, info_ptr);
340 png_set_flush(png_ptr, 512);
341
342 if (pixelformat == 32) {
343 png_color_8 sig_bit;
344
345 /* Save exact colour/alpha resolution */
346 sig_bit.alpha = 0;
347 sig_bit.blue = 8;
348 sig_bit.green = 8;
349 sig_bit.red = 8;
350 sig_bit.gray = 8;
351 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
352
353 if constexpr (std::endian::native == std::endian::little) {
354 png_set_bgr(png_ptr);
355 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
356 } else {
357 png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
358 }
359 }
360
361 /* use by default 64k temp memory */
362 maxlines = Clamp(65536 / w, 16, 128);
363
364 /* now generate the bitmap bits */
365 std::vector<uint8_t> buff(static_cast<size_t>(w) * maxlines * bpp); // by default generate 128 lines at a time.
366
367 y = 0;
368 do {
369 /* determine # lines to write */
370 n = std::min(h - y, maxlines);
371
372 /* render the pixels into the buffer */
373 callb(userdata, buff.data(), y, w, n);
374 y += n;
375
376 /* write them to png */
377 for (i = 0; i != n; i++) {
378 png_write_row(png_ptr, (png_bytep)buff.data() + i * w * bpp);
379 }
380 } while (y != h);
381
382 png_write_end(png_ptr, info_ptr);
383 png_destroy_write_struct(&png_ptr, &info_ptr);
384
385 return true;
386}
387#endif /* WITH_PNG */
388
389
390/*************************************************
391 **** SCREENSHOT CODE FOR ZSOFT PAINTBRUSH (.PCX)
392 *************************************************/
393
395struct PcxHeader {
396 uint8_t manufacturer;
397 uint8_t version;
398 uint8_t rle;
399 uint8_t bpp;
400 uint32_t unused;
401 uint16_t xmax, ymax;
402 uint16_t hdpi, vdpi;
403 uint8_t pal_small[16 * 3];
404 uint8_t reserved;
405 uint8_t planes;
406 uint16_t pitch;
407 uint16_t cpal;
408 uint16_t width;
409 uint16_t height;
410 uint8_t filler[54];
411};
412static_assert(sizeof(PcxHeader) == 128);
413
426static bool MakePCXImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
427{
428 uint maxlines;
429 uint y;
430 PcxHeader pcx;
431 bool success;
432
433 if (pixelformat == 32) {
434 Debug(misc, 0, "Can't convert a 32bpp screenshot to PCX format. Please pick another format.");
435 return false;
436 }
437 if (pixelformat != 8 || w == 0) return false;
438
439 auto of = FileHandle::Open(name, "wb");
440 if (!of.has_value()) return false;
441 auto &f = *of;
442
443 memset(&pcx, 0, sizeof(pcx));
444
445 /* setup pcx header */
446 pcx.manufacturer = 10;
447 pcx.version = 5;
448 pcx.rle = 1;
449 pcx.bpp = 8;
450 pcx.xmax = TO_LE16(w - 1);
451 pcx.ymax = TO_LE16(h - 1);
452 pcx.hdpi = TO_LE16(320);
453 pcx.vdpi = TO_LE16(320);
454
455 pcx.planes = 1;
456 pcx.cpal = TO_LE16(1);
457 pcx.width = pcx.pitch = TO_LE16(w);
458 pcx.height = TO_LE16(h);
459
460 /* write pcx header */
461 if (fwrite(&pcx, sizeof(pcx), 1, f) != 1) {
462 return false;
463 }
464
465 /* use by default 64k temp memory */
466 maxlines = Clamp(65536 / w, 16, 128);
467
468 /* now generate the bitmap bits */
469 std::vector<uint8_t> buff(static_cast<size_t>(w) * maxlines); // by default generate 128 lines at a time.
470
471 y = 0;
472 do {
473 /* determine # lines to write */
474 uint n = std::min(h - y, maxlines);
475 uint i;
476
477 /* render the pixels into the buffer */
478 callb(userdata, buff.data(), y, w, n);
479 y += n;
480
481 /* write them to pcx */
482 for (i = 0; i != n; i++) {
483 const uint8_t *bufp = buff.data() + i * w;
484 uint8_t runchar = bufp[0];
485 uint runcount = 1;
486 uint j;
487
488 /* for each pixel... */
489 for (j = 1; j < w; j++) {
490 uint8_t ch = bufp[j];
491
492 if (ch != runchar || runcount >= 0x3f) {
493 if (runcount > 1 || (runchar & 0xC0) == 0xC0) {
494 if (fputc(0xC0 | runcount, f) == EOF) {
495 return false;
496 }
497 }
498 if (fputc(runchar, f) == EOF) {
499 return false;
500 }
501 runcount = 0;
502 runchar = ch;
503 }
504 runcount++;
505 }
506
507 /* write remaining bytes.. */
508 if (runcount > 1 || (runchar & 0xC0) == 0xC0) {
509 if (fputc(0xC0 | runcount, f) == EOF) {
510 return false;
511 }
512 }
513 if (fputc(runchar, f) == EOF) {
514 return false;
515 }
516 }
517 } while (y != h);
518
519 /* write 8-bit colour palette */
520 if (fputc(12, f) == EOF) {
521 return false;
522 }
523
524 /* Palette is word-aligned, copy it to a temporary byte array */
525 uint8_t tmp[256 * 3];
526
527 for (uint i = 0; i < 256; i++) {
528 tmp[i * 3 + 0] = palette[i].r;
529 tmp[i * 3 + 1] = palette[i].g;
530 tmp[i * 3 + 2] = palette[i].b;
531 }
532 success = fwrite(tmp, sizeof(tmp), 1, f) == 1;
533
534 return success;
535}
536
537/*************************************************
538 **** GENERIC SCREENSHOT CODE
539 *************************************************/
540
543#if defined(WITH_PNG)
544 {"png", &MakePNGImage},
545#endif
546 {"bmp", &MakeBMPImage},
547 {"pcx", &MakePCXImage},
548};
549
550/* The currently loaded screenshot format. Set to a valid value as it might be used in early crash logs, when InitializeScreenshotFormats has not been called yet. */
551static const ScreenshotFormat *_cur_screenshot_format = std::begin(_screenshot_formats);
552
555{
556 return _cur_screenshot_format->extension;
557}
558
561{
562 for (auto &format : _screenshot_formats) {
563 if (_screenshot_format_name == format.extension) {
564 _cur_screenshot_format = &format;
565 return;
566 }
567 }
568
569 _cur_screenshot_format = std::begin(_screenshot_formats);
570}
571
576static void CurrentScreenCallback(void *, void *buf, uint y, uint pitch, uint n)
577{
579 void *src = blitter->MoveTo(_screen.dst_ptr, 0, y);
580 blitter->CopyImageToBuffer(src, buf, _screen.width, n, pitch);
581}
582
591static void LargeWorldCallback(void *userdata, void *buf, uint y, uint pitch, uint n)
592{
593 Viewport *vp = (Viewport *)userdata;
594 DrawPixelInfo dpi;
595 int wx, left;
596
597 /* We are no longer rendering to the screen */
598 DrawPixelInfo old_screen = _screen;
599 bool old_disable_anim = _screen_disable_anim;
600
601 _screen.dst_ptr = buf;
602 _screen.width = pitch;
603 _screen.height = n;
604 _screen.pitch = pitch;
606
607 AutoRestoreBackup dpi_backup(_cur_dpi, &dpi);
608
609 dpi.dst_ptr = buf;
610 dpi.height = n;
611 dpi.width = vp->width;
612 dpi.pitch = pitch;
613 dpi.zoom = ZOOM_LVL_WORLD_SCREENSHOT;
614 dpi.left = 0;
615 dpi.top = y;
616
617 /* Render viewport in blocks of 1600 pixels width */
618 left = 0;
619 while (vp->width - left != 0) {
620 wx = std::min(vp->width - left, 1600);
621 left += wx;
622
623 ViewportDoDraw(vp,
624 ScaleByZoom(left - wx - vp->left, vp->zoom) + vp->virtual_left,
625 ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top,
626 ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
627 ScaleByZoom((y + n) - vp->top, vp->zoom) + vp->virtual_top
628 );
629 }
630
631 /* Switch back to rendering to the screen */
632 _screen = old_screen;
633 _screen_disable_anim = old_disable_anim;
634}
635
643static const char *MakeScreenshotName(const char *default_fn, const char *ext, bool crashlog = false)
644{
645 bool generate = _screenshot_name.empty();
646
647 if (generate) {
648 if (_game_mode == GM_EDITOR || _game_mode == GM_MENU || _local_company == COMPANY_SPECTATOR) {
649 _screenshot_name = default_fn;
650 } else {
652 }
653 }
654
655 /* Handle user-specified filenames ending in # with automatic numbering */
656 if (_screenshot_name.ends_with("#")) {
657 generate = true;
658 _screenshot_name.pop_back();
659 }
660
661 size_t len = _screenshot_name.size();
662 /* Add extension to screenshot file */
663 _screenshot_name += fmt::format(".{}", ext);
664
665 const char *screenshot_dir = crashlog ? _personal_dir.c_str() : FiosGetScreenshotDir();
666
667 for (uint serial = 1;; serial++) {
668 _full_screenshot_path = fmt::format("{}{}", screenshot_dir, _screenshot_name);
669
670 if (!generate) break; // allow overwriting of non-automatic filenames
672 /* If file exists try another one with same name, but just with a higher index */
673 _screenshot_name.erase(len);
674 _screenshot_name += fmt::format("#{}.{}", serial, ext);
675 }
676
677 return _full_screenshot_path.c_str();
678}
679
681static bool MakeSmallScreenshot(bool crashlog)
682{
683 return _cur_screenshot_format->proc(MakeScreenshotName(SCREENSHOT_NAME, _cur_screenshot_format->extension, crashlog), CurrentScreenCallback, nullptr, _screen.width, _screen.height,
685}
686
694void SetupScreenshotViewport(ScreenshotType t, Viewport *vp, uint32_t width, uint32_t height)
695{
696 switch(t) {
697 case SC_VIEWPORT:
698 case SC_CRASHLOG: {
699 assert(width == 0 && height == 0);
700
701 Window *w = GetMainWindow();
706
707 /* Compute pixel coordinates */
708 vp->left = 0;
709 vp->top = 0;
710 vp->width = _screen.width;
711 vp->height = _screen.height;
712 vp->overlay = w->viewport->overlay;
713 break;
714 }
715 case SC_WORLD: {
716 assert(width == 0 && height == 0);
717
718 /* Determine world coordinates of screenshot */
720
722 TileIndex south_tile = Map::Size() - 1;
723
724 /* We need to account for a hill or high building at tile 0,0. */
725 int extra_height_top = TilePixelHeight(north_tile) + 150;
726 /* If there is a hill at the bottom don't create a large black area. */
727 int reclaim_height_bottom = TilePixelHeight(south_tile);
728
729 vp->virtual_left = RemapCoords(TileX(south_tile) * TILE_SIZE, TileY(north_tile) * TILE_SIZE, 0).x;
730 vp->virtual_top = RemapCoords(TileX(north_tile) * TILE_SIZE, TileY(north_tile) * TILE_SIZE, extra_height_top).y;
731 vp->virtual_width = RemapCoords(TileX(north_tile) * TILE_SIZE, TileY(south_tile) * TILE_SIZE, 0).x - vp->virtual_left + 1;
732 vp->virtual_height = RemapCoords(TileX(south_tile) * TILE_SIZE, TileY(south_tile) * TILE_SIZE, reclaim_height_bottom).y - vp->virtual_top + 1;
733
734 /* Compute pixel coordinates */
735 vp->left = 0;
736 vp->top = 0;
737 vp->width = UnScaleByZoom(vp->virtual_width, vp->zoom);
738 vp->height = UnScaleByZoom(vp->virtual_height, vp->zoom);
739 vp->overlay = nullptr;
740 break;
741 }
742 default: {
744
745 Window *w = GetMainWindow();
748
749 if (width == 0 || height == 0) {
752 } else {
753 vp->virtual_width = width << vp->zoom;
754 vp->virtual_height = height << vp->zoom;
755 }
756
757 /* Compute pixel coordinates */
758 vp->left = 0;
759 vp->top = 0;
760 vp->width = UnScaleByZoom(vp->virtual_width, vp->zoom);
761 vp->height = UnScaleByZoom(vp->virtual_height, vp->zoom);
762 vp->overlay = nullptr;
763 break;
764 }
765 }
766}
767
775static bool MakeLargeWorldScreenshot(ScreenshotType t, uint32_t width = 0, uint32_t height = 0)
776{
777 Viewport vp;
778 SetupScreenshotViewport(t, &vp, width, height);
779
780 return _cur_screenshot_format->proc(MakeScreenshotName(SCREENSHOT_NAME, _cur_screenshot_format->extension), LargeWorldCallback, &vp, vp.width, vp.height,
782}
783
791static void HeightmapCallback(void *, void *buffer, uint y, uint, uint n)
792{
793 uint8_t *buf = (uint8_t *)buffer;
794 while (n > 0) {
795 TileIndex ti = TileXY(Map::MaxX(), y);
796 for (uint x = Map::MaxX(); true; x--) {
797 *buf = 256 * TileHeight(ti) / (1 + _heightmap_highest_peak);
798 buf++;
799 if (x == 0) break;
800 ti = TileAddXY(ti, -1, 0);
801 }
802 y++;
803 n--;
804 }
805}
806
811bool MakeHeightmapScreenshot(const char *filename)
812{
813 Colour palette[256];
814 for (uint i = 0; i < lengthof(palette); i++) {
815 palette[i].a = 0xff;
816 palette[i].r = i;
817 palette[i].g = i;
818 palette[i].b = i;
819 }
820
822 for (const auto tile : Map::Iterate()) {
823 uint h = TileHeight(tile);
825 }
826
827 return _cur_screenshot_format->proc(filename, HeightmapCallback, nullptr, Map::SizeX(), Map::SizeY(), 8, palette);
828}
829
831
836static void ScreenshotConfirmationCallback(Window *, bool confirmed)
837{
838 if (confirmed) MakeScreenshot(_confirmed_screenshot_type, {});
839}
840
848{
849 Viewport vp;
851
852 bool heightmap_or_minimap = t == SC_HEIGHTMAP || t == SC_MINIMAP;
853 uint64_t width = (heightmap_or_minimap ? Map::SizeX() : vp.width);
854 uint64_t height = (heightmap_or_minimap ? Map::SizeY() : vp.height);
855
856 if (width * height > 8192 * 8192) {
857 /* Ask for confirmation */
859 SetDParam(0, width);
860 SetDParam(1, height);
861 ShowQuery(STR_WARNING_SCREENSHOT_SIZE_CAPTION, STR_WARNING_SCREENSHOT_SIZE_MESSAGE, nullptr, ScreenshotConfirmationCallback);
862 } else {
863 /* Less than 64M pixels, just do it */
864 MakeScreenshot(t, {});
865 }
866}
867
876static bool RealMakeScreenshot(ScreenshotType t, std::string name, uint32_t width, uint32_t height)
877{
878 if (t == SC_VIEWPORT) {
879 /* First draw the dirty parts of the screen and only then change the name
880 * of the screenshot. This way the screenshot will always show the name
881 * of the previous screenshot in the 'successful' message instead of the
882 * name of the new screenshot (or an empty name). */
884 UndrawMouseCursor();
887 }
888
889 _screenshot_name = name;
890
891 bool ret;
892 switch (t) {
893 case SC_VIEWPORT:
894 ret = MakeSmallScreenshot(false);
895 break;
896
897 case SC_CRASHLOG:
898 ret = MakeSmallScreenshot(true);
899 break;
900
901 case SC_ZOOMEDIN:
902 case SC_DEFAULTZOOM:
903 ret = MakeLargeWorldScreenshot(t, width, height);
904 break;
905
906 case SC_WORLD:
908 break;
909
910 case SC_HEIGHTMAP: {
912 break;
913 }
914
915 case SC_MINIMAP:
917 break;
918
919 default:
920 NOT_REACHED();
921 }
922
923 if (ret) {
924 if (t == SC_HEIGHTMAP) {
927 ShowErrorMessage(STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY, INVALID_STRING_ID, WL_WARNING);
928 } else {
930 ShowErrorMessage(STR_MESSAGE_SCREENSHOT_SUCCESSFULLY, INVALID_STRING_ID, WL_WARNING);
931 }
932 } else {
933 ShowErrorMessage(STR_ERROR_SCREENSHOT_FAILED, INVALID_STRING_ID, WL_ERROR);
934 }
935
936 return ret;
937}
938
949bool MakeScreenshot(ScreenshotType t, std::string name, uint32_t width, uint32_t height)
950{
951 if (t == SC_CRASHLOG) {
952 /* Video buffer might or might not be locked. */
954
955 return RealMakeScreenshot(t, name, width, height);
956 }
957
958 VideoDriver::GetInstance()->QueueOnMainThread([=] { // Capture by value to not break scope.
959 RealMakeScreenshot(t, name, width, height);
960 });
961
962 return true;
963}
964
965
966static void MinimapScreenCallback(void *, void *buf, uint y, uint pitch, uint n)
967{
968 uint32_t *ubuf = (uint32_t *)buf;
969 uint num = (pitch * n);
970 for (uint i = 0; i < num; i++) {
971 uint row = y + (int)(i / pitch);
972 uint col = (Map::SizeX() - 1) - (i % pitch);
973
974 TileIndex tile = TileXY(col, row);
975 uint8_t val = GetSmallMapOwnerPixels(tile, GetTileType(tile), IncludeHeightmap::Never) & 0xFF;
976
977 uint32_t colour_buf = 0;
978 colour_buf = (_cur_palette.palette[val].b << 0);
979 colour_buf |= (_cur_palette.palette[val].g << 8);
980 colour_buf |= (_cur_palette.palette[val].r << 16);
981
982 *ubuf = colour_buf;
983 ubuf++; // Skip alpha
984 }
985}
986
991{
992 return _cur_screenshot_format->proc(MakeScreenshotName(SCREENSHOT_NAME, _cur_screenshot_format->extension), MinimapScreenCallback, nullptr, Map::SizeX(), Map::SizeY(), 32, _cur_palette.palette);
993}
AIInfo keeps track of all information of an AI, like Author, Description, ...
Class for backupping variables and making sure they are restored later.
Generic functions for replacing base data (graphics, sounds).
static uint32_t BSWAP32(uint32_t x)
Perform a 32 bits endianness bitswap on x.
static const GraphicsSet * GetUsedSet()
Return the used set.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition factory.hpp:138
How all blitters should look like.
Definition base.hpp:29
virtual void * MoveTo(void *video, int x, int y)=0
Move the destination pointer the requested amount x and y, keeping in mind any pitch and bpp of the r...
virtual uint8_t GetScreenDepth()=0
Get the screen depth this blitter works for.
virtual void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch)=0
Copy from the screen to a buffer in a palette format for 8bpp and RGBA format for 32bpp.
static std::optional< FileHandle > Open(const std::string &filename, const std::string &mode)
Open an RAII file handle if possible.
Definition fileio.cpp:1170
void QueueOnMainThread(std::function< void()> &&func)
Queue a function to be called on the main thread with game state lock held and video buffer locked.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Functions related to companies.
@ COMPANY_SPECTATOR
The client is spectating.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition debug.h:37
Function to handling different endian machines.
Functions related to errors.
void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc)
Display an error message in a window.
@ WL_WARNING
Other information.
Definition error.h:25
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition error.h:26
Factory to 'query' all available blitters.
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition fileio.cpp:869
bool FileExists(const std::string &filename)
Test whether the given filename exists.
Definition fileio.cpp:132
Functions for Standard In/Out file operations.
const char * FiosGetScreenshotDir()
Get the directory for screenshots.
Definition fios.cpp:602
bool _screen_disable_anim
Disable palette animation (important for 32bpp-anim blitter during giant screenshot)
Definition gfx.cpp:46
Functions related to the gfx engine.
Palette _cur_palette
Current palette.
Definition palette.cpp:24
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition gfx.cpp:1397
Functions related to OTTD's landscape.
Point RemapCoords(int x, int y, int z)
Map 3D world or tile coordinate to equivalent 2D coordinate as used in the viewports and smallmap.
Definition landscape.h:79
TileIndex TileAddXY(TileIndex tile, int x, int y)
Adds a given offset to a tile.
Definition map_func.h:467
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:373
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:425
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:415
constexpr T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
Definition math_func.hpp:37
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
void ShowQuery(StringID caption, StringID message, Window *parent, QueryCallbackProc *callback, bool focus)
Show a confirmation window with standard 'yes' and 'no' buttons The window is aligned to the centre o...
GRFConfig * _grfconfig
First item in list of current GRF set up.
Functions to find and configure NewGRFs.
declaration of OTTD revision dependent variables
A number of safeguards to prevent using unsafe methods.
std::string GenerateDefaultSaveName()
Get the default name for a savegame or screenshot.
Functions/types related to saving and loading games.
static void CurrentScreenCallback(void *, void *buf, uint y, uint pitch, uint n)
Callback of the screenshot generator that dumps the current video buffer.
const char * GetCurrentScreenshotExtension()
Get filename extension of current screenshot file format.
static void ScreenshotConfirmationCallback(Window *, bool confirmed)
Callback on the confirmation window for huge screenshots.
void ScreenshotCallback(void *userdata, void *buf, uint y, uint pitch, uint n)
Callback function signature for generating lines of pixel data to be written to the screenshot file.
void MakeScreenshotWithConfirm(ScreenshotType t)
Make a screenshot.
static ScreenshotType _confirmed_screenshot_type
Screenshot type the current query is about to confirm.
std::string _screenshot_format_name
Extension of the current screenshot format (corresponds with _cur_screenshot_format).
static bool MakeBMPImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
Generic .BMP writer.
static bool MakeSmallScreenshot(bool crashlog)
Make a screenshot of the current screen.
static void HeightmapCallback(void *, void *buffer, uint y, uint, uint n)
Callback for generating a heightmap.
bool MakeHeightmapScreenshot(const char *filename)
Make a heightmap of the current map.
static const char *const SCREENSHOT_NAME
Default filename of a saved screenshot.
void InitializeScreenshotFormats()
Initialize screenshot format information on startup, with _screenshot_format_name filled from the loa...
bool MakeMinimapWorldScreenshot()
Make a minimap screenshot.
static bool RealMakeScreenshot(ScreenshotType t, std::string name, uint32_t width, uint32_t height)
Make a screenshot.
static bool MakeLargeWorldScreenshot(ScreenshotType t, uint32_t width=0, uint32_t height=0)
Make a screenshot of the map.
static std::string _screenshot_name
Filename of the screenshot file.
bool MakeScreenshot(ScreenshotType t, std::string name, uint32_t width, uint32_t height)
Schedule making a screenshot.
void SetupScreenshotViewport(ScreenshotType t, Viewport *vp, uint32_t width, uint32_t height)
Configure a Viewport for rendering (a part of) the map into a screenshot.
static bool MakePNGImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
Generic .PNG file image writer.
std::string _full_screenshot_path
Pathname of the screenshot file.
static void LargeWorldCallback(void *userdata, void *buf, uint y, uint pitch, uint n)
generate a large piece of the world
static const ScreenshotFormat _screenshot_formats[]
Available screenshot formats.
static bool MakePCXImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
Generic .PCX file image writer.
static const char *const HEIGHTMAP_NAME
Default filename of a saved heightmap.
uint _heightmap_highest_peak
When saving a heightmap, this contains the highest peak on the map.
bool ScreenshotHandlerProc(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
Function signature for a screenshot generation routine for one of the available formats.
static const char * MakeScreenshotName(const char *default_fn, const char *ext, bool crashlog=false)
Construct a pathname for a screenshot file.
Functions to make screenshots.
ScreenshotType
Type of requested screenshot.
Definition screenshot.h:18
@ SC_VIEWPORT
Screenshot of viewport.
Definition screenshot.h:19
@ SC_CRASHLOG
Raw screenshot from blitter buffer.
Definition screenshot.h:20
@ SC_ZOOMEDIN
Fully zoomed in screenshot of the visible area.
Definition screenshot.h:21
@ SC_HEIGHTMAP
Heightmap of the world.
Definition screenshot.h:24
@ SC_WORLD
World screenshot.
Definition screenshot.h:23
@ SC_MINIMAP
Minimap screenshot.
Definition screenshot.h:25
@ SC_DEFAULTZOOM
Zoomed to default zoom level screenshot of the visible area.
Definition screenshot.h:22
void SetScreenshotWindowVisibility(bool hide)
Set the visibility of the screenshot window when taking a screenshot.
GUI functions related to screenshots.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:57
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
uint32_t GetSmallMapOwnerPixels(TileIndex tile, TileType t, IncludeHeightmap include_heightmap)
Return the colour a tile would be displayed with in the small map in mode "Owner".
Smallmap GUI functions.
@ Never
Never include the heightmap.
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:280
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:81
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
void SetDParamStr(size_t n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition strings.cpp:371
Functions related to OTTD's strings.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
BMP Info Header (stored in little endian)
GUISettings gui
settings related to the GUI
bool freeform_edges
allow terraforming the tiles at the map edges
Data about how and where to blit pixels.
Definition gfx_type.h:157
Information about GRF, used in the game and (part of it) in savegames.
struct GRFConfig * next
NOSAVE: Next item in the linked list.
ZoomLevel zoom_min
minimum zoom out level
ConstructionSettings construction
construction of things in-game
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:279
static IterateWrapper Iterate()
Returns an iterable ensemble of all Tiles.
Definition map_func.h:363
static debug_inline uint SizeX()
Get the size of the map along the X.
Definition map_func.h:270
static debug_inline uint Size()
Get the size of the map.
Definition map_func.h:288
static debug_inline uint MaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition map_func.h:297
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
Definition gfx_type.h:329
Definition of a PCX file header.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Format of palette data in BMP header.
Screenshot format information.
ScreenshotHandlerProc * proc
Function for writing the screenshot.
const char * extension
File extension.
Helper struct to ensure the video buffer is locked and ready for drawing.
Data structure for viewport, display of a part of the world.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int virtual_top
Virtual top coordinate.
int virtual_left
Virtual left coordinate.
int virtual_width
width << zoom
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
int virtual_height
height << zoom
Data structure for an opened window.
Definition window_gui.h:273
ViewportData * viewport
Pointer to viewport data, if present.
Definition window_gui.h:318
Stuff related to the text buffer GUI.
Map writing/reading functions for tiles.
static debug_inline TileType GetTileType(Tile tile)
Get the tiletype of a given tile.
Definition tile_map.h:96
uint TilePixelHeight(Tile tile)
Returns the height of a tile in pixels.
Definition tile_map.h:72
static debug_inline uint TileHeight(Tile tile)
Returns the height of a tile.
Definition tile_map.h:29
static const uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
Structure to access the alpha, red, green, and blue channels from a 32 bit number.
Definition gfx_type.h:165
uint32_t data
Conversion of the channel information to a 32 bit number.
Definition gfx_type.h:166
uint8_t b
colour channels in BE order
Definition gfx_type.h:171
Base of all video drivers.
Functions related to (drawing on) viewports.
std::mutex lock
synchronization for playback status fields
Definition win32_m.cpp:35
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1127
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZOOM_LVL_MIN) When shifting right,...
Definition zoom_func.h:22
int UnScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift right (when zoom > ZOOM_LVL_MIN) When shifting right,...
Definition zoom_func.h:34
@ ZOOM_LVL_WORLD_SCREENSHOT
Default zoom level for the world screen shot.
Definition zoom_type.h:36
@ ZOOM_LVL_VIEWPORT
Default zoom level for viewports.
Definition zoom_type.h:28