canvas::Image: allow aspect ratio preserving display.
This commit is contained in:
parent
6925c2a2be
commit
c54e3f8101
@ -96,9 +96,10 @@ namespace canvas
|
||||
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);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@ -214,6 +215,10 @@ namespace canvas
|
||||
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
|
||||
{
|
||||
@ -290,6 +295,66 @@ namespace canvas
|
||||
|
||||
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
|
||||
@ -413,6 +478,20 @@ namespace canvas
|
||||
_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)
|
||||
{
|
||||
@ -427,13 +506,6 @@ namespace canvas
|
||||
_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
|
||||
{
|
||||
|
@ -24,6 +24,7 @@
|
||||
#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
|
||||
@ -61,6 +62,17 @@ namespace canvas
|
||||
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)
|
||||
*
|
||||
@ -79,11 +91,6 @@ namespace canvas
|
||||
*/
|
||||
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);
|
||||
@ -126,9 +133,11 @@ namespace canvas
|
||||
SGRect<float> _src_rect,
|
||||
_region;
|
||||
|
||||
CSSBorder _slice,
|
||||
_slice_width,
|
||||
_outset;
|
||||
SVGpreserveAspectRatio _preserve_aspect_ratio;
|
||||
|
||||
CSSBorder _outset,
|
||||
_slice,
|
||||
_slice_width;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
|
@ -5,6 +5,7 @@ set(HEADERS
|
||||
CSSBorder.hxx
|
||||
ListDiff.hxx
|
||||
ResourceManager.hxx
|
||||
SVGpreserveAspectRatio.hxx
|
||||
interpolator.hxx
|
||||
make_new.hxx
|
||||
sg_dir.hxx
|
||||
@ -22,6 +23,7 @@ set(HEADERS
|
||||
set(SOURCES
|
||||
CSSBorder.cxx
|
||||
ResourceManager.cxx
|
||||
SVGpreserveAspectRatio.cxx
|
||||
interpolator.cxx
|
||||
sg_dir.cxx
|
||||
sg_path.cxx
|
||||
@ -62,3 +64,8 @@ add_test(path ${EXECUTABLE_OUTPUT_PATH}/test_path)
|
||||
target_link_libraries(test_path ${TEST_LIBS})
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
|
||||
add_boost_test(SVGpreserveAspectRatio
|
||||
SOURCES SVGpreserveAspectRatio_test.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
192
simgear/misc/SVGpreserveAspectRatio.cxx
Normal file
192
simgear/misc/SVGpreserveAspectRatio.cxx
Normal file
@ -0,0 +1,192 @@
|
||||
// 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
|
67
simgear/misc/SVGpreserveAspectRatio.hxx
Normal file
67
simgear/misc/SVGpreserveAspectRatio.hxx
Normal file
@ -0,0 +1,67 @@
|
||||
///@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_ */
|
60
simgear/misc/SVGpreserveAspectRatio_test.cxx
Normal file
60
simgear/misc/SVGpreserveAspectRatio_test.cxx
Normal file
@ -0,0 +1,60 @@
|
||||
/// 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 );
|
||||
}
|
Loading…
Reference in New Issue
Block a user