1// Aseprite
2// Copyright (c) 2018-2022 Igara Studio S.A.
3//
4// This program is distributed under the terms of
5// the End-User License Agreement for Aseprite.
6//
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/console.h"
13#include "app/context.h"
14#include "app/doc.h"
15#include "app/file/file.h"
16#include "app/file/file_format.h"
17#include "app/file/format_options.h"
18#include "app/pref/preferences.h"
19#include "base/cfile.h"
20#include "base/file_handle.h"
21#include "doc/doc.h"
22#include "ui/window.h"
23
24#include "svg_options.xml.h"
25
26namespace app {
27
28using namespace base;
29
30class SvgFormat : public FileFormat {
31 // Data for SVG files
32 class SvgOptions : public FormatOptions {
33 public:
34 SvgOptions() : pixelScale(1) { }
35 int pixelScale;
36 };
37
38 const char* onGetName() const override {
39 return "svg";
40 }
41
42 void onGetExtensions(base::paths& exts) const override {
43 exts.push_back("svg");
44 }
45
46 dio::FileFormat onGetDioFormat() const override {
47 return dio::FileFormat::SVG_IMAGE;
48 }
49
50 int onGetFlags() const override {
51 return
52 FILE_SUPPORT_SAVE |
53 FILE_SUPPORT_RGB |
54 FILE_SUPPORT_RGBA |
55 FILE_SUPPORT_GRAY |
56 FILE_SUPPORT_GRAYA |
57 FILE_SUPPORT_INDEXED |
58 FILE_SUPPORT_SEQUENCES |
59 FILE_SUPPORT_GET_FORMAT_OPTIONS |
60 FILE_SUPPORT_PALETTE_WITH_ALPHA;
61 }
62
63 bool onLoad(FileOp* fop) override;
64#ifdef ENABLE_SAVE
65 bool onSave(FileOp* fop) override;
66#endif
67 FormatOptionsPtr onAskUserForFormatOptions(FileOp* fop) override;
68};
69
70FileFormat* CreateSvgFormat()
71{
72 return new SvgFormat;
73}
74
75bool SvgFormat::onLoad(FileOp* fop)
76{
77 return false;
78}
79
80#ifdef ENABLE_SAVE
81
82bool SvgFormat::onSave(FileOp* fop)
83{
84 const ImageRef image = fop->sequenceImage();
85 int x, y, c, r, g, b, a, alpha;
86 const auto svg_options = std::static_pointer_cast<SvgOptions>(fop->formatOptions());
87 const int pixelScaleValue = std::clamp(svg_options->pixelScale, 0, 10000);
88 FileHandle handle(open_file_with_exception_sync_on_close(fop->filename(), "wb"));
89 FILE* f = handle.get();
90 auto printcol = [f](int x, int y,int r, int g, int b, int a, int pxScale) {
91 fprintf(f, "<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" fill=\"#%02X%02X%02X\" ",
92 x*pxScale, y*pxScale, pxScale, pxScale, r, g, b);
93 if (a != 255)
94 fprintf(f, "opacity=\"%f\" ", (float)a / 255.0);
95 fprintf(f, "/>\n");
96 };
97 fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
98 fprintf(f, "<svg version=\"1.1\" width=\"%d\" height=\"%d\" xmlns=\"http://www.w3.org/2000/svg\" shape-rendering=\"crispEdges\">\n",
99 image->width()*pixelScaleValue, image->height()*pixelScaleValue);
100
101 switch (image->pixelFormat()) {
102
103 case IMAGE_RGB: {
104 for (y=0; y<image->height(); y++) {
105 for (x=0; x<image->width(); x++) {
106 c = get_pixel_fast<RgbTraits>(image.get(), x, y);
107 alpha = rgba_geta(c);
108 if (alpha != 0x00)
109 printcol(x, y, rgba_getr(c), rgba_getg(c), rgba_getb(c), alpha, pixelScaleValue);
110 }
111 fop->setProgress((float)y / (float)(image->height()));
112 }
113 break;
114 }
115 case IMAGE_GRAYSCALE: {
116 for (y=0; y<image->height(); y++) {
117 for (x=0; x<image->width(); x++) {
118 c = get_pixel_fast<GrayscaleTraits>(image.get(), x, y);
119 auto v = graya_getv(c);
120 alpha = graya_geta(c);
121 if (alpha != 0x00)
122 printcol(x, y, v, v, v, alpha, pixelScaleValue);
123 }
124 fop->setProgress((float)y / (float)(image->height()));
125 }
126 break;
127 }
128 case IMAGE_INDEXED: {
129 unsigned char image_palette[256][4];
130 for (y=0; y<256; y++) {
131 fop->sequenceGetColor(y, &r, &g, &b);
132 image_palette[y][0] = r;
133 image_palette[y][1] = g;
134 image_palette[y][2] = b;
135 fop->sequenceGetAlpha(y, &a);
136 image_palette[y][3] = a;
137 }
138 color_t mask_color = -1;
139 if (fop->document()->sprite()->backgroundLayer() == NULL ||
140 !fop->document()->sprite()->backgroundLayer()->isVisible()) {
141 mask_color = fop->document()->sprite()->transparentColor();
142 }
143 for (y=0; y<image->height(); y++) {
144 for (x=0; x<image->width(); x++) {
145 c = get_pixel_fast<IndexedTraits>(image.get(), x, y);
146 if (c != mask_color)
147 printcol(x, y, image_palette[c][0] & 0xff,
148 image_palette[c][1] & 0xff,
149 image_palette[c][2] & 0xff,
150 image_palette[c][3] & 0xff,
151 pixelScaleValue);
152 }
153 fop->setProgress((float)y / (float)(image->height()));
154 }
155 break;
156 }
157 }
158 fprintf(f, "</svg>");
159 if (ferror(f)) {
160 fop->setError("Error writing file.\n");
161 return false;
162 }
163 else {
164 return true;
165 }
166}
167#endif
168
169// Shows the SVG configuration dialog.
170FormatOptionsPtr SvgFormat::onAskUserForFormatOptions(FileOp* fop)
171{
172 auto opts = fop->formatOptionsOfDocument<SvgOptions>();
173#ifdef ENABLE_UI
174 if (fop->context() && fop->context()->isUIAvailable()) {
175 try {
176 auto& pref = Preferences::instance();
177
178 if (pref.isSet(pref.svg.pixelScale))
179 opts->pixelScale = pref.svg.pixelScale();
180
181 if (pref.svg.showAlert()) {
182 app::gen::SvgOptions win;
183 win.pxsc()->setTextf("%d", opts->pixelScale);
184 win.openWindowInForeground();
185
186 if (win.closer() == win.ok()) {
187 pref.svg.pixelScale((int)win.pxsc()->textInt());
188 pref.svg.showAlert(!win.dontShow()->isSelected());
189
190 opts->pixelScale = pref.svg.pixelScale();
191 }
192 else {
193 opts.reset();
194 }
195 }
196 }
197 catch (std::exception& e) {
198 Console::showException(e);
199 return std::shared_ptr<SvgOptions>(nullptr);
200 }
201 }
202#endif
203 return opts;
204}
205
206} // namespace app
207