1 | // Aseprite |
2 | // Copyright (C) 2001-2018 David Capello |
3 | // |
4 | // This program is distributed under the terms of |
5 | // the End-User License Agreement for Aseprite. |
6 | |
7 | #ifdef HAVE_CONFIG_H |
8 | #include "config.h" |
9 | #endif |
10 | |
11 | #include "base/cfile.h" |
12 | #include "base/file_handle.h" |
13 | #include "doc/color_scales.h" |
14 | #include "doc/image.h" |
15 | #include "doc/palette.h" |
16 | #include "doc/primitives.h" |
17 | |
18 | #include <cstdio> |
19 | #include <memory> |
20 | |
21 | namespace app { |
22 | |
23 | using namespace doc; |
24 | |
25 | // Loads a PIC file (Animator and Animator Pro format) |
26 | Image* load_pic_file(const char* filename, int* x, int* y, Palette** palette) |
27 | { |
28 | std::unique_ptr<Image> image; |
29 | int size, compression; |
30 | int block_size; |
31 | int block_type; |
32 | int version; |
33 | int r, g, b; |
34 | int c, u, v; |
35 | int magic; |
36 | int w, h; |
37 | int byte; |
38 | int bpp; |
39 | |
40 | base::FileHandle handle(base::open_file_with_exception(filename, "rb" )); |
41 | FILE* f = handle.get(); |
42 | |
43 | // Animator format? |
44 | magic = base::fgetw(f); |
45 | if (magic == 0x9119) { |
46 | // Read Animator PIC file |
47 | w = base::fgetw(f); // width |
48 | h = base::fgetw(f); // height |
49 | *x = ((short)base::fgetw(f)); // X offset |
50 | *y = ((short)base::fgetw(f)); // Y offset |
51 | bpp = std::fgetc(f); // bits per pixel (must be 8) |
52 | compression = std::fgetc(f); // compression flag (must be 0) |
53 | base::fgetl(f); // image size (in bytes) |
54 | std::fgetc(f); // reserved |
55 | |
56 | if (bpp != 8 || compression != 0) { |
57 | return NULL; |
58 | } |
59 | |
60 | // Read palette (RGB in 0-63) |
61 | if (palette) { |
62 | *palette = new Palette(frame_t(0), 256); |
63 | } |
64 | for (c=0; c<256; c++) { |
65 | r = std::fgetc(f); |
66 | g = std::fgetc(f); |
67 | b = std::fgetc(f); |
68 | if (palette) |
69 | (*palette)->setEntry(c, rgba( |
70 | scale_6bits_to_8bits(r), |
71 | scale_6bits_to_8bits(g), |
72 | scale_6bits_to_8bits(b), 255)); |
73 | } |
74 | |
75 | // Read image |
76 | image.reset(Image::create(IMAGE_INDEXED, w, h)); |
77 | |
78 | for (v=0; v<h; v++) |
79 | for (u=0; u<w; u++) |
80 | image->putPixel(u, v, std::fgetc(f)); |
81 | |
82 | return image.release(); |
83 | } |
84 | |
85 | // rewind |
86 | handle.reset(); |
87 | handle = base::open_file_with_exception(filename, "rb" ); |
88 | f = handle.get(); |
89 | |
90 | // read a PIC/MSK Animator Pro file |
91 | size = base::fgetl(f); // file size |
92 | magic = base::fgetw(f); // magic number 9500h |
93 | if (magic != 0x9500) |
94 | return NULL; |
95 | |
96 | w = base::fgetw(f); // width |
97 | h = base::fgetw(f); // height |
98 | *x = base::fgetw(f); // X offset |
99 | *y = base::fgetw(f); // Y offset |
100 | base::fgetl(f); // user ID, is 0 |
101 | bpp = std::fgetc(f); // bits per pixel |
102 | |
103 | if ((bpp != 1 && bpp != 8) || (w<1) || (h<1) || (w>9999) || (h>9999)) |
104 | return NULL; |
105 | |
106 | // Skip reserved data |
107 | for (c=0; c<45; c++) |
108 | std::fgetc(f); |
109 | |
110 | size -= 64; // The header uses 64 bytes |
111 | |
112 | image.reset(Image::create(bpp == 8 ? IMAGE_INDEXED: IMAGE_BITMAP, w, h)); |
113 | |
114 | /* read blocks to end of file */ |
115 | while (size > 0) { |
116 | block_size = base::fgetl(f); |
117 | block_type = base::fgetw(f); |
118 | |
119 | switch (block_type) { |
120 | |
121 | // Color palette info |
122 | case 0: |
123 | version = base::fgetw(f); // Palette version |
124 | if (version != 0) |
125 | return NULL; |
126 | |
127 | // 256 RGB entries in 0-255 format |
128 | for (c=0; c<256; c++) { |
129 | r = std::fgetc(f); |
130 | g = std::fgetc(f); |
131 | b = std::fgetc(f); |
132 | if (palette) |
133 | (*palette)->setEntry(c, rgba(r, g, b, 255)); |
134 | } |
135 | break; |
136 | |
137 | // Byte-per-pixel image data |
138 | case 1: |
139 | for (v=0; v<h; v++) |
140 | for (u=0; u<w; u++) |
141 | image->putPixel(u, v, std::fgetc(f)); |
142 | break; |
143 | |
144 | // Bit-per-pixel image data |
145 | case 2: |
146 | for (v=0; v<h; v++) |
147 | for (u=0; u<(w+7)/8; u++) { |
148 | byte = std::fgetc(f); |
149 | for (c=0; c<8; c++) |
150 | put_pixel(image.get(), u*8+c, v, byte & (1<<(7-c))); |
151 | } |
152 | break; |
153 | } |
154 | |
155 | size -= block_size; |
156 | } |
157 | |
158 | return image.release(); |
159 | } |
160 | |
161 | // Saves an Animator Pro PIC file |
162 | int save_pic_file(const char *filename, int x, int y, const Palette* palette, const Image* image) |
163 | { |
164 | int c, u, v, bpp, size, byte; |
165 | |
166 | if (image->pixelFormat() == IMAGE_INDEXED) |
167 | bpp = 8; |
168 | else if (image->pixelFormat() == IMAGE_BITMAP) |
169 | bpp = 1; |
170 | else |
171 | return -1; |
172 | |
173 | if ((bpp == 8) && (!palette)) |
174 | return -1; |
175 | |
176 | base::FileHandle handle(base::open_file_with_exception_sync_on_close(filename, "wb" )); |
177 | FILE* f = handle.get(); |
178 | |
179 | size = 64; |
180 | // Bit-per-pixel image data block |
181 | if (bpp == 1) |
182 | size += (4+2+((image->width()+7)/8)*image->height()); |
183 | // Color palette info + byte-per-pixel image data block |
184 | else |
185 | size += (4+2+2+256*3) + (4+2+image->width()*image->height()); |
186 | |
187 | base::fputl(size, f); /* file size */ |
188 | base::fputw(0x9500, f); /* magic number 9500h */ |
189 | base::fputw(image->width(), f); /* width */ |
190 | base::fputw(image->height(), f); /* height */ |
191 | base::fputw(x, f); /* X offset */ |
192 | base::fputw(y, f); /* Y offset */ |
193 | base::fputl(0, f); /* user ID, is 0 */ |
194 | std::fputc(bpp, f); /* bits per pixel */ |
195 | |
196 | // Reserved data |
197 | for (c=0; c<45; c++) |
198 | std::fputc(0, f); |
199 | |
200 | // 1 bpp |
201 | if (bpp == 1) { |
202 | // Bit-per-data image data block |
203 | base::fputl((4+2+((image->width()+7)/8)*image->height()), f); // Block size |
204 | base::fputw(2, f); // Block type |
205 | for (v=0; v<image->height(); v++) // Image data |
206 | for (u=0; u<(image->width()+7)/8; u++) { |
207 | byte = 0; |
208 | for (c=0; c<8; c++) |
209 | if (get_pixel(image, u*8+c, v)) |
210 | byte |= (1<<(7-c)); |
211 | std::fputc(byte, f); |
212 | } |
213 | } |
214 | // 8 bpp |
215 | else { |
216 | ASSERT(palette); |
217 | |
218 | // Color palette info |
219 | base::fputl((4+2+2+256*3), f); // Block size |
220 | base::fputw(0, f); // Block type |
221 | base::fputw(0, f); // Version |
222 | for (c=0; c<256; c++) { // 256 palette entries |
223 | color_t color = palette->getEntry(c); |
224 | std::fputc(rgba_getr(color), f); |
225 | std::fputc(rgba_getg(color), f); |
226 | std::fputc(rgba_getb(color), f); |
227 | } |
228 | |
229 | // Pixel-per-data image data block |
230 | base::fputl((4+2+image->width()*image->height()), f); // Block size |
231 | base::fputw(1, f); // Block type |
232 | for (v=0; v<image->height(); v++) // Image data |
233 | for (u=0; u<image->width(); u++) |
234 | std::fputc(image->getPixel(u, v), f); |
235 | } |
236 | |
237 | return 0; |
238 | } |
239 | |
240 | } // namespace app |
241 | |