1 #include <fstream>
2 #include <memory>
3 
4 #include <pangolin/image/typed_image.h>
5 
6 #ifdef HAVE_LZ4
7 #  include <lz4.h>
8 #endif
9 
10 namespace pangolin {
11 
12 #pragma pack(push, 1)
13 struct lz4_image_header
14 {
15     char magic[3];
16     char fmt[16];
17     size_t w, h;
18     int64_t compressed_size;
19 };
20 #pragma pack(pop)
21 
SaveLz4(const Image<unsigned char> & image,const pangolin::PixelFormat & fmt,std::ostream & out,int compression_level)22 void SaveLz4(const Image<unsigned char>& image, const pangolin::PixelFormat& fmt, std::ostream& out, int compression_level)
23 {
24 #ifdef HAVE_LZ4
25     const int64_t src_size = image.SizeBytes();
26     const int64_t max_dst_size = LZ4_compressBound(src_size);
27     std::unique_ptr<char[]> output_buffer(new char[max_dst_size]);
28 
29     // Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
30     // The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
31     // It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
32     // An acceleration value of "1" is the same as regular LZ4_compress_default()
33     // Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
34     const int64_t compressed_data_size = LZ4_compress_fast((char*)image.ptr, output_buffer.get(), src_size, max_dst_size, compression_level);
35 
36     if (compressed_data_size < 0)
37         throw std::runtime_error("A negative result from LZ4_compress_default indicates a failure trying to compress the data.");
38     if (compressed_data_size == 0)
39         throw std::runtime_error("A result of 0 for LZ4 means compression worked, but was stopped because the destination buffer couldn't hold all the information.");
40 
41     lz4_image_header header;
42     strncpy(header.magic,"LZ4",3);
43     strncpy(header.fmt, fmt.format.c_str(), sizeof(header.fmt));
44     header.w = image.w;
45     header.h = image.h;
46     header.compressed_size = compressed_data_size;
47     out.write((char*)&header, sizeof(header));
48 
49     out.write(output_buffer.get(), compressed_data_size);
50 
51 #else
52     PANGOLIN_UNUSED(image);
53     PANGOLIN_UNUSED(fmt);
54     PANGOLIN_UNUSED(out);
55     PANGOLIN_UNUSED(compression_level);
56     throw std::runtime_error("Rebuild Pangolin for LZ4 support.");
57 #endif // HAVE_LZ4
58 }
59 
LoadLz4(std::istream & in)60 TypedImage LoadLz4(std::istream& in)
61 {
62 #ifdef HAVE_LZ4
63     // Read in header, uncompressed
64     lz4_image_header header;
65     in.read( (char*)&header, sizeof(header));
66 
67     TypedImage img(header.w, header.h, PixelFormatFromString(header.fmt));
68     std::unique_ptr<char[]> input_buffer(new char[header.compressed_size]);
69 
70     in.read(input_buffer.get(), header.compressed_size);
71     const int decompressed_size = LZ4_decompress_safe(input_buffer.get(), (char*)img.ptr, header.compressed_size, img.SizeBytes());
72     if (decompressed_size < 0)
73         throw std::runtime_error(FormatString("A negative result from LZ4_decompress_safe indicates a failure trying to decompress the data.  See exit code (%) for value returned.", decompressed_size));
74       if (decompressed_size == 0)
75         throw std::runtime_error("I'm not sure this function can ever return 0.  Documentation in lz4.h doesn't indicate so.");
76     if (decompressed_size != (int)img.SizeBytes())
77         throw std::runtime_error(FormatString("decompressed size % is not equal to predicted size %", decompressed_size, img.SizeBytes()));
78 
79     return img;
80 #else
81     PANGOLIN_UNUSED(in);
82     throw std::runtime_error("Rebuild Pangolin for LZ4 support.");
83 #endif // HAVE_LZ4
84 }
85 
86 }
87