diff options
| author | Tavmjong Bah <tavmjong@free.fr> | 2014-02-06 14:29:15 +0000 |
|---|---|---|
| committer | tavmjong-free <tavmjong@free.fr> | 2014-02-06 14:29:15 +0000 |
| commit | 7dd239eed97761b22ef635b6896a8f65c4939462 (patch) | |
| tree | a9c4eb95e2ae1356cd584151683dc9373e4f1140 /src | |
| parent | Extensions->Render submenu. Scaling of input parameters into document units, ... (diff) | |
| download | inkscape-7dd239eed97761b22ef635b6896a8f65c4939462.tar.gz inkscape-7dd239eed97761b22ef635b6896a8f65c4939462.zip | |
Added new base class to handle viewBox and preserveAspectRatio.
Updated sp-root, sp-symbol, sp-image, sp-pattern, marker to use new class.
Fixed some viewport issues when % used.
(bzr r13002)
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/Makefile_insert | 3 | ||||
| -rw-r--r-- | src/document.cpp | 10 | ||||
| -rw-r--r-- | src/marker.cpp | 257 | ||||
| -rw-r--r-- | src/marker.h | 14 | ||||
| -rw-r--r-- | src/sp-image.cpp | 347 | ||||
| -rw-r--r-- | src/sp-image.h | 14 | ||||
| -rw-r--r-- | src/sp-pattern.cpp | 55 | ||||
| -rw-r--r-- | src/sp-pattern.h | 6 | ||||
| -rw-r--r-- | src/sp-root.cpp | 327 | ||||
| -rw-r--r-- | src/sp-root.h | 15 | ||||
| -rw-r--r-- | src/sp-symbol.cpp | 234 | ||||
| -rw-r--r-- | src/sp-symbol.h | 15 | ||||
| -rw-r--r-- | src/viewbox.cpp | 277 | ||||
| -rw-r--r-- | src/viewbox.h | 66 |
15 files changed, 601 insertions, 1042 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54b15d342..d1a3d194e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -78,7 +78,7 @@ set(sp_SRC sp-use-reference.cpp sp-use.cpp splivarot.cpp - + viewbox.cpp # ------- # Headers @@ -163,6 +163,7 @@ set(sp_SRC sp-tspan.h sp-use-reference.h sp-use.h + viewbox.h ) set(inkscape_SRC diff --git a/src/Makefile_insert b/src/Makefile_insert index 5e441560a..7aedb38ee 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -228,7 +228,8 @@ ink_common_sources += \ uri-references.cpp uri-references.h \ vanishing-point.cpp vanishing-point.h \ verbs.cpp verbs.h \ - version.cpp version.h + version.cpp version.h \ + viewbox.cpp viewbox.h # Additional dependencies diff --git a/src/document.cpp b/src/document.cpp index 634462001..6f957cace 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -381,16 +381,6 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc, rroot->setAttribute("inkscape:version", Inkscape::version_string); /* fixme: Again, I moved these here to allow version determining in ::build (Lauris) */ - /* Quick hack 2 - get default image size into document */ - if (!rroot->attribute("width")) rroot->setAttribute("width", "100%"); - if (!rroot->attribute("height")) rroot->setAttribute("height", "100%"); - /* End of quick hack 2 */ - - /* Quick hack 3 - Set uri attributes */ -// if (uri) { // this is done in do_change_uri() -// rroot->setAttribute("sodipodi:docname", uri); -// } - /* End of quick hack 3 */ /* Eliminate obsolete sodipodi:docbase, for privacy reasons */ rroot->setAttribute("sodipodi:docbase", NULL); diff --git a/src/marker.cpp b/src/marker.cpp index b9464186d..f6e55b3ec 100644 --- a/src/marker.cpp +++ b/src/marker.cpp @@ -47,18 +47,15 @@ namespace { bool markerRegistered = SPFactory::instance().registerObject("svg:marker", createMarker); } -SPMarker::SPMarker() : SPGroup() { - this->aspect_clip = 0; - this->aspect_align = 0; - this->aspect_set = 0; - this->markerUnits = 0; - this->orient_auto = 0; - this->markerUnits_set = 0; - this->orient_set = 0; - this->orient = 0; - - this->viewBox = Geom::OptRect(); - this->c2p.setIdentity(); +SPMarker::SPMarker() : SPGroup(), SPViewBox() { + + this->markerUnits = 0; + this->markerUnits_set = 0; + + this->orient_auto = 0; + this->orient_set = 0; + this->orient = 0; + this->views = NULL; } @@ -177,133 +174,14 @@ void SPMarker::set(unsigned int key, const gchar* value) { break; case SP_ATTR_VIEWBOX: - this->viewBox = Geom::OptRect(); - - if (value) { - double x, y, width, height; - char *eptr; - - /* fixme: We have to take original item affine into account */ - /* fixme: Think (Lauris) */ - eptr = (gchar *) value; - x = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - y = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - width = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - height = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - if ((width > 0) && (height > 0)) { - /* Set viewbox */ - this->viewBox = Geom::Rect(Geom::Point(x, y), Geom::Point(x + width, y + height)); - } - } - - this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); - break; + set_viewBox( value ); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); + break; case SP_ATTR_PRESERVEASPECTRATIO: - /* Do setup before, so we can use break to escape */ - this->aspect_set = FALSE; - this->aspect_align = SP_ASPECT_NONE; - this->aspect_clip = SP_ASPECT_MEET; - - this->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 { - 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; - } - } - - this->aspect_set = TRUE; - this->aspect_align = align; - this->aspect_clip = clip; - } - break; + set_preserveAspectRatio( value ); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); + break; default: SPGroup::set(key, value); @@ -312,112 +190,23 @@ void SPMarker::set(unsigned int key, const gchar* value) { } void SPMarker::update(SPCtx *ctx, guint flags) { - SPItemCtx rctx; - // fixme: We have to set up clip here too + SPItemCtx ictx; // Copy parent context - rctx.flags = ctx->flags; + ictx.flags = ctx->flags; // Initialize transformations - rctx.i2doc = Geom::identity(); - rctx.i2vp = Geom::identity(); + ictx.i2doc = Geom::identity(); + ictx.i2vp = Geom::identity(); // Set up viewport - rctx.viewport = Geom::Rect::from_xywh(0, 0, this->markerWidth.computed, this->markerHeight.computed); - - // Start with identity transform - this->c2p.setIdentity(); + ictx.viewport = Geom::Rect::from_xywh(0, 0, this->markerWidth.computed, this->markerHeight.computed); - // Viewbox is always present, either implicitly or explicitly - Geom::Rect vb; - if (this->viewBox) { - vb = *this->viewBox; - } else { - vb = rctx.viewport; - } + SPItemCtx rctx = get_rctx( &ictx ); - // Now set up viewbox transformation - - // Determine actual viewbox in viewport coordinates - // double x = 0; - // double y = 0; - double width = 0; - double height = 0; - - if (this->aspect_align == SP_ASPECT_NONE) { - // x = 0.0; - // y = 0.0; - width = rctx.viewport.width(); - height = rctx.viewport.height(); - } else { - double scalex, scaley, scale; - // Things are getting interesting - scalex = rctx.viewport.width() / (vb.width()); - scaley = rctx.viewport.height() / (vb.height()); - scale = (this->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley); - width = (vb.width()) * scale; - height = (vb.height()) * scale; - - // Now place viewbox to requested position - /*switch (marker->aspect_align) { - case SP_ASPECT_XMIN_YMIN: - x = 0.0; - y = 0.0; - break; - case SP_ASPECT_XMID_YMIN: - x = 0.5 * (rctx.viewport.width() - width); - y = 0.0; - break; - case SP_ASPECT_XMAX_YMIN: - x = 1.0 * (rctx.viewport.width() - width); - y = 0.0; - break; - case SP_ASPECT_XMIN_YMID: - x = 0.0; - y = 0.5 * (rctx.viewport.height() - height); - break; - case SP_ASPECT_XMID_YMID: - x = 0.5 * (rctx.viewport.width() - width); - y = 0.5 * (rctx.viewport.height() - height); - break; - case SP_ASPECT_XMAX_YMID: - x = 1.0 * (rctx.viewport.width() - width); - y = 0.5 * (rctx.viewport.height() - height); - break; - case SP_ASPECT_XMIN_YMAX: - x = 0.0; - y = 1.0 * (rctx.viewport.height() - height); - break; - case SP_ASPECT_XMID_YMAX: - x = 0.5 * (rctx.viewport.width() - width); - y = 1.0 * (rctx.viewport.height() - height); - break; - case SP_ASPECT_XMAX_YMAX: - x = 1.0 * (rctx.viewport.width() - width); - y = 1.0 * (rctx.viewport.height() - height); - break; - default: - x = 0.0; - y = 0.0; - break; - }*/ - } - - // TODO fixme: all that work is done to figure out x and y, which are just ignored. Check why. - - // viewbox transformation and reference translation - this->c2p = Geom::Translate(-this->refX.computed, -this->refY.computed) * - Geom::Scale(width / vb.width(), height / vb.height()); - - rctx.i2doc = this->c2p * rctx.i2doc; - - // If viewBox is set reinitialize child viewport - // Otherwise it already correct - if (this->viewBox) { - rctx.viewport = *this->viewBox; - rctx.i2vp = Geom::identity(); - } + // Shift according to refX, refY + this->c2p = Geom::Translate(this->viewBox.left()-this->refX.computed, this->viewBox.top()-this->refY.computed) * this->c2p; // And invoke parent method SPGroup::update((SPCtx *) &rctx, flags); diff --git a/src/marker.h b/src/marker.h index b780950de..d41df69b4 100644 --- a/src/marker.h +++ b/src/marker.h @@ -31,8 +31,9 @@ struct SPMarkerView; #include "sp-item-group.h" #include "sp-marker-loc.h" #include "uri-references.h" +#include "viewbox.h" -class SPMarker : public SPGroup { +class SPMarker : public SPGroup, public SPViewBox { public: SPMarker(); virtual ~SPMarker(); @@ -54,17 +55,6 @@ public: unsigned int orient_auto : 1; float orient; - /* viewBox; */ - Geom::OptRect viewBox; - - /* preserveAspectRatio */ - unsigned int aspect_set : 1; - unsigned int aspect_align : 4; - unsigned int aspect_clip : 1; - - /* Child to parent additional transform */ - Geom::Affine c2p; - /* Private views */ SPMarkerView *views; diff --git a/src/sp-image.cpp b/src/sp-image.cpp index 8f7a60ca6..5f630f7b7 100644 --- a/src/sp-image.cpp +++ b/src/sp-image.cpp @@ -119,14 +119,12 @@ SPObject* createImage() { bool imageRegistered = SPFactory::instance().registerObject("svg:image", createImage); } -SPImage::SPImage() : SPItem() { - this->aspect_clip = 0; +SPImage::SPImage() : SPItem(), SPViewBox() { this->x.unset(); this->y.unset(); this->width.unset(); this->height.unset(); - this->aspect_align = SP_ASPECT_NONE; this->clipbox = Geom::Rect(); this->sx = this->sy = 1.0; this->ox = this->oy = 0.0; @@ -195,8 +193,8 @@ void SPImage::set(unsigned int key, const gchar* value) { break; case SP_ATTR_X: - if (!this->x.readAbsolute(value)) { - /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ + /* ex, em not handled correctly. */ + if (!this->x.read(value)) { this->x.unset(); } @@ -204,8 +202,8 @@ void SPImage::set(unsigned int key, const gchar* value) { break; case SP_ATTR_Y: - if (!this->y.readAbsolute(value)) { - /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ + /* ex, em not handled correctly. */ + if (!this->y.read(value)) { this->y.unset(); } @@ -213,8 +211,8 @@ void SPImage::set(unsigned int key, const gchar* value) { break; case SP_ATTR_WIDTH: - if (!this->width.readAbsolute(value)) { - /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ + /* ex, em not handled correctly. */ + if (!this->width.read(value)) { this->width.unset(); } @@ -222,8 +220,8 @@ void SPImage::set(unsigned int key, const gchar* value) { break; case SP_ATTR_HEIGHT: - if (!this->height.readAbsolute(value)) { - /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ + /* ex, em not handled correctly. */ + if (!this->height.read(value)) { this->height.unset(); } @@ -231,67 +229,8 @@ void SPImage::set(unsigned int key, const gchar* value) { break; case SP_ATTR_PRESERVEASPECTRATIO: - /* Do setup before, so we can use break to escape */ - this->aspect_align = SP_ASPECT_NONE; - this->aspect_clip = SP_ASPECT_MEET; + set_preserveAspectRatio( value ); this->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 { - 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; - } - } - - this->aspect_align = align; - this->aspect_clip = clip; - } break; #if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) @@ -322,7 +261,79 @@ void SPImage::set(unsigned int key, const gchar* value) { sp_image_set_curve(this); //creates a curve at the image's boundary for snapping } +// BLIP +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) +void SPImage::apply_profile(Inkscape::Pixbuf *pixbuf) { + + // TODO: this will prevent using MIME data when exporting. + // Integrate color correction into loading. + pixbuf->ensurePixelFormat(Inkscape::Pixbuf::PF_GDK); + int imagewidth = pixbuf->width(); + int imageheight = pixbuf->height(); + int rowstride = pixbuf->rowstride();; + guchar* px = pixbuf->pixels(); + + if ( px ) { + DEBUG_MESSAGE( lcmsFive, "in <image>'s sp_image_update. About to call colorprofile_get_handle()" ); + + guint profIntent = Inkscape::RENDERING_INTENT_UNKNOWN; + cmsHPROFILE prof = Inkscape::CMSSystem::getHandle( this->document, + &profIntent, + this->color_profile ); + if ( prof ) { + cmsProfileClassSignature profileClass = cmsGetDeviceClass( prof ); + if ( profileClass != cmsSigNamedColorClass ) { + int intent = INTENT_PERCEPTUAL; + + switch ( profIntent ) { + case Inkscape::RENDERING_INTENT_RELATIVE_COLORIMETRIC: + intent = INTENT_RELATIVE_COLORIMETRIC; + break; + case Inkscape::RENDERING_INTENT_SATURATION: + intent = INTENT_SATURATION; + break; + case Inkscape::RENDERING_INTENT_ABSOLUTE_COLORIMETRIC: + intent = INTENT_ABSOLUTE_COLORIMETRIC; + break; + case Inkscape::RENDERING_INTENT_PERCEPTUAL: + case Inkscape::RENDERING_INTENT_UNKNOWN: + case Inkscape::RENDERING_INTENT_AUTO: + default: + intent = INTENT_PERCEPTUAL; + } + + cmsHPROFILE destProf = cmsCreate_sRGBProfile(); + cmsHTRANSFORM transf = cmsCreateTransform( prof, + TYPE_RGBA_8, + destProf, + TYPE_RGBA_8, + intent, 0 ); + if ( transf ) { + guchar* currLine = px; + for ( int y = 0; y < imageheight; y++ ) { + // Since the types are the same size, we can do the transformation in-place + cmsDoTransform( transf, currLine, currLine, imagewidth ); + currLine += rowstride; + } + + cmsDeleteTransform( transf ); + } else { + DEBUG_MESSAGE( lcmsSix, "in <image>'s sp_image_update. Unable to create LCMS transform." ); + } + + cmsCloseProfile( destProf ); + } else { + DEBUG_MESSAGE( lcmsSeven, "in <image>'s sp_image_update. Profile type is named color. Can't transform." ); + } + } else { + DEBUG_MESSAGE( lcmsEight, "in <image>'s sp_image_update. No profile found." ); + } + } +} +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + void SPImage::update(SPCtx *ctx, unsigned int flags) { + SPDocument *doc = this->document; SPItem::update(ctx, flags); @@ -339,170 +350,84 @@ void SPImage::update(SPCtx *ctx, unsigned int flags) { doc->getBase()); if (pixbuf) { -// BLIP #if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - if ( this->color_profile ) - { - // TODO: this will prevent using MIME data when exporting. - // Integrate color correction into loading. - pixbuf->ensurePixelFormat(Inkscape::Pixbuf::PF_GDK); - int imagewidth = pixbuf->width(); - int imageheight = pixbuf->height(); - int rowstride = pixbuf->rowstride();; - guchar* px = pixbuf->pixels(); - - if ( px ) { - DEBUG_MESSAGE( lcmsFive, "in <image>'s sp_image_update. About to call colorprofile_get_handle()" ); - - guint profIntent = Inkscape::RENDERING_INTENT_UNKNOWN; - cmsHPROFILE prof = Inkscape::CMSSystem::getHandle( this->document, - &profIntent, - this->color_profile ); - if ( prof ) { - cmsProfileClassSignature profileClass = cmsGetDeviceClass( prof ); - if ( profileClass != cmsSigNamedColorClass ) { - int intent = INTENT_PERCEPTUAL; - - switch ( profIntent ) { - case Inkscape::RENDERING_INTENT_RELATIVE_COLORIMETRIC: - intent = INTENT_RELATIVE_COLORIMETRIC; - break; - case Inkscape::RENDERING_INTENT_SATURATION: - intent = INTENT_SATURATION; - break; - case Inkscape::RENDERING_INTENT_ABSOLUTE_COLORIMETRIC: - intent = INTENT_ABSOLUTE_COLORIMETRIC; - break; - case Inkscape::RENDERING_INTENT_PERCEPTUAL: - case Inkscape::RENDERING_INTENT_UNKNOWN: - case Inkscape::RENDERING_INTENT_AUTO: - default: - intent = INTENT_PERCEPTUAL; - } - - cmsHPROFILE destProf = cmsCreate_sRGBProfile(); - cmsHTRANSFORM transf = cmsCreateTransform( prof, - TYPE_RGBA_8, - destProf, - TYPE_RGBA_8, - intent, 0 ); - if ( transf ) { - guchar* currLine = px; - for ( int y = 0; y < imageheight; y++ ) { - // Since the types are the same size, we can do the transformation in-place - cmsDoTransform( transf, currLine, currLine, imagewidth ); - currLine += rowstride; - } - - cmsDeleteTransform( transf ); - } else { - DEBUG_MESSAGE( lcmsSix, "in <image>'s sp_image_update. Unable to create LCMS transform." ); - } - - cmsCloseProfile( destProf ); - } else { - DEBUG_MESSAGE( lcmsSeven, "in <image>'s sp_image_update. Profile type is named color. Can't transform." ); - } - } else { - DEBUG_MESSAGE( lcmsEight, "in <image>'s sp_image_update. No profile found." ); - } - } - } -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - + if ( this->color_profile ) apply_profile( pixbuf ); +#endif this->pixbuf = pixbuf; } } } + SPItemCtx *ictx = (SPItemCtx *) ctx; + + // Why continue without a pixbuf? So we can display "Missing Image" png. + // Eventually, we should properly support SVG image type (i.e. render it ourselves). + if (this->pixbuf) { - /* fixme: We are slightly violating spec here (Lauris) */ + if (!this->x._set) { + this->x.unit = SVGLength::PX; + this->x.computed = 0; + } + + if (!this->y._set) { + this->y.unit = SVGLength::PX; + this->y.computed = 0; + } + if (!this->width._set) { + this->width.unit = SVGLength::PX; this->width.computed = this->pixbuf->width(); } if (!this->height._set) { + this->height.unit = SVGLength::PX; this->height.computed = this->pixbuf->height(); } } - this->clipbox = Geom::Rect::from_xywh( - this->x.computed, this->y.computed, - this->width.computed, this->height.computed); + + // Calculate x, y, width, height from parent/initial viewport, see sp-root.cpp + if (this->x.unit == SVGLength::PERCENT) { + this->x.computed = this->x.value * ictx->viewport.width(); + } + + if (this->y.unit == SVGLength::PERCENT) { + this->y.computed = this->y.value * ictx->viewport.height(); + } + + if (this->width.unit == SVGLength::PERCENT) { + this->width.computed = this->width.value * ictx->viewport.width(); + } + + if (this->height.unit == SVGLength::PERCENT) { + this->height.computed = this->height.value * ictx->viewport.height(); + } + + // Image creates a new viewport + ictx->viewport= Geom::Rect::from_xywh( this->x.computed, this->y.computed, + this->width.computed, this->height.computed); + + this->clipbox = ictx->viewport; this->ox = this->x.computed; this->oy = this->y.computed; if (this->pixbuf) { - int pixwidth = this->pixbuf->width(); - int pixheight = this->pixbuf->height(); - - this->sx = this->width.computed / pixwidth; - this->sy = this->height.computed / pixheight; - - // preserveAspectRatio calculate bounds / clipping rectangle -- EAF - if (this->aspect_align != SP_ASPECT_NONE) { - double x, y; - - switch (this->aspect_align) { - case SP_ASPECT_XMIN_YMIN: - x = 0.0; - y = 0.0; - break; - case SP_ASPECT_XMID_YMIN: - x = 0.5; - y = 0.0; - break; - case SP_ASPECT_XMAX_YMIN: - x = 1.0; - y = 0.0; - break; - case SP_ASPECT_XMIN_YMID: - x = 0.0; - y = 0.5; - break; - case SP_ASPECT_XMID_YMID: - x = 0.5; - y = 0.5; - break; - case SP_ASPECT_XMAX_YMID: - x = 1.0; - y = 0.5; - break; - case SP_ASPECT_XMIN_YMAX: - x = 0.0; - y = 1.0; - break; - case SP_ASPECT_XMID_YMAX: - x = 0.5; - y = 1.0; - break; - case SP_ASPECT_XMAX_YMAX: - x = 1.0; - y = 1.0; - break; - default: - x = 0.0; - y = 0.0; - break; - } - if (this->aspect_clip == SP_ASPECT_SLICE) { - double scale = std::max(this->sx, this->sy); - this->sx = scale; - this->sy = scale; - } else { - double scale = std::min(this->sx, this->sy); - this->sx = scale; - this->sy = scale; - } + // Viewbox is either from SVG (not supported) or dimensions of pixbuf (PNG, JPG) + this->viewBox = Geom::Rect::from_xywh(0, 0, this->pixbuf->width(), this->pixbuf->height()); + this->viewBox_set = true; - double vw = pixwidth * this->sx; - double vh = pixheight * this->sy; - this->ox += x * (this->width.computed - vw); - this->oy += y * (this->height.computed - vh); - } + // SPItemCtx rctx = + get_rctx( ictx ); + + this->ox = c2p[4]; + this->oy = c2p[5]; + this->sx = c2p[0]; + this->sy = c2p[3]; } + + // TODO: eliminate ox, oy, sx, sy sp_image_update_canvas_image ((SPImage *) this); } diff --git a/src/sp-image.h b/src/sp-image.h index 3b7208487..50eb731d7 100644 --- a/src/sp-image.h +++ b/src/sp-image.h @@ -18,6 +18,7 @@ #include <glibmm/ustring.h> #include "svg/svg-length.h" #include "sp-shape.h" +#include "viewbox.h" #define SP_IMAGE(obj) (dynamic_cast<SPImage*>((SPObject*)obj)) #define SP_IS_IMAGE(obj) (dynamic_cast<const SPImage*>((SPObject*)obj) != NULL) @@ -25,7 +26,7 @@ #define SP_IMAGE_HREF_MODIFIED_FLAG SP_OBJECT_USER_MODIFIED_FLAG_A namespace Inkscape { class Pixbuf; } -class SPImage : public SPItem { +class SPImage : public SPItem, public SPViewBox { public: SPImage(); virtual ~SPImage(); @@ -39,13 +40,6 @@ public: double sx, sy; double ox, oy; - // Added by EAF - /* preserveAspectRatio */ - unsigned int aspect_align : 4; - unsigned int aspect_clip : 1; - //int trimx, trimy, trimwidth, trimheight; - //double viewx, viewy, viewwidth, viewheight; - SPCurve *curve; // This curve is at the image's boundary for snapping gchar *href; @@ -69,6 +63,10 @@ public: virtual Inkscape::DrawingItem* show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags); virtual void snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) const; virtual Geom::Affine set_transform(Geom::Affine const &transform); + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + void apply_profile(Inkscape::Pixbuf *pixbuf); +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) }; /* Return duplicate of curve or NULL */ diff --git a/src/sp-pattern.cpp b/src/sp-pattern.cpp index ad5449f34..711f26428 100644 --- a/src/sp-pattern.cpp +++ b/src/sp-pattern.cpp @@ -53,7 +53,7 @@ namespace { bool patternRegistered = SPFactory::instance().registerObject("svg:pattern", createPattern); } -SPPattern::SPPattern() : SPPaintServer() { +SPPattern::SPPattern() : SPPaintServer(), SPViewBox() { this->href = NULL; this->ref = new SPPatternReference(this); @@ -72,8 +72,6 @@ SPPattern::SPPattern() : SPPaintServer() { this->y.unset(); this->width.unset(); this->height.unset(); - - this->viewBox_set = FALSE; } SPPattern::~SPPattern() { @@ -90,6 +88,7 @@ void SPPattern::build(SPDocument* doc, Inkscape::XML::Node* repr) { this->readAttr( "width" ); this->readAttr( "height" ); this->readAttr( "viewBox" ); + this->readAttr( "preserveAspectRatio" ); this->readAttr( "xlink:href" ); /* Register ourselves */ @@ -180,50 +179,16 @@ void SPPattern::set(unsigned int key, const gchar* value) { this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; - case SP_ATTR_VIEWBOX: { - /* fixme: Think (Lauris) */ - double x, y, width, height; - char *eptr; - - if (value) { - eptr = (gchar *) value; - x = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - y = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - width = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - height = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } + case SP_ATTR_VIEWBOX: + set_viewBox( value ); + this->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); + break; - if ((width > 0) && (height > 0)) { - this->viewBox = Geom::Rect::from_xywh(x, y, width, height); - this->viewBox_set = TRUE; - } else { - this->viewBox_set = FALSE; - } - } else { - this->viewBox_set = FALSE; - } + case SP_ATTR_PRESERVEASPECTRATIO: + set_preserveAspectRatio( value ); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); + break; - this->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); - break; - } case SP_ATTR_XLINK_HREF: if ( value && this->href && ( strcmp(value, this->href) == 0 ) ) { /* Href unchanged, do nothing. */ diff --git a/src/sp-pattern.h b/src/sp-pattern.h index 4e3657ccf..f69ba10b3 100644 --- a/src/sp-pattern.h +++ b/src/sp-pattern.h @@ -25,12 +25,13 @@ class SPPatternReference; #include "svg/svg-length.h" #include "sp-paint-server.h" #include "uri-references.h" +#include "viewbox.h" #include <stddef.h> #include <sigc++/connection.h> -class SPPattern : public SPPaintServer { +class SPPattern : public SPPaintServer, public SPViewBox { public: SPPattern(); virtual ~SPPattern(); @@ -52,9 +53,6 @@ public: SVGLength y; SVGLength width; SVGLength height; - /* VieBox */ - Geom::Rect viewBox; - guint viewBox_set : 1; sigc::connection modified_connection; diff --git a/src/sp-root.cpp b/src/sp-root.cpp index 5466649a2..bc870b116 100644 --- a/src/sp-root.cpp +++ b/src/sp-root.cpp @@ -42,12 +42,9 @@ SPObject *createRoot() bool rootRegistered = SPFactory::instance().registerObject("svg:svg", createRoot); } -SPRoot::SPRoot() : SPGroup() +SPRoot::SPRoot() : SPGroup(), SPViewBox() { - this->aspect_set = 0; - this->aspect_align = 0; this->onload = NULL; - this->aspect_clip = 0; static Inkscape::Version const zero_version(0, 0); @@ -57,15 +54,11 @@ SPRoot::SPRoot() : SPGroup() this->version.inkscape = zero_version; this->original.inkscape = zero_version; - this->x.unset(); - this->y.unset(); + this->x.unset(SVGLength::PERCENT, 0.0, 0.0); // Ignored for root SVG element + this->y.unset(SVGLength::PERCENT, 0.0, 0.0); this->width.unset(SVGLength::PERCENT, 1.0, 1.0); this->height.unset(SVGLength::PERCENT, 1.0, 1.0); - this->viewBox_set = false; - - this->c2p.setIdentity(); - this->defs = NULL; } @@ -129,9 +122,9 @@ void SPRoot::set(unsigned int key, const gchar *value) break; case SP_ATTR_X: - if (!this->x.readAbsolute(value)) { - /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ - this->x.unset(); + /* Valid for non-root SVG elements; ex, em not handled correctly. */ + if (!this->x.read(value)) { + this->x.unset(SVGLength::PERCENT, 0.0, 0.0); } /* fixme: I am almost sure these do not require viewport flag (Lauris) */ @@ -139,9 +132,9 @@ void SPRoot::set(unsigned int key, const gchar *value) break; case SP_ATTR_Y: - if (!this->y.readAbsolute(value)) { - /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ - this->y.unset(); + /* Valid for non-root SVG elements; ex, em not handled correctly. */ + if (!this->y.read(value)) { + this->y.unset(SVGLength::PERCENT, 0.0, 0.0); } /* fixme: I am almost sure these do not require viewport flag (Lauris) */ @@ -149,153 +142,27 @@ void SPRoot::set(unsigned int key, const gchar *value) break; case SP_ATTR_WIDTH: - if (!this->width.readAbsolute(value) || !(this->width.computed > 0.0)) { - /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ + if (!this->width.read(value) || !(this->width.computed > 0.0)) { this->width.unset(SVGLength::PERCENT, 1.0, 1.0); } - this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); break; case SP_ATTR_HEIGHT: - if (!this->height.readAbsolute(value) || !(this->height.computed > 0.0)) { - /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ + if (!this->height.read(value) || !(this->height.computed > 0.0)) { this->height.unset(SVGLength::PERCENT, 1.0, 1.0); } - this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); break; case SP_ATTR_VIEWBOX: - if (value) { - double x, y, width, height; - char *eptr; - - /* fixme: We have to take original item affine into account */ - /* fixme: Think (Lauris) */ - eptr = (gchar *) value; - x = g_ascii_strtod(eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - y = g_ascii_strtod(eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - width = g_ascii_strtod(eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - height = g_ascii_strtod(eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - if ((width > 0) && (height > 0)) { - /* Set viewbox */ - this->viewBox = Geom::Rect::from_xywh(x, y, width, height); - this->viewBox_set = TRUE; - } else { - this->viewBox_set = FALSE; - } - } else { - this->viewBox_set = FALSE; - } - + set_viewBox( value ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); break; case SP_ATTR_PRESERVEASPECTRATIO: - /* Do setup before, so we can use break to escape */ - this->aspect_set = FALSE; - this->aspect_align = SP_ASPECT_XMID_YMID; - this->aspect_clip = SP_ASPECT_MEET; - + set_preserveAspectRatio( value ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); - - if (value) { - int len; - gchar c[256]; - gchar const *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 { - 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; - } - } - - this->aspect_set = TRUE; - this->aspect_align = align; - this->aspect_clip = clip; - } break; case SP_ATTR_ONLOAD: @@ -356,141 +223,75 @@ void SPRoot::update(SPCtx *ctx, guint flags) { SPItemCtx *ictx = (SPItemCtx *) ctx; - /* fixme: This will be invoked too often (Lauris) */ - /* fixme: We should calculate only if parent viewport has changed (Lauris) */ - /* If position is specified as percentage, calculate actual values */ - if (this->x.unit == SVGLength::PERCENT) { - this->x.computed = this->x.value * ictx->viewport.width(); - } - - if (this->y.unit == SVGLength::PERCENT) { - this->y.computed = this->y.value * ictx->viewport.height(); - } - - if (this->width.unit == SVGLength::PERCENT) { - this->width.computed = this->width.value * ictx->viewport.width(); - } - - if (this->height.unit == SVGLength::PERCENT) { - this->height.computed = this->height.value * ictx->viewport.height(); - } - - /* Create copy of item context */ - SPItemCtx rctx = *ictx; - - /* Calculate child to parent transformation */ - this->c2p.setIdentity(); + if( !this->parent ) { - if (this->parent) { /* - * fixme: I am not sure whether setting x and y does or does not - * fixme: translate the content of inner SVG. - * fixme: Still applying translation and setting viewport to width and - * fixme: height seems natural, as this makes the inner svg element - * fixme: self-contained. The spec is vague here. + * This is the root SVG element: + * + * x, y, width, and height apply to positioning the SVG element inside a parent. + * For the root SVG in Inkscape there is no parent, thus special rules apply: + * If width, height not set, width = 100%, height = 100% (as always). + * If width and height are in percent, they are percent of viewBox width/height. + * If width, height, and viewBox are not set... pick "random" width/height. + * x, y are ignored. + * initial viewport = (0 0 width height) */ - this->c2p = Geom::Affine(Geom::Translate(this->x.computed, this->y.computed)); - } - - if (this->viewBox_set) { - double x, y, width, height; - /* Determine actual viewbox in viewport coordinates */ - if (this->aspect_align == SP_ASPECT_NONE) { - x = 0.0; - y = 0.0; - width = this->width.computed; - height = this->height.computed; - } else { - double scalex, scaley, scale; - /* Things are getting interesting */ - scalex = this->width.computed / this->viewBox.width(); - scaley = this->height.computed / this->viewBox.height(); - scale = (this->aspect_clip == SP_ASPECT_MEET) ? MIN(scalex, scaley) : MAX(scalex, scaley); - width = this->viewBox.width() * scale; - height = this->viewBox.height() * scale; - - /* Now place viewbox to requested position */ - /* todo: Use an array lookup to find the 0.0/0.5/1.0 coefficients, - as is done for dialogs/align.cpp. */ - switch (this->aspect_align) { - case SP_ASPECT_XMIN_YMIN: - x = 0.0; - y = 0.0; - break; - - case SP_ASPECT_XMID_YMIN: - x = 0.5 * (this->width.computed - width); - y = 0.0; - break; - - case SP_ASPECT_XMAX_YMIN: - x = 1.0 * (this->width.computed - width); - y = 0.0; - break; - - case SP_ASPECT_XMIN_YMID: - x = 0.0; - y = 0.5 * (this->height.computed - height); - break; - - case SP_ASPECT_XMID_YMID: - x = 0.5 * (this->width.computed - width); - y = 0.5 * (this->height.computed - height); - break; + if( this->viewBox_set ) { - case SP_ASPECT_XMAX_YMID: - x = 1.0 * (this->width.computed - width); - y = 0.5 * (this->height.computed - height); - break; + if( this->width._set ) { + // Check if this is necessary + if (this->width.unit == SVGLength::PERCENT) { + this->width.computed = this->width.value * this->viewBox.width(); + } + } else { + this->width.set( SVGLength::PX, this->viewBox.width(), this->viewBox.width() ); + } - case SP_ASPECT_XMIN_YMAX: - x = 0.0; - y = 1.0 * (this->height.computed - height); - break; + if( this->height._set ) { + if (this->height.unit == SVGLength::PERCENT) { + this->height.computed = this->height.value * this->viewBox.height(); + } + } else { + this->height.set(SVGLength::PX, this->viewBox.height(), this->viewBox.height() ); + } - case SP_ASPECT_XMID_YMAX: - x = 0.5 * (this->width.computed - width); - y = 1.0 * (this->height.computed - height); - break; + } else { - case SP_ASPECT_XMAX_YMAX: - x = 1.0 * (this->width.computed - width); - y = 1.0 * (this->height.computed - height); - break; + if( !this->width._set ) { + this->width.set( SVGLength::PX, 100, 100 ); // Random default + } - default: - x = 0.0; - y = 0.0; - break; + if( !this->height._set ) { + this->height.set( SVGLength::PX, 100, 100 ); // Random default } } - /* Compose additional transformation from scale and position */ - Geom::Scale const viewBox_length(this->viewBox.dimensions()); - Geom::Scale const new_length(width, height); + // Ignore x, y values for root element + this->x.unset(SVGLength::PERCENT, 0.0, 0.0); + this->y.unset(SVGLength::PERCENT, 0.0, 0.0); + } - /* Append viewbox transformation */ - /* TODO: The below looks suspicious to me (pjrm): I wonder whether the RHS - expression should have c2p at the beginning rather than at the end. Test it. */ - this->c2p = Geom::Translate(-this->viewBox.min()) * (new_length * viewBox_length.inverse()) * Geom::Translate(x, y) * this->c2p; + // Calculate x, y, width, height from parent/initial viewport + if (this->x.unit == SVGLength::PERCENT) { + this->x.computed = this->x.value * ictx->viewport.width(); } - rctx.i2doc = this->c2p * rctx.i2doc; + if (this->y.unit == SVGLength::PERCENT) { + this->y.computed = this->y.value * ictx->viewport.height(); + } - /* Initialize child viewport */ - if (this->viewBox_set) { - rctx.viewport = this->viewBox; - } else { - /* fixme: I wonder whether this logic is correct (Lauris) */ - Geom::Point minp(0, 0); - if (this->parent) { - minp = Geom::Point(this->x.computed, this->y.computed); - } + if (this->width.unit == SVGLength::PERCENT) { + this->width.computed = this->width.value * ictx->viewport.width(); + } - rctx.viewport = Geom::Rect::from_xywh(minp[Geom::X], minp[Geom::Y], this->width.computed, this->height.computed); + if (this->height.unit == SVGLength::PERCENT) { + this->height.computed = this->height.value * ictx->viewport.height(); } - rctx.i2vp = Geom::identity(); + // Calculate new viewport + ictx->viewport = Geom::Rect::from_xywh( this->x.computed, this->y.computed, + this->width.computed, this->height.computed ); + SPItemCtx rctx = get_rctx( ictx ); /* And invoke parent method */ SPGroup::update((SPCtx *) &rctx, flags); diff --git a/src/sp-root.h b/src/sp-root.h index 2931391ff..1c9faed9b 100644 --- a/src/sp-root.h +++ b/src/sp-root.h @@ -18,6 +18,7 @@ #include "svg/svg-length.h" #include "enums.h" #include "sp-item-group.h" +#include "viewbox.h" #define SP_ROOT(obj) (dynamic_cast<SPRoot*>((SPObject*)obj)) #define SP_IS_ROOT(obj) (dynamic_cast<const SPRoot*>((SPObject*)obj) != NULL) @@ -25,7 +26,7 @@ class SPDefs; /** \<svg\> element */ -class SPRoot : public SPGroup { +class SPRoot : public SPGroup, public SPViewBox { public: SPRoot(); virtual ~SPRoot(); @@ -40,18 +41,6 @@ public: SVGLength width; SVGLength height; - /* viewBox; */ - bool viewBox_set; - Geom::Rect viewBox; - - /* preserveAspectRatio */ - unsigned int aspect_set : 1; - unsigned int aspect_align : 4; - unsigned int aspect_clip : 1; - - /** Child to parent additional transform. */ - Geom::Affine c2p; - gchar *onload; /** diff --git a/src/sp-symbol.cpp b/src/sp-symbol.cpp index 8ffc2ab2c..817411a32 100644 --- a/src/sp-symbol.cpp +++ b/src/sp-symbol.cpp @@ -36,13 +36,7 @@ namespace { bool symbolRegistered = SPFactory::instance().registerObject("svg:symbol", createSymbol); } -SPSymbol::SPSymbol() : SPGroup() { - this->aspect_align = 0; - this->aspect_clip = 0; - this->aspect_set = 0; - - this->viewBox_set = FALSE; - this->c2p = Geom::identity(); +SPSymbol::SPSymbol() : SPGroup(), SPViewBox() { } SPSymbol::~SPSymbol() { @@ -62,134 +56,15 @@ void SPSymbol::release() { void SPSymbol::set(unsigned int key, const gchar* value) { switch (key) { case SP_ATTR_VIEWBOX: - if (value) { - double x, y, width, height; - char *eptr; - - /* fixme: We have to take original item affine into account */ - /* fixme: Think (Lauris) */ - eptr = (gchar *) value; - x = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - y = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - width = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - height = g_ascii_strtod (eptr, &eptr); - - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { - eptr++; - } - - if ((width > 0) && (height > 0)) { - /* Set viewbox */ - this->viewBox = Geom::Rect::from_xywh(x, y, width, height); - this->viewBox_set = TRUE; - } else { - this->viewBox_set = FALSE; - } - } else { - this->viewBox_set = FALSE; - } - + set_viewBox( value ); + std::cout << "Symbol: ViewBox: " << viewBox << std::endl; this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); break; case SP_ATTR_PRESERVEASPECTRATIO: - /* Do setup before, so we can use break to escape */ - this->aspect_set = FALSE; - this->aspect_align = SP_ASPECT_NONE; - this->aspect_clip = SP_ASPECT_MEET; + set_preserveAspectRatio( value ); + std::cout << "Symbol: Preserve aspect ratio: " << aspect_align << ", " << aspect_clip << std::endl; this->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 { - 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; - } - } - - this->aspect_set = TRUE; - this->aspect_align = align; - this->aspect_clip = clip; - } break; default: @@ -204,105 +79,10 @@ void SPSymbol::child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) void SPSymbol::update(SPCtx *ctx, guint flags) { - SPItemCtx *ictx = (SPItemCtx *) ctx; - SPItemCtx rctx; - if (this->cloned) { - /* Cloned <symbol> is actually renderable */ - - /* fixme: We have to set up clip here too */ - - /* Create copy of item context */ - rctx = *ictx; - - /* Calculate child to parent transformation */ - /* Apply parent <use> translation (set up as vewport) */ - this->c2p = Geom::Translate(rctx.viewport.min()); - - if (this->viewBox_set) { - double x, y, width, height; - - /* Determine actual viewbox in viewport coordinates */ - if (this->aspect_align == SP_ASPECT_NONE) { - x = 0.0; - y = 0.0; - width = rctx.viewport.width(); - height = rctx.viewport.height(); - } else { - double scalex, scaley, scale; - /* Things are getting interesting */ - scalex = rctx.viewport.width() / this->viewBox.width(); - scaley = rctx.viewport.height() / this->viewBox.height(); - scale = (this->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley); - width = this->viewBox.width() * scale; - height = this->viewBox.height() * scale; - - /* Now place viewbox to requested position */ - switch (this->aspect_align) { - case SP_ASPECT_XMIN_YMIN: - x = 0.0; - y = 0.0; - break; - case SP_ASPECT_XMID_YMIN: - x = 0.5 * (rctx.viewport.width() - width); - y = 0.0; - break; - case SP_ASPECT_XMAX_YMIN: - x = 1.0 * (rctx.viewport.width() - width); - y = 0.0; - break; - case SP_ASPECT_XMIN_YMID: - x = 0.0; - y = 0.5 * (rctx.viewport.height() - height); - break; - case SP_ASPECT_XMID_YMID: - x = 0.5 * (rctx.viewport.width() - width); - y = 0.5 * (rctx.viewport.height() - height); - break; - case SP_ASPECT_XMAX_YMID: - x = 1.0 * (rctx.viewport.width() - width); - y = 0.5 * (rctx.viewport.height() - height); - break; - case SP_ASPECT_XMIN_YMAX: - x = 0.0; - y = 1.0 * (rctx.viewport.height() - height); - break; - case SP_ASPECT_XMID_YMAX: - x = 0.5 * (rctx.viewport.width() - width); - y = 1.0 * (rctx.viewport.height() - height); - break; - case SP_ASPECT_XMAX_YMAX: - x = 1.0 * (rctx.viewport.width() - width); - y = 1.0 * (rctx.viewport.height() - height); - break; - default: - x = 0.0; - y = 0.0; - break; - } - } - - /* Compose additional transformation from scale and position */ - Geom::Affine q; - q[0] = width / this->viewBox.width(); - q[1] = 0.0; - q[2] = 0.0; - q[3] = height / this->viewBox.height(); - q[4] = -this->viewBox.left() * q[0] + x; - q[5] = -this->viewBox.top() * q[3] + y; - - /* Append viewbox transformation */ - this->c2p = q * this->c2p; - } - rctx.i2doc = this->c2p * (Geom::Affine)rctx.i2doc; - - /* If viewBox is set initialize child viewport */ - /* Otherwise <use> has set it up already */ - if (this->viewBox_set) { - rctx.viewport = this->viewBox; - rctx.i2vp = Geom::identity(); - } + SPItemCtx *ictx = (SPItemCtx *) ctx; + SPItemCtx rctx = get_rctx( ictx ); // And invoke parent method SPGroup::update((SPCtx *) &rctx, flags); diff --git a/src/sp-symbol.h b/src/sp-symbol.h index 7a43ed658..d1e62e923 100644 --- a/src/sp-symbol.h +++ b/src/sp-symbol.h @@ -25,24 +25,13 @@ #include "svg/svg-length.h" #include "enums.h" #include "sp-item-group.h" +#include "viewbox.h" -class SPSymbol : public SPGroup { +class SPSymbol : public SPGroup, public SPViewBox { public: SPSymbol(); virtual ~SPSymbol(); - /* viewBox; */ - unsigned int viewBox_set : 1; - Geom::Rect viewBox; - - /* preserveAspectRatio */ - unsigned int aspect_set : 1; - unsigned int aspect_align : 4; - unsigned int aspect_clip : 1; - - /* Child to parent additional transform */ - Geom::Affine c2p; - virtual void build(SPDocument *document, Inkscape::XML::Node *repr); virtual void release(); virtual void set(unsigned int key, gchar const* value); diff --git a/src/viewbox.cpp b/src/viewbox.cpp new file mode 100644 index 000000000..2314cbf6a --- /dev/null +++ b/src/viewbox.cpp @@ -0,0 +1,277 @@ +/* + * viewBox helper class, common code used by root, symbol, marker, pattern, image, view + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> (code extracted from symbol.cpp) + * Tavmjong Bah <tavmjong@free.fr> + * + * Copyright (C) 2013 Tavmjong Bah, authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + * + */ + +#include <2geom/transforms.h> + +#include "viewbox.h" +#include "attributes.h" +#include "enums.h" +#include "sp-item.h" + +SPViewBox::SPViewBox() { + + this->viewBox_set = FALSE; + + this->aspect_set = FALSE; + this->aspect_align = SP_ASPECT_XMID_YMID; // Default per spec; + this->aspect_clip = SP_ASPECT_MEET; + + this->c2p = Geom::identity(); +} + +SPViewBox::~SPViewBox() { +} + +void SPViewBox::set_viewBox(const gchar* value) { + + if (value) { + double x, y, width, height; + char *eptr; + + eptr = (gchar *) value; + x = g_ascii_strtod (eptr, &eptr); + + while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { + eptr++; + } + + y = g_ascii_strtod (eptr, &eptr); + + while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { + eptr++; + } + + width = g_ascii_strtod (eptr, &eptr); + + while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { + eptr++; + } + + height = g_ascii_strtod (eptr, &eptr); + + while (*eptr && ((*eptr == ',') || (*eptr == ' '))) { + eptr++; + } + + if ((width > 0) && (height > 0)) { + /* Set viewbox */ + this->viewBox = Geom::Rect::from_xywh(x, y, width, height); + this->viewBox_set = TRUE; + } else { + this->viewBox_set = FALSE; + } + } else { + this->viewBox_set = FALSE; + } + + // The C++ way? + // std::string sv( value ); + // std::replace( sv.begin(), sv.end(), ',', ' '); + // std::stringstream ss( sv ); + // double x, y, width, height; + // ss >> x >> y >> width >> height; +} + +void SPViewBox::set_preserveAspectRatio(const gchar* value) { + + /* Do setup before, so we can use break to escape */ + this->aspect_set = FALSE; + this->aspect_align = SP_ASPECT_XMID_YMID; // Default per spec + this->aspect_clip = SP_ASPECT_MEET; + + 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) { + return; + } + + e = p; + + while (*e && *e != 32) { + e += 1; + } + + len = e - p; + + if (len > 8) { + return; + } + + 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 { + return; + } + + 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 { + return; + } + } + + this->aspect_set = TRUE; + this->aspect_align = align; + this->aspect_clip = clip; + } +} + +// Apply scaling from viewbox +void SPViewBox::apply_viewbox(const Geom::Rect& in) { + + /* Determine actual viewbox in viewport coordinates */ + double x = 0.0; + double y = 0.0; + double width = in.width(); + double height = in.height(); + // std::cout << " width: " << width << " height: " << height << std::endl; + + if (this->aspect_align != SP_ASPECT_NONE) { + double scalex, scaley, scale; + /* Things are getting interesting */ + scalex = in.width() / this->viewBox.width(); + scaley = in.height() / this->viewBox.height(); + scale = (this->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley); + width = this->viewBox.width() * scale; + height = this->viewBox.height() * scale; + + /* Now place viewbox to requested position */ + switch (this->aspect_align) { + case SP_ASPECT_XMIN_YMIN: + break; + case SP_ASPECT_XMID_YMIN: + x = 0.5 * (in.width() - width); + break; + case SP_ASPECT_XMAX_YMIN: + x = 1.0 * (in.width() - width); + break; + case SP_ASPECT_XMIN_YMID: + y = 0.5 * (in.height() - height); + break; + case SP_ASPECT_XMID_YMID: + x = 0.5 * (in.width() - width); + y = 0.5 * (in.height() - height); + break; + case SP_ASPECT_XMAX_YMID: + x = 1.0 * (in.width() - width); + y = 0.5 * (in.height() - height); + break; + case SP_ASPECT_XMIN_YMAX: + y = 1.0 * (in.height() - height); + break; + case SP_ASPECT_XMID_YMAX: + x = 0.5 * (in.width() - width); + y = 1.0 * (in.height() - height); + break; + case SP_ASPECT_XMAX_YMAX: + x = 1.0 * (in.width() - width); + y = 1.0 * (in.height() - height); + break; + default: + break; + } + } + + /* Viewbox transform from scale and position */ + Geom::Affine q; + q[0] = width / this->viewBox.width(); + q[1] = 0.0; + q[2] = 0.0; + q[3] = height / this->viewBox.height(); + q[4] = -this->viewBox.left() * q[0] + x; + q[5] = -this->viewBox.top() * q[3] + y; + + // std::cout << " q\n" << q << std::endl; + + /* Append viewbox transformation */ + this->c2p = q * this->c2p; +} + +SPItemCtx SPViewBox::get_rctx(const SPItemCtx* ictx) { + + /* Create copy of item context */ + SPItemCtx rctx = *ictx; + + /* Calculate child to parent transformation */ + /* Apply parent translation (set up as viewport) */ + this->c2p = Geom::Translate(rctx.viewport.min()); + + if (this->viewBox_set) { + // Adjusts c2p for viewbox + apply_viewbox( rctx.viewport ); + } + + rctx.i2doc = this->c2p * rctx.i2doc; + + /* If viewBox is set initialize child viewport */ + /* Otherwise it is already correct */ + if (this->viewBox_set) { + rctx.viewport = this->viewBox; + rctx.i2vp = Geom::identity(); + } + + return rctx; +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-basic-offset:2 + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=2:tabstop=8:softtabstop=2:fileencoding=utf-8:textwidth=99 : diff --git a/src/viewbox.h b/src/viewbox.h new file mode 100644 index 000000000..24356ed17 --- /dev/null +++ b/src/viewbox.h @@ -0,0 +1,66 @@ +#ifndef __SP_VIEWBOX_H__ +#define __SP_VIEWBOX_H__ + +/* + * viewBox helper class, common code used by root, symbol, marker, pattern, image, view + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> (code extracted from sp-symbol.h) + * Tavmjong Bah + * + * Copyright (C) 2013 Tavmjong Bah, authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + * + */ + +#include <2geom/rect.h> +#include <glib.h> + +namespace Geom { +class Rect; +} +class SPItemCtx; + +class SPViewBox { + + public: + + SPViewBox(); + ~SPViewBox(); + + /* viewBox; */ + unsigned int viewBox_set : 1; + Geom::Rect viewBox; // Could use optrect + + /* preserveAspectRatio */ + unsigned int aspect_set : 1; + unsigned int aspect_align : 4; + unsigned int aspect_clip : 1; + + /* Child to parent additional transform */ + Geom::Affine c2p; + + void set_viewBox(const gchar* value); + void set_preserveAspectRatio(const gchar* value); + + /* Adjusts c2p for viewbox */ + void apply_viewbox(const Geom::Rect& in); + + SPItemCtx get_rctx( const SPItemCtx* ictx); + +}; + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-basic-offset:2 + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=2:tabstop=8:softtabstop=2:fileencoding=utf-8:textwidth=99 : |
