return;
addStyle("fill", "color", &Image::setFill);
+ addStyle("outset", "", &Image::setOutset);
+ addStyle("preserveAspectRatio", "", &Image::setPreserveAspectRatio);
addStyle("slice", "", &Image::setSlice);
addStyle("slice-width", "", &Image::setSliceWidth);
- addStyle("outset", "", &Image::setOutset);
}
//----------------------------------------------------------------------------
if( !_slice.isValid() )
{
setQuad(0, region.getMin(), region.getMax());
+
+ if( !_preserve_aspect_ratio.scaleToFill() )
+ // We need to update texture coordinates to keep the aspect ratio
+ _attributes_dirty |= SRC_RECT;
}
else
{
if( !_slice.isValid() )
{
+ // Image scaling preserving aspect ratio. Change texture coordinates to
+ // scale image accordingly.
+ //
+ // TODO allow to specify what happens to not filled space (eg. color,
+ // or texture repeat/mirror)
+ //
+ // http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
+ if( !_preserve_aspect_ratio.scaleToFill() )
+ {
+ osg::BoundingBox const& bb = getBoundingBox();
+ float dst_width = bb._max.x() - bb._min.x(),
+ dst_height = bb._max.y() - bb._min.y();
+ float scale_x = dst_width / tex_dim.width(),
+ scale_y = dst_height / tex_dim.height();
+
+ float scale = _preserve_aspect_ratio.scaleToFit()
+ ? std::min(scale_x, scale_y)
+ : std::max(scale_x, scale_y);
+
+ if( scale_x != scale )
+ {
+ float d = scale_x / scale - 1;
+ if( _preserve_aspect_ratio.alignX()
+ == SVGpreserveAspectRatio::ALIGN_MIN )
+ {
+ src_rect.r() += d;
+ }
+ else if( _preserve_aspect_ratio.alignX()
+ == SVGpreserveAspectRatio::ALIGN_MAX )
+ {
+ src_rect.l() -= d;
+ }
+ else
+ {
+ src_rect.l() -= d / 2;
+ src_rect.r() += d / 2;
+ }
+ }
+
+ if( scale_y != scale )
+ {
+ float d = scale_y / scale - 1;
+ if( _preserve_aspect_ratio.alignY()
+ == SVGpreserveAspectRatio::ALIGN_MIN )
+ {
+ src_rect.b() -= d;
+ }
+ else if( _preserve_aspect_ratio.alignY()
+ == SVGpreserveAspectRatio::ALIGN_MAX )
+ {
+ src_rect.t() += d;
+ }
+ else
+ {
+ src_rect.t() += d / 2;
+ src_rect.b() -= d / 2;
+ }
+ }
+ }
+
setQuadUV(0, src_rect.getMin(), src_rect.getMax());
}
else
_colors->dirty();
}
+ //----------------------------------------------------------------------------
+ void Image::setOutset(const std::string& outset)
+ {
+ _outset = CSSBorder::parse(outset);
+ _attributes_dirty |= DEST_SIZE;
+ }
+
+ //----------------------------------------------------------------------------
+ void Image::setPreserveAspectRatio(const std::string& scale)
+ {
+ _preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale);
+ _attributes_dirty |= SRC_RECT;
+ }
+
//----------------------------------------------------------------------------
void Image::setSlice(const std::string& slice)
{
_attributes_dirty |= DEST_SIZE;
}
- //----------------------------------------------------------------------------
- void Image::setOutset(const std::string& outset)
- {
- _outset = CSSBorder::parse(outset);
- _attributes_dirty |= DEST_SIZE;
- }
-
//----------------------------------------------------------------------------
const SGRect<float>& Image::getRegion() const
{
#include <simgear/canvas/canvas_fwd.hxx>
#include <simgear/io/HTTPClient.hxx>
#include <simgear/misc/CSSBorder.hxx>
+#include <simgear/misc/SVGpreserveAspectRatio.hxx>
#include <osg/Texture2D>
namespace simgear
void setImage(osg::Image *img);
void setFill(const std::string& fill);
+ /**
+ * @see http://www.w3.org/TR/css3-background/#border-image-outset
+ */
+ void setOutset(const std::string& outset);
+
+ /**
+ * @see
+ * http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
+ */
+ void setPreserveAspectRatio(const std::string& scale);
+
/**
* Set image slice (aka. 9-scale)
*
*/
void setSliceWidth(const std::string& width);
- /**
- * http://www.w3.org/TR/css3-background/#border-image-outset
- */
- void setOutset(const std::string& outset);
-
const SGRect<float>& getRegion() const;
bool handleEvent(const EventPtr& event);
SGRect<float> _src_rect,
_region;
- CSSBorder _slice,
- _slice_width,
- _outset;
+ SVGpreserveAspectRatio _preserve_aspect_ratio;
+
+ CSSBorder _outset,
+ _slice,
+ _slice_width;
};
} // namespace canvas
CSSBorder.hxx
ListDiff.hxx
ResourceManager.hxx
+ SVGpreserveAspectRatio.hxx
interpolator.hxx
make_new.hxx
sg_dir.hxx
set(SOURCES
CSSBorder.cxx
ResourceManager.cxx
+ SVGpreserveAspectRatio.cxx
interpolator.cxx
sg_dir.cxx
sg_path.cxx
target_link_libraries(test_path ${TEST_LIBS})
endif(ENABLE_TESTS)
+
+add_boost_test(SVGpreserveAspectRatio
+ SOURCES SVGpreserveAspectRatio_test.cxx
+ LIBRARIES ${TEST_LIBS}
+)
\ No newline at end of file
--- /dev/null
+// Parse and represent SVG preserveAspectRatio attribute
+//
+// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#include "SVGpreserveAspectRatio.hxx"
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/strutils.hxx>
+
+#include <boost/tokenizer.hpp>
+
+namespace simgear
+{
+
+ //----------------------------------------------------------------------------
+ SVGpreserveAspectRatio::SVGpreserveAspectRatio():
+ _align_x(ALIGN_NONE),
+ _align_y(ALIGN_NONE),
+ _meet(true)
+ {
+
+ }
+
+ //----------------------------------------------------------------------------
+ SVGpreserveAspectRatio::Align SVGpreserveAspectRatio::alignX() const
+ {
+ return _align_x;
+ }
+
+ //----------------------------------------------------------------------------
+ SVGpreserveAspectRatio::Align SVGpreserveAspectRatio::alignY() const
+ {
+ return _align_y;
+ }
+
+ //----------------------------------------------------------------------------
+ bool SVGpreserveAspectRatio::scaleToFill() const
+ {
+ return (_align_x == ALIGN_NONE) && (_align_y == ALIGN_NONE);
+ }
+
+ //----------------------------------------------------------------------------
+ bool SVGpreserveAspectRatio::scaleToFit() const
+ {
+ return !scaleToFill() && _meet;
+ }
+
+ //----------------------------------------------------------------------------
+ bool SVGpreserveAspectRatio::scaleToCrop() const
+ {
+ return !scaleToFill() && !_meet;
+ }
+
+ //----------------------------------------------------------------------------
+ bool SVGpreserveAspectRatio::meet() const
+ {
+ return _meet;
+ }
+
+ //----------------------------------------------------------------------------
+ bool
+ SVGpreserveAspectRatio::operator==(const SVGpreserveAspectRatio& rhs) const
+ {
+ return (_align_x == rhs._align_x)
+ && (_align_y == rhs._align_y)
+ && (_meet == rhs._meet || scaleToFill());
+ }
+
+ //----------------------------------------------------------------------------
+ SVGpreserveAspectRatio SVGpreserveAspectRatio::parse(const std::string& str)
+ {
+ SVGpreserveAspectRatio ret;
+ enum
+ {
+ PARSE_defer,
+ PARSE_align,
+ PARSE_meetOrSlice,
+ PARSE_done,
+ PARSE_error
+ } parse_state = PARSE_defer;
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ const boost::char_separator<char> del(" \t\n");
+
+ tokenizer tokens(str.begin(), str.end(), del);
+ for( tokenizer::const_iterator tok = tokens.begin();
+ tok != tokens.end()
+ && parse_state != PARSE_error;
+ ++tok )
+ {
+ const std::string& cur_tok = tok.current_token();
+
+ switch( parse_state )
+ {
+ case PARSE_defer:
+ if( cur_tok == "defer" )
+ {
+ SG_LOG( SG_GENERAL,
+ SG_INFO,
+ "SVGpreserveAspectRatio: 'defer' is ignored." );
+ parse_state = PARSE_align;
+ break;
+ }
+ // fall through
+ case PARSE_align:
+ if( cur_tok == "none" )
+ {
+ ret._align_x = ALIGN_NONE;
+ ret._align_y = ALIGN_NONE;
+ }
+ else if( cur_tok.length() == 8 )
+ {
+ if( strutils::starts_with(cur_tok, "xMin") )
+ ret._align_x = ALIGN_MIN;
+ else if( strutils::starts_with(cur_tok, "xMid") )
+ ret._align_x = ALIGN_MID;
+ else if( strutils::starts_with(cur_tok, "xMax") )
+ ret._align_x = ALIGN_MAX;
+ else
+ {
+ parse_state = PARSE_error;
+ break;
+ }
+
+ if( strutils::ends_with(cur_tok, "YMin") )
+ ret._align_y = ALIGN_MIN;
+ else if( strutils::ends_with(cur_tok, "YMid") )
+ ret._align_y = ALIGN_MID;
+ else if( strutils::ends_with(cur_tok, "YMax") )
+ ret._align_y = ALIGN_MAX;
+ else
+ {
+ parse_state = PARSE_error;
+ break;
+ }
+ }
+ else
+ {
+ parse_state = PARSE_error;
+ break;
+ }
+ parse_state = PARSE_meetOrSlice;
+ break;
+ case PARSE_meetOrSlice:
+ if( cur_tok == "meet" )
+ ret._meet = true;
+ else if( cur_tok == "slice" )
+ ret._meet = false;
+ else
+ {
+ parse_state = PARSE_error;
+ break;
+ }
+ parse_state = PARSE_done;
+ break;
+ case PARSE_done:
+ SG_LOG( SG_GENERAL,
+ SG_WARN,
+ "SVGpreserveAspectRatio: Ignoring superfluous token"
+ " '" << cur_tok << "'" );
+ break;
+ default:
+ break;
+ }
+ }
+
+ if( parse_state == PARSE_error )
+ {
+ SG_LOG( SG_GENERAL,
+ SG_WARN,
+ "SVGpreserveAspectRatio: Failed to parse: '" << str << "'" );
+ return SVGpreserveAspectRatio();
+ }
+
+ return ret;
+ }
+
+} // namespace simgear
--- /dev/null
+///@file Parse and represent SVG preserveAspectRatio attribute
+//
+// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#ifndef SG_SVG_PRESERVE_ASPECT_RATIO_HXX_
+#define SG_SVG_PRESERVE_ASPECT_RATIO_HXX_
+
+#include <string>
+
+namespace simgear
+{
+
+ /**
+ * SVG preserveAspectRatio attribute
+ *
+ * @see http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
+ */
+ class SVGpreserveAspectRatio
+ {
+ public:
+ enum Align
+ {
+ ALIGN_NONE,
+ ALIGN_MIN,
+ ALIGN_MID,
+ ALIGN_MAX
+ };
+
+ SVGpreserveAspectRatio();
+
+ Align alignX() const;
+ Align alignY() const;
+
+ bool scaleToFill() const;
+ bool scaleToFit() const;
+ bool scaleToCrop() const;
+
+ bool meet() const;
+
+ bool operator==(const SVGpreserveAspectRatio& rhs) const;
+
+ static SVGpreserveAspectRatio parse(const std::string& str);
+
+ private:
+ Align _align_x,
+ _align_y;
+ bool _meet; //!< uniform scale to fit, if true
+ // uniform scale to fill+crop, if false
+ };
+
+} // namespace simgear
+
+#endif /* SG_SVG_PRESERVE_ASPECT_RATIO_HXX_ */
--- /dev/null
+/// Unit tests for SVGpreserveAspectRatio
+#define BOOST_TEST_MODULE misc
+#include <BoostTestTargetConfig.h>
+
+#include "SVGpreserveAspectRatio.hxx"
+
+namespace simgear
+{
+ std::ostream& operator<<( std::ostream& strm,
+ const SVGpreserveAspectRatio& ar )
+ {
+ strm << "[ align_x=" << ar.alignX() <<
+ ", align_y=" << ar.alignY() <<
+ ", meet=" << ar.meet() <<
+ "]";
+ return strm;
+ }
+}
+
+BOOST_AUTO_TEST_CASE( parse_attribute )
+{
+ using simgear::SVGpreserveAspectRatio;
+
+ SVGpreserveAspectRatio ar = SVGpreserveAspectRatio::parse("none");
+ BOOST_CHECK( ar.scaleToFill() );
+ BOOST_CHECK( !ar.scaleToFit() );
+ BOOST_CHECK( !ar.scaleToCrop() );
+ BOOST_CHECK_EQUAL( ar.alignX(), SVGpreserveAspectRatio::ALIGN_NONE );
+ BOOST_CHECK_EQUAL( ar.alignY(), SVGpreserveAspectRatio::ALIGN_NONE );
+
+ SVGpreserveAspectRatio ar_meet = SVGpreserveAspectRatio::parse("none meet");
+ SVGpreserveAspectRatio ar_slice = SVGpreserveAspectRatio::parse("none slice");
+
+ BOOST_CHECK_EQUAL( ar, ar_meet );
+ BOOST_CHECK_EQUAL( ar, ar_slice );
+
+ ar_meet = SVGpreserveAspectRatio::parse("xMidYMid meet");
+ BOOST_CHECK( !ar_meet.scaleToFill() );
+ BOOST_CHECK( ar_meet.scaleToFit() );
+ BOOST_CHECK( !ar_meet.scaleToCrop() );
+ BOOST_CHECK_EQUAL( ar_meet.alignX(), SVGpreserveAspectRatio::ALIGN_MID );
+ BOOST_CHECK_EQUAL( ar_meet.alignY(), SVGpreserveAspectRatio::ALIGN_MID );
+
+ ar_slice = SVGpreserveAspectRatio::parse("xMidYMid slice");
+ BOOST_CHECK( !ar_slice.scaleToFill() );
+ BOOST_CHECK( !ar_slice.scaleToFit() );
+ BOOST_CHECK( ar_slice.scaleToCrop() );
+ BOOST_CHECK_EQUAL( ar_slice.alignX(), SVGpreserveAspectRatio::ALIGN_MID );
+ BOOST_CHECK_EQUAL( ar_slice.alignY(), SVGpreserveAspectRatio::ALIGN_MID );
+
+ BOOST_CHECK_NE(ar_meet, ar_slice);
+
+ // defer is ignored, meet is default
+ ar_meet = SVGpreserveAspectRatio::parse("defer xMinYMin");
+ BOOST_CHECK( !ar_meet.scaleToFill() );
+ BOOST_CHECK( ar_meet.scaleToFit() );
+ BOOST_CHECK( !ar_meet.scaleToCrop() );
+ BOOST_CHECK_EQUAL( ar_meet.alignX(), SVGpreserveAspectRatio::ALIGN_MIN );
+ BOOST_CHECK_EQUAL( ar_meet.alignY(), SVGpreserveAspectRatio::ALIGN_MIN );
+}