From 13344fbb6252431f73aa7a2a01d411132ee71863 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Fri, 31 May 2013 19:10:22 +0200 Subject: [PATCH] Canvas: separate CSSBorder parsing from image. --- simgear/canvas/elements/CanvasImage.cxx | 137 +++----------------- simgear/canvas/elements/CanvasImage.hxx | 31 +---- simgear/misc/CMakeLists.txt | 8 +- simgear/misc/CSSBorder.cxx | 158 ++++++++++++++++++++++++ simgear/misc/CSSBorder.hxx | 71 +++++++++++ simgear/misc/CSSBorder_test.cxx | 91 ++++++++++++++ 6 files changed, 344 insertions(+), 152 deletions(-) create mode 100644 simgear/misc/CSSBorder.cxx create mode 100644 simgear/misc/CSSBorder.hxx create mode 100644 simgear/misc/CSSBorder_test.cxx diff --git a/simgear/canvas/elements/CanvasImage.cxx b/simgear/canvas/elements/CanvasImage.cxx index f7a4d92c..bc6ff091 100644 --- a/simgear/canvas/elements/CanvasImage.cxx +++ b/simgear/canvas/elements/CanvasImage.cxx @@ -31,9 +31,6 @@ #include #include -#include -#include -#include namespace simgear { @@ -161,11 +158,11 @@ namespace canvas // 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() ) { @@ -179,16 +176,16 @@ namespace canvas // http://www.w3.org/TR/css3-background/#border-image-outset SGRect 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()); } @@ -215,8 +212,9 @@ namespace canvas -------------------- - 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, @@ -265,13 +263,13 @@ namespace canvas // 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, @@ -387,21 +385,21 @@ namespace canvas //---------------------------------------------------------------------------- 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; } @@ -430,9 +428,10 @@ namespace canvas - 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; @@ -448,34 +447,6 @@ namespace canvas return handled || src_canvas->handleMouseEvent(mouse_event); } - //---------------------------------------------------------------------------- - Image::CSSOffsets - Image::CSSBorder::getRelOffsets(const SGRect& 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& 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) { @@ -635,81 +606,5 @@ namespace canvas (*_texCoords)[i + 3].set(tl.x(), br.y()); } - //---------------------------------------------------------------------------- - Image::CSSBorder Image::parseSideOffsets(const std::string& str) const - { - if( str.empty() ) - return CSSBorder(); - - // ['%'?]{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 > tokenizer; - const boost::char_separator 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 - ( - 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 diff --git a/simgear/canvas/elements/CanvasImage.hxx b/simgear/canvas/elements/CanvasImage.hxx index 453f7dce..105c628f 100644 --- a/simgear/canvas/elements/CanvasImage.hxx +++ b/simgear/canvas/elements/CanvasImage.hxx @@ -22,7 +22,7 @@ #include "CanvasElement.hxx" #include -#include +#include #include namespace simgear @@ -91,33 +91,6 @@ namespace canvas 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& dim) const; - CSSOffsets getAbsOffsets(const SGRect& dim) const; - - CSSOffsets offsets; - CSSOffsetsTypes types; - std::string keyword; - bool valid; - }; - virtual void childChanged(SGPropertyNode * child); void setupDefaultDimensions(); @@ -126,8 +99,6 @@ namespace canvas 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 _texture; // TODO optionally forward events to canvas CanvasWeakPtr _src_canvas; diff --git a/simgear/misc/CMakeLists.txt b/simgear/misc/CMakeLists.txt index f9260285..11c43d6f 100644 --- a/simgear/misc/CMakeLists.txt +++ b/simgear/misc/CMakeLists.txt @@ -2,6 +2,7 @@ include (SimGearComponent) set(HEADERS + CSSBorder.hxx ResourceManager.hxx interpolator.hxx make_new.hxx @@ -17,7 +18,8 @@ set(HEADERS gzcontainerfile.hxx ) -set(SOURCES +set(SOURCES + CSSBorder.cxx ResourceManager.cxx interpolator.cxx sg_dir.cxx @@ -34,6 +36,10 @@ simgear_component(misc misc "${SOURCES}" "${HEADERS}") 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}) diff --git a/simgear/misc/CSSBorder.cxx b/simgear/misc/CSSBorder.cxx new file mode 100644 index 00000000..cf4dd9e7 --- /dev/null +++ b/simgear/misc/CSSBorder.cxx @@ -0,0 +1,158 @@ +// Parse and represent CSS border definitions (eg. margin, border-image-width) +// +// Copyright (C) 2013 Thomas Geymayer +// +// 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 +#include +#include + +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& 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& 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(); + + // ['%'?]{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 > tokenizer; + const boost::char_separator 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 + ( + 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 diff --git a/simgear/misc/CSSBorder.hxx b/simgear/misc/CSSBorder.hxx new file mode 100644 index 00000000..6ccf7cb8 --- /dev/null +++ b/simgear/misc/CSSBorder.hxx @@ -0,0 +1,71 @@ +// Parse and represent CSS border definitions (eg. margin, border-image-width) +// +// Copyright (C) 2013 Thomas Geymayer +// +// 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 +#include +#include + +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& dim) const; + Offsets getAbsOffsets(const SGRect& 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_ */ diff --git a/simgear/misc/CSSBorder_test.cxx b/simgear/misc/CSSBorder_test.cxx new file mode 100644 index 00000000..29d43366 --- /dev/null +++ b/simgear/misc/CSSBorder_test.cxx @@ -0,0 +1,91 @@ +#include + +#include "CSSBorder.hxx" + +#include +#include + +#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()); + 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()); + 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()); + 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()); + 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(0,0,200,200)); + COMPARE(o.t, 10); + COMPARE(o.r, 20); + COMPARE(o.b, 30); + COMPARE(o.l, 40); + + o = b.getRelOffsets(SGRect(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(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(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; +} -- 2.39.5