CanvasImage: Fix border abs/rel calculations; add slice-width property

This commit is contained in:
Thomas Geymayer
2013-02-28 01:09:27 +01:00
parent cfdc7db79a
commit e1302bcf17
2 changed files with 135 additions and 42 deletions

View File

@@ -21,6 +21,7 @@
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasMgr.hxx>
#include <simgear/canvas/CanvasSystemAdapter.hxx>
#include <simgear/canvas/MouseEvent.hxx>
#include <simgear/scene/util/parse_color.hxx>
#include <simgear/misc/sg_path.hxx>
@@ -115,6 +116,7 @@ namespace canvas
addStyle("fill", &Image::setFill, this);
addStyle("slice", &Image::setSlice, this);
addStyle("slice-width", &Image::setSliceWidth, this);
addStyle("outset", &Image::setOutset, this);
setFill("#ffffff"); // TODO how should we handle default values?
@@ -162,10 +164,11 @@ namespace canvas
SGRect<float> region = _region;
if( _outset.valid )
{
region.t() -= _outset.t;
region.r() += _outset.r;
region.b() += _outset.b;
region.l() -= _outset.l;
const CSSOffsets& outset = _outset.getAbsOffsets(tex_dim);
region.t() -= outset.t;
region.r() += outset.r;
region.b() += outset.b;
region.l() -= outset.l;
}
if( !_slice.valid )
@@ -195,16 +198,18 @@ namespace canvas
-------------------- - y[3]
*/
const CSSOffsets& slice =
(_slice_width.valid ? _slice_width : _slice).getAbsOffsets(tex_dim);
float x[4] = {
region.l(),
region.l() + _slice.l * tex_dim.width(),
region.r() - _slice.r * tex_dim.width(),
region.l() + slice.l,
region.r() - slice.r,
region.r()
};
float y[4] = {
region.t(),
region.t() + _slice.t * tex_dim.height(),
region.b() - _slice.b * tex_dim.height(),
region.t() + slice.t,
region.b() - slice.b,
region.b()
};
@@ -249,16 +254,17 @@ namespace canvas
}
else
{
const CSSOffsets& slice = _slice.getRelOffsets(tex_dim);
float x[4] = {
src_rect.l(),
src_rect.l() + _slice.l,
src_rect.r() - _slice.r,
src_rect.l() + slice.l,
src_rect.r() - slice.r,
src_rect.r()
};
float y[4] = {
src_rect.t(),
src_rect.t() - _slice.t,
src_rect.b() + _slice.b,
src_rect.t() - slice.t,
src_rect.b() + slice.b,
src_rect.b()
};
@@ -338,6 +344,13 @@ namespace canvas
_attributes_dirty |= SRC_RECT | DEST_SIZE;
}
//----------------------------------------------------------------------------
void Image::setSliceWidth(const std::string& width)
{
_slice_width = parseSideOffsets(width);
_attributes_dirty |= DEST_SIZE;
}
//----------------------------------------------------------------------------
void Image::setOutset(const std::string& outset)
{
@@ -351,6 +364,57 @@ namespace canvas
return _region;
}
//----------------------------------------------------------------------------
bool Image::handleMouseEvent(MouseEventPtr event)
{
CanvasPtr src_canvas = _src_canvas.lock();
if( !src_canvas )
return false;
if( _outset.valid )
{
CSSOffsets outset = _outset.getAbsOffsets(getTextureDimensions());
event.reset( new MouseEvent(*event) );
event->client_pos += osg::Vec2f(outset.l, outset.t);
event->client_pos.x() *= src_canvas->getViewWidth()
/ (_region.width() + outset.l + outset.r);
event->client_pos.y() *= src_canvas->getViewHeight()
/ (_region.height() + outset.t + outset.b);
}
return src_canvas->handleMouseEvent(event);
}
//----------------------------------------------------------------------------
Image::CSSOffsets
Image::CSSBorder::getRelOffsets(const SGRect<int>& dim) const
{
CSSOffsets ret;
for(int i = 0; i < 4; ++i)
{
ret.val[i] = offsets.val[i];
if( !types.rel[i] )
ret.val[i] /= (i & 1) ? dim.height() : dim.width();
}
return ret;
}
//----------------------------------------------------------------------------
Image::CSSOffsets
Image::CSSBorder::getAbsOffsets(const SGRect<int>& dim) const
{
CSSOffsets ret;
for(int i = 0; i < 4; ++i)
{
ret.val[i] = offsets.val[i];
if( types.rel[i] )
ret.val[i] *= (i & 1) ? dim.height() : dim.width();
}
return ret;
}
//----------------------------------------------------------------------------
void Image::childChanged(SGPropertyNode* child)
{
@@ -511,12 +575,10 @@ namespace canvas
}
//----------------------------------------------------------------------------
Image::CSSOffsets Image::parseSideOffsets(const std::string& str) const
Image::CSSBorder Image::parseSideOffsets(const std::string& str) const
{
if( str.empty() )
return CSSOffsets();
const SGRect<int>& dim = getTextureDimensions();
return CSSBorder();
// [<number>'%'?]{1,4} (top[,right[,bottom[,left]]])
//
@@ -524,7 +586,7 @@ namespace canvas
// image for the horizontal offsets, the height for vertical offsets.
// Numbers represent pixels in the image.
int c = 0;
CSSOffsets ret;
CSSBorder ret;
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
const boost::char_separator<char> del(" \t\n");
@@ -538,46 +600,52 @@ namespace canvas
ret.keyword = *tok;
else
{
bool rel = (*tok->rbegin() == '%');
ret.offsets[c] =
bool rel = ret.types.rel[c] = (*tok->rbegin() == '%');
ret.offsets.val[c] =
// Negative values are not allowed and values bigger than the size of
// the image are interpreted as 100%.
std::max(0.f,
std::min(1.f,
// the image are interpreted as 100%. TODO check max
std::max
(
0.f,
boost::lexical_cast<float>
(
rel ? boost::make_iterator_range(tok->begin(), tok->end() - 1)
: *tok
)
/
(
rel ? 100
: (c & 1) ? dim.width() : dim.height()
)
));
(rel ? 100 : 1)
);
++c;
}
}
// When four values are specified, they set the offsets on the top, right,
// bottom and left sides in that order.
#define CSS_COPY_VAL(dest, src)\
{\
ret.offsets.val[dest] = ret.offsets.val[src];\
ret.types.rel[dest] = ret.types.rel[src];\
}
if( c < 4 )
{
if( c < 3 )
{
if( c < 2 )
// if the right is missing, it is the same as the top.
ret.offsets[1] = ret.offsets[0];
CSS_COPY_VAL(1, 0);
// if the bottom is missing, it is the same as the top
ret.offsets[2] = ret.offsets[0];
CSS_COPY_VAL(2, 0);
}
// If the left is missing, it is the same as the right
ret.offsets[3] = ret.offsets[1];
CSS_COPY_VAL(3, 1);
}
#undef CSS_COPY_VAL
ret.valid = true;
return ret;
}

