A toolkit for working with phylogenetic data.
v0.24.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-2019 Lucas Czech and HITS gGmbH
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 <lucas.czech@h-its.org>
20  Exelixis Lab, Heidelberg Institute for Theoretical Studies
21  Schloss-Wolfsbrunnenweg 35, D-69118 Heidelberg, Germany
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 
53 void BmpWriter::to_stream( Matrix<Color> const& image, std::ostream& outstream ) const
54 {
55  // Use some nicer names.
56  auto const width = image.cols();
57  auto const height = image.rows();
58 
59  // Padding needed to fill rows to a multiple of 4 bytes.
60  size_t const row_pad = (4 - (( width * 3 ) % 4 )) % 4;
61 
62  // Length of one line of the image, in bytes.
63  size_t const line_len = width * 3 + row_pad;
64  assert(( row_pad < 4 ) && ( line_len % 4 == 0 ));
65 
66  // Total resulting data size in bytes
67  size_t const data_size = line_len * height;
68 
69  // Size checks! Bitmaps can't be larger than ~4GB
70  if(
71  height > static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) ||
72  width > static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) ||
73  14 + 40 + data_size > static_cast<size_t>( std::numeric_limits<uint32_t>::max() )
74  ) {
75  throw std::runtime_error(
76  "Cannot save Bitmap larger than " +
77  std::to_string( std::numeric_limits<uint32_t>::max() ) +
78  " bytes."
79  );
80  }
81  assert( data_size <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
82  assert( height <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
83  assert( width <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
84 
85  // File header.
86  BitmapFileheader file_header;
87  file_header.bfSize = 14 + 40 + static_cast<uint32_t>( data_size );
88  file_header.bfOffBits = 14 + 40;
89 
90  // Info header.
91  BitmapInfoheader info_header;
92  info_header.biWidth = static_cast<uint32_t>( width );
93  info_header.biHeight = static_cast<uint32_t>( height );
94  info_header.biBitCount = 24;
95 
96  // Write headers.
97  write_file_header_( file_header, outstream );
98  write_info_header_( info_header, outstream );
99 
100  // Write data.
101  for( size_t yr = 0; yr < height; ++yr ) {
102  // Run row-wise backwards (demanded by bmp standard).
103  auto y = height - 1 - yr;
104 
105  for( size_t x = 0; x < width; ++x ) {
106 
107  // Store color in BGR order (demanded by bmp standard).
108  auto const& color = image( y, x );
109  outstream.put( color.b_byte() );
110  outstream.put( color.g_byte() );
111  outstream.put( color.r_byte() );
112  }
113 
114  // Fill row to multiple of 4 bytes.
115  for( size_t x = 0; x < row_pad; ++x ) {
116  outstream.put( 0 );
117  }
118  }
119 }
120 
121 void BmpWriter::to_file( Matrix<Color> const& image, std::string const& filename ) const
122 {
123  std::ofstream ofs;
124  utils::file_output_stream( filename, ofs );
125  to_stream( image, ofs );
126 }
127 
128 // =================================================================================================
129 // Writing Matrix of unsigned char
130 // =================================================================================================
131 
132 void BmpWriter::to_stream( Matrix<unsigned char> const& image, std::ostream& outstream ) const
133 {
134  // Build a simple grayscale palette.
135  auto palette = std::vector<Color>( 256 );
136  for( size_t i = 0; i < 256; ++i ) {
137  palette[i].r_byte( i );
138  palette[i].g_byte( i );
139  palette[i].b_byte( i );
140  }
141  to_stream( image, palette, outstream );
142 }
143 
144 void BmpWriter::to_file( Matrix<unsigned char> const& image, std::string const& filename ) const
145 {
146  std::ofstream ofs;
147  utils::file_output_stream( filename, ofs );
148  to_stream( image, ofs );
149 }
150 
151 // =================================================================================================
152 // Writing Matrix of unsigned char with Color palette
153 // =================================================================================================
154 
156  Matrix<unsigned char> const& image, std::vector<Color> const& palette, std::ostream& outstream
157 ) const {
158  // Use some nicer names.
159  auto const width = image.cols();
160  auto const height = image.rows();
161 
162  // Padding needed to fill rows to a multiple of 4 bytes.
163  size_t const row_pad = (4 - ( width % 4 )) % 4;
164 
165  // Length of one line of the image, in bytes.
166  size_t const line_len = width + row_pad;
167  assert(( row_pad < 4 ) && ( line_len % 4 == 0 ));
168 
169  // Total resulting data size in bytes
170  size_t const data_size = line_len * height;
171 
172  // Size checks! Bitmaps can't be larger than ~4GB
173  if(
174  height > static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) ||
175  width > static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) ||
176  14 + 40 + 256 * 4 + data_size > static_cast<size_t>( std::numeric_limits<uint32_t>::max() )
177  ) {
178  throw std::runtime_error(
179  "Cannot save Bitmap larger than " +
180  std::to_string( std::numeric_limits<uint32_t>::max() ) +
181  " bytes."
182  );
183  }
184  assert( data_size <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
185  assert( height <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
186  assert( width <= static_cast<size_t>( std::numeric_limits<uint32_t>::max() ) );
187 
188  // File header.
189  BitmapFileheader file_header;
190  file_header.bfSize = 14 + 40 + 256 * 4 + static_cast<uint32_t>( data_size );
191  file_header.bfOffBits = 14 + 40 + 256 * 4;
192 
193  // Info and info header.
194  BitmapInfo info;
195  info.bmiHeader.biWidth = static_cast<uint32_t>( width );
196  info.bmiHeader.biHeight = static_cast<uint32_t>( height );
197  info.bmiHeader.biBitCount = 8;
198 
199  // Palette size check.
200  if( palette.size() != 256 ) {
201  throw std::invalid_argument( "Bitmap color palette needs to have 256 entries." );
202  }
203 
204  // Color palette.
205  info.bmiColors = std::vector<RgbQuad>( 256 );
206  for( size_t i = 0; i < 256; ++i ) {
207  info.bmiColors[i].rgbBlue = palette[i].b_byte();
208  info.bmiColors[i].rgbGreen = palette[i].g_byte();
209  info.bmiColors[i].rgbRed = palette[i].r_byte();
210  }
211 
212  // Write headers.
213  write_file_header_( file_header, outstream );
214  write_info_( info, outstream );
215 
216  // Write data.
217  for( size_t yr = 0; yr < height; ++yr ) {
218  // Run row-wise backwards (demanded by bmp standard).
219  auto y = height - 1 - yr;
220 
221  for( size_t x = 0; x < width; ++x ) {
222  outstream.put( image( y, x ));
223  }
224 
225  // Fill row to multiple of 4 bytes.
226  for( size_t x = 0; x < row_pad; ++x ) {
227  outstream.put( 0 );
228  }
229  }
230 }
231 
233  Matrix<unsigned char> const& image, std::vector<Color> const& palette, std::string const& filename
234 ) const {
235  std::ofstream ofs;
236  utils::file_output_stream( filename, ofs );
237  to_stream( image, palette, ofs );
238 }
239 
240 // =================================================================================================
241 // Internal Helpers
242 // =================================================================================================
243 
244 void BmpWriter::write_uint16_( uint16_t data, std::ostream& target ) const
245 {
246  auto const bytes = reinterpret_cast< uint8_t const* >( &data );
247 
248  if( Options::is_little_endian() ) {
249  target.put( bytes[0] );
250  target.put( bytes[1] );
251  } else {
252  target.put( bytes[1] );
253  target.put( bytes[0] );
254  }
255 }
256 
257 void BmpWriter::write_uint32_( uint32_t data, std::ostream& target ) const
258 {
259  auto const bytes = reinterpret_cast< uint8_t const* >( &data );
260 
261  if( Options::is_little_endian() ) {
262  target.put( bytes[0] );
263  target.put( bytes[1] );
264  target.put( bytes[2] );
265  target.put( bytes[3] );
266  } else {
267  target.put( bytes[3] );
268  target.put( bytes[2] );
269  target.put( bytes[1] );
270  target.put( bytes[0] );
271  }
272 }
273 
274 void BmpWriter::write_file_header_( BitmapFileheader const& header, std::ostream& target ) const
275 {
276  write_uint16_( header.bfType, target );
277  write_uint32_( header.bfSize, target );
278  write_uint16_( header.bfReserved1, target );
279  write_uint16_( header.bfReserved2, target );
280  write_uint32_( header.bfOffBits, target );
281 }
282 
283 void BmpWriter::write_info_header_( BitmapInfoheader const& header, std::ostream& target ) const
284 {
285  write_uint32_( header.biSize, target );
286  write_uint32_( header.biWidth, target );
287  write_uint32_( header.biHeight, target );
288  write_uint16_( header.biPlanes, target );
289  write_uint16_( header.biBitCount, target );
290  write_uint32_( header.biCompression, target );
291  write_uint32_( header.biSizeImage, target );
292  write_uint32_( header.biXPelsPerMeter, target );
293  write_uint32_( header.biYPelsPerMeter, target );
294  write_uint32_( header.biClrUsed, target );
295  write_uint32_( header.biClrImportant, target );
296 }
297 
298 void BmpWriter::write_info_( BitmapInfo const& info, std::ostream& target ) const
299 {
300  write_info_header_( info.bmiHeader, target );
301  for( auto const& c : info.bmiColors ) {
302  target.put( c.rgbBlue );
303  target.put( c.rgbGreen );
304  target.put( c.rgbRed );
305  target.put( c.rgbReserved );
306  }
307 }
308 
309 } // namespace utils
310 } // namespace genesis
Bitmap info that describes dimensions and color information.
void file_output_stream(std::string const &filename, std::ofstream &out_stream, std::ios_base::openmode mode=std::ios_base::out)
Helper function to obtain an output stream to a file.
Container namespace for all symbols of genesis in order to keep them separate when used as a library...
Header of Color class.
void to_stream(Matrix< Color > const &image, std::ostream &outstream) const
Write a full 24bit RGB Color image to a stream.
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...
Provides functions for accessing the file system.
void to_file(Matrix< Color > const &image, std::string const &filename) const
Write a full 24bit RGB Color image to a file.
std::shared_ptr< BaseOutputTarget > to_string(std::string &target_string)
Obtain an output target for writing to a string.
Color operators and functions.
static bool is_little_endian()
Return whether the system uses little endian memory.
Definition: options.cpp:270