Code

Filters. New custom predefined filters (all ABCs, Neon draw, Color shilf, Silhouette...
[inkscape.git] / src / extension / internal / gimpgrad.cpp
index 9560c5df6e55e2fbedb682ffbb892bb0adcd3a5f..e92a4e17e0e14f0ccb6257abac3cce709f39b1cc 100644 (file)
@@ -5,6 +5,7 @@
 /*
  * Authors:
  *   Ted Gould <ted@gould.cx>
+ *   Abhishek Sharma
  *
  * Copyright (C) 2004-2005 Authors
  *
 #include <color-rgba.h>
 #include "io/sys.h"
 #include "extension/system.h"
+#include "svg/css-ostringstream.h"
+#include "svg/svg-color.h"
 
 #include "gimpgrad.h"
+#include "streq.h"
+#include "strneq.h"
 
 namespace Inkscape {
 namespace Extension {
@@ -31,7 +36,7 @@ namespace Internal {
     \return Whether the load was sucessful
 */
 bool
-GimpGrad::load (Inkscape::Extension::Extension *module)
+GimpGrad::load (Inkscape::Extension::Extension */*module*/)
 {
     // std::cout << "Hey, I'm loading!\n" << std::endl;
     return TRUE;
@@ -43,12 +48,20 @@ GimpGrad::load (Inkscape::Extension::Extension *module)
     \return None
 */
 void
-GimpGrad::unload (Inkscape::Extension::Extension *module)
+GimpGrad::unload (Inkscape::Extension::Extension */*module*/)
 {
     // std::cout << "Nooo! I'm being unloaded!" << std::endl;
     return;
 }
 
+static void
+append_css_num(Glib::ustring &str, double const num)
+{
+    CSSOStringStream stream;
+    stream << num;
+    str += stream.str();
+}
+
 /**
     \brief  A function to turn a color into a gradient stop
     \param  in_color  The color for the stop
@@ -61,29 +74,25 @@ GimpGrad::unload (Inkscape::Extension::Extension *module)
     hex values from 0 to 255 for color.  Otherwise mostly this is just
     turning the values into strings and returning it.
 */
-Glib::ustring
-GimpGrad::new_stop (ColorRGBA in_color, float location)
+static Glib::ustring
+stop_svg(ColorRGBA const in_color, double const location)
 {
-    char temp_string[25];
-    Glib::ustring mystring("<stop style=\"stop-color:#");
-
-    for (int i = 0; i < 3; i++) {
-        unsigned char temp;
+    Glib::ustring ret("<stop stop-color=\"");
 
-        temp = (unsigned char)(in_color[i] * 255.0);
+    char stop_color_css[16];
+    sp_svg_write_color(stop_color_css, sizeof(stop_color_css), in_color.getIntValue());
+    ret += stop_color_css;
+    ret += '"';
 
-        sprintf(temp_string, "%2.2X", temp);
-        mystring += temp_string;
+    if (in_color[3] != 1) {
+        ret += " stop-opacity=\"";
+        append_css_num(ret, in_color[3]);
+        ret += '"';
     }
-
-    mystring += ";stop-opacity:";
-    sprintf(temp_string, "%1.8f", in_color[3]);
-    mystring += temp_string;
-    mystring += ";\" offset=\"";
-    sprintf(temp_string, "%1.8f", location);
-    mystring += temp_string;
-    mystring += "\"/>\n";
-    return mystring;
+    ret += " offset=\"";
+    append_css_num(ret, location);
+    ret += "\"/>\n";
+    return ret;
 }
 
 /**
@@ -122,98 +131,154 @@ GimpGrad::new_stop (ColorRGBA in_color, float location)
     to Inkscape.
 */
 SPDocument *
-GimpGrad::open (Inkscape::Extension::Input *module, gchar const *filename)
+GimpGrad::open (Inkscape::Extension::Input */*module*/, gchar const *filename)
 {
-    FILE * gradient;
-    // std::cout << "Open filename: " << filename << std::endl;
-
     Inkscape::IO::dump_fopen_call(filename, "I");
-    gradient = Inkscape::IO::fopen_utf8name(filename, "r");
-    if (gradient == NULL) return NULL;
-
-    char tempstr[1024];
-    if (fgets(tempstr, 1024, gradient) == 0) {
-        // std::cout << "Seems that the read failed" << std::endl;
-        fclose(gradient);
-        return NULL;
-    }
-
-    if (!strcmp(tempstr, "GIMP Gradient")) {
-        // std::cout << "This doesn't appear to be a GIMP gradient" << std::endl;
-        fclose(gradient);
-        return NULL;
-    }
-
-    if (fgets(tempstr, 1024, gradient) == 0) {
-        // std::cout << "Seems that the second read failed" << std::endl;
-        fclose(gradient);
+    FILE *gradient = Inkscape::IO::fopen_utf8name(filename, "r");
+    if (gradient == NULL) {
         return NULL;
     }
 
-    if (fgets(tempstr, 1024, gradient) == 0) {
-        // std::cout << "Seems that the third read failed" << std::endl;
-        fclose(gradient);
-        return NULL;
-    }
+    {
+        char tempstr[1024];
+        if (fgets(tempstr, 1024, gradient) == 0) {
+            // std::cout << "Seems that the read failed" << std::endl;
+            goto error;
+        }
+        if (!streq(tempstr, "GIMP Gradient\n")) {
+            // std::cout << "This doesn't appear to be a GIMP gradient" << std::endl;
+            goto error;
+        }
 
-    ColorRGBA last_color(-1.0, -1.0, -1.0, -1.0);
-    float lastlocation = -1.0;
-    Glib::ustring outsvg("<svg><defs><linearGradient>\n");
-    while (fgets(tempstr, 1024, gradient) != 0) {
-        float left, middle, right;
-        float temp_color[4];
-        int type;
-        int color;
-        gchar * end;
-
-        left = g_ascii_strtod(tempstr, &end);
-        middle = g_ascii_strtod(end, &end);
-        right = g_ascii_strtod(end, &end);
-
-        for (int i = 0; i < 4; i++) {
-            temp_color[i] = g_ascii_strtod(end, &end);
+        /* Name field. */
+        if (fgets(tempstr, 1024, gradient) == 0) {
+            // std::cout << "Seems that the second read failed" << std::endl;
+            goto error;
+        }
+        if (!strneq(tempstr, "Name: ", 6)) {
+            goto error;
+        }
+        /* Handle very long names.  (And also handle nul bytes gracefully: don't use strlen.) */
+        while (memchr(tempstr, '\n', sizeof(tempstr) - 1) == NULL) {
+            if (fgets(tempstr, sizeof(tempstr), gradient) == 0) {
+                goto error;
+            }
         }
-        ColorRGBA leftcolor(temp_color[0], temp_color[1], temp_color[2], temp_color[3]);
 
-        for (int i = 0; i < 4; i++) {
-            temp_color[i] = g_ascii_strtod(end, &end);
+        /* n. segments */
+        if (fgets(tempstr, 1024, gradient) == 0) {
+            // std::cout << "Seems that the third read failed" << std::endl;
+            goto error;
+        }
+        char *endptr = NULL;
+        long const n_segs = strtol(tempstr, &endptr, 10);
+        if ((*endptr != '\n')
+            || n_segs < 1) {
+            /* SVG gradients are allowed to have zero stops (treated as `none'), but gimp 2.2
+             * requires at least one segment (i.e. at least two stops) (see gimp_gradient_load in
+             * gimpgradient-load.c).  We try to use the same error handling as gimp, so that
+             * .ggr files that work in one program work in both programs. */
+            goto error;
         }
-        ColorRGBA rightcolor(temp_color[0], temp_color[1], temp_color[2], temp_color[3]);
 
-        sscanf(end, "%d %d", &type, &color);
+        ColorRGBA prev_color(-1.0, -1.0, -1.0, -1.0);
+        Glib::ustring outsvg("<svg><defs><linearGradient>\n");
+        long n_segs_found = 0;
+        double prev_right = 0.0;
+        while (fgets(tempstr, 1024, gradient) != 0) {
+            double dbls[3 + 4 + 4];
+            gchar *p = tempstr;
+            for (unsigned i = 0; i < G_N_ELEMENTS(dbls); ++i) {
+                gchar *end = NULL;
+                double const xi = g_ascii_strtod(p, &end);
+                if (!end || end == p || !g_ascii_isspace(*end)) {
+                    goto error;
+                }
+                if (xi < 0 || 1 < xi) {
+                    goto error;
+                }
+                dbls[i] = xi;
+                p = end + 1;
+            }
+
+            double const left = dbls[0];
+            if (left != prev_right) {
+                goto error;
+            }
+            double const middle = dbls[1];
+            if (!(left <= middle)) {
+                goto error;
+            }
+            double const right = dbls[2];
+            if (!(middle <= right)) {
+                goto error;
+            }
+
+            ColorRGBA const leftcolor(dbls[3], dbls[4], dbls[5], dbls[6]);
+            ColorRGBA const rightcolor(dbls[7], dbls[8], dbls[9], dbls[10]);
+            g_assert(11 == G_N_ELEMENTS(dbls));
+
+            /* Interpolation enums: curve shape and colour space. */
+            {
+                /* TODO: Currently we silently ignore type & color, assuming linear interpolation in
+                 * sRGB space (or whatever the default in SVG is).  See gimp/app/core/gimpgradient.c
+                 * for how gimp uses these.  We could use gimp functions to sample at a few points, and
+                 * add some intermediate stops to convert to the linear/sRGB interpolation */
+                int type; /* enum: linear, curved, sine, sphere increasing, sphere decreasing. */
+                int color_interpolation; /* enum: rgb, hsv anticlockwise, hsv clockwise. */
+                if (sscanf(p, "%d %d", &type, &color_interpolation) != 2) {
+                    continue;
+                }
+            }
+
+            if (prev_color != leftcolor) {
+                outsvg += stop_svg(leftcolor, left);
+            }
+            if (fabs(middle - .5 * (left + right)) > 1e-4) {
+                outsvg += stop_svg(leftcolor.average(rightcolor), middle);
+            }
+            outsvg += stop_svg(rightcolor, right);
+
+            prev_color = rightcolor;
+            prev_right = right;
+            ++n_segs_found;
+        }
+        if (prev_right != 1.0) {
+            goto error;
+        }
 
-        if (!(last_color == leftcolor) || left != lastlocation) {
-            outsvg += new_stop(leftcolor, left);
+        if (n_segs_found != n_segs) {
+            goto error;
         }
-        outsvg += new_stop(leftcolor.average(rightcolor), middle);
-        outsvg += new_stop(rightcolor, right);
 
-        last_color = rightcolor;
-        lastlocation = right;
-    }
+        outsvg += "</linearGradient></defs></svg>";
+
+        // std::cout << "SVG Output: " << outsvg << std::endl;
 
-    outsvg += "</linearGradient></defs></svg>";
+        fclose(gradient);
 
-    // std::cout << "SVG Output: " << outsvg << std::endl;
+        return SPDocument::createNewDocFromMem(outsvg.c_str(), outsvg.length(), TRUE);
+    }
 
+error:
     fclose(gradient);
-
-    return sp_document_new_from_mem(outsvg.c_str(), outsvg.length(), TRUE);
+    return NULL;
 }
 
+#include "clear-n_.h"
+
 void
 GimpGrad::init (void)
 {
     Inkscape::Extension::build_from_mem(
-        "<inkscape-extension>\n"
-            "<name>GIMP Gradients</name>\n"
+        "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+            "<name>" N_("GIMP Gradients") "</name>\n"
             "<id>org.inkscape.input.gimpgrad</id>\n"
-            "<dependency type=\"plugin\" location=\"plugins\">gimpgrad</dependency>\n"
             "<input>\n"
                 "<extension>.ggr</extension>\n"
                 "<mimetype>application/x-gimp-gradient</mimetype>\n"
-                "<filetypename>GIMP Gradient (*.ggr)</filetypename>\n"
-                "<filetypetooltip>Gradients used in GIMP</filetypetooltip>\n"
+                "<filetypename>" N_("GIMP Gradient (*.ggr)") "</filetypename>\n"
+                "<filetypetooltip>" N_("Gradients used in GIMP") "</filetypetooltip>\n"
             "</input>\n"
         "</inkscape-extension>\n", new GimpGrad());
     return;