A library for working with phylogenetic and population genetic data.
v0.32.0
utils/formats/bmp/writer.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2024 Lucas Czech
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  Contact:
19  Lucas Czech <lczech@carnegiescience.edu>
20  Department of Plant Biology, Carnegie Institution For Science
21  260 Panama Street, Stanford, CA 94305, USA
22 */
23 
32 
39 
40 #include <cassert>
41 #include <fstream>
42 #include <ostream>
43 #include <limits>
44 #include <stdexcept>
45 
46 namespace genesis {
47 namespace utils {
48 
49 // =================================================================================================
50 // Writing Matrix of Color
51 // =================================================================================================
52 
54  Matrix<Color> const& image,
55  std::shared_ptr<utils::BaseOutputTarget> target
56 ) const {
57  auto& outstream = target->ostream();
58 
59  // Use some nicer names.
60  auto const width = image.cols();
61  auto const height = image.rows();
62 
63  // Padding needed to fill rows to a multiple of 4 bytes.
64  size_t const row_pad = (4 - (( width * 3 ) % 4 )) % 4;
65 
66  // Length of one line of the image, in bytes.
67  size_t const line_len = width * 3 + row_pad;
68  assert(( row_pad < 4 ) && ( line_len % 4 == 0 ));
69 
70  // Total resulting data size in bytes
71  size_t const data_size = line_len * height;
72 
73  // Size checks! Bitmaps can't be larger than ~4GB
74  if(
75  height > static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) ||
76  width > static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) ||
77  14 + 40 + data_size > static_cast<size_t>( std::numeric_limits<uint32_t>::max() )
78  ) {
79  throw std::runtime_error(
80  "Cannot save Bitmap larger than " +
81  std::to_string( std::numeric_limits<uint32_t>::max() ) +
82  " bytes."
83  );
84  }
85  assert( data_size <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
86  assert( height <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
87  assert( width <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
88 
89  // File header.
90  BitmapFileheader file_header;
91  file_header.bfSize = 14 + 40 + static_cast<uint32_t>( data_size );
92  file_header.bfOffBits = 14 + 40;
93 
94  // Info header.
95  BitmapInfoheader info_header;
96  info_header.biWidth = static_cast<uint32_t>( width );
97  info_header.biHeight = static_cast<uint32_t>( height );
98  info_header.biBitCount = 24;
99 
100  // Write headers.
101  write_file_header_( file_header, outstream );
102  write_info_header_( info_header, outstream );
103 
104  // Write data.
105  for( size_t yr = 0; yr < height; ++yr ) {
106  // Run row-wise backwards (demanded by bmp standard).
107  auto y = height - 1 - yr;
108 
109  for( size_t x = 0; x < width; ++x ) {
110 
111  // Store color in BGR order (demanded by bmp standard).
112  auto const& color = image( y, x );
113  outstream.put( color.b_byte() );
114  outstream.put( color.g_byte() );
115  outstream.put( color.r_byte() );
116  }
117 
118  // Fill row to multiple of 4 bytes.
119  for( size_t x = 0; x < row_pad; ++x ) {
120  outstream.put( 0 );
121  }
122  }
123 }
124 
125 // =================================================================================================
126 // Writing Matrix of unsigned char
127 // =================================================================================================
128 
130  Matrix<unsigned char> const& image,
131  std::shared_ptr<utils::BaseOutputTarget> target
132 ) const {
133  // Build a simple grayscale palette.
134  auto palette = std::vector<Color>( 256 );
135  for( size_t i = 0; i < 256; ++i ) {
136  palette[i].r_byte( i );
137  palette[i].g_byte( i );
138  palette[i].b_byte( i );
139  }
140  write( image, palette, target );
141 }
142 
143 // =================================================================================================
144 // Writing Matrix of unsigned char with Color palette
145 // =================================================================================================
146 
148  Matrix<unsigned char> const& image,
149  std::vector<Color> const& palette,
150  std::shared_ptr<utils::BaseOutputTarget> target
151 ) const {
152  auto& outstream = target->ostream();
153 
154  // Use some nicer names.
155  auto const width = image.cols();
156  auto const height = image.rows();
157 
158  // Padding needed to fill rows to a multiple of 4 bytes.
159  size_t const row_pad = (4 - ( width % 4 )) % 4;
160 
161  // Length of one line of the image, in bytes.
162  size_t const line_len = width + row_pad;
163  assert(( row_pad < 4 ) && ( line_len % 4 == 0 ));
164 
165  // Total resulting data size in bytes
166  size_t const data_size = line_len * height;
167 
168  // Size checks! Bitmaps can't be larger than ~4GB
169  if(
170  height > static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) ||
171  width > static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) ||
172  14 + 40 + 256 * 4 + data_size > static_cast<size_t>( std::numeric_limits<uint32_t>::max() )
173  ) {
174  throw std::runtime_error(
175  "Cannot save Bitmap larger than " +
176  std::to_string( std::numeric_limits<uint32_t>::max() ) +
177  " bytes."
178  );
179  }
180  assert( data_size <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
181  assert( height <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
182  assert( width <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
183 
184  // File header.
185  BitmapFileheader file_header;
186  file_header.bfSize = 14 + 40 + 256 * 4 + static_cast<uint32_t>( data_size );
187  file_header.bfOffBits = 14 + 40 + 256 * 4;
188 
189  // Info and info header.
190  BitmapInfo info;
191  info.bmiHeader.biWidth = static_cast<uint32_t>( width );
192  info.bmiHeader.biHeight = static_cast<uint32_t>( height );
193  info.bmiHeader.biBitCount = 8;
194 
195  // Palette size check.
196  if( palette.size() != 256 ) {
197  throw std::invalid_argument( "Bitmap color palette needs to have 256 entries." );
198  }
199 
200  // Color palette.
201  info.bmiColors = std::vector<RgbQuad>( 256 );
202  for( size_t i = 0; i < 256; ++i ) {
203  info.bmiColors[i].rgbBlue = palette[i].b_byte();
204  info.bmiColors[i].rgbGreen = palette[i].g_byte();
205  info.bmiColors[i].rgbRed = palette[i].r_byte();
206  }
207 
208  // Write headers.
209  write_file_header_( file_header, outstream );
210  write_info_( info, outstream );
211 
212  // Write data.
213  for( size_t yr = 0; yr < height; ++yr ) {
214  // Run row-wise backwards (demanded by bmp standard).
215  auto y = height - 1 - yr;
216 
217  for( size_t x = 0; x < width; ++x ) {
218  outstream.put( image( y, x ));
219  }
220 
221  // Fill row to multiple of 4 bytes.
222  for( size_t x = 0; x < row_pad; ++x ) {
223  outstream.put( 0 );
224  }
225  }
226 }
227 
228 // =================================================================================================
229 // Internal Helpers
230 // =================================================================================================
231 
232 void BmpWriter::write_uint16_( uint16_t data, std::ostream& target ) const
233 {
234  auto const bytes = reinterpret_cast< uint8_t const* >( &data );
235 
236  static const bool is_little_endian = info_get_hardware().is_little_endian;
237  if( is_little_endian ) {
238  target.put( bytes[0] );
239  target.put( bytes[1] );
240  } else {
241  target.put( bytes[1] );
242  target.put( bytes[0] );
243  }
244 }
245 
246 void BmpWriter::write_uint32_( uint32_t data, std::ostream& target ) const
247 {
248  auto const bytes = reinterpret_cast< uint8_t const* >( &data );
249 
250  static const bool is_little_endian = info_get_hardware().is_little_endian;
251  if( is_little_endian ) {
252  target.put( bytes[0] );
253  target.put( bytes[1] );
254  target.put( bytes[2] );
255  target.put( bytes[3] );
256  } else {
257  target.put( bytes[3] );
258  target.put( bytes[2] );
259  target.put( bytes[1] );
260  target.put( bytes[0] );
261  }
262 }
263 
264 void BmpWriter::write_file_header_( BitmapFileheader const& header, std::ostream& target ) const
265 {
266  write_uint16_( header.bfType, target );
267  write_uint32_( header.bfSize, target );
268  write_uint16_( header.bfReserved1, target );
269  write_uint16_( header.bfReserved2, target );
270  write_uint32_( header.bfOffBits, target );
271 }
272 
273 void BmpWriter::write_info_header_( BitmapInfoheader const& header, std::ostream& target ) const
274 {
275  write_uint32_( header.biSize, target );
276  write_uint32_( header.biWidth, target );
277  write_uint32_( header.biHeight, target );
278  write_uint16_( header.biPlanes, target );
279  write_uint16_( header.biBitCount, target );
280  write_uint32_( header.biCompression, target );
281  write_uint32_( header.biSizeImage, target );
282  write_uint32_( header.biXPelsPerMeter, target );
283  write_uint32_( header.biYPelsPerMeter, target );
284  write_uint32_( header.biClrUsed, target );
285  write_uint32_( header.biClrImportant, target );
286 }
287 
288 void BmpWriter::write_info_( BitmapInfo const& info, std::ostream& target ) const
289 {
290  write_info_header_( info.bmiHeader, target );
291  for( auto const& c : info.bmiColors ) {
292  target.put( c.rgbBlue );
293  target.put( c.rgbGreen );
294  target.put( c.rgbRed );
295  target.put( c.rgbReserved );
296  }
297 }
298 
299 } // namespace utils
300 } // namespace genesis
genesis::utils::Matrix::cols
size_t cols() const
Definition: containers/matrix.hpp:181
fs.hpp
Provides functions for accessing the file system.
genesis::tree::height
double height(Tree const &tree)
Get the height of the tree, i.e., the longest distance from the root to a leaf, measured using the br...
Definition: tree/common_tree/functions.cpp:169
genesis::utils::BmpWriter::BitmapInfoheader::biWidth
uint32_t biWidth
Definition: utils/formats/bmp/writer.hpp:129
output_stream.hpp
genesis::utils::InfoHardware::is_little_endian
bool is_little_endian
System uses little endian memory. If false, system uses big endian.
Definition: info.hpp:157
genesis::utils::info_get_hardware
InfoHardware const & info_get_hardware()
Return information about hardware features.
Definition: info.cpp:665
genesis::utils::BmpWriter::BitmapFileheader::bfOffBits
uint32_t bfOffBits
Definition: utils/formats/bmp/writer.hpp:93
genesis::utils::Matrix
Definition: placement/function/emd.hpp:53
genesis::population::to_string
std::string to_string(GenomeLocus const &locus)
Definition: function/genome_locus.hpp:52
genesis::utils::BmpWriter::BitmapInfo::bmiColors
std::vector< RgbQuad > bmiColors
Definition: utils/formats/bmp/writer.hpp:170
genesis::utils::BmpWriter::write
void write(Matrix< Color > const &image, std::shared_ptr< utils::BaseOutputTarget > target) const
Write a full 24bit RGB Color image to an output target.
Definition: utils/formats/bmp/writer.cpp:53
matrix.hpp
info.hpp
genesis
Container namespace for all symbols of genesis in order to keep them separate when used as a library.
Definition: placement/formats/edge_color.cpp:42
functions.hpp
Color operators and functions.
genesis::utils::BmpWriter::BitmapInfo
Bitmap info that describes dimensions and color information.
Definition: utils/formats/bmp/writer.hpp:168
genesis::utils::BmpWriter::BitmapFileheader::bfSize
uint32_t bfSize
Definition: utils/formats/bmp/writer.hpp:90
color.hpp
Header of Color class.
genesis::utils::BmpWriter::BitmapInfo::bmiHeader
BitmapInfoheader bmiHeader
Definition: utils/formats/bmp/writer.hpp:169
writer.hpp
genesis::utils::BmpWriter::BitmapInfoheader::biBitCount
uint16_t biBitCount
Definition: utils/formats/bmp/writer.hpp:132
genesis::utils::Matrix::rows
size_t rows() const
Definition: containers/matrix.hpp:176
genesis::utils::BmpWriter::BitmapInfoheader
Bitmap Info Header.
Definition: utils/formats/bmp/writer.hpp:126
genesis::utils::BmpWriter::BitmapFileheader
Bitmap File Header.
Definition: utils/formats/bmp/writer.hpp:87
genesis::utils::BmpWriter::BitmapInfoheader::biHeight
uint32_t biHeight
Definition: utils/formats/bmp/writer.hpp:130