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
21namespace app {
22
23using namespace doc;
24
25// Loads a PIC file (Animator and Animator Pro format)
26Image* 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
162int 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