lsnes rr0-β0
[lsnes.git] / png.cpp
blob191359eb29a768c17cd68dfc0087c2bb5151abc2
1 #include "png.hpp"
2 #include <fstream>
3 #include <iostream>
4 #include <cstdint>
5 #include <boost/iostreams/categories.hpp>
6 #include <boost/iostreams/copy.hpp>
7 #include <boost/iostreams/stream.hpp>
8 #include <boost/iostreams/stream_buffer.hpp>
9 #include <boost/iostreams/filter/symmetric.hpp>
10 #include <boost/iostreams/filter/zlib.hpp>
11 #include <boost/iostreams/filtering_stream.hpp>
12 #include <boost/iostreams/device/back_inserter.hpp>
13 #include <zlib.h>
15 namespace
17 void encode32(char* _buf, uint32_t val) throw()
19 unsigned char* buf = reinterpret_cast<unsigned char*>(_buf);
20 buf[0] = ((val >> 24) & 0xFF);
21 buf[1] = ((val >> 16) & 0xFF);
22 buf[2] = ((val >> 8) & 0xFF);
23 buf[3] = (val & 0xFF);
26 class png_hunk_output
28 public:
29 typedef char char_type;
30 struct category : boost::iostreams::closable_tag, boost::iostreams::sink_tag {};
31 png_hunk_output(std::ostream& _os, uint32_t _type)
32 : os(_os), type(_type)
36 void close()
38 uint32_t crc = crc32(0, NULL, 0);
39 char fixed[12];
40 encode32(fixed, stream.size());
41 encode32(fixed + 4, type);
42 crc = crc32(crc, reinterpret_cast<Bytef*>(fixed + 4), 4);
43 if(stream.size() > 0)
44 crc = crc32(crc, reinterpret_cast<Bytef*>(&stream[0]), stream.size());
45 encode32(fixed + 8, crc);
46 os.write(fixed, 8);
47 os.write(&stream[0], stream.size());
48 os.write(fixed + 8, 4);
51 std::streamsize write(const char* s, std::streamsize n)
53 size_t oldsize = stream.size();
54 stream.resize(oldsize + n);
55 memcpy(&stream[oldsize], s, n);
56 return n;
58 protected:
59 std::vector<char> stream;
60 std::ostream& os;
61 uint32_t type;
65 void save_png_data(const std::string& file, uint8_t* data24, uint32_t width, uint32_t height) throw(std::bad_alloc,
66 std::runtime_error)
68 char* data = reinterpret_cast<char*>(data24);
69 std::ofstream filp(file.c_str());
70 if(!filp)
71 throw std::runtime_error("Can't open target PNG file");
72 char png_magic[] = {-119, 80, 78, 71, 13, 10, 26, 10};
73 filp.write(png_magic, sizeof(png_magic));
74 char ihdr[] = {25, 25, 25, 25, 25, 25, 25, 25, 8, 2, 0, 0, 0};
75 boost::iostreams::stream<png_hunk_output> ihdr_h(filp, 0x49484452);
76 encode32(ihdr, width);
77 encode32(ihdr + 4, height);
78 ihdr_h.write(ihdr, sizeof(ihdr));
79 ihdr_h.close();
81 boost::iostreams::filtering_ostream idat_h;
82 boost::iostreams::zlib_params params;
83 params.noheader = false;
84 idat_h.push(boost::iostreams::zlib_compressor(params));
85 idat_h.push(png_hunk_output(filp, 0x49444154));
86 for(uint32_t i = 0; i < height; i++) {
87 char identity_filter = 0;
88 idat_h.write(&identity_filter, 1);
89 idat_h.write(data + i * 3 * width, 3 * width);
91 idat_h.pop();
92 idat_h.pop();
94 boost::iostreams::stream<png_hunk_output> iend_h(filp, 0x49454E44);
95 iend_h.close();
96 if(!filp)
97 throw std::runtime_error("Can't write target PNG file");