Code

Added image mask support
authormiklosh <miklosh@users.sourceforge.net>
Mon, 30 Jul 2007 16:10:47 +0000 (16:10 +0000)
committermiklosh <miklosh@users.sourceforge.net>
Mon, 30 Jul 2007 16:10:47 +0000 (16:10 +0000)
src/extension/internal/pdfinput/pdf-parser.cpp
src/extension/internal/pdfinput/svg-builder.cpp
src/extension/internal/pdfinput/svg-builder.h

index a75f468d5317288615aeaa7292d44170f93e410a..1d312c1165004da7c3d6e0b224ae61007afef9fe 100644 (file)
@@ -2431,7 +2431,7 @@ void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
     obj1.free();
 
     // draw it
-    //out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
+    builder->addImageMask(state, str, width, height, invert);
 
   } else {
 
@@ -2621,12 +2621,12 @@ void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
 
     // draw it
     if (haveSoftMask) {
-/*      out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
-                              maskStr, maskWidth, maskHeight, maskColorMap);*/
+        builder->addSoftMaskedImage(state, str, width, height, colorMap,
+                                    maskStr, maskWidth, maskHeight, maskColorMap);
       delete maskColorMap;
     } else if (haveExplicitMask) {
-/*      out->drawMaskedImage(state, ref, str, width, height, colorMap,
-                          maskStr, maskWidth, maskHeight, maskInvert);*/
+        builder->addMaskedImage(state, str, width, height, colorMap,
+                                maskStr, maskWidth, maskHeight, maskInvert);
     } else {
       builder->addImage(state, str, width, height, colorMap,
                        haveColorKeyMask ? maskColors : (int *)NULL);
index 3f1416f16a916dc89585f3193dd9c547cfdc7282..9fc4fa766ab1e5f3f891d1d51791973d2b22bd3d 100644 (file)
@@ -1229,7 +1229,7 @@ Inkscape::XML::Node *SvgBuilder::_createImage(Stream *str, int width, int height
     }
 
     // Set header data
-    if (!invert_alpha) {
+    if ( !invert_alpha && !alpha_only ) {
         png_set_invert_alpha(png_ptr);
     }
     png_color_8 sig_bit;
@@ -1278,20 +1278,39 @@ Inkscape::XML::Node *SvgBuilder::_createImage(Stream *str, int width, int height
         image_stream->reset();
 
         // Convert grayscale values
-        if (color_map) {
-            unsigned char *buffer = new unsigned char[width];
+        unsigned char *buffer = NULL;
+        if ( color_map || invert_alpha ) {
+            buffer = new unsigned char[width];
+        }
+        if ( color_map ) {
             for ( int y = 0 ; y < height ; y++ ) {
                 unsigned char *row = image_stream->getLine();
                 color_map->getGrayLine(row, buffer, width);
+                if (invert_alpha) {
+                    unsigned char *buf_ptr = buffer;
+                    for ( int x = 0 ; x < width ; x++ ) {
+                        *buf_ptr++ = ~(*buf_ptr);
+                    }
+                }
                 png_write_row(png_ptr, (png_bytep)buffer);
             }
-            delete buffer;
         } else {
             for ( int y = 0 ; y < height ; y++ ) {
                 unsigned char *row = image_stream->getLine();
-                png_write_row(png_ptr, (png_bytep)row);
+                if (invert_alpha) {
+                    unsigned char *buf_ptr = buffer;
+                    for ( int x = 0 ; x < width ; x++ ) {
+                        *buf_ptr++ = ~row[x];
+                    }
+                    png_write_row(png_ptr, (png_bytep)buffer);
+                } else {
+                    png_write_row(png_ptr, (png_bytep)row);
+                }
             }
         }
+        if (buffer) {
+            delete buffer;
+        }
     } else if (color_map) {
         image_stream = new ImageStream(str, width,
                                        color_map->getNumPixelComps(),
@@ -1371,6 +1390,43 @@ Inkscape::XML::Node *SvgBuilder::_createImage(Stream *str, int width, int height
     return image_node;
 }
 
+/**
+ * \brief Creates a <mask> with the specified width and height and adds to <defs>
+ *  If we're not the top-level SvgBuilder, creates a <defs> too and adds the mask to it.
+ * \return the created XML node
+ */
+Inkscape::XML::Node *SvgBuilder::_createMask(double width, double height) {
+    Inkscape::XML::Node *mask_node = _xml_doc->createElement("svg:mask");
+    mask_node->setAttribute("maskUnits", "userSpaceOnUse");
+    sp_repr_set_svg_double(mask_node, "x", 0.0);
+    sp_repr_set_svg_double(mask_node, "y", 0.0);
+    sp_repr_set_svg_double(mask_node, "width", width);
+    sp_repr_set_svg_double(mask_node, "height", height);
+    // Append mask to defs
+    if (_is_top_level) {
+        SP_OBJECT_REPR (SP_DOCUMENT_DEFS (_doc))->appendChild(mask_node);
+        Inkscape::GC::release(mask_node);
+        return SP_OBJECT_REPR (SP_DOCUMENT_DEFS (_doc))->lastChild();
+    } else {    // Work around for renderer bug when mask isn't defined in pattern
+        static int mask_count = 0;
+        Inkscape::XML::Node *defs = _root->firstChild();
+        Inkscape::XML::Node *result = NULL;
+        if ( !( defs && !strcmp(defs->name(), "svg:defs") ) ) {
+            // Create <defs> node
+            defs = _xml_doc->createElement("svg:defs");
+            _root->addChild(defs, NULL);
+            Inkscape::GC::release(defs);
+            defs = _root->firstChild();
+        }
+        gchar *mask_id = g_strdup_printf("_mask%d", mask_count++);
+        mask_node->setAttribute("id", mask_id);
+        g_free(mask_id);
+        defs->appendChild(mask_node);
+        Inkscape::GC::release(mask_node);
+        return defs->lastChild();
+    }
+}
+
 void SvgBuilder::addImage(GfxState *state, Stream *str, int width, int height,
                           GfxImageColorMap *color_map, int *mask_colors) {
 
@@ -1381,6 +1437,103 @@ void SvgBuilder::addImage(GfxState *state, Stream *str, int width, int height,
      }
 }
 
+void SvgBuilder::addImageMask(GfxState *state, Stream *str, int width, int height,
+                              bool invert) {
+
+    // Create a rectangle
+    Inkscape::XML::Node *rect = _xml_doc->createElement("svg:rect");
+    sp_repr_set_svg_double(rect, "x", 0.0);
+    sp_repr_set_svg_double(rect, "y", 0.0);
+    sp_repr_set_svg_double(rect, "width", 1.0);
+    sp_repr_set_svg_double(rect, "height", 1.0);
+    svgSetTransform(rect, 1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
+    // Get current fill style and set it on the rectangle
+    SPCSSAttr *css = sp_repr_css_attr_new();
+    _setFillStyle(css, state, false);
+    sp_repr_css_change(rect, css, "style");
+    sp_repr_css_attr_unref(css);
+
+    // Scaling 1x1 surfaces might not work so skip setting a mask with this size
+    if ( width > 1 || height > 1 ) {
+        Inkscape::XML::Node *mask_image_node = _createImage(str, width, height, NULL, NULL, true, invert);
+        if (mask_image_node) {
+            // Create the mask
+            Inkscape::XML::Node *mask_node = _createMask(1.0, 1.0);
+            // Remove unnecessary transformation from the mask image
+            mask_image_node->setAttribute("transform", NULL);
+            mask_node->appendChild(mask_image_node);
+            Inkscape::GC::release(mask_image_node);
+            gchar *mask_url = g_strdup_printf("url(#%s)", mask_node->attribute("id"));
+            rect->setAttribute("mask", mask_url);
+            g_free(mask_url);
+        }
+    }
+
+    // Add the rectangle to the container
+    _container->appendChild(rect);
+    Inkscape::GC::release(rect);
+}
+
+void SvgBuilder::addMaskedImage(GfxState *state, Stream *str, int width, int height,
+                                GfxImageColorMap *color_map,
+                                Stream *mask_str, int mask_width, int mask_height,
+                                bool invert_mask) {
+
+    Inkscape::XML::Node *mask_image_node = _createImage(mask_str, mask_width, mask_height,
+                                                        NULL, NULL, true, invert_mask);
+    Inkscape::XML::Node *image_node = _createImage(str, width, height, color_map, NULL);
+    if ( mask_image_node && image_node ) {
+        // Create mask for the image
+        Inkscape::XML::Node *mask_node = _createMask(1.0, 1.0);
+        // Remove unnecessary transformation from the mask image
+        mask_image_node->setAttribute("transform", NULL);
+        mask_node->appendChild(mask_image_node);
+        // Scale the mask to the size of the image
+        NR::Matrix mask_transform((double)width, 0.0, 0.0, (double)height, 0.0, 0.0);
+        gchar *transform_text = sp_svg_transform_write(mask_transform);
+        mask_node->setAttribute("maskTransform", transform_text);
+        g_free(transform_text);
+        // Set mask and add image
+        gchar *mask_url = g_strdup_printf("url(#%s)", mask_node->attribute("id"));
+        image_node->setAttribute("mask", mask_url);
+        g_free(mask_url);
+        _container->appendChild(image_node);
+    }
+    if (mask_image_node) {
+        Inkscape::GC::release(mask_image_node);
+    }
+    if (image_node) {
+        Inkscape::GC::release(image_node);
+    }
+}
+    
+void SvgBuilder::addSoftMaskedImage(GfxState *state, Stream *str, int width, int height,
+                                    GfxImageColorMap *color_map,
+                                    Stream *mask_str, int mask_width, int mask_height,
+                                    GfxImageColorMap *mask_color_map) {
+
+    Inkscape::XML::Node *mask_image_node = _createImage(mask_str, mask_width, mask_height,
+                                                        mask_color_map, NULL, true);
+    Inkscape::XML::Node *image_node = _createImage(str, width, height, color_map, NULL);
+    if ( mask_image_node && image_node ) {
+        // Create mask for the image
+        Inkscape::XML::Node *mask_node = _createMask(1.0, 1.0);
+        // Remove unnecessary transformation from the mask image
+        mask_image_node->setAttribute("transform", NULL);
+        mask_node->appendChild(mask_image_node);
+        // Set mask and add image
+        gchar *mask_url = g_strdup_printf("url(#%s)", mask_node->attribute("id"));
+        image_node->setAttribute("mask", mask_url);
+        g_free(mask_url);
+        _container->appendChild(image_node);
+    }
+    if (mask_image_node) {
+        Inkscape::GC::release(mask_image_node);
+    }
+    if (image_node) {
+        Inkscape::GC::release(image_node);
+    }
+}
 
 } } } /* namespace Inkscape, Extension, Internal */
 
index 08035e2143e10351cdee8c96bc7d00a5c22e8486..01a4514d6c3bc2273fd598f4a6af580d1edff500 100644 (file)
@@ -98,6 +98,16 @@ public:
     // Image handling
     void addImage(GfxState *state, Stream *str, int width, int height,
                   GfxImageColorMap *color_map, int *mask_colors);
+    void addImageMask(GfxState *state, Stream *str, int width, int height,
+                      bool invert);
+    void addMaskedImage(GfxState *state, Stream *str, int width, int height,
+                        GfxImageColorMap *color_map,
+                        Stream *mask_str, int mask_width, int mask_height,
+                        bool invert_mask);
+    void addSoftMaskedImage(GfxState *state, Stream *str, int width, int height,
+                            GfxImageColorMap *color_map,
+                            Stream *mask_str, int mask_width, int mask_height,
+                            GfxImageColorMap *mask_color_map);
 
     // Text handling
     void beginString(GfxState *state, GooString *s);
@@ -145,6 +155,7 @@ private:
     Inkscape::XML::Node *_createImage(Stream *str, int width, int height,
                                       GfxImageColorMap *color_map, int *mask_colors,
                                       bool alpha_only=false, bool invert_alpha=false);
+    Inkscape::XML::Node *_createMask(double width, double height);
     // Style setting
     SPCSSAttr *_setStyle(GfxState *state, bool fill, bool stroke, bool even_odd=false);
     void _setStrokeStyle(SPCSSAttr *css, GfxState *state);