1// Aseprite Document Library
2// Copyright (c) 2019-2020 Igara Studio S.A.
3// Copyright (c) 2001-2018 David Capello
4//
5// This file is released under the terms of the MIT license.
6// Read LICENSE.txt for more information.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "doc/image_io.h"
13
14#include "base/exception.h"
15#include "base/serialization.h"
16#include "doc/cancel_io.h"
17#include "doc/image.h"
18#include "zlib.h"
19
20#include <algorithm>
21#include <iostream>
22#include <memory>
23
24namespace doc {
25
26using namespace base::serialization;
27using namespace base::serialization::little_endian;
28
29// TODO Create a zlib wrapper for iostreams
30
31bool write_image(std::ostream& os, const Image* image, CancelIO* cancel)
32{
33 write32(os, image->id());
34 write8(os, image->pixelFormat()); // Pixel format
35 write16(os, image->width()); // Width
36 write16(os, image->height()); // Height
37 write32(os, image->maskColor()); // Mask color
38
39 int rowSize = image->getRowStrideSize();
40#if 0
41 {
42 for (int c=0; c<image->height(); c++)
43 os.write((char*)image->getPixelAddress(0, c), rowSize);
44 }
45#else
46 {
47 std::ostream::pos_type total_output_pos = os.tellp();
48 write32(os, 0); // Compressed size (we update this value later)
49
50 z_stream zstream;
51 zstream.zalloc = (alloc_func)0;
52 zstream.zfree = (free_func)0;
53 zstream.opaque = (voidpf)0;
54 int err = deflateInit(&zstream, Z_DEFAULT_COMPRESSION);
55 if (err != Z_OK)
56 throw base::Exception("ZLib error %d in deflateInit().", err);
57
58 std::vector<uint8_t> compressed(4096);
59 int total_output_bytes = 0;
60
61 for (int y=0; y<image->height(); y++) {
62 if (cancel && cancel->isCanceled()) {
63 deflateEnd(&zstream);
64 return false;
65 }
66
67 zstream.next_in = (Bytef*)image->getPixelAddress(0, y);
68 zstream.avail_in = rowSize;
69 int flush = (y == image->height()-1 ? Z_FINISH: Z_NO_FLUSH);
70
71 do {
72 zstream.next_out = (Bytef*)&compressed[0];
73 zstream.avail_out = compressed.size();
74
75 // Compress
76 err = deflate(&zstream, flush);
77 if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR)
78 throw base::Exception("ZLib error %d in deflate().", err);
79
80 int output_bytes = compressed.size() - zstream.avail_out;
81 if (output_bytes > 0) {
82 if (os.write((char*)&compressed[0], output_bytes).fail())
83 throw base::Exception("Error writing compressed image pixels.\n");
84
85 total_output_bytes += output_bytes;
86 }
87 } while (zstream.avail_out == 0);
88 }
89
90 err = deflateEnd(&zstream);
91 if (err != Z_OK)
92 throw base::Exception("ZLib error %d in deflateEnd().", err);
93
94 std::ostream::pos_type bak = os.tellp();
95 os.seekp(total_output_pos);
96 write32(os, total_output_bytes);
97 os.seekp(bak);
98 }
99#endif
100 return true;
101}
102
103Image* read_image(std::istream& is, bool setId)
104{
105 ObjectId id = read32(is);
106 int pixelFormat = read8(is); // Pixel format
107 int width = read16(is); // Width
108 int height = read16(is); // Height
109 uint32_t maskColor = read32(is); // Mask color
110
111 if ((pixelFormat != IMAGE_RGB &&
112 pixelFormat != IMAGE_GRAYSCALE &&
113 pixelFormat != IMAGE_INDEXED &&
114 pixelFormat != IMAGE_BITMAP &&
115 pixelFormat != IMAGE_TILEMAP) ||
116 (width < 1 || height < 1) ||
117 (width > 0xfffff || height > 0xfffff))
118 return nullptr;
119
120 std::unique_ptr<Image> image(
121 Image::create(static_cast<PixelFormat>(pixelFormat), width, height));
122 int rowSize = image->getRowStrideSize();
123
124#if 0
125 {
126 for (int c=0; c<image->height(); c++)
127 is.read((char*)image->getPixelAddress(0, c), rowSize);
128 }
129#else
130 {
131 int avail_bytes = read32(is);
132
133 z_stream zstream;
134 zstream.zalloc = (alloc_func)0;
135 zstream.zfree = (free_func)0;
136 zstream.opaque = (voidpf)0;
137
138 int err = inflateInit(&zstream);
139 if (err != Z_OK)
140 throw base::Exception("ZLib error %d in inflateInit().", err);
141
142 int uncompressed_size = image->height() * rowSize;
143 int uncompressed_offset = 0;
144 int remain = avail_bytes;
145
146 std::vector<uint8_t> compressed(4096);
147 uint8_t* address = image->getPixelAddress(0, 0);
148 uint8_t* address_end = image->getPixelAddress(0, 0) + uncompressed_size;
149
150 while (remain > 0) {
151 int len = std::min(remain, int(compressed.size()));
152 if (is.read((char*)&compressed[0], len).fail()) {
153 ASSERT(false);
154 throw base::Exception("Error reading stream to restore image");
155 }
156
157 int bytes_read = (int)is.gcount();
158 if (bytes_read == 0) {
159 ASSERT(remain == 0);
160 break;
161 }
162
163 remain -= bytes_read;
164
165 zstream.next_in = (Bytef*)&compressed[0];
166 zstream.avail_in = (uInt)bytes_read;
167
168 do {
169 zstream.next_out = (Bytef*)address;
170 zstream.avail_out = address_end - address;
171
172 err = inflate(&zstream, Z_NO_FLUSH);
173 if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR)
174 throw base::Exception("ZLib error %d in inflate().", err);
175
176 int uncompressed_bytes = (int)((address_end - address) - zstream.avail_out);
177 if (uncompressed_bytes > 0) {
178 if (uncompressed_offset+uncompressed_bytes > uncompressed_size)
179 throw base::Exception("Bad compressed image.");
180
181 uncompressed_offset += uncompressed_bytes;
182 address += uncompressed_bytes;
183 }
184 } while (zstream.avail_in != 0 && zstream.avail_out == 0);
185 }
186
187 err = inflateEnd(&zstream);
188 if (err != Z_OK)
189 throw base::Exception("ZLib error %d in inflateEnd().", err);
190 }
191#endif
192
193 image->setMaskColor(maskColor);
194 if (setId)
195 image->setId(id);
196 return image.release();
197}
198
199}
200