OpenTTD Source 20260721-master-g25ec12c62d
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "core/math_func.hpp"
13#include "fileio_func.h"
14#include "screenshot_type.h"
15
16#include "safeguards.h"
17
18constexpr size_t BITMAP_FILE_HEADER_SIZE = 14;
19constexpr size_t BITMAP_INFO_HEADER_SIZE = 40;
20
22struct RgbQuad {
23 uint8_t blue, green, red, reserved;
24};
25static_assert(sizeof(RgbQuad) == 4);
26
27class ScreenshotProvider_Bmp : public ScreenshotProvider {
28public:
29 ScreenshotProvider_Bmp() : ScreenshotProvider("bmp", "BMP", 10) {}
30
31 bool MakeImage(std::string_view name, const ScreenshotCallback &callb, uint w, uint h, int pixelformat, const Colour *palette) const override
32 {
33 uint bpp; // bytes per pixel
34 switch (pixelformat) {
35 case 8: bpp = 1; break;
36 /* 32bpp mode is saved as 24bpp BMP */
37 case 32: bpp = 3; break;
38 /* Only implemented for 8bit and 32bit images so far */
39 default: return false;
40 }
41
42 auto of = FileHandle::Open(name, "wb");
43 if (!of.has_value()) return false;
44 auto &f = *of;
45
46 /* Each scanline must be aligned on a 32bit boundary */
47 uint bytewidth = Align(w * bpp, 4); // bytes per line in file
48
49 /* Size of palette. Only present for 8bpp mode */
50 uint pal_size = pixelformat == 8 ? sizeof(RgbQuad) * 256 : 0;
51
52 std::string header;
53 StringBuilder sb(header);
54 sb.Put("BM"); // header field
55 sb.PutUint32LE(BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_SIZE + pal_size + static_cast<size_t>(bytewidth) * h); // full image size
56 sb.PutUint16LE(0); // reserved 1
57 sb.PutUint16LE(0); // reserved 2
58 sb.PutUint32LE(BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_SIZE + pal_size); // offset of bitmap image data
59
60 sb.PutUint32LE(BITMAP_INFO_HEADER_SIZE); // size of info header
61 sb.PutUint32LE(w); // width of the image
62 sb.PutUint32LE(h); // height of the image
63 sb.PutUint16LE(1); // number of planes
64 sb.PutUint16LE(bpp * 8); // bits per pixel
65 sb.PutUint32LE(0); // compression
66 sb.PutUint32LE(0); // size of raw image data, dummy of 0 is allowed
67 sb.PutUint32LE(0); // vertical resolution in pixels per meter
68 sb.PutUint32LE(0); // horizontal resolution in pixels per meter
69 sb.PutUint32LE(0); // number of colours in the palette, 0 is allowed
70 sb.PutUint32LE(0); // number of important colours, 0 is all colours are important
71 assert(header.size() == BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_SIZE);
72
73 /* Write headers */
74 if (fwrite(header.data(), header.size(), 1, f) != 1) {
75 return false;
76 }
77
78 if (pixelformat == 8) {
79 /* Convert the palette to the windows format */
80 RgbQuad rq[256];
81 for (uint i = 0; i < 256; i++) {
82 rq[i].red = palette[i].r;
83 rq[i].green = palette[i].g;
84 rq[i].blue = palette[i].b;
85 rq[i].reserved = 0;
86 }
87 /* Write the palette */
88 if (fwrite(rq, sizeof(rq), 1, f) != 1) {
89 return false;
90 }
91 }
92
93 /* Try to use 64k of memory, store between 16 and 128 lines */
94 uint maxlines = Clamp(65536 / (w * pixelformat / 8), 16, 128); // number of lines per iteration
95
96 std::vector<uint8_t> buff(maxlines * w * pixelformat / 8); // buffer which is rendered to
97 std::vector<uint8_t> line(bytewidth); // one line, stored to file
98
99 /* Start at the bottom, since bitmaps are stored bottom up */
100 do {
101 uint n = std::min(h, maxlines);
102 h -= n;
103
104 /* Render the pixels */
105 callb(buff.data(), h, w, n);
106
107 /* Write each line */
108 while (n-- != 0) {
109 if (pixelformat == 8) {
110 /* Move to 'line', leave last few pixels in line zeroed */
111 std::copy_n(buff.data() + n * w, w, line.data());
112 } else {
113 /* Convert from 'native' 32bpp to BMP-like 24bpp.
114 * Works for both big and little endian machines */
115 Colour *src = ((Colour *)buff.data()) + n * w;
116 uint8_t *dst = line.data();
117 for (uint i = 0; i < w; i++) {
118 dst[i * 3 ] = src[i].b;
119 dst[i * 3 + 1] = src[i].g;
120 dst[i * 3 + 2] = src[i].r;
121 }
122 }
123 /* Write to file */
124 if (fwrite(line.data(), bytewidth, 1, f) != 1) {
125 return false;
126 }
127 }
128 } while (h != 0);
129
130
131 return true;
132 }
133
134private:
135 static ScreenshotProvider_Bmp instance;
136};
137
138/* static */ ScreenshotProvider_Bmp ScreenshotProvider_Bmp::instance{};
void PutUint32LE(uint32_t value)
Append binary uint32 using little endian.
void PutUint16LE(uint16_t value)
Append binary uint16 using little endian.
void Put(std::string_view str)
Append string.
static std::optional< FileHandle > Open(const std::string &filename, std::string_view mode)
Open an RAII file handle if possible.
Definition fileio.cpp:1219
bool MakeImage(std::string_view name, const ScreenshotCallback &callb, uint w, uint h, int pixelformat, const Colour *palette) const override
Create and write an image to a file.
Compose data into a growing std::string.
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.
constexpr size_t BITMAP_FILE_HEADER_SIZE
The size of a bitmap file header.
constexpr size_t BITMAP_INFO_HEADER_SIZE
The size of a bitmap info header.
Types related to screenshot providers.
std::function< void(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.
Compose strings from textual and binary data.
Format of palette data in BMP header.