OpenTTD Source 20250312-master-gcdcc6b491d
screenshot_bmp.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/endian_func.hpp"
12#include "core/math_func.hpp"
13#include "fileio_func.h"
14#include "screenshot_type.h"
15
16#include "safeguards.h"
17
19PACK(struct BitmapFileHeader {
20 uint16_t type;
21 uint32_t size;
22 uint32_t reserved;
23 uint32_t off_bits;
24});
25static_assert(sizeof(BitmapFileHeader) == 14);
26
29 uint32_t size;
30 int32_t width, height;
31 uint16_t planes, bitcount;
32 uint32_t compression, sizeimage, xpels, ypels, clrused, clrimp;
33};
34static_assert(sizeof(BitmapInfoHeader) == 40);
35
37struct RgbQuad {
38 uint8_t blue, green, red, reserved;
39};
40static_assert(sizeof(RgbQuad) == 4);
41
43public:
44 ScreenshotProvider_Bmp() : ScreenshotProvider("bmp", "BMP", 10) {}
45
46 bool MakeImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette) override
47 {
48 uint bpp; // bytes per pixel
49 switch (pixelformat) {
50 case 8: bpp = 1; break;
51 /* 32bpp mode is saved as 24bpp BMP */
52 case 32: bpp = 3; break;
53 /* Only implemented for 8bit and 32bit images so far */
54 default: return false;
55 }
56
57 auto of = FileHandle::Open(name, "wb");
58 if (!of.has_value()) return false;
59 auto &f = *of;
60
61 /* Each scanline must be aligned on a 32bit boundary */
62 uint bytewidth = Align(w * bpp, 4); // bytes per line in file
63
64 /* Size of palette. Only present for 8bpp mode */
65 uint pal_size = pixelformat == 8 ? sizeof(RgbQuad) * 256 : 0;
66
67 /* Setup the file header */
68 BitmapFileHeader bfh;
69 bfh.type = TO_LE16('MB');
70 bfh.size = TO_LE32(sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + pal_size + static_cast<size_t>(bytewidth) * h);
71 bfh.reserved = 0;
72 bfh.off_bits = TO_LE32(sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + pal_size);
73
74 /* Setup the info header */
76 bih.size = TO_LE32(sizeof(BitmapInfoHeader));
77 bih.width = TO_LE32(w);
78 bih.height = TO_LE32(h);
79 bih.planes = TO_LE16(1);
80 bih.bitcount = TO_LE16(bpp * 8);
81 bih.compression = 0;
82 bih.sizeimage = 0;
83 bih.xpels = 0;
84 bih.ypels = 0;
85 bih.clrused = 0;
86 bih.clrimp = 0;
87
88 /* Write file header and info header */
89 if (fwrite(&bfh, sizeof(bfh), 1, f) != 1 || fwrite(&bih, sizeof(bih), 1, f) != 1) {
90 return false;
91 }
92
93 if (pixelformat == 8) {
94 /* Convert the palette to the windows format */
95 RgbQuad rq[256];
96 for (uint i = 0; i < 256; i++) {
97 rq[i].red = palette[i].r;
98 rq[i].green = palette[i].g;
99 rq[i].blue = palette[i].b;
100 rq[i].reserved = 0;
101 }
102 /* Write the palette */
103 if (fwrite(rq, sizeof(rq), 1, f) != 1) {
104 return false;
105 }
106 }
107
108 /* Try to use 64k of memory, store between 16 and 128 lines */
109 uint maxlines = Clamp(65536 / (w * pixelformat / 8), 16, 128); // number of lines per iteration
110
111 std::vector<uint8_t> buff(maxlines * w * pixelformat / 8); // buffer which is rendered to
112 std::vector<uint8_t> line(bytewidth); // one line, stored to file
113
114 /* Start at the bottom, since bitmaps are stored bottom up */
115 do {
116 uint n = std::min(h, maxlines);
117 h -= n;
118
119 /* Render the pixels */
120 callb(userdata, buff.data(), h, w, n);
121
122 /* Write each line */
123 while (n-- != 0) {
124 if (pixelformat == 8) {
125 /* Move to 'line', leave last few pixels in line zeroed */
126 memcpy(line.data(), buff.data() + n * w, w);
127 } else {
128 /* Convert from 'native' 32bpp to BMP-like 24bpp.
129 * Works for both big and little endian machines */
130 Colour *src = ((Colour *)buff.data()) + n * w;
131 uint8_t *dst = line.data();
132 for (uint i = 0; i < w; i++) {
133 dst[i * 3 ] = src[i].b;
134 dst[i * 3 + 1] = src[i].g;
135 dst[i * 3 + 2] = src[i].r;
136 }
137 }
138 /* Write to file */
139 if (fwrite(line.data(), bytewidth, 1, f) != 1) {
140 return false;
141 }
142 }
143 } while (h != 0);
144
145
146 return true;
147 }
148};
149
150static ScreenshotProvider_Bmp s_screenshot_provider_bmp;
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.
Function to handling different endian machines.
Functions for Standard In/Out file operations.
Integer math functions.
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
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.
BMP Info Header (stored in little endian)
Format of palette data in BMP header.