#include <osg/PrimitiveSet>
#include <boost/algorithm/string/predicate.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/range.hpp>
-#include <boost/tokenizer.hpp>
namespace simgear
{
// The ‘fill’ keyword, if present, causes the middle part of the image to be
// preserved. (By default it is discarded, i.e., treated as empty.)
- bool fill = (_slice.keyword == "fill");
+ bool fill = (_slice.getKeyword() == "fill");
if( _attributes_dirty & DEST_SIZE )
{
- size_t num_vertices = (_slice.valid ? (fill ? 9 : 8) : 1) * 4;
+ size_t num_vertices = (_slice.isValid() ? (fill ? 9 : 8) : 1) * 4;
if( num_vertices != _prim->getNumPrimitives() )
{
// http://www.w3.org/TR/css3-background/#border-image-outset
SGRect<float> region = _region;
- if( _outset.valid )
+ if( _outset.isValid() )
{
- const CSSOffsets& outset = _outset.getAbsOffsets(tex_dim);
+ const CSSBorder::Offsets& outset = _outset.getAbsOffsets(tex_dim);
region.t() -= outset.t;
region.r() += outset.r;
region.b() += outset.b;
region.l() -= outset.l;
}
- if( !_slice.valid )
+ if( !_slice.isValid() )
{
setQuad(0, region.getMin(), region.getMax());
}
-------------------- - y[3]
*/
- const CSSOffsets& slice =
- (_slice_width.valid ? _slice_width : _slice).getAbsOffsets(tex_dim);
+ const CSSBorder::Offsets& slice =
+ (_slice_width.isValid() ? _slice_width : _slice)
+ .getAbsOffsets(tex_dim);
float x[4] = {
region.l(),
region.l() + slice.l,
// Image coordinate systems y-axis is flipped
std::swap(src_rect.t(), src_rect.b());
- if( !_slice.valid )
+ if( !_slice.isValid() )
{
setQuadUV(0, src_rect.getMin(), src_rect.getMax());
}
else
{
- const CSSOffsets& slice = _slice.getRelOffsets(tex_dim);
+ const CSSBorder::Offsets& slice = _slice.getRelOffsets(tex_dim);
float x[4] = {
src_rect.l(),
src_rect.l() + slice.l,
//----------------------------------------------------------------------------
void Image::setSlice(const std::string& slice)
{
- _slice = parseSideOffsets(slice);
+ _slice = CSSBorder::parse(slice);
_attributes_dirty |= SRC_RECT | DEST_SIZE;
}
//----------------------------------------------------------------------------
void Image::setSliceWidth(const std::string& width)
{
- _slice_width = parseSideOffsets(width);
+ _slice_width = CSSBorder::parse(width);
_attributes_dirty |= DEST_SIZE;
}
//----------------------------------------------------------------------------
void Image::setOutset(const std::string& outset)
{
- _outset = parseSideOffsets(outset);
+ _outset = CSSBorder::parse(outset);
_attributes_dirty |= DEST_SIZE;
}
- toOsg(_region.getMin());
osg::Vec2f size(_region.width(), _region.height());
- if( _outset.valid )
+ if( _outset.isValid() )
{
- CSSOffsets outset = _outset.getAbsOffsets(getTextureDimensions());
+ CSSBorder::Offsets outset =
+ _outset.getAbsOffsets(getTextureDimensions());
mouse_event->client_pos += osg::Vec2f(outset.l, outset.t);
size.x() += outset.l + outset.r;
return handled || src_canvas->handleMouseEvent(mouse_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)
{
(*_texCoords)[i + 3].set(tl.x(), br.y());
}
- //----------------------------------------------------------------------------
- Image::CSSBorder Image::parseSideOffsets(const std::string& str) const
- {
- if( str.empty() )
- return CSSBorder();
-
- // [<number>'%'?]{1,4} (top[,right[,bottom[,left]]])
- //
- // Percentages are relative to the size of the image: the width of the
- // image for the horizontal offsets, the height for vertical offsets.
- // Numbers represent pixels in the image.
- int c = 0;
- CSSBorder ret;
-
- 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() && c < 4;
- ++tok )
- {
- if( isalpha(*tok->begin()) )
- ret.keyword = *tok;
- else
- {
- 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%’. TODO check max
- std::max
- (
- 0.f,
- boost::lexical_cast<float>
- (
- rel ? boost::make_iterator_range(tok->begin(), tok->end() - 1)
- : *tok
- )
- /
- (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.
- CSS_COPY_VAL(1, 0);
-
- // if the bottom is missing, it is the same as the top
- CSS_COPY_VAL(2, 0);
- }
-
- // If the left is missing, it is the same as the right
- CSS_COPY_VAL(3, 1);
- }
-
-#undef CSS_COPY_VAL
-
- ret.valid = true;
- return ret;
- }
-
} // namespace canvas
} // namespace simgear
#include "CanvasElement.hxx"
#include <simgear/canvas/canvas_fwd.hxx>
-#include <simgear/math/SGRect.hxx>
+#include <simgear/misc/CSSBorder.hxx>
#include <osg/Texture2D>
namespace simgear
DEST_SIZE = SRC_RECT << 1 // Element size
};
- union 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)
- {}
-
- CSSOffsets getRelOffsets(const SGRect<int>& dim) const;
- CSSOffsets getAbsOffsets(const SGRect<int>& dim) const;
-
- CSSOffsets offsets;
- CSSOffsetsTypes types;
- std::string keyword;
- bool valid;
- };
-
virtual void childChanged(SGPropertyNode * child);
void setupDefaultDimensions();
void setQuad(size_t index, const SGVec2f& tl, const SGVec2f& br);
void setQuadUV(size_t index, const SGVec2f& tl, const SGVec2f& br);
- CSSBorder parseSideOffsets(const std::string& str) const;
-
osg::ref_ptr<osg::Texture2D> _texture;
// TODO optionally forward events to canvas
CanvasWeakPtr _src_canvas;
include (SimGearComponent)
set(HEADERS
+ CSSBorder.hxx
ResourceManager.hxx
interpolator.hxx
make_new.hxx
gzcontainerfile.hxx
)
-set(SOURCES
+set(SOURCES
+ CSSBorder.cxx
ResourceManager.cxx
interpolator.cxx
sg_dir.cxx
if(ENABLE_TESTS)
+add_executable(test_CSSBorder CSSBorder_test.cxx)
+add_test(CSSBorder ${EXECUTABLE_OUTPUT_PATH}/test_CSSBorder)
+target_link_libraries(test_CSSBorder ${TEST_LIBS})
+
add_executable(test_tabbed_values tabbed_values_test.cxx)
add_test(tabbed_values ${EXECUTABLE_OUTPUT_PATH}/test_tabbed_values)
target_link_libraries(test_tabbed_values ${TEST_LIBS})
--- /dev/null
+// Parse and represent CSS border definitions (eg. margin, border-image-width)
+//
+// Copyright (C) 2013 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 "CSSBorder.hxx"
+
+#include <boost/lexical_cast.hpp>
+#include <boost/range.hpp>
+#include <boost/tokenizer.hpp>
+
+namespace simgear
+{
+
+ //----------------------------------------------------------------------------
+ bool CSSBorder::isValid() const
+ {
+ return valid;
+ }
+
+ //----------------------------------------------------------------------------
+ bool CSSBorder::isNone() const
+ {
+ return !valid
+ || ( offsets.t == 0
+ && offsets.r == 0
+ && offsets.b == 0
+ && offsets.l == 0 );
+ }
+
+ //----------------------------------------------------------------------------
+ const std::string& CSSBorder::getKeyword() const
+ {
+ return keyword;
+ }
+
+ //----------------------------------------------------------------------------
+ CSSBorder::Offsets CSSBorder::getRelOffsets(const SGRect<int>& dim) const
+ {
+ Offsets 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;
+ }
+
+ //----------------------------------------------------------------------------
+ CSSBorder::Offsets CSSBorder::getAbsOffsets(const SGRect<int>& dim) const
+ {
+ Offsets 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;
+ }
+
+ //----------------------------------------------------------------------------
+ CSSBorder CSSBorder::parse(const std::string& str)
+ {
+ if( str.empty() )
+ return CSSBorder();
+
+ // [<number>'%'?]{1,4} (top[,right[,bottom[,left]]])
+ //
+ // Percentages are relative to the size of the image: the width of the
+ // image for the horizontal offsets, the height for vertical offsets.
+ // Numbers represent pixels in the image.
+ int c = 0;
+ CSSBorder ret;
+
+ 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() && c < 4;
+ ++tok )
+ {
+ if( isalpha(*tok->begin()) )
+ ret.keyword = *tok;
+ else
+ {
+ 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%’. TODO check max
+ std::max
+ (
+ 0.f,
+ boost::lexical_cast<float>
+ (
+ rel ? boost::make_iterator_range(tok->begin(), tok->end() - 1)
+ : *tok
+ )
+ /
+ (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.
+ CSS_COPY_VAL(1, 0);
+
+ // if the bottom is missing, it is the same as the top
+ CSS_COPY_VAL(2, 0);
+ }
+
+ // If the left is missing, it is the same as the right
+ CSS_COPY_VAL(3, 1);
+ }
+
+#undef CSS_COPY_VAL
+
+ if( ret.keyword == "none" )
+ {
+ memset(&ret.offsets, 0, sizeof(Offsets));
+ ret.keyword.clear();
+ }
+
+ ret.valid = true;
+ return ret;
+ }
+
+} // namespace simgear
--- /dev/null
+// Parse and represent CSS border definitions (eg. margin, border-image-width)
+//
+// Copyright (C) 2013 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_CSSBORDER_HXX_
+#define SG_CSSBORDER_HXX_
+
+#include <simgear/math/SGMath.hxx>
+#include <simgear/math/SGRect.hxx>
+#include <string>
+
+namespace simgear
+{
+
+ class CSSBorder
+ {
+ public:
+ union Offsets
+ {
+ float val[4];
+ struct { float t, r, b, l; };
+ };
+
+ union OffsetsTypes
+ {
+ bool rel[4];
+ struct { bool t_rel, r_rel, b_rel, l_rel; };
+ };
+
+ CSSBorder():
+ valid(false)
+ {}
+
+ bool isValid() const;
+
+ /**
+ * Get whether a non-zero offset exists
+ */
+ bool isNone() const;
+
+ const std::string& getKeyword() const;
+
+ Offsets getRelOffsets(const SGRect<int>& dim) const;
+ Offsets getAbsOffsets(const SGRect<int>& dim) const;
+
+ static CSSBorder parse(const std::string& str);
+
+ private:
+ Offsets offsets;
+ OffsetsTypes types;
+ std::string keyword;
+ bool valid;
+ };
+
+} // namespace simgear
+
+#endif /* SG_CSSBORDER_HXX_ */
--- /dev/null
+#include <simgear/compiler.h>
+
+#include "CSSBorder.hxx"
+
+#include <cmath>
+#include <iostream>
+
+#define COMPARE(a, b) \
+ if( std::fabs((a) - (b)) > 1e-4 ) \
+ { \
+ std::cerr << "line " << __LINE__ << ": failed: "\
+ << #a << " != " << #b << " d = " << ((a) - (b)) << std::endl; \
+ return 1; \
+ }
+
+#define VERIFY(a) \
+ if( !(a) ) \
+ { \
+ std::cerr << "line " << __LINE__ << ": failed: "\
+ << #a << std::endl; \
+ return 1; \
+ }
+
+using namespace simgear;
+
+int main (int ac, char ** av)
+{
+ CSSBorder b = CSSBorder::parse("5");
+ VERIFY(b.isValid());
+ VERIFY(!b.isNone());
+ CSSBorder::Offsets o = b.getAbsOffsets(SGRect<int>());
+ COMPARE(o.t, 5);
+ COMPARE(o.r, 5);
+ COMPARE(o.b, 5);
+ COMPARE(o.l, 5);
+
+ b = CSSBorder::parse("5 10");
+ o = b.getAbsOffsets(SGRect<int>());
+ COMPARE(o.t, 5);
+ COMPARE(o.r, 10);
+ COMPARE(o.b, 5);
+ COMPARE(o.l, 10);
+
+ b = CSSBorder::parse("5 10 15");
+ o = b.getAbsOffsets(SGRect<int>());
+ COMPARE(o.t, 5);
+ COMPARE(o.r, 10);
+ COMPARE(o.b, 15);
+ COMPARE(o.l, 10);
+
+ b = CSSBorder::parse("5 10 15 20");
+ o = b.getAbsOffsets(SGRect<int>());
+ COMPARE(o.t, 5);
+ COMPARE(o.r, 10);
+ COMPARE(o.b, 15);
+ COMPARE(o.l, 20);
+
+ b = CSSBorder::parse("5% 10% 15% 20%");
+ o = b.getAbsOffsets(SGRect<int>(0,0,200,200));
+ COMPARE(o.t, 10);
+ COMPARE(o.r, 20);
+ COMPARE(o.b, 30);
+ COMPARE(o.l, 40);
+
+ o = b.getRelOffsets(SGRect<int>(0,0,200,200));
+ COMPARE(o.t, 0.05);
+ COMPARE(o.r, 0.1);
+ COMPARE(o.b, 0.15);
+ COMPARE(o.l, 0.2);
+
+ b = CSSBorder::parse("5% none");
+ o = b.getAbsOffsets(SGRect<int>(0,0,200,200));
+ COMPARE(o.t, 0);
+ COMPARE(o.r, 0);
+ COMPARE(o.b, 0);
+ COMPARE(o.l, 0);
+ VERIFY(b.getKeyword().empty());
+ VERIFY(b.isNone());
+
+ b = CSSBorder::parse("none");
+ o = b.getRelOffsets(SGRect<int>(0,0,200,200));
+ COMPARE(o.t, 0);
+ COMPARE(o.r, 0);
+ COMPARE(o.b, 0);
+ COMPARE(o.l, 0);
+ VERIFY(b.getKeyword().empty());
+ VERIFY(b.isNone());
+
+ std::cout << "all tests passed successfully!" << std::endl;
+ return 0;
+}