View File

@@ -62,6 +62,17 @@ namespace canvas
*/
void setSlice(const std::string& slice);
/**
* Set image slice width.
*
* By default the size of the 9-scale grid is the same as specified
* with setSlice/"slice". Using this method allows setting values
* different to the size in the source image.
*
* @see http://www.w3.org/TR/css3-background/#border-image-width
*/
void setSliceWidth(const std::string& width);
/**
* http://www.w3.org/TR/css3-background/#border-image-outset
*/
@@ -69,6 +80,8 @@ namespace canvas
const SGRect<float>& getRegion() const;
bool handleMouseEvent(MouseEventPtr event);
protected:
enum ImageAttributes
@@ -77,20 +90,31 @@ namespace canvas
DEST_SIZE = SRC_RECT << 1 // Element size
};
struct CSSOffsets
union CSSOffsets
{
CSSOffsets():
float val[4];
struct { float t, r, b, l; };
};
union CSSOffsetsTypes
{
bool rel[4];
struct { bool t_rel, r_rel, b_rel, l_rel; };
};
struct CSSBorder
{
CSSBorder():
valid(false)
{}
union
{
float offsets[4];
struct { float t, r, b, l; };
};
CSSOffsets getRelOffsets(const SGRect<int>& dim) const;
CSSOffsets getAbsOffsets(const SGRect<int>& dim) const;
std::string keyword;
bool valid;
CSSOffsets offsets;
CSSOffsetsTypes types;
std::string keyword;
bool valid;
};
virtual void childChanged(SGPropertyNode * child);
@@ -101,7 +125,7 @@ namespace canvas
void setQuad(size_t index, const SGVec2f& tl, const SGVec2f& br);
void setQuadUV(size_t index, const SGVec2f& tl, const SGVec2f& br);
CSSOffsets parseSideOffsets(const std::string& str) const;
CSSBorder parseSideOffsets(const std::string& str) const;
osg::ref_ptr<osg::Texture2D> _texture;
// TODO optionally forward events to canvas
@@ -117,7 +141,8 @@ namespace canvas
SGRect<float> _src_rect,
_region;
CSSOffsets _slice,
CSSBorder _slice,
_slice_width,
_outset;
};