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