1 | // Aseprite Document IO Library |
2 | // Copyright (c) 2018-2022 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 "dio/aseprite_decoder.h" |
13 | |
14 | #include "base/cfile.h" |
15 | #include "base/exception.h" |
16 | #include "base/file_handle.h" |
17 | #include "base/fs.h" |
18 | #include "dio/aseprite_common.h" |
19 | #include "dio/decode_delegate.h" |
20 | #include "dio/file_interface.h" |
21 | #include "dio/pixel_io.h" |
22 | #include "doc/doc.h" |
23 | #include "doc/user_data.h" |
24 | #include "doc/util.h" |
25 | #include "fixmath/fixmath.h" |
26 | #include "fmt/format.h" |
27 | #include "gfx/color_space.h" |
28 | #include "zlib.h" |
29 | |
30 | #include <cstdio> |
31 | |
32 | namespace dio { |
33 | |
34 | bool AsepriteDecoder::decode() |
35 | { |
36 | bool ignore_old_color_chunks = false; |
37 | |
38 | AsepriteHeader ; |
39 | if (!readHeader(&header)) { |
40 | delegate()->error("Error reading header" ); |
41 | return false; |
42 | } |
43 | |
44 | if (header.depth != 32 && |
45 | header.depth != 16 && |
46 | header.depth != 8) { |
47 | delegate()->error( |
48 | fmt::format("Invalid color depth {0}" , |
49 | header.depth)); |
50 | return false; |
51 | } |
52 | |
53 | if (header.width < 1 || header.height < 1) { |
54 | delegate()->error( |
55 | fmt::format("Invalid sprite size {0}x{1}" , |
56 | header.width, header.height)); |
57 | return false; |
58 | } |
59 | |
60 | // Create the new sprite |
61 | std::unique_ptr<doc::Sprite> sprite( |
62 | new doc::Sprite(doc::ImageSpec(header.depth == 32 ? doc::ColorMode::RGB: |
63 | header.depth == 16 ? doc::ColorMode::GRAYSCALE: |
64 | doc::ColorMode::INDEXED, |
65 | header.width, |
66 | header.height), |
67 | header.ncolors)); |
68 | |
69 | // Set frames and speed |
70 | sprite->setTotalFrames(doc::frame_t(header.frames)); |
71 | sprite->setDurationForAllFrames(header.speed); |
72 | |
73 | // Set transparent entry |
74 | sprite->setTransparentColor(header.transparent_index); |
75 | |
76 | // Set pixel ratio |
77 | sprite->setPixelRatio(doc::PixelRatio(header.pixel_width, header.pixel_height)); |
78 | |
79 | // Set grid bounds |
80 | sprite->setGridBounds(gfx::Rect(header.grid_x, header.grid_y, |
81 | header.grid_width, header.grid_height)); |
82 | |
83 | // Prepare variables for layer chunks |
84 | doc::Layer* last_layer = sprite->root(); |
85 | doc::WithUserData* last_object_with_user_data = sprite.get(); |
86 | doc::Cel* last_cel = nullptr; |
87 | auto tag_it = sprite->tags().begin(); |
88 | auto tag_end = sprite->tags().end(); |
89 | |
90 | m_allLayers.clear(); |
91 | |
92 | int current_level = -1; |
93 | AsepriteExternalFiles extFiles; |
94 | |
95 | // Just one frame? |
96 | doc::frame_t nframes = sprite->totalFrames(); |
97 | if (nframes > 1 && delegate()->decodeOneFrame()) |
98 | nframes = 1; |
99 | |
100 | // Read frame by frame to end-of-file |
101 | for (doc::frame_t frame=0; frame<nframes; ++frame) { |
102 | // Start frame position |
103 | size_t frame_pos = f()->tell(); |
104 | delegate()->progress((float)frame_pos / (float)header.size); |
105 | |
106 | // Read frame header |
107 | AsepriteFrameHeader ; |
108 | readFrameHeader(&frame_header); |
109 | |
110 | // Correct frame type |
111 | if (frame_header.magic == ASE_FILE_FRAME_MAGIC) { |
112 | // Use frame-duration field? |
113 | if (frame_header.duration > 0) |
114 | sprite->setFrameDuration(frame, frame_header.duration); |
115 | |
116 | // Read chunks |
117 | for (uint32_t c=0; c<frame_header.chunks; c++) { |
118 | // Start chunk position |
119 | size_t chunk_pos = f()->tell(); |
120 | delegate()->progress((float)chunk_pos / (float)header.size); |
121 | |
122 | // Read chunk information |
123 | int chunk_size = read32(); |
124 | int chunk_type = read16(); |
125 | |
126 | switch (chunk_type) { |
127 | |
128 | case ASE_FILE_CHUNK_FLI_COLOR: |
129 | case ASE_FILE_CHUNK_FLI_COLOR2: |
130 | if (!ignore_old_color_chunks) { |
131 | doc::Palette* prevPal = sprite->palette(frame); |
132 | std::unique_ptr<doc::Palette> pal( |
133 | chunk_type == ASE_FILE_CHUNK_FLI_COLOR ? |
134 | readColorChunk(prevPal, frame): |
135 | readColor2Chunk(prevPal, frame)); |
136 | |
137 | if (prevPal->countDiff(pal.get(), NULL, NULL) > 0) |
138 | sprite->setPalette(pal.get(), true); |
139 | } |
140 | break; |
141 | |
142 | case ASE_FILE_CHUNK_PALETTE: { |
143 | doc::Palette* prevPal = sprite->palette(frame); |
144 | std::unique_ptr<doc::Palette> pal( |
145 | readPaletteChunk(prevPal, frame)); |
146 | |
147 | if (prevPal->countDiff(pal.get(), NULL, NULL) > 0) |
148 | sprite->setPalette(pal.get(), true); |
149 | |
150 | ignore_old_color_chunks = true; |
151 | break; |
152 | } |
153 | |
154 | case ASE_FILE_CHUNK_LAYER: { |
155 | doc::Layer* newLayer = |
156 | readLayerChunk(&header, sprite.get(), |
157 | &last_layer, |
158 | ¤t_level); |
159 | if (newLayer) { |
160 | m_allLayers.push_back(newLayer); |
161 | last_object_with_user_data = newLayer; |
162 | } |
163 | break; |
164 | } |
165 | |
166 | case ASE_FILE_CHUNK_CEL: { |
167 | doc::Cel* cel = |
168 | readCelChunk(sprite.get(), frame, |
169 | sprite->pixelFormat(), &header, |
170 | chunk_pos+chunk_size); |
171 | if (cel) { |
172 | last_cel = cel; |
173 | last_object_with_user_data = cel->data(); |
174 | } |
175 | break; |
176 | } |
177 | |
178 | case ASE_FILE_CHUNK_CEL_EXTRA: { |
179 | if (last_cel) |
180 | readCelExtraChunk(last_cel); |
181 | break; |
182 | } |
183 | |
184 | case ASE_FILE_CHUNK_COLOR_PROFILE: { |
185 | readColorProfile(sprite.get()); |
186 | break; |
187 | } |
188 | |
189 | case ASE_FILE_CHUNK_EXTERNAL_FILE: |
190 | readExternalFiles(extFiles); |
191 | break; |
192 | |
193 | case ASE_FILE_CHUNK_MASK: { |
194 | doc::Mask* mask = readMaskChunk(); |
195 | if (mask) |
196 | delete mask; // TODO add the mask in some place? |
197 | else |
198 | delegate()->error("Warning: Cannot load a mask chunk" ); |
199 | break; |
200 | } |
201 | |
202 | case ASE_FILE_CHUNK_PATH: |
203 | // Ignore |
204 | break; |
205 | |
206 | case ASE_FILE_CHUNK_TAGS: |
207 | readTagsChunk(&sprite->tags()); |
208 | tag_it = sprite->tags().begin(); |
209 | tag_end = sprite->tags().end(); |
210 | |
211 | if (tag_it != tag_end) |
212 | last_object_with_user_data = *tag_it; |
213 | else |
214 | last_object_with_user_data = nullptr; |
215 | break; |
216 | |
217 | case ASE_FILE_CHUNK_SLICES: { |
218 | readSlicesChunk(sprite->slices()); |
219 | break; |
220 | } |
221 | |
222 | case ASE_FILE_CHUNK_SLICE: { |
223 | doc::Slice* slice = readSliceChunk(sprite->slices()); |
224 | if (slice) |
225 | last_object_with_user_data = slice; |
226 | break; |
227 | } |
228 | |
229 | case ASE_FILE_CHUNK_USER_DATA: { |
230 | doc::UserData userData; |
231 | readUserDataChunk(&userData); |
232 | |
233 | if (last_object_with_user_data) { |
234 | last_object_with_user_data->setUserData(userData); |
235 | |
236 | if (last_object_with_user_data->type() == doc::ObjectType::Tag) { |
237 | // Tags are a special case, user data for tags come |
238 | // all together (one next to other) after the tags |
239 | // chunk, in the same order: |
240 | // |
241 | // * TAGS CHUNK (TAG1, TAG2, ..., TAGn) |
242 | // * USER DATA CHUNK FOR TAG1 |
243 | // * USER DATA CHUNK FOR TAG2 |
244 | // * ... |
245 | // * USER DATA CHUNK FOR TAGn |
246 | // |
247 | // So here we expect that the next user data chunk |
248 | // will correspond to the next tag in the tags |
249 | // collection. |
250 | ++tag_it; |
251 | |
252 | if (tag_it != tag_end) |
253 | last_object_with_user_data = *tag_it; |
254 | else |
255 | last_object_with_user_data = nullptr; |
256 | } |
257 | } |
258 | break; |
259 | } |
260 | |
261 | case ASE_FILE_CHUNK_TILESET: { |
262 | readTilesetChunk(sprite.get(), &header, extFiles); |
263 | break; |
264 | } |
265 | |
266 | default: |
267 | delegate()->error( |
268 | fmt::format("Warning: Unsupported chunk type {0} (skipping)" , chunk_type)); |
269 | break; |
270 | } |
271 | |
272 | // Skip chunk size |
273 | f()->seek(chunk_pos+chunk_size); |
274 | } |
275 | } |
276 | |
277 | // Skip frame size |
278 | f()->seek(frame_pos+frame_header.size); |
279 | |
280 | if (delegate()->isCanceled()) |
281 | break; |
282 | } |
283 | |
284 | delegate()->onSprite(sprite.release()); |
285 | return true; |
286 | } |
287 | |
288 | bool AsepriteDecoder::(AsepriteHeader* ) |
289 | { |
290 | size_t = f()->tell(); |
291 | |
292 | header->size = read32(); |
293 | header->magic = read16(); |
294 | |
295 | // Developers can open any .ase file |
296 | #if !defined(ENABLE_DEVMODE) |
297 | if (header->magic != ASE_FILE_MAGIC) |
298 | return false; |
299 | #endif |
300 | |
301 | header->frames = read16(); |
302 | header->width = read16(); |
303 | header->height = read16(); |
304 | header->depth = read16(); |
305 | header->flags = read32(); |
306 | header->speed = read16(); |
307 | header->next = read32(); |
308 | header->frit = read32(); |
309 | header->transparent_index = read8(); |
310 | header->ignore[0] = read8(); |
311 | header->ignore[1] = read8(); |
312 | header->ignore[2] = read8(); |
313 | header->ncolors = read16(); |
314 | header->pixel_width = read8(); |
315 | header->pixel_height = read8(); |
316 | header->grid_x = (int16_t)read16(); |
317 | header->grid_y = (int16_t)read16(); |
318 | header->grid_width = read16(); |
319 | header->grid_height = read16(); |
320 | |
321 | if (header->depth != 8) // Transparent index only valid for indexed images |
322 | header->transparent_index = 0; |
323 | |
324 | if (header->ncolors == 0) // 0 means 256 (old .ase files) |
325 | header->ncolors = 256; |
326 | |
327 | if (header->pixel_width == 0 || |
328 | header->pixel_height == 0) { |
329 | header->pixel_width = 1; |
330 | header->pixel_height = 1; |
331 | } |
332 | |
333 | #if defined(ENABLE_DEVMODE) |
334 | // This is useful to read broken .ase files |
335 | if (header->magic != ASE_FILE_MAGIC) { |
336 | header->frames = 256; // Frames number might be not enought for some files |
337 | header->width = 1024; // Size doesn't matter, the sprite can be crop |
338 | header->height = 1024; |
339 | } |
340 | #endif |
341 | |
342 | f()->seek(headerPos+128); |
343 | return true; |
344 | } |
345 | |
346 | void AsepriteDecoder::(AsepriteFrameHeader* ) |
347 | { |
348 | frame_header->size = read32(); |
349 | frame_header->magic = read16(); |
350 | frame_header->chunks = read16(); |
351 | frame_header->duration = read16(); |
352 | readPadding(2); |
353 | uint32_t nchunks = read32(); |
354 | |
355 | if (frame_header->chunks == 0xFFFF && |
356 | frame_header->chunks < nchunks) |
357 | frame_header->chunks = nchunks; |
358 | } |
359 | |
360 | void AsepriteDecoder::readPadding(int bytes) |
361 | { |
362 | for (int c=0; c<bytes; c++) |
363 | read8(); |
364 | } |
365 | |
366 | std::string AsepriteDecoder::readString() |
367 | { |
368 | int length = read16(); |
369 | if (length == EOF) |
370 | return "" ; |
371 | |
372 | std::string string; |
373 | string.reserve(length+1); |
374 | |
375 | for (int c=0; c<length; c++) |
376 | string.push_back(read8()); |
377 | |
378 | return string; |
379 | } |
380 | |
381 | doc::Palette* AsepriteDecoder::readColorChunk(doc::Palette* prevPal, |
382 | doc::frame_t frame) |
383 | { |
384 | int i, c, r, g, b, packets, skip, size; |
385 | doc::Palette* pal = new doc::Palette(*prevPal); |
386 | pal->setFrame(frame); |
387 | |
388 | packets = read16(); // Number of packets |
389 | skip = 0; |
390 | |
391 | // Read all packets |
392 | for (i=0; i<packets; i++) { |
393 | skip += read8(); |
394 | size = read8(); |
395 | if (!size) size = 256; |
396 | |
397 | for (c=skip; c<skip+size; c++) { |
398 | r = read8(); |
399 | g = read8(); |
400 | b = read8(); |
401 | pal->setEntry(c, doc::rgba(doc::scale_6bits_to_8bits(r), |
402 | doc::scale_6bits_to_8bits(g), |
403 | doc::scale_6bits_to_8bits(b), 255)); |
404 | } |
405 | } |
406 | |
407 | return pal; |
408 | } |
409 | |
410 | doc::Palette* AsepriteDecoder::readColor2Chunk(doc::Palette* prevPal, |
411 | doc::frame_t frame) |
412 | { |
413 | int i, c, r, g, b, packets, skip, size; |
414 | doc::Palette* pal = new doc::Palette(*prevPal); |
415 | pal->setFrame(frame); |
416 | |
417 | packets = read16(); // Number of packets |
418 | skip = 0; |
419 | |
420 | // Read all packets |
421 | for (i=0; i<packets; i++) { |
422 | skip += read8(); |
423 | size = read8(); |
424 | if (!size) size = 256; |
425 | |
426 | for (c=skip; c<skip+size; c++) { |
427 | r = read8(); |
428 | g = read8(); |
429 | b = read8(); |
430 | pal->setEntry(c, doc::rgba(r, g, b, 255)); |
431 | } |
432 | } |
433 | |
434 | return pal; |
435 | } |
436 | |
437 | doc::Palette* AsepriteDecoder::readPaletteChunk(doc::Palette* prevPal, |
438 | doc::frame_t frame) |
439 | { |
440 | doc::Palette* pal = new doc::Palette(*prevPal); |
441 | pal->setFrame(frame); |
442 | |
443 | int newSize = read32(); |
444 | int from = read32(); |
445 | int to = read32(); |
446 | readPadding(8); |
447 | |
448 | if (newSize > 0) |
449 | pal->resize(newSize); |
450 | |
451 | for (int c=from; c<=to; ++c) { |
452 | int flags = read16(); |
453 | int r = read8(); |
454 | int g = read8(); |
455 | int b = read8(); |
456 | int a = read8(); |
457 | pal->setEntry(c, doc::rgba(r, g, b, a)); |
458 | |
459 | // Skip name |
460 | if (flags & ASE_PALETTE_FLAG_HAS_NAME) { |
461 | std::string name = readString(); |
462 | // Ignore color entry name |
463 | } |
464 | } |
465 | |
466 | return pal; |
467 | } |
468 | |
469 | doc::Layer* AsepriteDecoder::(AsepriteHeader* , |
470 | doc::Sprite* sprite, |
471 | doc::Layer** previous_layer, |
472 | int* current_level) |
473 | { |
474 | // Read chunk data |
475 | int flags = read16(); |
476 | int layer_type = read16(); |
477 | int child_level = read16(); |
478 | read16(); // default width |
479 | read16(); // default height |
480 | int blendmode = read16(); // blend mode |
481 | int opacity = read8(); // opacity |
482 | readPadding(3); |
483 | std::string name = readString(); |
484 | |
485 | doc::Layer* layer = nullptr; |
486 | switch (layer_type) { |
487 | |
488 | case ASE_FILE_LAYER_IMAGE: |
489 | layer = new doc::LayerImage(sprite); |
490 | break; |
491 | |
492 | case ASE_FILE_LAYER_GROUP: |
493 | layer = new doc::LayerGroup(sprite); |
494 | break; |
495 | |
496 | case ASE_FILE_LAYER_TILEMAP: { |
497 | doc::tileset_index tsi = read32(); |
498 | if (!sprite->tilesets()->get(tsi)) { |
499 | delegate()->error(fmt::format("Error: tileset {0} not found" , tsi)); |
500 | return nullptr; |
501 | } |
502 | layer = new doc::LayerTilemap(sprite, tsi); |
503 | break; |
504 | } |
505 | } |
506 | |
507 | if (layer) { |
508 | if (layer->isImage() && |
509 | // Only transparent layers can have blend mode and opacity |
510 | !(flags & int(doc::LayerFlags::Background))) { |
511 | static_cast<doc::LayerImage*>(layer)->setBlendMode((doc::BlendMode)blendmode); |
512 | if (header->flags & ASE_FILE_FLAG_LAYER_WITH_OPACITY) |
513 | static_cast<doc::LayerImage*>(layer)->setOpacity(opacity); |
514 | } |
515 | |
516 | // flags |
517 | layer->setFlags(static_cast<doc::LayerFlags>( |
518 | flags & |
519 | static_cast<int>(doc::LayerFlags::PersistentFlagsMask))); |
520 | |
521 | // name |
522 | layer->setName(name.c_str()); |
523 | |
524 | // Child level |
525 | if (child_level == *current_level) |
526 | (*previous_layer)->parent()->addLayer(layer); |
527 | else if (child_level > *current_level) |
528 | static_cast<doc::LayerGroup*>(*previous_layer)->addLayer(layer); |
529 | else if (child_level < *current_level) { |
530 | doc::LayerGroup* parent = (*previous_layer)->parent(); |
531 | ASSERT(parent); |
532 | if (parent) { |
533 | int levels = (*current_level - child_level); |
534 | while (levels--) { |
535 | ASSERT(parent->parent()); |
536 | if (!parent->parent()) |
537 | break; |
538 | parent = parent->parent(); |
539 | } |
540 | parent->addLayer(layer); |
541 | } |
542 | } |
543 | |
544 | *previous_layer = layer; |
545 | *current_level = child_level; |
546 | } |
547 | |
548 | return layer; |
549 | } |
550 | |
551 | ////////////////////////////////////////////////////////////////////// |
552 | // Raw Image |
553 | ////////////////////////////////////////////////////////////////////// |
554 | |
555 | namespace { |
556 | |
557 | template<typename ImageTraits> |
558 | void (FileInterface* f, |
559 | DecodeDelegate* delegate, |
560 | doc::Image* image, |
561 | const AsepriteHeader* ) |
562 | { |
563 | PixelIO<ImageTraits> pixel_io; |
564 | int x, y; |
565 | int w = image->width(); |
566 | int h = image->height(); |
567 | |
568 | for (y=0; y<h; ++y) { |
569 | for (x=0; x<w; ++x) { |
570 | doc::put_pixel_fast<ImageTraits>(image, x, y, pixel_io.read_pixel(f)); |
571 | } |
572 | delegate->progress((float)f->tell() / (float)header->size); |
573 | } |
574 | } |
575 | |
576 | ////////////////////////////////////////////////////////////////////// |
577 | // Compressed Image |
578 | ////////////////////////////////////////////////////////////////////// |
579 | |
580 | template<typename ImageTraits> |
581 | void (FileInterface* f, |
582 | DecodeDelegate* delegate, |
583 | doc::Image* image, |
584 | const AsepriteHeader* , |
585 | const size_t chunk_end) |
586 | { |
587 | PixelIO<ImageTraits> pixel_io; |
588 | z_stream zstream; |
589 | int y, err; |
590 | |
591 | zstream.zalloc = (alloc_func)0; |
592 | zstream.zfree = (free_func)0; |
593 | zstream.opaque = (voidpf)0; |
594 | |
595 | err = inflateInit(&zstream); |
596 | if (err != Z_OK) |
597 | throw base::Exception("ZLib error %d in inflateInit()." , err); |
598 | |
599 | std::vector<uint8_t> scanline(ImageTraits::getRowStrideBytes(image->width())); |
600 | std::vector<uint8_t> uncompressed(image->height() * ImageTraits::getRowStrideBytes(image->width())); |
601 | std::vector<uint8_t> compressed(4096); |
602 | int uncompressed_offset = 0; |
603 | |
604 | while (true) { |
605 | size_t input_bytes; |
606 | |
607 | if (f->tell()+compressed.size() > chunk_end) { |
608 | input_bytes = chunk_end - f->tell(); // Remaining bytes |
609 | ASSERT(input_bytes < compressed.size()); |
610 | |
611 | if (input_bytes == 0) |
612 | break; // Done, we consumed all chunk |
613 | } |
614 | else { |
615 | input_bytes = compressed.size(); |
616 | } |
617 | |
618 | size_t bytes_read = f->readBytes(&compressed[0], input_bytes); |
619 | |
620 | // Error reading "input_bytes" bytes, broken file? chunk without |
621 | // enough compressed data? |
622 | if (bytes_read == 0) { |
623 | delegate->error( |
624 | fmt::format("Error reading {} bytes of compressed data" , |
625 | input_bytes)); |
626 | break; |
627 | } |
628 | |
629 | zstream.next_in = (Bytef*)&compressed[0]; |
630 | zstream.avail_in = bytes_read; |
631 | |
632 | do { |
633 | zstream.next_out = (Bytef*)&scanline[0]; |
634 | zstream.avail_out = scanline.size(); |
635 | |
636 | err = inflate(&zstream, Z_NO_FLUSH); |
637 | if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) |
638 | throw base::Exception("ZLib error %d in inflate()." , err); |
639 | |
640 | size_t uncompressed_bytes = scanline.size() - zstream.avail_out; |
641 | if (uncompressed_bytes > 0) { |
642 | if (uncompressed_offset+uncompressed_bytes > uncompressed.size()) |
643 | throw base::Exception("Bad compressed image." ); |
644 | |
645 | std::copy(scanline.begin(), scanline.begin()+uncompressed_bytes, |
646 | uncompressed.begin()+uncompressed_offset); |
647 | |
648 | uncompressed_offset += uncompressed_bytes; |
649 | } |
650 | } while (zstream.avail_out == 0); |
651 | |
652 | delegate->progress((float)f->tell() / (float)header->size); |
653 | } |
654 | |
655 | uncompressed_offset = 0; |
656 | for (y=0; y<image->height(); y++) { |
657 | typename ImageTraits::address_t address = |
658 | (typename ImageTraits::address_t)image->getPixelAddress(0, y); |
659 | |
660 | pixel_io.read_scanline(address, image->width(), &uncompressed[uncompressed_offset]); |
661 | |
662 | uncompressed_offset += ImageTraits::getRowStrideBytes(image->width()); |
663 | } |
664 | |
665 | err = inflateEnd(&zstream); |
666 | if (err != Z_OK) |
667 | throw base::Exception("ZLib error %d in inflateEnd()." , err); |
668 | } |
669 | |
670 | void (FileInterface* f, |
671 | DecodeDelegate* delegate, |
672 | doc::Image* image, |
673 | const AsepriteHeader* , |
674 | const size_t chunk_end) |
675 | { |
676 | // Try to read pixel data |
677 | try { |
678 | switch (image->pixelFormat()) { |
679 | |
680 | case doc::IMAGE_RGB: |
681 | read_compressed_image_templ<doc::RgbTraits>( |
682 | f, delegate, image, header, chunk_end); |
683 | break; |
684 | |
685 | case doc::IMAGE_GRAYSCALE: |
686 | read_compressed_image_templ<doc::GrayscaleTraits>( |
687 | f, delegate, image, header, chunk_end); |
688 | break; |
689 | |
690 | case doc::IMAGE_INDEXED: |
691 | read_compressed_image_templ<doc::IndexedTraits>( |
692 | f, delegate, image, header, chunk_end); |
693 | break; |
694 | |
695 | case doc::IMAGE_TILEMAP: |
696 | read_compressed_image_templ<doc::TilemapTraits>( |
697 | f, delegate, image, header, chunk_end); |
698 | break; |
699 | } |
700 | } |
701 | // OK, in case of error we can show the problem, but continue |
702 | // loading more cels. |
703 | catch (const std::exception& e) { |
704 | delegate->error(e.what()); |
705 | } |
706 | } |
707 | |
708 | } // anonymous namespace |
709 | |
710 | ////////////////////////////////////////////////////////////////////// |
711 | // Cel Chunk |
712 | ////////////////////////////////////////////////////////////////////// |
713 | |
714 | doc::Cel* AsepriteDecoder::(doc::Sprite* sprite, |
715 | doc::frame_t frame, |
716 | doc::PixelFormat pixelFormat, |
717 | const AsepriteHeader* , |
718 | const size_t chunk_end) |
719 | { |
720 | // Read chunk data |
721 | doc::layer_t layer_index = read16(); |
722 | int x = ((int16_t)read16()); |
723 | int y = ((int16_t)read16()); |
724 | int opacity = read8(); |
725 | int cel_type = read16(); |
726 | readPadding(7); |
727 | |
728 | doc::Layer* layer = nullptr; |
729 | if (layer_index >= 0 && layer_index < doc::layer_t(m_allLayers.size())) |
730 | layer = m_allLayers[layer_index]; |
731 | |
732 | if (!layer) { |
733 | delegate()->error( |
734 | fmt::format("Frame {0} didn't found layer with index {1}" , |
735 | (int)frame, (int)layer_index)); |
736 | return nullptr; |
737 | } |
738 | if (!layer->isImage()) { |
739 | delegate()->error( |
740 | fmt::format("Invalid .ase file (frame {0} in layer {1} which does not contain images" , |
741 | (int)frame, (int)layer_index)); |
742 | return nullptr; |
743 | } |
744 | |
745 | // Create the new frame. |
746 | std::unique_ptr<doc::Cel> cel; |
747 | |
748 | switch (cel_type) { |
749 | |
750 | case ASE_FILE_RAW_CEL: { |
751 | // Read width and height |
752 | int w = read16(); |
753 | int h = read16(); |
754 | |
755 | if (w > 0 && h > 0) { |
756 | doc::ImageRef image(doc::Image::create(pixelFormat, w, h)); |
757 | |
758 | // Read pixel data |
759 | switch (image->pixelFormat()) { |
760 | |
761 | case doc::IMAGE_RGB: |
762 | read_raw_image<doc::RgbTraits>(f(), delegate(), image.get(), header); |
763 | break; |
764 | |
765 | case doc::IMAGE_GRAYSCALE: |
766 | read_raw_image<doc::GrayscaleTraits>(f(), delegate(), image.get(), header); |
767 | break; |
768 | |
769 | case doc::IMAGE_INDEXED: |
770 | read_raw_image<doc::IndexedTraits>(f(), delegate(), image.get(), header); |
771 | break; |
772 | } |
773 | |
774 | cel.reset(new doc::Cel(frame, image)); |
775 | cel->setPosition(x, y); |
776 | cel->setOpacity(opacity); |
777 | } |
778 | break; |
779 | } |
780 | |
781 | case ASE_FILE_LINK_CEL: { |
782 | // Read link position |
783 | doc::frame_t link_frame = doc::frame_t(read16()); |
784 | doc::Cel* link = layer->cel(link_frame); |
785 | |
786 | if (link) { |
787 | // There were a beta version that allow to the user specify |
788 | // different X, Y, or opacity per link, in that case we must |
789 | // create a copy. |
790 | if (link->x() == x && link->y() == y && link->opacity() == opacity) { |
791 | cel.reset(doc::Cel::MakeLink(frame, link)); |
792 | } |
793 | else { |
794 | cel.reset(doc::Cel::MakeCopy(frame, link)); |
795 | cel->setPosition(x, y); |
796 | cel->setOpacity(opacity); |
797 | } |
798 | } |
799 | else { |
800 | // Linked cel doesn't found |
801 | return nullptr; |
802 | } |
803 | break; |
804 | } |
805 | |
806 | case ASE_FILE_COMPRESSED_CEL: { |
807 | // Read width and height |
808 | int w = read16(); |
809 | int h = read16(); |
810 | |
811 | if (w > 0 && h > 0) { |
812 | doc::ImageRef image(doc::Image::create(pixelFormat, w, h)); |
813 | read_compressed_image(f(), delegate(), image.get(), header, chunk_end); |
814 | |
815 | cel.reset(new doc::Cel(frame, image)); |
816 | cel->setPosition(x, y); |
817 | cel->setOpacity(opacity); |
818 | } |
819 | break; |
820 | } |
821 | |
822 | case ASE_FILE_COMPRESSED_TILEMAP: { |
823 | // Read width and height |
824 | int w = read16(); |
825 | int h = read16(); |
826 | int bitsPerTile = read16(); // TODO add support for more bpp |
827 | uint32_t tileIDMask = read32(); |
828 | uint32_t flipxMask = read32(); |
829 | uint32_t flipyMask = read32(); |
830 | uint32_t rot90Mask = read32(); |
831 | uint32_t flagsMask = (flipxMask | flipyMask | rot90Mask); |
832 | readPadding(10); |
833 | |
834 | if (w > 0 && h > 0) { |
835 | doc::ImageRef image(doc::Image::create(doc::IMAGE_TILEMAP, w, h)); |
836 | image->setMaskColor(doc::notile); |
837 | image->clear(doc::notile); |
838 | read_compressed_image(f(), delegate(), image.get(), header, chunk_end); |
839 | |
840 | // Check if the tileset of this tilemap has the |
841 | // "ASE_TILESET_FLAG_ZERO_IS_NOTILE" we have to adjust all |
842 | // tile references to the new format (where empty tile is |
843 | // zero) |
844 | doc::Tileset* ts = static_cast<doc::LayerTilemap*>(layer)->tileset(); |
845 | doc::tileset_index tsi = static_cast<doc::LayerTilemap*>(layer)->tilesetIndex(); |
846 | ASSERT(tsi >= 0 && tsi < m_tilesetFlags.size()); |
847 | if (tsi >= 0 && tsi < m_tilesetFlags.size() && |
848 | (m_tilesetFlags[tsi] & ASE_TILESET_FLAG_ZERO_IS_NOTILE) == 0) { |
849 | doc::fix_old_tilemap(image.get(), ts, tileIDMask, flagsMask); |
850 | } |
851 | |
852 | // normalize the tile, so its value is never out of bounds |
853 | const doc::tile_index tilesetSize = ts->size(); |
854 | doc::transform_image<doc::TilemapTraits>( |
855 | image.get(), |
856 | [tilesetSize](const doc::tile_t& tile){ |
857 | return doc::tile_geti(tile) >= tilesetSize ? doc::notile : tile; |
858 | }); |
859 | |
860 | cel.reset(new doc::Cel(frame, image)); |
861 | cel->setPosition(x, y); |
862 | cel->setOpacity(opacity); |
863 | } |
864 | break; |
865 | } |
866 | |
867 | } |
868 | |
869 | if (!cel) |
870 | return nullptr; |
871 | |
872 | static_cast<doc::LayerImage*>(layer)->addCel(cel.get()); |
873 | return cel.release(); |
874 | } |
875 | |
876 | void AsepriteDecoder::(doc::Cel* cel) |
877 | { |
878 | // Read chunk data |
879 | int flags = read32(); |
880 | if (flags & ASE_CEL_EXTRA_FLAG_PRECISE_BOUNDS) { |
881 | fixmath::fixed x = read32(); |
882 | fixmath::fixed y = read32(); |
883 | fixmath::fixed w = read32(); |
884 | fixmath::fixed h = read32(); |
885 | if (w && h) { |
886 | gfx::RectF bounds(fixmath::fixtof(x), |
887 | fixmath::fixtof(y), |
888 | fixmath::fixtof(w), |
889 | fixmath::fixtof(h)); |
890 | cel->setBoundsF(bounds); |
891 | } |
892 | } |
893 | } |
894 | |
895 | void AsepriteDecoder::readColorProfile(doc::Sprite* sprite) |
896 | { |
897 | int type = read16(); |
898 | int flags = read16(); |
899 | fixmath::fixed gamma = read32(); |
900 | readPadding(8); |
901 | |
902 | // Without color space, like old Aseprite versions |
903 | gfx::ColorSpaceRef cs(nullptr); |
904 | |
905 | switch (type) { |
906 | |
907 | case ASE_FILE_NO_COLOR_PROFILE: |
908 | if (flags & ASE_COLOR_PROFILE_FLAG_GAMMA) |
909 | cs = gfx::ColorSpace::MakeSRGBWithGamma(fixmath::fixtof(gamma)); |
910 | else |
911 | cs = gfx::ColorSpace::MakeNone(); |
912 | break; |
913 | |
914 | case ASE_FILE_SRGB_COLOR_PROFILE: |
915 | if (flags & ASE_COLOR_PROFILE_FLAG_GAMMA) |
916 | cs = gfx::ColorSpace::MakeSRGBWithGamma(fixmath::fixtof(gamma)); |
917 | else |
918 | cs = gfx::ColorSpace::MakeSRGB(); |
919 | break; |
920 | |
921 | case ASE_FILE_ICC_COLOR_PROFILE: { |
922 | size_t length = read32(); |
923 | if (length > 0) { |
924 | std::vector<uint8_t> data(length); |
925 | readBytes(&data[0], length); |
926 | cs = gfx::ColorSpace::MakeICC(std::move(data)); |
927 | } |
928 | break; |
929 | } |
930 | } |
931 | |
932 | sprite->setColorSpace(cs); |
933 | } |
934 | |
935 | void AsepriteDecoder::readExternalFiles(AsepriteExternalFiles& extFiles) |
936 | { |
937 | uint32_t n = read32(); |
938 | readPadding(8); |
939 | for (uint32_t i=0; i<n; ++i) { |
940 | uint32_t id = read32(); |
941 | readPadding(8); |
942 | std::string fn = readString(); |
943 | extFiles.to_fn[id] = fn; |
944 | extFiles.to_id[fn] = id; |
945 | } |
946 | } |
947 | |
948 | doc::Mask* AsepriteDecoder::readMaskChunk() |
949 | { |
950 | int c, u, v, byte; |
951 | doc::Mask* mask; |
952 | // Read chunk data |
953 | int x = read16(); |
954 | int y = read16(); |
955 | int w = read16(); |
956 | int h = read16(); |
957 | |
958 | readPadding(8); |
959 | std::string name = readString(); |
960 | |
961 | mask = new doc::Mask(); |
962 | mask->setName(name.c_str()); |
963 | mask->replace(gfx::Rect(x, y, w, h)); |
964 | |
965 | // Read image data |
966 | for (v=0; v<h; v++) |
967 | for (u=0; u<(w+7)/8; u++) { |
968 | byte = read8(); |
969 | for (c=0; c<8; c++) |
970 | doc::put_pixel(mask->bitmap(), u*8+c, v, byte & (1<<(7-c))); |
971 | } |
972 | |
973 | return mask; |
974 | } |
975 | |
976 | void AsepriteDecoder::readTagsChunk(doc::Tags* tags) |
977 | { |
978 | size_t ntags = read16(); |
979 | |
980 | read32(); // 8 reserved bytes |
981 | read32(); |
982 | |
983 | for (size_t c=0; c<ntags; ++c) { |
984 | doc::frame_t from = read16(); |
985 | doc::frame_t to = read16(); |
986 | int aniDir = read8(); |
987 | if (aniDir != int(doc::AniDir::FORWARD) && |
988 | aniDir != int(doc::AniDir::REVERSE) && |
989 | aniDir != int(doc::AniDir::PING_PONG)) { |
990 | aniDir = int(doc::AniDir::FORWARD); |
991 | } |
992 | |
993 | read32(); // 8 reserved bytes |
994 | read32(); |
995 | |
996 | int r = read8(); |
997 | int g = read8(); |
998 | int b = read8(); |
999 | read8(); // Skip |
1000 | |
1001 | std::string name = readString(); |
1002 | |
1003 | auto tag = new doc::Tag(from, to); |
1004 | |
1005 | // We read the color field just in case that this is a .aseprite |
1006 | // file written by an old version of Aseprite (where the there are |
1007 | // not user data for tags). |
1008 | tag->setColor(doc::rgba(r, g, b, 255)); |
1009 | |
1010 | tag->setName(name); |
1011 | tag->setAniDir((doc::AniDir)aniDir); |
1012 | tags->add(tag); |
1013 | } |
1014 | } |
1015 | |
1016 | void AsepriteDecoder::readUserDataChunk(doc::UserData* userData) |
1017 | { |
1018 | size_t flags = read32(); |
1019 | |
1020 | if (flags & ASE_USER_DATA_FLAG_HAS_TEXT) { |
1021 | std::string text = readString(); |
1022 | userData->setText(text); |
1023 | } |
1024 | |
1025 | if (flags & ASE_USER_DATA_FLAG_HAS_COLOR) { |
1026 | int r = read8(); |
1027 | int g = read8(); |
1028 | int b = read8(); |
1029 | int a = read8(); |
1030 | userData->setColor(doc::rgba(r, g, b, a)); |
1031 | } |
1032 | } |
1033 | |
1034 | void AsepriteDecoder::readSlicesChunk(doc::Slices& slices) |
1035 | { |
1036 | size_t nslices = read32(); // Number of slices |
1037 | read32(); // 8 bytes reserved |
1038 | read32(); |
1039 | |
1040 | for (size_t i=0; i<nslices; ++i) { |
1041 | doc::Slice* slice = readSliceChunk(slices); |
1042 | // Set the user data |
1043 | if (slice) { |
1044 | // Default slice color |
1045 | slice->userData().setColor(delegate()->defaultSliceColor()); |
1046 | } |
1047 | } |
1048 | } |
1049 | |
1050 | doc::Slice* AsepriteDecoder::readSliceChunk(doc::Slices& slices) |
1051 | { |
1052 | const size_t nkeys = read32(); // Number of keys |
1053 | const int flags = read32(); // Flags |
1054 | read32(); // 4 bytes reserved |
1055 | std::string name = readString(); // Name |
1056 | |
1057 | std::unique_ptr<doc::Slice> slice(new doc::Slice); |
1058 | slice->setName(name); |
1059 | |
1060 | // For each key |
1061 | for (size_t j=0; j<nkeys; ++j) { |
1062 | gfx::Rect bounds, center; |
1063 | gfx::Point pivot = doc::SliceKey::NoPivot; |
1064 | doc::frame_t frame = read32(); |
1065 | bounds.x = ((int32_t)read32()); |
1066 | bounds.y = ((int32_t)read32()); |
1067 | bounds.w = read32(); |
1068 | bounds.h = read32(); |
1069 | |
1070 | if (flags & ASE_SLICE_FLAG_HAS_CENTER_BOUNDS) { |
1071 | center.x = ((int32_t)read32()); |
1072 | center.y = ((int32_t)read32()); |
1073 | center.w = read32(); |
1074 | center.h = read32(); |
1075 | } |
1076 | |
1077 | if (flags & ASE_SLICE_FLAG_HAS_PIVOT_POINT) { |
1078 | pivot.x = ((int32_t)read32()); |
1079 | pivot.y = ((int32_t)read32()); |
1080 | } |
1081 | |
1082 | slice->insert(frame, doc::SliceKey(bounds, center, pivot)); |
1083 | } |
1084 | |
1085 | slices.add(slice.get()); |
1086 | return slice.release(); |
1087 | } |
1088 | |
1089 | void AsepriteDecoder::(doc::Sprite* sprite, |
1090 | const AsepriteHeader* , |
1091 | const AsepriteExternalFiles& extFiles) |
1092 | { |
1093 | const doc::tileset_index id = read32(); |
1094 | const uint32_t flags = read32(); |
1095 | const doc::tile_index ntiles = read32(); |
1096 | const int w = read16(); |
1097 | const int h = read16(); |
1098 | const int baseIndex = short(read16()); |
1099 | readPadding(14); |
1100 | const std::string name = readString(); |
1101 | |
1102 | // Errors |
1103 | if (ntiles < 0 || w < 1 || h < 1) { |
1104 | delegate()->error( |
1105 | fmt::format("Error: Invalid tileset (number of tiles={0}, tile size={1}x{2})" , |
1106 | ntiles, w, h)); |
1107 | return; |
1108 | } |
1109 | |
1110 | doc::Grid grid(gfx::Size(w, h)); |
1111 | auto tileset = new doc::Tileset(sprite, grid, ntiles); |
1112 | tileset->setName(name); |
1113 | tileset->setBaseIndex(baseIndex); |
1114 | |
1115 | if (flags & ASE_TILESET_FLAG_EXTERNAL_FILE) { |
1116 | const uint32_t extFileId = read32(); // filename ID in the external files chunk |
1117 | const doc::tileset_index extTilesetId = read32(); // tileset ID in the external file |
1118 | |
1119 | auto it = extFiles.to_fn.find(extFileId); |
1120 | if (it != extFiles.to_fn.end()) { |
1121 | auto fn = it->second; |
1122 | tileset->setExternal(fn, extTilesetId); |
1123 | } |
1124 | else { |
1125 | delegate()->error( |
1126 | fmt::format("Error: Invalid external file reference (id={0} not found)" , |
1127 | extFileId)); |
1128 | } |
1129 | } |
1130 | |
1131 | if (flags & ASE_TILESET_FLAG_EMBEDDED) { |
1132 | if (ntiles > 0) { |
1133 | const size_t dataSize = read32(); // Size of compressed data |
1134 | const size_t dataBeg = f()->tell(); |
1135 | const size_t dataEnd = dataBeg+dataSize; |
1136 | |
1137 | doc::ImageRef alltiles(doc::Image::create(sprite->pixelFormat(), w, h*ntiles)); |
1138 | alltiles->setMaskColor(sprite->transparentColor()); |
1139 | |
1140 | read_compressed_image(f(), delegate(), alltiles.get(), header, dataEnd); |
1141 | f()->seek(dataEnd); |
1142 | |
1143 | for (doc::tile_index i=0; i<ntiles; ++i) { |
1144 | doc::ImageRef tile(doc::crop_image(alltiles.get(), 0, i*h, w, h, alltiles->maskColor())); |
1145 | tileset->set(i, tile); |
1146 | } |
1147 | |
1148 | // If we are reading and old .aseprite file (where empty tile is not the zero] |
1149 | if ((flags & ASE_TILESET_FLAG_ZERO_IS_NOTILE) == 0) |
1150 | doc::fix_old_tileset(tileset); |
1151 | } |
1152 | sprite->tilesets()->set(id, tileset); |
1153 | } |
1154 | |
1155 | if (id >= m_tilesetFlags.size()) |
1156 | m_tilesetFlags.resize(id+1, 0); |
1157 | m_tilesetFlags[id] = flags; |
1158 | } |
1159 | |
1160 | } // namespace dio |
1161 | |