Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
gimpgrad.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/*
7 * Authors:
8 * Ted Gould <ted@gould.cx>
9 * Abhishek Sharma
10 *
11 * Copyright (C) 2004-2005 Authors
12 *
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 */
15
16#include "io/sys.h"
17#include "extension/system.h"
19
20#include "colors/color.h"
21#include "colors/manager.h"
22#include "gimpgrad.h"
23#include "streq.h"
24#include "strneq.h"
25#include "document.h"
26#include "extension/extension.h"
27
28using namespace Inkscape::Colors;
29
31
33{
34 return true;
35}
36
41
42static void append_css_num(Glib::ustring &str, double const num)
43{
44 CSSOStringStream stream;
45 stream << num;
46 str += stream.str();
47}
48
61static Glib::ustring stop_svg(Color const &in_color, double const location)
62{
63 Glib::ustring ret("<stop stop-color=\"");
64
65 ret += in_color.converted(Space::Type::RGB)->toString(false);
66 ret += '"';
67
68 if (in_color[3] != 1) {
69 ret += " stop-opacity=\"";
70 append_css_num(ret, in_color.getOpacity());
71 ret += '"';
72 }
73 ret += " offset=\"";
74 append_css_num(ret, location);
75 ret += "\"/>\n";
76 return ret;
77}
78
114std::unique_ptr<SPDocument> GimpGrad::open(Inkscape::Extension::Input *, char const *filename, bool)
115{
116 Inkscape::IO::dump_fopen_call(filename, "I");
117 FILE *gradient = Inkscape::IO::fopen_utf8name(filename, "r");
118 if (gradient == nullptr) {
119 return nullptr;
120 }
121
122 {
123 char tempstr[1024];
124 if (fgets(tempstr, 1024, gradient) == nullptr) {
125 // std::cout << "Seems that the read failed" << std::endl;
126 goto error;
127 }
128 if (!streq(tempstr, "GIMP Gradient\n")) {
129 // std::cout << "This doesn't appear to be a GIMP gradient" << std::endl;
130 goto error;
131 }
132
133 /* Name field. */
134 if (fgets(tempstr, 1024, gradient) == nullptr) {
135 // std::cout << "Seems that the second read failed" << std::endl;
136 goto error;
137 }
138 if (!strneq(tempstr, "Name: ", 6)) {
139 goto error;
140 }
141 /* Handle very long names. (And also handle nul bytes gracefully: don't use strlen.) */
142 while (memchr(tempstr, '\n', sizeof(tempstr) - 1) == nullptr) {
143 if (fgets(tempstr, sizeof(tempstr), gradient) == nullptr) {
144 goto error;
145 }
146 }
147
148 /* n. segments */
149 if (fgets(tempstr, 1024, gradient) == nullptr) {
150 // std::cout << "Seems that the third read failed" << std::endl;
151 goto error;
152 }
153 char *endptr = nullptr;
154 long const n_segs = strtol(tempstr, &endptr, 10);
155 if ((*endptr != '\n')
156 || n_segs < 1) {
157 /* SVG gradients are allowed to have zero stops (treated as `none'), but gimp 2.2
158 * requires at least one segment (i.e. at least two stops) (see gimp_gradient_load in
159 * gimpgradient-load.c). We try to use the same error handling as gimp, so that
160 * .ggr files that work in one program work in both programs. */
161 goto error;
162 }
163
164 Color prev_color(0x0);
165 Glib::ustring outsvg("<svg><defs><linearGradient>\n");
166 long n_segs_found = 0;
167 double prev_right = 0.0;
168 while (fgets(tempstr, 1024, gradient) != nullptr) {
169 double dbls[3 + 4 + 4];
170 gchar *p = tempstr;
171 for (double & dbl : dbls) {
172 gchar *end = nullptr;
173 double const xi = g_ascii_strtod(p, &end);
174 if (!end || end == p || !g_ascii_isspace(*end)) {
175 goto error;
176 }
177 if (xi < 0 || 1 < xi) {
178 goto error;
179 }
180 dbl = xi;
181 p = end + 1;
182 }
183
184 double const left = dbls[0];
185 if (left != prev_right) {
186 goto error;
187 }
188 double const middle = dbls[1];
189 if (!(left <= middle)) {
190 goto error;
191 }
192 double const right = dbls[2];
193 if (!(middle <= right)) {
194 goto error;
195 }
196
197 g_assert(11 == G_N_ELEMENTS(dbls));
198 auto leftcolor = Color(Space::Type::RGB, {dbls[3], dbls[4], dbls[5], dbls[6]});
199 auto rightcolor = Color(Space::Type::RGB, {dbls[7], dbls[8], dbls[9], dbls[10]});
200
201 /* Interpolation enums: curve shape and colour space. */
202 {
203 /* TODO: Currently we silently ignore type & color, assuming linear interpolation in
204 * sRGB space (or whatever the default in SVG is). See gimp/app/core/gimpgradient.c
205 * for how gimp uses these. We could use gimp functions to sample at a few points, and
206 * add some intermediate stops to convert to the linear/sRGB interpolation */
207 int type; /* enum: linear, curved, sine, sphere increasing, sphere decreasing. */
208 int color_interpolation; /* enum: rgb, hsv anticlockwise, hsv clockwise. */
209 if (sscanf(p, "%8d %8d", &type, &color_interpolation) != 2) {
210 continue;
211 }
212 }
213
214 if (prev_color != leftcolor) {
215 outsvg += stop_svg(leftcolor, left);
216 }
217 if (fabs(middle - .5 * (left + right)) > 1e-4) {
218 outsvg += stop_svg(leftcolor.averaged(rightcolor), middle);
219 }
220 outsvg += stop_svg(rightcolor, right);
221
222 prev_color = rightcolor;
223 prev_right = right;
224 ++n_segs_found;
225 }
226 if (prev_right != 1.0) {
227 goto error;
228 }
229
230 if (n_segs_found != n_segs) {
231 goto error;
232 }
233
234 outsvg += "</linearGradient></defs></svg>";
235
236 // std::cout << "SVG Output: " << outsvg << std::endl;
237
238 fclose(gradient);
239
240 return SPDocument::createNewDocFromMem(outsvg.raw(), true);
241 }
242
243error:
244 fclose(gradient);
245 return nullptr;
246}
247
248#include "clear-n_.h"
249
251{
252 // clang-format off
254 "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
255 "<name>" N_("GIMP Gradients") "</name>\n"
256 "<id>org.inkscape.input.gimpgrad</id>\n"
257 "<input>\n"
258 "<extension>.ggr</extension>\n"
259 "<mimetype>application/x-gimp-gradient</mimetype>\n"
260 "<filetypename>" N_("GIMP Gradient (*.ggr)") "</filetypename>\n"
261 "<filetypetooltip>" N_("Gradients used in GIMP") "</filetypetooltip>\n"
262 "</input>\n"
263 "</inkscape-extension>\n", std::make_unique<GimpGrad>());
264 // clang-format on
265 return;
266}
267
268} // namespace Inkscape::Extension::Internal
269
270/*
271 Local Variables:
272 mode:c++
273 c-file-style:"stroustrup"
274 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
275 indent-tabs-mode:nil
276 fill-column:99
277 End:
278*/
279// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
A thin wrapper around std::ostringstream, but writing floating point numbers in the format required b...
std::optional< Color > converted(Color const &other) const
Return a copy of this color converted to the same format as the other color.
Definition color.cpp:189
double getOpacity() const
Get the opacity in this color, if it's stored.
Definition color.cpp:407
The object that is the basis for the Extension system.
Definition extension.h:133
void unload(Inkscape::Extension::Extension *module) override
Definition gimpgrad.cpp:37
std::unique_ptr< SPDocument > open(Inkscape::Extension::Input *module, char const *filename, bool is_importing) override
Actually open the gradient and turn it into an SPDocument.
Definition gimpgrad.cpp:114
bool load(Inkscape::Extension::Extension *module) override
Definition gimpgrad.cpp:32
static std::unique_ptr< SPDocument > createNewDocFromMem(std::span< char const > buffer, bool keepalive, std::string const &filename="")
Definition document.cpp:708
A way to clear the N_ macro, which is defined as an inline function.
TODO: insert short description here.
Inkscape::Extension::Extension: Frontend to certain, possibly pluggable, actions.
Geom::Point end
A set of useful color modifying functions which do not fit as generic methods on the color class itse...
Definition profile.cpp:24
static Glib::ustring stop_svg(Color const &in_color, double const location)
A function to turn a color into a gradient stop.
Definition gimpgrad.cpp:61
static void append_css_num(Glib::ustring &str, double const num)
Definition gimpgrad.cpp:42
void build_from_mem(gchar const *buffer, std::unique_ptr< Implementation::Implementation > in_imp)
Create a module from a buffer holding an XML description.
Definition system.cpp:459
void dump_fopen_call(char const *utf8name, char const *id)
Definition sys.cpp:37
FILE * fopen_utf8name(char const *utf8name, char const *mode)
Open a file with g_fopen().
Definition sys.cpp:72
int num
Definition scribble.cpp:47
TODO: insert short description here.
bool streq(char const *a, char const *b)
Convenience/readability wrapper for strcmp(a,b)==0.
Definition streq.h:17
TODO: insert short description here.
bool strneq(char const *a, char const *b, size_t n)
Convenience/readability wrapper for strncmp(a,b,n)==0.
Definition strneq.h:17