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
32namespace dio {
33
34bool AsepriteDecoder::decode()
35{
36 bool ignore_old_color_chunks = false;
37
38 AsepriteHeader header;
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 frame_header;
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 &current_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
288bool AsepriteDecoder::readHeader(AsepriteHeader* header)
289{
290 size_t headerPos = 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
346void AsepriteDecoder::readFrameHeader(AsepriteFrameHeader* frame_header)
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
360void AsepriteDecoder::readPadding(int bytes)
361{
362 for (int c=0; c<bytes; c++)
363 read8();
364}
365
366std::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
381doc::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
410doc::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
437doc::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
469doc::Layer* AsepriteDecoder::readLayerChunk(AsepriteHeader* header,
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
555namespace {
556
557template<typename ImageTraits>
558void read_raw_image(FileInterface* f,
559 DecodeDelegate* delegate,
560 doc::Image* image,
561 const AsepriteHeader* header)
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
580template<typename ImageTraits>
581void read_compressed_image_templ(FileInterface* f,
582 DecodeDelegate* delegate,
583 doc::Image* image,
584 const AsepriteHeader* header,
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
670void read_compressed_image(FileInterface* f,
671 DecodeDelegate* delegate,
672 doc::Image* image,
673 const AsepriteHeader* header,
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
714doc::Cel* AsepriteDecoder::readCelChunk(doc::Sprite* sprite,
715 doc::frame_t frame,
716 doc::PixelFormat pixelFormat,
717 const AsepriteHeader* header,
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
876void AsepriteDecoder::readCelExtraChunk(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
895void 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
935void 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
948doc::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
976void 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
1016void 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
1034void 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
1050doc::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
1089void AsepriteDecoder::readTilesetChunk(doc::Sprite* sprite,
1090 const AsepriteHeader* header,
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