OpenTTD Source  20241120-master-g6d3adc6169
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"
30 #include "video/video_driver.hpp"
31 #include "smallmap_gui.h"
32 
33 #include "table/strings.h"
34 
35 #include "safeguards.h"
36 
37 static const char * const SCREENSHOT_NAME = "screenshot";
38 static const char * const HEIGHTMAP_NAME = "heightmap";
39 
41 static std::string _screenshot_name;
42 std::string _full_screenshot_path;
44 
53 typedef void ScreenshotCallback(void *userdata, void *buf, uint y, uint pitch, uint n);
54 
66 typedef bool ScreenshotHandlerProc(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette);
67 
70  const char *extension;
72 };
73 
74 #define MKCOLOUR(x) TO_LE32(x)
75 
76 /*************************************************
77  **** SCREENSHOT CODE FOR WINDOWS BITMAP (.BMP)
78  *************************************************/
79 
81 PACK(struct BitmapFileHeader {
82  uint16_t type;
83  uint32_t size;
84  uint32_t reserved;
85  uint32_t off_bits;
86 });
87 static_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 };
96 static_assert(sizeof(BitmapInfoHeader) == 40);
97 
99 struct RgbQuad {
100  uint8_t blue, green, red, reserved;
101 };
102 static_assert(sizeof(RgbQuad) == 4);
103 
116 static 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 */
145  BitmapInfoHeader bih;
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 
233 static 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 
239 static 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 
256 static 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 
395 struct 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 };
412 static_assert(sizeof(PcxHeader) == 128);
413 
426 static 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. */
551 static 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 
576 static 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 
591 static 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;
605  _screen_disable_anim = true;
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 
643 static 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
671  if (!FileExists(_full_screenshot_path)) break;
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 
681 static 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 
694 void 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();
703  vp->virtual_top = w->viewport->virtual_top;
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 
721  TileIndex north_tile = _settings_game.construction.freeform_edges ? TileXY(1, 1) : TileXY(0, 0);
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();
746  vp->virtual_left = w->viewport->virtual_left;
747  vp->virtual_top = w->viewport->virtual_top;
748 
749  if (width == 0 || height == 0) {
750  vp->virtual_width = w->viewport->virtual_width;
751  vp->virtual_height = w->viewport->virtual_height;
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 
775 static 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 
791 static 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 
811 bool 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 (TileIndex tile = 0; tile < Map::Size(); tile++) {
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 
836 static void ScreenshotConfirmationCallback(Window *, bool confirmed)
837 {
838  if (confirmed) MakeScreenshot(_confirmed_screenshot_type, {});
839 }
840 
848 {
849  Viewport vp;
850  SetupScreenshotViewport(t, &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 
876 static 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();
885  DrawDirtyBlocks();
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:
907  ret = MakeLargeWorldScreenshot(t);
908  break;
909 
910  case SC_HEIGHTMAP: {
911  ret = MakeHeightmapScreenshot(MakeScreenshotName(HEIGHTMAP_NAME, _cur_screenshot_format->extension));
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 
949 bool 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 
966 static 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 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.
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...
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.
Definition: company_cmd.cpp:52
Functions related to companies.
@ COMPANY_SPECTATOR
The client is spectating.
Definition: company_type.h:35
#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.
Definition: error_gui.cpp:367
@ 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...
Definition: misc_gui.cpp:1223
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.
Definition: saveload.cpp:3214
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.
Definition: screenshot.cpp:576
static void ScreenshotConfirmationCallback(Window *, bool confirmed)
Callback on the confirmation window for huge screenshots.
Definition: screenshot.cpp:836
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.
Definition: screenshot.cpp:53
static const char * MakeScreenshotName(const char *default_fn, const char *ext, bool crashlog=false)
Construct a pathname for a screenshot file.
Definition: screenshot.cpp:643
void MakeScreenshotWithConfirm(ScreenshotType t)
Make a screenshot.
Definition: screenshot.cpp:847
static ScreenshotType _confirmed_screenshot_type
Screenshot type the current query is about to confirm.
Definition: screenshot.cpp:830
const char * GetCurrentScreenshotExtension()
Get filename extension of current screenshot file format.
Definition: screenshot.cpp:554
std::string _screenshot_format_name
Extension of the current screenshot format (corresponds with #_cur_screenshot_format).
Definition: screenshot.cpp:40
static bool MakeBMPImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
Generic .BMP writer.
Definition: screenshot.cpp:116
static bool MakeSmallScreenshot(bool crashlog)
Make a screenshot of the current screen.
Definition: screenshot.cpp:681
static void HeightmapCallback(void *, void *buffer, uint y, uint, uint n)
Callback for generating a heightmap.
Definition: screenshot.cpp:791
bool MakeHeightmapScreenshot(const char *filename)
Make a heightmap of the current map.
Definition: screenshot.cpp:811
static const char *const SCREENSHOT_NAME
Default filename of a saved screenshot.
Definition: screenshot.cpp:37
void InitializeScreenshotFormats()
Initialize screenshot format information on startup, with _screenshot_format_name filled from the loa...
Definition: screenshot.cpp:560
bool MakeMinimapWorldScreenshot()
Make a minimap screenshot.
Definition: screenshot.cpp:990
static bool RealMakeScreenshot(ScreenshotType t, std::string name, uint32_t width, uint32_t height)
Make a screenshot.
Definition: screenshot.cpp:876
static bool MakeLargeWorldScreenshot(ScreenshotType t, uint32_t width=0, uint32_t height=0)
Make a screenshot of the map.
Definition: screenshot.cpp:775
static std::string _screenshot_name
Filename of the screenshot file.
Definition: screenshot.cpp:41
bool MakeScreenshot(ScreenshotType t, std::string name, uint32_t width, uint32_t height)
Schedule making a screenshot.
Definition: screenshot.cpp:949
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.
Definition: screenshot.cpp:694
static bool MakePNGImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
Generic .PNG file image writer.
Definition: screenshot.cpp:256
std::string _full_screenshot_path
Pathname of the screenshot file.
Definition: screenshot.cpp:42
static void LargeWorldCallback(void *userdata, void *buf, uint y, uint pitch, uint n)
generate a large piece of the world
Definition: screenshot.cpp:591
PACK(struct BitmapFileHeader { uint16_t type;uint32_t size;uint32_t reserved;uint32_t off_bits;})
BMP File Header (stored in little endian)
static const ScreenshotFormat _screenshot_formats[]
Available screenshot formats.
Definition: screenshot.cpp:542
static bool MakePCXImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
Generic .PCX file image writer.
Definition: screenshot.cpp:426
static const char *const HEIGHTMAP_NAME
Default filename of a saved heightmap.
Definition: screenshot.cpp:38
uint _heightmap_highest_peak
When saving a heightmap, this contains the highest peak on the map.
Definition: screenshot.cpp:43
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.
Definition: screenshot.cpp:66
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:357
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)
Definition: strings_type.h:17
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)
Definition: screenshot.cpp:90
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 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.
Definition: screenshot.cpp:395
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:388
Format of palette data in BMP header.
Definition: screenshot.cpp:99
Screenshot format information.
Definition: screenshot.cpp:69
ScreenshotHandlerProc * proc
Function for writing the screenshot.
Definition: screenshot.cpp:71
const char * extension
File extension.
Definition: screenshot.cpp:70
Helper struct to ensure the video buffer is locked and ready for drawing.
Data structure for viewport, display of a part of the world.
Definition: viewport_type.h:22
int top
Screen coordinate top edge of the viewport.
Definition: viewport_type.h:24
int width
Screen width of the viewport.
Definition: viewport_type.h:25
ZoomLevel zoom
The zoom level of the viewport.
Definition: viewport_type.h:33
int virtual_top
Virtual top coordinate.
Definition: viewport_type.h:29
int virtual_left
Virtual left coordinate.
Definition: viewport_type.h:28
int virtual_width
width << zoom
Definition: viewport_type.h:30
int left
Screen coordinate left edge of the viewport.
Definition: viewport_type.h:23
int height
Screen height of the viewport.
Definition: viewport_type.h:26
int virtual_height
height << zoom
Definition: viewport_type.h:31
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 a
colour channels in LE order
Definition: gfx_type.h:173
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