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
38namespace doc {
39 class Tag;
40}
41
42namespace doc {
43 class Cel;
44 class Image;
45 class Layer;
46 class LayerImage;
47 class Palette;
48 class Slice;
49 class Sprite;
50}
51
52namespace 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