OpenTTD Source 20250312-master-gcdcc6b491d
screenshot_png.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/math_func.hpp"
12#include "debug.h"
13#include "fileio_func.h"
14#include "screenshot_type.h"
15
16#include <png.h>
17
18#ifdef PNG_TEXT_SUPPORTED
19#include "rev.h"
20#include "newgrf_config.h"
21#include "ai/ai_info.hpp"
22#include "company_base.h"
23#include "base_media_base.h"
24#endif /* PNG_TEXT_SUPPORTED */
25
26#include "safeguards.h"
27
29public:
30 ScreenshotProvider_Png() : ScreenshotProvider("png", "PNG", 0) {}
31
32 bool MakeImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette) override
33 {
34 png_color rq[256];
35 uint i, y, n;
36 uint maxlines;
37 uint bpp = pixelformat / 8;
38 png_structp png_ptr;
39 png_infop info_ptr;
40
41 /* only implemented for 8bit and 32bit images so far. */
42 if (pixelformat != 8 && pixelformat != 32) return false;
43
44 auto of = FileHandle::Open(name, "wb");
45 if (!of.has_value()) return false;
46 auto &f = *of;
47
48 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, const_cast<char *>(name), png_my_error, png_my_warning);
49
50 if (png_ptr == nullptr) {
51 return false;
52 }
53
54 info_ptr = png_create_info_struct(png_ptr);
55 if (info_ptr == nullptr) {
56 png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
57 return false;
58 }
59
60 if (setjmp(png_jmpbuf(png_ptr))) {
61 png_destroy_write_struct(&png_ptr, &info_ptr);
62 return false;
63 }
64
65 png_init_io(png_ptr, f);
66
67 png_set_filter(png_ptr, 0, PNG_FILTER_NONE);
68
69 png_set_IHDR(png_ptr, info_ptr, w, h, 8, pixelformat == 8 ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB,
70 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
71
72#ifdef PNG_TEXT_SUPPORTED
73
74 /* Try to add some game metadata to the PNG screenshot so
75 * it's more useful for debugging and archival purposes. */
76 png_text_struct text[2];
77 memset(text, 0, sizeof(text));
78 text[0].key = const_cast<char *>("Software");
79 text[0].text = const_cast<char *>(_openttd_revision);
80 text[0].text_length = strlen(_openttd_revision);
81 text[0].compression = PNG_TEXT_COMPRESSION_NONE;
82
83 std::string message;
84 message.reserve(1024);
85 fmt::format_to(std::back_inserter(message), "Graphics set: {} ({})\n", BaseGraphics::GetUsedSet()->name, BaseGraphics::GetUsedSet()->version);
86 message += "NewGRFs:\n";
87 if (_game_mode != GM_MENU) {
88 for (const auto &c : _grfconfig) {
89 fmt::format_to(std::back_inserter(message), "{:08X} {} {}\n", std::byteswap(c->ident.grfid), FormatArrayAsHex(c->ident.md5sum), c->filename);
90 }
91 }
92 message += "\nCompanies:\n";
93 for (const Company *c : Company::Iterate()) {
94 if (c->ai_info == nullptr) {
95 fmt::format_to(std::back_inserter(message), "{:2d}: Human\n", c->index);
96 } else {
97 fmt::format_to(std::back_inserter(message), "{:2d}: {} (v{})\n", c->index, c->ai_info->GetName(), c->ai_info->GetVersion());
98 }
99 }
100 text[1].key = const_cast<char *>("Description");
101 text[1].text = const_cast<char *>(message.c_str());
102 text[1].text_length = message.size();
103 text[1].compression = PNG_TEXT_COMPRESSION_zTXt;
104 png_set_text(png_ptr, info_ptr, text, 2);
105
106#endif /* PNG_TEXT_SUPPORTED */
107
108 if (pixelformat == 8) {
109 /* convert the palette to the .PNG format. */
110 for (i = 0; i != 256; i++) {
111 rq[i].red = palette[i].r;
112 rq[i].green = palette[i].g;
113 rq[i].blue = palette[i].b;
114 }
115
116 png_set_PLTE(png_ptr, info_ptr, rq, 256);
117 }
118
119 png_write_info(png_ptr, info_ptr);
120 png_set_flush(png_ptr, 512);
121
122 if (pixelformat == 32) {
123 png_color_8 sig_bit;
124
125 /* Save exact colour/alpha resolution */
126 sig_bit.alpha = 0;
127 sig_bit.blue = 8;
128 sig_bit.green = 8;
129 sig_bit.red = 8;
130 sig_bit.gray = 8;
131 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
132
133 if constexpr (std::endian::native == std::endian::little) {
134 png_set_bgr(png_ptr);
135 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
136 } else {
137 png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
138 }
139 }
140
141 /* use by default 64k temp memory */
142 maxlines = Clamp(65536 / w, 16, 128);
143
144 /* now generate the bitmap bits */
145 std::vector<uint8_t> buff(static_cast<size_t>(w) * maxlines * bpp); // by default generate 128 lines at a time.
146
147 y = 0;
148 do {
149 /* determine # lines to write */
150 n = std::min(h - y, maxlines);
151
152 /* render the pixels into the buffer */
153 callb(userdata, buff.data(), y, w, n);
154 y += n;
155
156 /* write them to png */
157 for (i = 0; i != n; i++) {
158 png_write_row(png_ptr, (png_bytep)buff.data() + i * w * bpp);
159 }
160 } while (y != h);
161
162 png_write_end(png_ptr, info_ptr);
163 png_destroy_write_struct(&png_ptr, &info_ptr);
164
165 return true;
166 }
167
168private:
169 static void PNGAPI png_my_error(png_structp png_ptr, png_const_charp message)
170 {
171 Debug(misc, 0, "[libpng] error: {} - {}", message, (const char *)png_get_error_ptr(png_ptr));
172 longjmp(png_jmpbuf(png_ptr), 1);
173 }
174
175 static void PNGAPI png_my_warning(png_structp png_ptr, png_const_charp message)
176 {
177 Debug(misc, 1, "[libpng] warning: {} - {}", message, (const char *)png_get_error_ptr(png_ptr));
178 }
179};
180
181static ScreenshotProvider_Png s_screenshot_provider_png;
AIInfo keeps track of all information of an AI, like Author, Description, ...
Generic functions for replacing base data (graphics, sounds).
static const GraphicsSet * GetUsedSet()
Return the used set.
static std::optional< FileHandle > Open(const std::string &filename, const std::string &mode)
Open an RAII file handle if possible.
Definition fileio.cpp:1170
Base interface for a SoundLoader implementation.
Definition of stuff that is very close to a company, like the company struct itself.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
Functions for Standard In/Out file operations.
Integer math functions.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
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.
Types related to screenshot providers.
void(void *userdata, void *buf, uint y, uint pitch, uint n) ScreenshotCallback
Callback function signature for generating lines of pixel data to be written to the screenshot file.
Definition of base types and functions in a cross-platform compatible way.
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:80
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.