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 | #ifndef APP_FILE_FILE_H_INCLUDED |
9 | #define APP_FILE_FILE_H_INCLUDED |
10 | #pragma once |
11 | |
12 | #include "app/color.h" |
13 | #include "app/doc.h" |
14 | #include "app/file/file_op_config.h" |
15 | #include "app/file/format_options.h" |
16 | #include "app/pref/preferences.h" |
17 | #include "base/paths.h" |
18 | #include "doc/frame.h" |
19 | #include "doc/image_ref.h" |
20 | #include "doc/pixel_format.h" |
21 | #include "doc/selected_frames.h" |
22 | #include "os/color_space.h" |
23 | |
24 | #include <cstdio> |
25 | #include <memory> |
26 | #include <mutex> |
27 | #include <string> |
28 | |
29 | // Flags for FileOp::createLoadDocumentOperation() |
30 | #define FILE_LOAD_SEQUENCE_NONE 0x00000001 |
31 | #define FILE_LOAD_SEQUENCE_ASK 0x00000002 |
32 | #define FILE_LOAD_SEQUENCE_ASK_CHECKBOX 0x00000004 |
33 | #define FILE_LOAD_SEQUENCE_YES 0x00000008 |
34 | #define FILE_LOAD_ONE_FRAME 0x00000010 |
35 | #define FILE_LOAD_DATA_FILE 0x00000020 |
36 | #define FILE_LOAD_CREATE_PALETTE 0x00000040 |
37 | |
38 | namespace doc { |
39 | class Tag; |
40 | } |
41 | |
42 | namespace doc { |
43 | class Cel; |
44 | class Image; |
45 | class Layer; |
46 | class LayerImage; |
47 | class Palette; |
48 | class Slice; |
49 | class Sprite; |
50 | } |
51 | |
52 | namespace app { |
53 | |
54 | class Context; |
55 | class FileFormat; |
56 | |
57 | using namespace doc; |
58 | |
59 | // File operations. |
60 | typedef enum { |
61 | FileOpLoad, |
62 | FileOpSave |
63 | } FileOpType; |
64 | |
65 | class IFileOpProgress { |
66 | public: |
67 | virtual ~IFileOpProgress() { } |
68 | virtual void ackFileOpProgress(double progress) = 0; |
69 | }; |
70 | |
71 | class FileOpROI { // Region of interest |
72 | public: |
73 | FileOpROI(); |
74 | FileOpROI(const Doc* doc, |
75 | const gfx::Rect& bounds, |
76 | const std::string& sliceName, |
77 | const std::string& tagName, |
78 | const doc::SelectedFrames& selFrames, |
79 | const bool adjustByTag); |
80 | |
81 | const Doc* document() const { return m_document; } |
82 | const gfx::Rect& bounds() const { return m_bounds; } |
83 | doc::Slice* slice() const { return m_slice; } |
84 | doc::Tag* tag() const { return m_tag; } |
85 | doc::frame_t fromFrame() const { return m_selFrames.firstFrame(); } |
86 | doc::frame_t toFrame() const { return m_selFrames.lastFrame(); } |
87 | const doc::SelectedFrames& selectedFrames() const { return m_selFrames; } |
88 | |
89 | doc::frame_t frames() const { |
90 | return (doc::frame_t)m_selFrames.size(); |
91 | } |
92 | |
93 | private: |
94 | const Doc* m_document; |
95 | gfx::Rect m_bounds; |
96 | doc::Slice* m_slice; |
97 | doc::Tag* m_tag; |
98 | doc::SelectedFrames m_selFrames; |
99 | }; |
100 | |
101 | // Used by file formats with FILE_ENCODE_ABSTRACT_IMAGE flag, to |
102 | // encode a sprite with an intermediate transformation on-the-fly |
103 | // (e.g. resizing). |
104 | class FileAbstractImage { |
105 | public: |
106 | virtual ~FileAbstractImage() { } |
107 | |
108 | virtual int width() const { return spec().width(); } |
109 | virtual int height() const { return spec().height(); } |
110 | |
111 | virtual const doc::ImageSpec& spec() const = 0; |
112 | virtual os::ColorSpaceRef osColorSpace() const = 0; |
113 | virtual bool needAlpha() const = 0; |
114 | virtual bool isOpaque() const = 0; |
115 | virtual int frames() const = 0; |
116 | virtual int frameDuration(doc::frame_t frame) const = 0; |
117 | |
118 | virtual const doc::Palette* palette(doc::frame_t frame) const = 0; |
119 | virtual doc::PalettesList palettes() const = 0; |
120 | |
121 | virtual const doc::ImageRef getScaledImage() const = 0; |
122 | |
123 | // In case the file format can encode scanline by scanline |
124 | // (e.g. PNG format). |
125 | virtual const uint8_t* getScanline(int y) const = 0; |
126 | |
127 | // In case that the encoder needs full frame renders (or compare |
128 | // between frames), e.g. GIF format. |
129 | virtual void renderFrame(const doc::frame_t frame, doc::Image* dst) const = 0; |
130 | }; |
131 | |
132 | // Structure to load & save files. |
133 | // |
134 | // TODO This class do to many things. There should be a previous |
135 | // instance (class) to verify what the user want to do with the |
136 | // sequence of files, and the result of that operation should be the |
137 | // input of this one. |
138 | class FileOp { |
139 | public: |
140 | static FileOp* createLoadDocumentOperation(Context* context, |
141 | const std::string& filename, |
142 | const int flags, |
143 | const FileOpConfig* config = nullptr); |
144 | |
145 | static FileOp* createSaveDocumentOperation(const Context* context, |
146 | const FileOpROI& roi, |
147 | const std::string& filename, |
148 | const std::string& filenameFormat, |
149 | const bool ignoreEmptyFrames); |
150 | |
151 | static bool checkIfFormatSupportResizeOnTheFly(const std::string& filename); |
152 | |
153 | ~FileOp(); |
154 | |
155 | bool isSequence() const { return !m_seq.filename_list.empty(); } |
156 | bool isOneFrame() const { return m_oneframe; } |
157 | bool preserveColorProfile() const { return m_config.preserveColorProfile; } |
158 | |
159 | const std::string& filename() const { return m_filename; } |
160 | const base::paths& filenames() const { return m_seq.filename_list; } |
161 | Context* context() const { return m_context; } |
162 | Doc* document() const { return m_document; } |
163 | Doc* releaseDocument() { |
164 | Doc* doc = m_document; |
165 | m_document = nullptr; |
166 | return doc; |
167 | } |
168 | |
169 | const FileOpROI& roi() const { return m_roi; } |
170 | |
171 | void createDocument(Sprite* spr); |
172 | void operate(IFileOpProgress* progress = nullptr); |
173 | |
174 | void done(); |
175 | void stop(); |
176 | bool isDone() const; |
177 | bool isStop() const; |
178 | |
179 | // Does extra post-load processing which may require user intervention. |
180 | void postLoad(); |
181 | |
182 | // Special options specific to the file format. |
183 | FormatOptionsPtr formatOptions() const { |
184 | return m_formatOptions; |
185 | } |
186 | |
187 | // Options to save the document. This function doesn't return |
188 | // nullptr. |
189 | template<typename T> |
190 | std::shared_ptr<T> formatOptionsForSaving() const { |
191 | auto opts = std::dynamic_pointer_cast<T>(m_formatOptions); |
192 | if (!opts) |
193 | opts = std::make_shared<T>(); |
194 | ASSERT(opts); |
195 | return opts; |
196 | } |
197 | |
198 | bool hasFormatOptionsOfDocument() const { |
199 | return (m_document->formatOptions() != nullptr); |
200 | } |
201 | |
202 | // Options from the document when it was loaded. This function |
203 | // doesn't return nullptr. |
204 | template<typename T> |
205 | std::shared_ptr<T> formatOptionsOfDocument() const { |
206 | // We use the dynamic cast because the document format options |
207 | // could be an instance of another class than T. |
208 | auto opts = std::dynamic_pointer_cast<T>(m_document->formatOptions()); |
209 | if (!opts) { |
210 | // If the document doesn't have format options (or the type |
211 | // doesn't matches T), we create default format options for |
212 | // this file. |
213 | opts = std::make_shared<T>(); |
214 | } |
215 | ASSERT(opts); |
216 | return opts; |
217 | } |
218 | |
219 | void setLoadedFormatOptions(const FormatOptionsPtr& opts); |
220 | |
221 | // Helpers for file decoder/encoder (FileFormat) with |
222 | // FILE_SUPPORT_SEQUENCES flag. |
223 | void sequenceSetNColors(int ncolors); |
224 | int sequenceGetNColors() const; |
225 | void sequenceSetColor(int index, int r, int g, int b); |
226 | void sequenceGetColor(int index, int* r, int* g, int* b) const; |
227 | void sequenceSetAlpha(int index, int a); |
228 | void sequenceGetAlpha(int index, int* a) const; |
229 | ImageRef sequenceImage(PixelFormat pixelFormat, int w, int h); |
230 | const ImageRef sequenceImage() const { return m_seq.image; } |
231 | const Palette* sequenceGetPalette() const { return m_seq.palette; } |
232 | bool sequenceGetHasAlpha() const { |
233 | return m_seq.has_alpha; |
234 | } |
235 | void sequenceSetHasAlpha(bool hasAlpha) { |
236 | m_seq.has_alpha = hasAlpha; |
237 | } |
238 | int sequenceFlags() const { |
239 | return m_seq.flags; |
240 | } |
241 | |
242 | // Can be used to encode sequences/static files (e.g. png files) |
243 | // or animations (e.g. gif) resizing the result on the fly. |
244 | FileAbstractImage* abstractImage(); |
245 | void setOnTheFlyScale(const gfx::PointF& scale); |
246 | |
247 | const std::string& error() const { return m_error; } |
248 | void setError(const char *error, ...); |
249 | bool hasError() const { return !m_error.empty(); } |
250 | |
251 | double progress() const; |
252 | void setProgress(double progress); |
253 | |
254 | void getFilenameList(base::paths& output) const; |
255 | |
256 | void setEmbeddedColorProfile() { m_embeddedColorProfile = true; } |
257 | bool hasEmbeddedColorProfile() const { return m_embeddedColorProfile; } |
258 | |
259 | void setEmbeddedGridBounds() { m_embeddedGridBounds = true; } |
260 | bool hasEmbeddedGridBounds() const { return m_embeddedGridBounds; } |
261 | |
262 | bool newBlend() const { return m_config.newBlend; } |
263 | |
264 | private: |
265 | FileOp(); // Undefined |
266 | FileOp(FileOpType type, |
267 | Context* context, |
268 | const FileOpConfig* config); |
269 | |
270 | FileOpType m_type; // Operation type: 0=load, 1=save. |
271 | FileFormat* m_format; |
272 | Context* m_context; |
273 | // TODO this should be a shared pointer (and we should remove |
274 | // releaseDocument() member function) |
275 | Doc* m_document; // Loaded document, or document to be saved. |
276 | std::string m_filename; // File-name to load/save. |
277 | std::string m_dataFilename; // File-name for a special XML .aseprite-data where extra sprite data can be stored |
278 | FileOpROI m_roi; |
279 | |
280 | // Shared fields between threads. |
281 | mutable std::mutex m_mutex; // Mutex to access to the next two fields. |
282 | double m_progress; // Progress (1.0 is ready). |
283 | IFileOpProgress* m_progressInterface; |
284 | std::string m_error; // Error string. |
285 | bool m_done; // True if the operation finished. |
286 | bool m_stop; // Force the break of the operation. |
287 | bool m_oneframe; // Load just one frame (in formats |
288 | // that support animation like |
289 | // GIF/FLI/ASE). |
290 | bool m_createPaletteFromRgba; |
291 | bool m_ignoreEmpty; |
292 | |
293 | // True if the file contained a color profile when it was loaded. |
294 | bool m_embeddedColorProfile; |
295 | |
296 | // True if the file contained a the grid bounds inside. |
297 | bool m_embeddedGridBounds; |
298 | |
299 | FileOpConfig m_config; |
300 | |
301 | // Options |
302 | FormatOptionsPtr m_formatOptions; |
303 | |
304 | // Data for sequences. |
305 | struct { |
306 | base::paths filename_list; // All file names to load/save. |
307 | Palette* palette; // Palette of the sequence. |
308 | ImageRef image; // Image to be saved/loaded. |
309 | // For the progress bar. |
310 | double progress_offset; // Progress offset from the current frame. |
311 | double progress_fraction; // Progress fraction for one frame. |
312 | // To load sequences. |
313 | frame_t frame; |
314 | bool has_alpha; |
315 | LayerImage* layer; |
316 | Cel* last_cel; |
317 | int duration; |
318 | // Flags after the user choose what to do with the sequence. |
319 | int flags; |
320 | } m_seq; |
321 | |
322 | class FileAbstractImageImpl; |
323 | std::unique_ptr<FileAbstractImageImpl> m_abstractImage; |
324 | |
325 | void prepareForSequence(); |
326 | void makeAbstractImage(); |
327 | }; |
328 | |
329 | // Available extensions for each load/save operation. |
330 | base::paths get_readable_extensions(); |
331 | base::paths get_writable_extensions(const int requiredFormatFlag = 0); |
332 | |
333 | // High-level routines to load/save documents. |
334 | Doc* load_document(Context* context, const std::string& filename); |
335 | int save_document(Context* context, Doc* document); |
336 | |
337 | // Returns true if the given filename contains a file extension that |
338 | // can be used to save only static images (i.e. animations are saved |
339 | // as sequence of files). |
340 | bool is_static_image_format(const std::string& filename); |
341 | |
342 | } // namespace app |
343 | |
344 | #endif |
345 | |