From 2c93e5fb2d1d7c8bfc72bc11826069686a67b113 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Fri, 11 Mar 2011 16:59:38 +0100 Subject: Add handling filter primitive subregion to filter primitive classes. More correct handling of image placement in feImage filter primitive. (bzr r10092) --- src/display/nr-filter-image.cpp | 156 +++++++++++++++++++++++++++--------- src/display/nr-filter-image.h | 6 +- src/display/nr-filter-primitive.cpp | 112 ++++++++++++++++++++++++-- src/display/nr-filter-primitive.h | 37 ++++----- src/display/nr-filter-units.h | 20 +++++ src/display/nr-filter.cpp | 2 +- src/display/nr-filter.h | 6 +- src/filters/image.cpp | 88 +++++++++++++++----- src/filters/image.h | 6 +- src/sp-filter-primitive.cpp | 39 ++++++++- src/sp-filter-primitive.h | 3 + 11 files changed, 386 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp index cc6dc18fa..9ecec69bf 100644 --- a/src/display/nr-filter-image.cpp +++ b/src/display/nr-filter-image.cpp @@ -6,7 +6,7 @@ * Tavmjong Bah * Abhishek Sharma * - * Copyright (C) 2007 authors + * Copyright (C) 2007-2011 authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -20,6 +20,8 @@ #include "libnr/nr-compose-transform.h" #include "libnr/nr-rect-l.h" #include "preferences.h" +#include "svg/svg-length.h" +#include "enums.h" namespace Inkscape { namespace Filters { @@ -148,60 +150,139 @@ int FilterImage::render(FilterSlot &slot, FilterUnits const &units) { image_pixbuf = image->get_pixels(); has_alpha = image->get_has_alpha(); } - int w,x,y; + + // Viewport is filter primitive area (in user coordinates). + Geom::Rect vp = filter_primitive_area( units ); + double feImageX = vp.min()[Geom::X]; + double feImageY = vp.min()[Geom::Y]; + double feImageWidth = vp.width(); + double feImageHeight = vp.height(); + + // Now that we have the viewport, we must map image inside. + // Partially copied from sp-image.cpp. + + // Do nothing if preserveAspectRatio is "none". + if( aspect_align != SP_ASPECT_NONE ) { + + // Check aspect ratio of image vs. viewport + double feAspect = feImageHeight/feImageWidth; + double aspect = (double)height/(double)width; + bool ratio = (feAspect < aspect); + + double ax, ay; // Align side + switch( aspect_align ) { + case SP_ASPECT_XMIN_YMIN: + ax = 0.0; + ay = 0.0; + break; + case SP_ASPECT_XMID_YMIN: + ax = 0.5; + ay = 0.0; + break; + case SP_ASPECT_XMAX_YMIN: + ax = 1.0; + ay = 0.0; + break; + case SP_ASPECT_XMIN_YMID: + ax = 0.0; + ay = 0.5; + break; + case SP_ASPECT_XMID_YMID: + ax = 0.5; + ay = 0.5; + break; + case SP_ASPECT_XMAX_YMID: + ax = 1.0; + ay = 0.5; + break; + case SP_ASPECT_XMIN_YMAX: + ax = 0.0; + ay = 1.0; + break; + case SP_ASPECT_XMID_YMAX: + ax = 0.5; + ay = 1.0; + break; + case SP_ASPECT_XMAX_YMAX: + ax = 1.0; + ay = 1.0; + break; + default: + ax = 0.0; + ay = 0.0; + break; + } + + if( aspect_clip == SP_ASPECT_SLICE ) { + // image clipped by viewbox + + if( ratio ) { + // clip top/bottom + feImageY -= ay * (feImageWidth * aspect - feImageHeight); + feImageHeight = feImageWidth * aspect; + } else { + // clip sides + feImageX -= ax * (feImageHeight / aspect - feImageWidth); + feImageWidth = feImageHeight / aspect; + } + + } else { + // image fits into viewbox + + if( ratio ) { + // fit to height + feImageX += ax * (feImageWidth - feImageHeight / aspect ); + feImageWidth = feImageHeight / aspect; + } else { + // fit to width + feImageY += ay * (feImageHeight - feImageWidth * aspect); + feImageHeight = feImageWidth * aspect; + } + } + } + + // Set up user coordinates to pix block transforms + double scaleX = width/feImageWidth; + double scaleY = height/feImageHeight; + Geom::Affine unit_trans = units.get_matrix_user2pb().inverse(); + + // Region being drawn on screen. Corresponds to Filter Region in current screen coordinates. + // Note, that the screen is refreshed in horizontal slices with y-axis inverted. NRPixBlock *in = slot.get(_input); if (!in) { g_warning("Missing source image for feImage (in=%d)", _input); return 1; } - - // This section needs to be fully tested!! - - // Region being drawn on screen int x0 = in->area.x0, y0 = in->area.y0; int x1 = in->area.x1, y1 = in->area.y1; - NRPixBlock *out = new NRPixBlock; - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1, true); - w = x1 - x0; + int w = x1 - x0; - // Get the object bounding box. Image is placed with respect to box. - // Array values: 0: width; 3: height; 4: -x; 5: -y. - Geom::Affine object_bbox = units.get_matrix_user2filterunits().inverse(); - - // feImage is suppose to use the same parameters as a normal SVG image. - // If a width or height is set to zero, the image is not suppose to be displayed. - // This does not seem to be what Firefox or Opera does, nor does the W3C displacement - // filter test expect this behavior. If the width and/or height are zero, we use - // the width and height of the object bounding box. - if( feImageWidth == 0 ) feImageWidth = object_bbox[0]; - if( feImageHeight == 0 ) feImageHeight = object_bbox[3]; - - double scaleX = width/feImageWidth; - double scaleY = height/feImageHeight; + Geom::Affine d2s = Geom::Translate(x0, y0) * unit_trans * Geom::Translate(-feImageX,-feImageY) * Geom::Scale(scaleX, scaleY); - int coordx,coordy; + // Set up pix block + NRPixBlock *out = new NRPixBlock; + nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1, true); unsigned char *out_data = NR_PIXBLOCK_PX(out); - Geom::Affine unit_trans = units.get_matrix_primitiveunits2pb().inverse(); - Geom::Affine d2s = Geom::Translate(x0, y0) * unit_trans * Geom::Translate(object_bbox[4]-feImageX, object_bbox[5]-feImageY) * Geom::Scale(scaleX, scaleY); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int nr_arena_image_x_sample = prefs->getInt("/options/bitmapoversample/value", 1); int nr_arena_image_y_sample = nr_arena_image_x_sample; + if (has_alpha) { nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM(out_data, x1-x0, y1-y0, 4*w, image_pixbuf, width, height, rowstride, d2s, 255, nr_arena_image_x_sample, nr_arena_image_y_sample); } else { - for (x=x0; x < x1; x++){ - for (y=y0; y < y1; y++){ + for (int x=x0; x < x1; x++){ + for (int y=y0; y < y1; y++){ //TODO: use interpolation // Temporarily add 0.5 so we sample center of "cell" - double indexX = scaleX * (((x+0.5) * unit_trans[0] + unit_trans[4]) - feImageX + object_bbox[4]); - double indexY = scaleY * (((y+0.5) * unit_trans[3] + unit_trans[5]) - feImageY + object_bbox[5]); + double indexX = scaleX * (((x+0.5) * unit_trans[0] + unit_trans[4]) - feImageX); + double indexY = scaleY * (((y+0.5) * unit_trans[3] + unit_trans[5]) - feImageY); // coordx == 0 and coordy == 0 must be included, but we protect // against negative numbers which round up to 0 with (int). - coordx = ( indexX >= 0 ? int( indexX ) : -1 ); - coordy = ( indexY >= 0 ? int( indexY ) : -1 ); + int coordx = ( indexX >= 0 ? int( indexX ) : -1 ); + int coordy = ( indexY >= 0 ? int( indexY ) : -1 ); if (coordx >= 0 && coordx < width && coordy >= 0 && coordy < height){ out_data[4*((x - x0)+w*(y - y0)) ] = (unsigned char) image_pixbuf[3*coordx + rowstride*coordy ]; //Red out_data[4*((x - x0)+w*(y - y0)) + 1] = (unsigned char) image_pixbuf[3*coordx + rowstride*coordy + 1]; //Green @@ -230,11 +311,12 @@ void FilterImage::set_document(SPDocument *doc){ document = doc; } -void FilterImage::set_region(SVGLength x, SVGLength y, SVGLength width, SVGLength height){ - feImageX=x.computed; - feImageY=y.computed; - feImageWidth=width.computed; - feImageHeight=height.computed; +void FilterImage::set_align( unsigned int align ) { + aspect_align = align; +} + +void FilterImage::set_clip( unsigned int clip ) { + aspect_clip = clip; } FilterTraits FilterImage::get_input_traits() { diff --git a/src/display/nr-filter-image.h b/src/display/nr-filter-image.h index 7b2fa8bc7..ecbc9597d 100644 --- a/src/display/nr-filter-image.h +++ b/src/display/nr-filter-image.h @@ -31,7 +31,8 @@ public: virtual FilterTraits get_input_traits(); void set_document( SPDocument *document ); void set_href(const gchar *href); - void set_region(SVGLength x, SVGLength y, SVGLength width, SVGLength height); + void set_align( unsigned int align ); + void set_clip( unsigned int clip ); bool from_element; SPItem* SVGElem; @@ -41,7 +42,8 @@ private: guint8* image_pixbuf; Glib::RefPtr image; int width, height, rowstride; - float feImageX,feImageY,feImageWidth,feImageHeight; + unsigned int aspect_align : 4; + unsigned int aspect_clip : 1; bool has_alpha; }; diff --git a/src/display/nr-filter-primitive.cpp b/src/display/nr-filter-primitive.cpp index f53ff4a7f..1af86ec3e 100644 --- a/src/display/nr-filter-primitive.cpp +++ b/src/display/nr-filter-primitive.cpp @@ -5,6 +5,7 @@ * * Author: * Niko Kiirala + * Tavmjong Bah (primitive subregion) * * Copyright (C) 2006 Niko Kiirala * @@ -19,17 +20,27 @@ namespace Inkscape { namespace Filters { +using Geom::X; +using Geom::Y; + FilterPrimitive::FilterPrimitive() { _input = NR_FILTER_SLOT_NOT_SET; _output = NR_FILTER_SLOT_NOT_SET; - // These defaults are according to SVG standard. + // Primitive subregion, should default to the union of all subregions of referenced nodes + // (i.e. other filter primitives except feTile). If no referenced nodes, defaults to filter + // region expressed in percent. At the moment, we do not check referenced nodes. + + // We must keep track if a value is set or not, if not set then the region defaults to 0%, 0%, + // 100%, 100% ("x", "y", "width", "height") of the -> filter <- region. If set, then + // percentages are in terms of bounding box or viewbox, depending on value of "primitiveUnits". + // NB: SVGLength.set takes prescaled percent values: 1 means 100% - _region_x.set(SVGLength::PERCENT, 0, 0); - _region_y.set(SVGLength::PERCENT, 0, 0); - _region_width.set(SVGLength::PERCENT, 1, 0); - _region_height.set(SVGLength::PERCENT, 1, 0); + _subregion_x.unset(SVGLength::PERCENT, 0, 0); + _subregion_y.unset(SVGLength::PERCENT, 0, 0); + _subregion_width.unset(SVGLength::PERCENT, 1, 0); + _subregion_height.unset(SVGLength::PERCENT, 1, 0); } FilterPrimitive::~FilterPrimitive() @@ -54,6 +65,97 @@ void FilterPrimitive::set_output(int slot) { if (slot >= 0) _output = slot; } +// We need to copy reference even if unset as we need to know if +// someone has unset a value. +void FilterPrimitive::set_x(SVGLength const &length) +{ + _subregion_x = length; +} + +void FilterPrimitive::set_y(SVGLength const &length) +{ + _subregion_y = length; +} +void FilterPrimitive::set_width(SVGLength const &length) +{ + _subregion_width = length; +} +void FilterPrimitive::set_height(SVGLength const &length) +{ + _subregion_height = length; +} + +void FilterPrimitive::set_subregion(SVGLength const &x, SVGLength const &y, + SVGLength const &width, SVGLength const &height) { + _subregion_x = x; + _subregion_y = y; + _subregion_width = width; + _subregion_height = height; +} + +Geom::Rect FilterPrimitive::filter_primitive_area(FilterUnits const &units) +{ + Geom::OptRect bb = units.get_item_bbox(); + Geom::OptRect fa = units.get_filter_area(); + + /* Update computed values for ex, em, %. For %, assumes primitive unit is objectBoundingBox. */ + /* TODO: fetch somehow the object ex and em lengths; 12, 6 are just dummy values. */ + double len_x = bb->width(); + double len_y = bb->height(); + _subregion_x.update(12, 6, len_x); + _subregion_y.update(12, 6, len_y); + _subregion_width.update(12, 6, len_x); + _subregion_height.update(12, 6, len_y); + + // x, y, width, and height are independently defined (i.e. one can be defined, by default, to + // the filter area while another is defined relative to the bounding box). It is better to keep + // track of them separately and then compose the Rect at the end. + double x = 0; + double y = 0; + double width = 0; + double height = 0; + + // If subregion not set, by special case use filter region. + if( !_subregion_x._set ) x = fa->min()[X]; + if( !_subregion_y._set ) y = fa->min()[Y]; + if( !_subregion_width._set ) width = fa->width(); + if( !_subregion_height._set ) height = fa->height(); + + if( units.get_primitive_units() == SP_FILTER_UNITS_OBJECTBOUNDINGBOX ) { + // Values are in terms of fraction of bounding box. + if( _subregion_x._set && _subregion_x.unit != SVGLength::PERCENT ) x = bb->min()[X] + bb->width() * _subregion_x.value; + if( _subregion_y._set && _subregion_y.unit != SVGLength::PERCENT ) y = bb->min()[Y] + bb->height() * _subregion_y.value; + if( _subregion_width._set && _subregion_width.unit != SVGLength::PERCENT ) width = bb->width() * _subregion_width.value; + if( _subregion_height._set && _subregion_height.unit != SVGLength::PERCENT ) height = bb->height() * _subregion_height.value; + // Values are in terms of percent + if( _subregion_x._set && _subregion_x.unit == SVGLength::PERCENT ) x = bb->min()[X] + _subregion_x.computed; + if( _subregion_y._set && _subregion_y.unit == SVGLength::PERCENT ) y = bb->min()[Y] + _subregion_y.computed; + if( _subregion_width._set && _subregion_width.unit == SVGLength::PERCENT ) width = _subregion_width.computed; + if( _subregion_height._set && _subregion_height.unit == SVGLength::PERCENT ) height = _subregion_height.computed; + } else { + // Values are in terms of user space coordinates or percent of viewbox (yuck!), + // which is usually the size of SVG drawing. Default. + if( _subregion_x._set && _subregion_x.unit != SVGLength::PERCENT ) x = _subregion_x.computed; + if( _subregion_y._set && _subregion_y.unit != SVGLength::PERCENT ) y = _subregion_y.computed; + if( _subregion_width._set && _subregion_width.unit != SVGLength::PERCENT ) width = _subregion_width.computed; + if( _subregion_height._set && _subregion_height.unit != SVGLength::PERCENT ) height = _subregion_height.computed; + // TODO: add percent of viewport TEMPORARY HACK FOR TESTING... + if( _subregion_x._set && _subregion_x.unit == SVGLength::PERCENT ) x = _subregion_x.value * 480; // viewport_x + if( _subregion_y._set && _subregion_y.unit == SVGLength::PERCENT ) y = _subregion_y.value * 360; + if( _subregion_width._set && _subregion_width.unit == SVGLength::PERCENT ) width = _subregion_width.value * 480; + if( _subregion_height._set && _subregion_height.unit == SVGLength::PERCENT ) height = _subregion_height.value * 360; + } + + Geom::Point minp, maxp; + minp[X] = x; + minp[Y] = y; + maxp[X] = x + width; + maxp[Y] = y + height; + + Geom::Rect area(minp, maxp); + return area; +} + FilterTraits FilterPrimitive::get_input_traits() { return TRAIT_ANYTHING; } diff --git a/src/display/nr-filter-primitive.h b/src/display/nr-filter-primitive.h index 7639a9d1d..0e8df3813 100644 --- a/src/display/nr-filter-primitive.h +++ b/src/display/nr-filter-primitive.h @@ -78,27 +78,27 @@ public: */ virtual void set_output(int slot); - void set_x(SVGLength &length); - void set_y(SVGLength &length); - void set_width(SVGLength &length); - void set_height(SVGLength &length); - /** * Sets the filter primitive subregion. Passing an unset length - * (length._set == false) as any parameter results in that parameter - * not being changed. - * Filter primitive will not hold any references to the passed - * SVGLength object after function returns. - * If any of the parameters does not get set the default value, as - * defined in SVG standard, for that parameter is used instead. + * (length._set == false) WILL change the parameter as it is + * important to know if a parameter is unset. */ - void set_region(SVGLength &x, SVGLength &y, - SVGLength &width, SVGLength &height); + void set_x(SVGLength const &length); + void set_y(SVGLength const &length); + void set_width(SVGLength const &length); + void set_height(SVGLength const &length); + void set_subregion(SVGLength const &x, SVGLength const &y, + SVGLength const &width, SVGLength const &height); /** * Resets the filter primitive subregion to its default value */ - void reset_region(); + void reset_subregion(); // Not implemented + + /** + * Returns the filter primitive area in user coordinate system. + */ + Geom::Rect filter_primitive_area(FilterUnits const &units); /** * Queries the filter, which traits it needs from its input buffers. @@ -112,10 +112,11 @@ protected: int _input; int _output; - SVGLength _region_x; - SVGLength _region_y; - SVGLength _region_width; - SVGLength _region_height; + /* Filter primitive subregion */ + SVGLength _subregion_x; + SVGLength _subregion_y; + SVGLength _subregion_width; + SVGLength _subregion_height; }; diff --git a/src/display/nr-filter-units.h b/src/display/nr-filter-units.h index 9a0d8ea83..b78e102cd 100644 --- a/src/display/nr-filter-units.h +++ b/src/display/nr-filter-units.h @@ -61,6 +61,26 @@ public: */ void set_automatic_resolution(bool const automatic); + /** + * Gets the item bounding box in user coordinates + */ + Geom::OptRect get_item_bbox() const { return item_bbox; }; + + /** + * Gets the filter effects area in user coordinates + */ + Geom::OptRect get_filter_area() const { return filter_area; }; + + /** + * Gets Filter Units (userSpaceOnUse or objectBoundingBox) + */ + SPFilterUnits get_filter_units() const { return filterUnits; }; + + /** + * Gets Primitive Units (userSpaceOnUse or objectBoundingBox) + */ + SPFilterUnits get_primitive_units() const { return primitiveUnits; }; + /** * Gets the user coordinates to pixblock coordinates transformation matrix. */ diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp index ea9b4f7da..85815c31a 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -326,7 +326,7 @@ Geom::Rect Filter::filter_effect_area(Geom::Rect const &bbox) minp[Y] = _region_y.computed; maxp[Y] = minp[Y] + _region_height.computed; } else { - g_warning("Error in Inkscape::Filters::Filter::bbox_enlarge: unrecognized value of _filter_units"); + g_warning("Error in Inkscape::Filters::Filter::filter_effect_area: unrecognized value of _filter_units"); } Geom::Rect area(minp, maxp); return area; diff --git a/src/display/nr-filter.h b/src/display/nr-filter.h index 23acea99f..ae5f96420 100644 --- a/src/display/nr-filter.h +++ b/src/display/nr-filter.h @@ -92,8 +92,8 @@ public: * If any of these parameters does not get set, the default value, as * defined in SVG standard, for that parameter is used instead. */ - void set_region(SVGLength &x, SVGLength &y, - SVGLength &width, SVGLength &height); + void set_region(SVGLength const &x, SVGLength const &y, + SVGLength const &width, SVGLength const &height); /** * Resets the filter effects region to its default value as defined @@ -131,7 +131,7 @@ public: void set_filter_units(SPFilterUnits unit); /** - * Set the primitiveUnits-properterty. If not set, the default value of + * Set the primitiveUnits-property. If not set, the default value of * userSpaceOnUse is used. If the parameter value is not a valid * enumeration value from SPFilterUnits, no changes to filter state * are made. diff --git a/src/filters/image.cpp b/src/filters/image.cpp index ebad7a002..f6ef310f7 100644 --- a/src/filters/image.cpp +++ b/src/filters/image.cpp @@ -19,6 +19,7 @@ #endif #include "uri.h" #include "uri-references.h" +#include "enums.h" #include "attributes.h" #include "svg/svg.h" #include "image.h" @@ -78,8 +79,10 @@ static void sp_feImage_class_init(SPFeImageClass *klass) sp_primitive_class->build_renderer = sp_feImage_build_renderer; } -static void sp_feImage_init(SPFeImage */*feImage*/) +static void sp_feImage_init(SPFeImage *feImage) { + feImage->aspect_align = SP_ASPECT_XMID_YMID; // Default + feImage->aspect_clip = SP_ASPECT_MEET; // Default } /** @@ -99,10 +102,7 @@ static void sp_feImage_build(SPObject *object, SPDocument *document, Inkscape::X /*LOAD ATTRIBUTES FROM REPR HERE*/ - object->readAttr( "x" ); - object->readAttr( "y" ); - object->readAttr( "width" ); - object->readAttr( "height" ); + object->readAttr( "preserveAspectRatio" ); object->readAttr( "xlink:href" ); } @@ -185,22 +185,67 @@ static void sp_feImage_set(SPObject *object, unsigned int key, gchar const *valu } break; - case SP_ATTR_X: - feImage->x.readOrUnset(value); - object->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_Y: - feImage->y.readOrUnset(value); - object->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_WIDTH: - feImage->width.readOrUnset(value); - object->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_HEIGHT: - feImage->height.readOrUnset(value); - object->requestModified(SP_OBJECT_MODIFIED_FLAG); + case SP_ATTR_PRESERVEASPECTRATIO: + /* Copied from sp-image.cpp */ + /* Do setup before, so we can use break to escape */ + feImage->aspect_align = SP_ASPECT_XMID_YMID; // Default + feImage->aspect_clip = SP_ASPECT_MEET; // Default + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); + if (value) { + int len; + gchar c[256]; + const gchar *p, *e; + unsigned int align, clip; + p = value; + while (*p && *p == 32) p += 1; + if (!*p) break; + e = p; + while (*e && *e != 32) e += 1; + len = e - p; + if (len > 8) break; + memcpy (c, value, len); + c[len] = 0; + /* Now the actual part */ + if (!strcmp (c, "none")) { + align = SP_ASPECT_NONE; + } else if (!strcmp (c, "xMinYMin")) { + align = SP_ASPECT_XMIN_YMIN; + } else if (!strcmp (c, "xMidYMin")) { + align = SP_ASPECT_XMID_YMIN; + } else if (!strcmp (c, "xMaxYMin")) { + align = SP_ASPECT_XMAX_YMIN; + } else if (!strcmp (c, "xMinYMid")) { + align = SP_ASPECT_XMIN_YMID; + } else if (!strcmp (c, "xMidYMid")) { + align = SP_ASPECT_XMID_YMID; + } else if (!strcmp (c, "xMaxYMid")) { + align = SP_ASPECT_XMAX_YMID; + } else if (!strcmp (c, "xMinYMax")) { + align = SP_ASPECT_XMIN_YMAX; + } else if (!strcmp (c, "xMidYMax")) { + align = SP_ASPECT_XMID_YMAX; + } else if (!strcmp (c, "xMaxYMax")) { + align = SP_ASPECT_XMAX_YMAX; + } else { + g_warning("Illegal preserveAspectRatio: %s", c); + break; + } + clip = SP_ASPECT_MEET; + while (*e && *e == 32) e += 1; + if (*e) { + if (!strcmp (e, "meet")) { + clip = SP_ASPECT_MEET; + } else if (!strcmp (e, "slice")) { + clip = SP_ASPECT_SLICE; + } else { + break; + } + } + feImage->aspect_align = align; + feImage->aspect_clip = clip; + } break; + default: if (((SPObjectClass *) feImage_parent_class)->set) ((SPObjectClass *) feImage_parent_class)->set(object, key, value); @@ -259,7 +304,8 @@ static void sp_feImage_build_renderer(SPFilterPrimitive *primitive, Inkscape::Fi nr_image->from_element = sp_image->from_element; nr_image->SVGElem = sp_image->SVGElem; - nr_image->set_region(sp_image->x, sp_image->y, sp_image->width, sp_image->height); + nr_image->set_align( sp_image->aspect_align ); + nr_image->set_clip( sp_image->aspect_clip ); nr_image->set_href(sp_image->href); nr_image->set_document(sp_image->document); } diff --git a/src/filters/image.h b/src/filters/image.h index 2445da5c6..535dd80fa 100644 --- a/src/filters/image.h +++ b/src/filters/image.h @@ -26,7 +26,11 @@ class SPFeImageClass; struct SPFeImage : public SPFilterPrimitive { /** IMAGE ATTRIBUTES HERE */ gchar *href; - SVGLength x, y, height, width; + + /* preserveAspectRatio */ + unsigned int aspect_align : 4; + unsigned int aspect_clip : 1; + SPDocument *document; bool from_element; SPItem* SVGElem; diff --git a/src/sp-filter-primitive.cpp b/src/sp-filter-primitive.cpp index 87f96fb90..c186c7fd9 100644 --- a/src/sp-filter-primitive.cpp +++ b/src/sp-filter-primitive.cpp @@ -84,6 +84,16 @@ sp_filter_primitive_init(SPFilterPrimitive *filter_primitive) { filter_primitive->image_in = Inkscape::Filters::NR_FILTER_SLOT_NOT_SET; filter_primitive->image_out = Inkscape::Filters::NR_FILTER_SLOT_NOT_SET; + + // We must keep track if a value is set or not, if not set then the region defaults to 0%, 0%, + // 100%, 100% ("x", "y", "width", "height") of the -> filter <- region. If set then + // percentages are in terms of bounding box or viewbox, depending on value of "primitiveUnits" + + // NB: SVGLength.set takes prescaled percent values: 1 means 100% + filter_primitive->x.unset(SVGLength::PERCENT, 0, 0); + filter_primitive->y.unset(SVGLength::PERCENT, 0, 0); + filter_primitive->width.unset(SVGLength::PERCENT, 1, 0); + filter_primitive->height.unset(SVGLength::PERCENT, 1, 0); } /** @@ -100,6 +110,10 @@ sp_filter_primitive_build(SPObject *object, SPDocument *document, Inkscape::XML: object->readAttr( "in" ); object->readAttr( "result" ); + object->readAttr( "x" ); + object->readAttr( "y" ); + object->readAttr( "width" ); + object->readAttr( "height" ); } /** @@ -121,7 +135,6 @@ sp_filter_primitive_set(SPObject *object, unsigned int key, gchar const *value) { SPFilterPrimitive *filter_primitive = SP_FILTER_PRIMITIVE(object); (void)filter_primitive; - int image_nr; switch (key) { case SP_ATTR_IN: @@ -146,6 +159,24 @@ sp_filter_primitive_set(SPObject *object, unsigned int key, gchar const *value) object->parent->requestModified(SP_OBJECT_MODIFIED_FLAG); } break; + + /* Filter primitive sub-region */ + case SP_ATTR_X: + filter_primitive->x.readOrUnset(value); + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_Y: + filter_primitive->y.readOrUnset(value); + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_WIDTH: + filter_primitive->width.readOrUnset(value); + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_HEIGHT: + filter_primitive->height.readOrUnset(value); + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; } /* See if any parents need this value. */ @@ -165,6 +196,10 @@ sp_filter_primitive_update(SPObject *object, SPCtx *ctx, guint flags) if (flags & SP_OBJECT_MODIFIED_FLAG) { object->readAttr( "in" ); object->readAttr( "result" ); + object->readAttr( "x" ); + object->readAttr( "y" ); + object->readAttr( "width" ); + object->readAttr( "height" ); } if (((SPObjectClass *) filter_primitive_parent_class)->update) { @@ -191,6 +226,7 @@ sp_filter_primitive_write(SPObject *object, Inkscape::XML::Document *doc, Inksca gchar const *out_name = sp_filter_name_for_image(parent, prim->image_out); repr->setAttribute("result", out_name); + /* Do we need to add x,y,width,height? */ if (((SPObjectClass *) filter_primitive_parent_class)->write) { ((SPObjectClass *) filter_primitive_parent_class)->write(object, doc, repr, flags); } @@ -279,6 +315,7 @@ void sp_filter_primitive_renderer_common(SPFilterPrimitive *sp_prim, Inkscape::F nr_prim->set_output(sp_prim->image_out); /* TODO: place here code to handle input images, filter area etc. */ + nr_prim->set_subregion( sp_prim->x, sp_prim->y, sp_prim->width, sp_prim->height ); } diff --git a/src/sp-filter-primitive.h b/src/sp-filter-primitive.h index 92f36e7ae..8922ac20a 100644 --- a/src/sp-filter-primitive.h +++ b/src/sp-filter-primitive.h @@ -30,6 +30,9 @@ class SPFilterPrimitiveClass; struct SPFilterPrimitive : public SPObject { int image_in, image_out; + + /* filter primitive subregion */ + SVGLength x, y, height, width; }; struct SPFilterPrimitiveClass { -- cgit v1.2.3