OpenTTD Source 20250205-master-gfd85ab1e2c
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 if (_game_mode != GM_MENU) {
311 for (const auto &c : _grfconfig) {
312 fmt::format_to(std::back_inserter(message), "{:08X} {} {}\n", std::byteswap(c->ident.grfid), FormatArrayAsHex(c->ident.md5sum), c->filename);
313 }
314 }
315 message += "\nCompanies:\n";
316 for (const Company *c : Company::Iterate()) {
317 if (c->ai_info == nullptr) {
318 fmt::format_to(std::back_inserter(message), "{:2d}: Human\n", (int)c->index);
319 } else {
320 fmt::format_to(std::back_inserter(message), "{:2d}: {} (v{})\n", (int)c->index, c->ai_info->GetName(), c->ai_info->GetVersion());
321 }
322 }
323 text[1].key = const_cast<char *>("Description");
324 text[1].text = const_cast<char *>(message.c_str());
325 text[1].text_length = message.size();
326 text[1].compression = PNG_TEXT_COMPRESSION_zTXt;
327 png_set_text(png_ptr, info_ptr, text, 2);
328#endif /* PNG_TEXT_SUPPORTED */
329
330 if (pixelformat == 8) {
331 /* convert the palette to the .PNG format. */
332 for (i = 0; i != 256; i++) {
333 rq[i].red = palette[i].r;
334 rq[i].green = palette[i].g;
335 rq[i].blue = palette[i].b;
336 }
337
338 png_set_PLTE(png_ptr, info_ptr, rq, 256);
339 }
340
341 png_write_info(png_ptr, info_ptr);
342 png_set_flush(png_ptr, 512);
343
344 if (pixelformat == 32) {
345 png_color_8 sig_bit;
346
347 /* Save exact colour/alpha resolution */
348 sig_bit.alpha = 0;
349 sig_bit.blue = 8;
350 sig_bit.green = 8;
351 sig_bit.red = 8;
352 sig_bit.gray = 8;
353 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
354
355 if constexpr (std::endian::native == std::endian::little) {
356 png_set_bgr(png_ptr);
357 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
358 } else {
359 png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
360 }
361 }
362
363 /* use by default 64k temp memory */
364 maxlines = Clamp(65536 / w, 16, 128);
365
366 /* now generate the bitmap bits */
367 std::vector<uint8_t> buff(static_cast<size_t>(w) * maxlines * bpp); // by default generate 128 lines at a time.
368
369 y = 0;
370 do {
371 /* determine # lines to write */
372 n = std::min(h - y, maxlines);
373
374 /* render the pixels into the buffer */
375 callb(userdata, buff.data(), y, w, n);
376 y += n;
377
378 /* write them to png */
379 for (i = 0; i != n; i++) {
380 png_write_row(png_ptr, (png_bytep)buff.data() + i * w * bpp);
381 }
382 } while (y != h);
383
384 png_write_end(png_ptr, info_ptr);
385 png_destroy_write_struct(&png_ptr, &info_ptr);
386
387 return true;
388}
389#endif /* WITH_PNG */
390
391
392/*************************************************
393 **** SCREENSHOT CODE FOR ZSOFT PAINTBRUSH (.PCX)
394 *************************************************/
395
397struct PcxHeader {
398 uint8_t manufacturer;
399 uint8_t version;
400 uint8_t rle;
401 uint8_t bpp;
402 uint32_t unused;
403 uint16_t xmax, ymax;
404 uint16_t hdpi, vdpi;
405 uint8_t pal_small[16 * 3];
406 uint8_t reserved;
407 uint8_t planes;
408 uint16_t pitch;
409 uint16_t cpal;
410 uint16_t width;
411 uint16_t height;
412 uint8_t filler[54];
413};
414static_assert(sizeof(PcxHeader) == 128);
415
428static bool MakePCXImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
429{
430 uint maxlines;
431 uint y;
432 PcxHeader pcx;
433 bool success;
434
435 if (pixelformat == 32) {
436 Debug(misc, 0, "Can't convert a 32bpp screenshot to PCX format. Please pick another format.");
437 return false;
438 }
439 if (pixelformat != 8 || w == 0) return false;
440
441 auto of = FileHandle::Open(name, "wb");
442 if (!of.has_value()) return false;
443 auto &f = *of;
444
445 memset(&pcx, 0, sizeof(pcx));
446
447 /* setup pcx header */
448 pcx.manufacturer = 10;
449 pcx.version = 5;
450 pcx.rle = 1;
451 pcx.bpp = 8;
452 pcx.xmax = TO_LE16(w - 1);
453 pcx.ymax = TO_LE16(h - 1);
454 pcx.hdpi = TO_LE16(320);
455 pcx.vdpi = TO_LE16(320);
456
457 pcx.planes = 1;
458 pcx.cpal = TO_LE16(1);
459 pcx.width = pcx.pitch = TO_LE16(w);
460 pcx.height = TO_LE16(h);
461
462 /* write pcx header */
463 if (fwrite(&pcx, sizeof(pcx), 1, f) != 1) {
464 return false;
465 }
466
467 /* use by default 64k temp memory */
468 maxlines = Clamp(65536 / w, 16, 128);
469
470 /* now generate the bitmap bits */
471 std::vector<uint8_t> buff(static_cast<size_t>(w) * maxlines); // by default generate 128 lines at a time.
472
473 y = 0;
474 do {
475 /* determine # lines to write */
476 uint n = std::min(h - y, maxlines);
477 uint i;
478
479 /* render the pixels into the buffer */
480 callb(userdata, buff.data(), y, w, n);
481 y += n;
482
483 /* write them to pcx */
484 for (i = 0; i != n; i++) {
485 const uint8_t *bufp = buff.data() + i * w;
486 uint8_t runchar = bufp[0];
487 uint runcount = 1;
488 uint j;
489
490 /* for each pixel... */
491 for (j = 1; j < w; j++) {
492 uint8_t ch = bufp[j];
493
494 if (ch != runchar || runcount >= 0x3f) {
495 if (runcount > 1 || (runchar & 0xC0) == 0xC0) {
496 if (fputc(0xC0 | runcount, f) == EOF) {
497 return false;
498 }
499 }
500 if (fputc(runchar, f) == EOF) {
501 return false;
502 }
503 runcount = 0;
504 runchar = ch;
505 }
506 runcount++;
507 }
508
509 /* write remaining bytes.. */
510 if (runcount > 1 || (runchar & 0xC0) == 0xC0) {
511 if (fputc(0xC0 | runcount, f) == EOF) {
512 return false;
513 }
514 }
515 if (fputc(runchar, f) == EOF) {
516 return false;
517 }
518 }
519 } while (y != h);
520
521 /* write 8-bit colour palette */
522 if (fputc(12, f) == EOF) {
523 return false;
524 }
525
526 /* Palette is word-aligned, copy it to a temporary byte array */
527 uint8_t tmp[256 * 3];
528
529 for (uint i = 0; i < 256; i++) {
530 tmp[i * 3 + 0] = palette[i].r;
531 tmp[i * 3 + 1] = palette[i].g;
532 tmp[i * 3 + 2] = palette[i].b;
533 }
534 success = fwrite(tmp, sizeof(tmp), 1, f) == 1;
535
536 return success;
537}
538
539/*************************************************
540 **** GENERIC SCREENSHOT CODE
541 *************************************************/
542
545#if defined(WITH_PNG)
546 {"png", &MakePNGImage},
547#endif
548 {"bmp", &MakeBMPImage},
549 {"pcx", &MakePCXImage},
550};
551
552/* 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. */
553static const ScreenshotFormat *_cur_screenshot_format = std::begin(_screenshot_formats);
554
557{
558 return _cur_screenshot_format->extension;
559}
560
563{
564 for (auto &format : _screenshot_formats) {
565 if (_screenshot_format_name == format.extension) {
566 _cur_screenshot_format = &format;
567 return;
568 }
569 }
570
571 _cur_screenshot_format = std::begin(_screenshot_formats);
572}
573
578static void CurrentScreenCallback(void *, void *buf, uint y, uint pitch, uint n)
579{
581 void *src = blitter->MoveTo(_screen.dst_ptr, 0, y);
582 blitter->CopyImageToBuffer(src, buf, _screen.width, n, pitch);
583}
584
593static void LargeWorldCallback(void *userdata, void *buf, uint y, uint pitch, uint n)
594{
595 Viewport *vp = (Viewport *)userdata;
596 DrawPixelInfo dpi;
597 int wx, left;
598
599 /* We are no longer rendering to the screen */
600 DrawPixelInfo old_screen = _screen;
601 bool old_disable_anim = _screen_disable_anim;
602
603 _screen.dst_ptr = buf;
604 _screen.width = pitch;
605 _screen.height = n;
606 _screen.pitch = pitch;
608
609 AutoRestoreBackup dpi_backup(_cur_dpi, &dpi);
610
611 dpi.dst_ptr = buf;
612 dpi.height = n;
613 dpi.width = vp->width;
614 dpi.pitch = pitch;
615 dpi.zoom = ZOOM_LVL_WORLD_SCREENSHOT;
616 dpi.left = 0;
617 dpi.top = y;
618
619 /* Render viewport in blocks of 1600 pixels width */
620 left = 0;
621 while (vp->width - left != 0) {
622 wx = std::min(vp->width - left, 1600);
623 left += wx;
624
625 ViewportDoDraw(vp,
626 ScaleByZoom(left - wx - vp->left, vp->zoom) + vp->virtual_left,
627 ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top,
628 ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
629 ScaleByZoom((y + n) - vp->top, vp->zoom) + vp->virtual_top
630 );
631 }
632
633 /* Switch back to rendering to the screen */
634 _screen = old_screen;
635 _screen_disable_anim = old_disable_anim;
636}
637
645static const char *MakeScreenshotName(const char *default_fn, const char *ext, bool crashlog = false)
646{
647 bool generate = _screenshot_name.empty();
648
649 if (generate) {
650 if (_game_mode == GM_EDITOR || _game_mode == GM_MENU || _local_company == COMPANY_SPECTATOR) {
651 _screenshot_name = default_fn;
652 } else {
654 }
655 }
656
657 /* Handle user-specified filenames ending in # with automatic numbering */
658 if (_screenshot_name.ends_with("#")) {
659 generate = true;
660 _screenshot_name.pop_back();
661 }
662
663 size_t len = _screenshot_name.size();
664 /* Add extension to screenshot file */
665 _screenshot_name += fmt::format(".{}", ext);
666
667 const char *screenshot_dir = crashlog ? _personal_dir.c_str() : FiosGetScreenshotDir();
668
669 for (uint serial = 1;; serial++) {
670 _full_screenshot_path = fmt::format("{}{}", screenshot_dir, _screenshot_name);
671
672 if (!generate) break; // allow overwriting of non-automatic filenames
674 /* If file exists try another one with same name, but just with a higher index */
675 _screenshot_name.erase(len);
676 _screenshot_name += fmt::format("#{}.{}", serial, ext);
677 }
678
679 return _full_screenshot_path.c_str();
680}
681
683static bool MakeSmallScreenshot(bool crashlog)
684{
685 return _cur_screenshot_format->proc(MakeScreenshotName(SCREENSHOT_NAME, _cur_screenshot_format->extension, crashlog), CurrentScreenCallback, nullptr, _screen.width, _screen.height,
687}
688
696void SetupScreenshotViewport(ScreenshotType t, Viewport *vp, uint32_t width, uint32_t height)
697{
698 switch(t) {
699 case SC_VIEWPORT:
700 case SC_CRASHLOG: {
701 assert(width == 0 && height == 0);
702
703 Window *w = GetMainWindow();
708
709 /* Compute pixel coordinates */
710 vp->left = 0;
711 vp->top = 0;
712 vp->width = _screen.width;
713 vp->height = _screen.height;
714 vp->overlay = w->viewport->overlay;
715 break;
716 }
717 case SC_WORLD: {
718 assert(width == 0 && height == 0);
719
720 /* Determine world coordinates of screenshot */
722
724 TileIndex south_tile{Map::Size() - 1};
725
726 /* We need to account for a hill or high building at tile 0,0. */
727 int extra_height_top = TilePixelHeight(north_tile) + 150;
728 /* If there is a hill at the bottom don't create a large black area. */
729 int reclaim_height_bottom = TilePixelHeight(south_tile);
730
731 vp->virtual_left = RemapCoords(TileX(south_tile) * TILE_SIZE, TileY(north_tile) * TILE_SIZE, 0).x;
732 vp->virtual_top = RemapCoords(TileX(north_tile) * TILE_SIZE, TileY(north_tile) * TILE_SIZE, extra_height_top).y;
733 vp->virtual_width = RemapCoords(TileX(north_tile) * TILE_SIZE, TileY(south_tile) * TILE_SIZE, 0).x - vp->virtual_left + 1;
734 vp->virtual_height = RemapCoords(TileX(south_tile) * TILE_SIZE, TileY(south_tile) * TILE_SIZE, reclaim_height_bottom).y - vp->virtual_top + 1;
735
736 /* Compute pixel coordinates */
737 vp->left = 0;
738 vp->top = 0;
739 vp->width = UnScaleByZoom(vp->virtual_width, vp->zoom);
740 vp->height = UnScaleByZoom(vp->virtual_height, vp->zoom);
741 vp->overlay = nullptr;
742 break;
743 }
744 default: {
746
747 Window *w = GetMainWindow();
750
751 if (width == 0 || height == 0) {
754 } else {
755 vp->virtual_width = width << vp->zoom;
756 vp->virtual_height = height << vp->zoom;
757 }
758
759 /* Compute pixel coordinates */
760 vp->left = 0;
761 vp->top = 0;
762 vp->width = UnScaleByZoom(vp->virtual_width, vp->zoom);
763 vp->height = UnScaleByZoom(vp->virtual_height, vp->zoom);
764 vp->overlay = nullptr;
765 break;
766 }
767 }
768}
769
777static bool MakeLargeWorldScreenshot(ScreenshotType t, uint32_t width = 0, uint32_t height = 0)
778{
779 Viewport vp;
780 SetupScreenshotViewport(t, &vp, width, height);
781
782 return _cur_screenshot_format->proc(MakeScreenshotName(SCREENSHOT_NAME, _cur_screenshot_format->extension), LargeWorldCallback, &vp, vp.width, vp.height,
784}
785
793static void HeightmapCallback(void *, void *buffer, uint y, uint, uint n)
794{
795 uint8_t *buf = (uint8_t *)buffer;
796 while (n > 0) {
797 TileIndex ti = TileXY(Map::MaxX(), y);
798 for (uint x = Map::MaxX(); true; x--) {
799 *buf = 256 * TileHeight(ti) / (1 + _heightmap_highest_peak);
800 buf++;
801 if (x == 0) break;
802 ti = TileAddXY(ti, -1, 0);
803 }
804 y++;
805 n--;
806 }
807}
808
813bool MakeHeightmapScreenshot(const char *filename)
814{
815 Colour palette[256];
816 for (uint i = 0; i < lengthof(palette); i++) {
817 palette[i].a = 0xff;
818 palette[i].r = i;
819 palette[i].g = i;
820 palette[i].b = i;
821 }
822
824 for (const auto tile : Map::Iterate()) {
825 uint h = TileHeight(tile);
827 }
828
829 return _cur_screenshot_format->proc(filename, HeightmapCallback, nullptr, Map::SizeX(), Map::SizeY(), 8, palette);
830}
831
833
838static void ScreenshotConfirmationCallback(Window *, bool confirmed)
839{
840 if (confirmed) MakeScreenshot(_confirmed_screenshot_type, {});
841}
842
850{
851 Viewport vp;
853
854 bool heightmap_or_minimap = t == SC_HEIGHTMAP || t == SC_MINIMAP;
855 uint64_t width = (heightmap_or_minimap ? Map::SizeX() : vp.width);
856 uint64_t height = (heightmap_or_minimap ? Map::SizeY() : vp.height);
857
858 if (width * height > 8192 * 8192) {
859 /* Ask for confirmation */
861 SetDParam(0, width);
862 SetDParam(1, height);
863 ShowQuery(STR_WARNING_SCREENSHOT_SIZE_CAPTION, STR_WARNING_SCREENSHOT_SIZE_MESSAGE, nullptr, ScreenshotConfirmationCallback);
864 } else {
865 /* Less than 64M pixels, just do it */
866 MakeScreenshot(t, {});
867 }
868}
869
878static bool RealMakeScreenshot(ScreenshotType t, std::string name, uint32_t width, uint32_t height)
879{
880 if (t == SC_VIEWPORT) {
881 /* First draw the dirty parts of the screen and only then change the name
882 * of the screenshot. This way the screenshot will always show the name
883 * of the previous screenshot in the 'successful' message instead of the
884 * name of the new screenshot (or an empty name). */
886 UndrawMouseCursor();
889 }
890
891 _screenshot_name = name;
892
893 bool ret;
894 switch (t) {
895 case SC_VIEWPORT:
896 ret = MakeSmallScreenshot(false);
897 break;
898
899 case SC_CRASHLOG:
900 ret = MakeSmallScreenshot(true);
901 break;
902
903 case SC_ZOOMEDIN:
904 case SC_DEFAULTZOOM:
905 ret = MakeLargeWorldScreenshot(t, width, height);
906 break;
907
908 case SC_WORLD:
910 break;
911
912 case SC_HEIGHTMAP: {
914 break;
915 }
916
917 case SC_MINIMAP:
919 break;
920
921 default:
922 NOT_REACHED();
923 }
924
925 if (ret) {
926 if (t == SC_HEIGHTMAP) {
929 ShowErrorMessage(STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY, INVALID_STRING_ID, WL_WARNING);
930 } else {
932 ShowErrorMessage(STR_MESSAGE_SCREENSHOT_SUCCESSFULLY, INVALID_STRING_ID, WL_WARNING);
933 }
934 } else {
935 ShowErrorMessage(STR_ERROR_SCREENSHOT_FAILED, INVALID_STRING_ID, WL_ERROR);
936 }
937
938 return ret;
939}
940
951bool MakeScreenshot(ScreenshotType t, std::string name, uint32_t width, uint32_t height)
952{
953 if (t == SC_CRASHLOG) {
954 /* Video buffer might or might not be locked. */
956
957 return RealMakeScreenshot(t, name, width, height);
958 }
959
960 VideoDriver::GetInstance()->QueueOnMainThread([=] { // Capture by value to not break scope.
961 RealMakeScreenshot(t, name, width, height);
962 });
963
964 return true;
965}
966
967
968static void MinimapScreenCallback(void *, void *buf, uint y, uint pitch, uint n)
969{
970 uint32_t *ubuf = (uint32_t *)buf;
971 uint num = (pitch * n);
972 for (uint i = 0; i < num; i++) {
973 uint row = y + (int)(i / pitch);
974 uint col = (Map::SizeX() - 1) - (i % pitch);
975
976 TileIndex tile = TileXY(col, row);
977 uint8_t val = GetSmallMapOwnerPixels(tile, GetTileType(tile), IncludeHeightmap::Never) & 0xFF;
978
979 uint32_t colour_buf = 0;
980 colour_buf = (_cur_palette.palette[val].b << 0);
981 colour_buf |= (_cur_palette.palette[val].g << 8);
982 colour_buf |= (_cur_palette.palette[val].r << 16);
983
984 *ubuf = colour_buf;
985 ubuf++; // Skip alpha
986 }
987}
988
993{
994 return _cur_screenshot_format->proc(MakeScreenshotName(SCREENSHOT_NAME, _cur_screenshot_format->extension), MinimapScreenCallback, nullptr, Map::SizeX(), Map::SizeY(), 32, _cur_palette.palette);
995}
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 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:1171
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.
@ WL_WARNING
Other information.
Definition error.h:25
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition error.h:26
void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc)
Display an error message in a window.
Factory to 'query' all available blitters.
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition fileio.cpp:870
bool FileExists(const std::string &filename)
Test whether the given filename exists.
Definition fileio.cpp:133
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:470
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...
GRFConfigList _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:277
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:370
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:156
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:361
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:272
ViewportData * viewport
Pointer to viewport data, if present.
Definition window_gui.h:317
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
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:1124
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