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 | |
33 | namespace app { |
34 | |
35 | using namespace doc; |
36 | |
37 | static const char* palExts[] = { "act" , "col" , "gpl" , "hex" , "pal" }; |
38 | |
39 | base::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 | |
47 | base::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 | |
55 | std::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 | |
121 | bool 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 | |