1// Aseprite
2// Copyright (C) 2018-2022 Igara Studio S.A.
3// Copyright (C) 2001-2018 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/context.h"
13#include "app/doc.h"
14#include "app/file/file.h"
15#include "app/file/file_format.h"
16#include "app/file/file_formats_manager.h"
17#include "base/fs.h"
18#include "base/string.h"
19#include "dio/detect_format.h"
20#include "doc/cel.h"
21#include "doc/file/act_file.h"
22#include "doc/file/col_file.h"
23#include "doc/file/gpl_file.h"
24#include "doc/file/hex_file.h"
25#include "doc/file/pal_file.h"
26#include "doc/image.h"
27#include "doc/layer.h"
28#include "doc/palette.h"
29#include "doc/sprite.h"
30
31#include <cstring>
32
33namespace app {
34
35using namespace doc;
36
37static const char* palExts[] = { "act", "col", "gpl", "hex", "pal" };
38
39base::paths get_readable_palette_extensions()
40{
41 base::paths paths = get_readable_extensions();
42 for (const char* s : palExts)
43 paths.push_back(s);
44 return paths;
45}
46
47base::paths get_writable_palette_extensions()
48{
49 base::paths paths = get_writable_extensions(FILE_SUPPORT_INDEXED);
50 for (const char* s : palExts)
51 paths.push_back(s);
52 return paths;
53}
54
55std::unique_ptr<doc::Palette> load_palette(
56 const char* filename,
57 const FileOpConfig* config)
58{
59 dio::FileFormat dioFormat = dio::detect_format(filename);
60 std::unique_ptr<Palette> pal = nullptr;
61
62 switch (dioFormat) {
63
64 case dio::FileFormat::ACT_PALETTE:
65 pal = doc::file::load_act_file(filename);
66 break;
67
68 case dio::FileFormat::COL_PALETTE:
69 pal = doc::file::load_col_file(filename);
70 break;
71
72 case dio::FileFormat::GPL_PALETTE:
73 pal = doc::file::load_gpl_file(filename);
74 break;
75
76 case dio::FileFormat::HEX_PALETTE:
77 pal = doc::file::load_hex_file(filename);
78 break;
79
80 case dio::FileFormat::PAL_PALETTE:
81 pal = doc::file::load_pal_file(filename);
82 break;
83
84 default: {
85 FileFormat* ff = FileFormatsManager::instance()->getFileFormat(dioFormat);
86 if (!ff || !ff->support(FILE_SUPPORT_LOAD))
87 break;
88
89 std::unique_ptr<FileOp> fop(
90 FileOp::createLoadDocumentOperation(
91 nullptr, filename,
92 FILE_LOAD_CREATE_PALETTE |
93 FILE_LOAD_SEQUENCE_NONE |
94 FILE_LOAD_ONE_FRAME,
95 config));
96
97 if (fop && !fop->hasError()) {
98 fop->operate(nullptr);
99 fop->postLoad();
100
101 if (fop->document() &&
102 fop->document()->sprite() &&
103 fop->document()->sprite()->palette(frame_t(0))) {
104 pal = std::make_unique<Palette>(
105 *fop->document()->sprite()->palette(frame_t(0)));
106 }
107
108 delete fop->releaseDocument();
109 fop->done();
110 }
111 break;
112 }
113 }
114
115 if (pal)
116 pal->setFilename(filename);
117
118 return pal;
119}
120
121bool save_palette(const char* filename, const Palette* pal, int columns,
122 const gfx::ColorSpaceRef& cs)
123{
124 dio::FileFormat dioFormat = dio::detect_format_by_file_extension(filename);
125 bool success = false;
126
127 switch (dioFormat) {
128
129 case dio::FileFormat::ACT_PALETTE:
130 success = doc::file::save_act_file(pal, filename);
131 break;
132
133 case dio::FileFormat::COL_PALETTE:
134 success = doc::file::save_col_file(pal, filename);
135 break;
136
137 case dio::FileFormat::GPL_PALETTE:
138 success = doc::file::save_gpl_file(pal, filename);
139 break;
140
141 case dio::FileFormat::HEX_PALETTE:
142 success = doc::file::save_hex_file(pal, filename);
143 break;
144
145 case dio::FileFormat::PAL_PALETTE:
146 success = doc::file::save_pal_file(pal, filename);
147 break;
148
149 default: {
150 FileFormat* ff = FileFormatsManager::instance()->getFileFormat(dioFormat);
151 if (!ff || !ff->support(FILE_SUPPORT_SAVE))
152 break;
153
154 int w = (columns > 0 ? std::clamp(columns, 0, pal->size()): pal->size());
155 int h = (pal->size() / w) + (pal->size() % w > 0 ? 1: 0);
156
157 Context tmpContext;
158 gfx::ColorSpaceRef colorSpace = (cs ? cs: gfx::ColorSpace::MakeNone());
159 Doc* doc = tmpContext.documents().add(
160 new Doc(Sprite::MakeStdSprite(
161 ImageSpec((pal->size() <= 256 ? doc::ColorMode::INDEXED:
162 doc::ColorMode::RGB),
163 w, h, 0, colorSpace), pal->size())));
164
165 Sprite* sprite = doc->sprite();
166 doc->sprite()->setPalette(pal, false);
167
168 LayerImage* layer = static_cast<LayerImage*>(sprite->root()->firstLayer());
169 layer->configureAsBackground();
170
171 Image* image = layer->cel(frame_t(0))->image();
172 image->clear(0);
173
174 int x, y, c;
175 for (y=c=0; y<h; ++y) {
176 for (x=0; x<w; ++x) {
177 if (doc->colorMode() == doc::ColorMode::INDEXED)
178 image->putPixel(x, y, c);
179 else
180 image->putPixel(x, y, pal->entry(c));
181
182 if (++c == pal->size())
183 goto done;
184 }
185 }
186 done:;
187
188 doc->setFilename(filename);
189 success = (save_document(&tmpContext, doc) == 0);
190
191 doc->close();
192 delete doc;
193 break;
194 }
195 }
196
197 return success;
198}
199
200}
201