]> git.mxchange.org Git - simgear.git/commitdiff
Canvas: separate CSSBorder parsing from image.
authorThomas Geymayer <tomgey@gmail.com>
Fri, 31 May 2013 17:10:22 +0000 (19:10 +0200)
committerThomas Geymayer <tomgey@gmail.com>
Fri, 31 May 2013 17:14:39 +0000 (19:14 +0200)
simgear/canvas/elements/CanvasImage.cxx
simgear/canvas/elements/CanvasImage.hxx
simgear/misc/CMakeLists.txt
simgear/misc/CSSBorder.cxx [new file with mode: 0644]
simgear/misc/CSSBorder.hxx [new file with mode: 0644]
simgear/misc/CSSBorder_test.cxx [new file with mode: 0644]

index f7a4d92cd41346e170b3c9acbcdfb55d3f089658..bc6ff0914727e0e3c531afbd52ea3e02791f5c97 100644 (file)
@@ -31,9 +31,6 @@
 #include <osg/PrimitiveSet>
 
 #include <boost/algorithm/string/predicate.hpp>
 #include <osg/PrimitiveSet>
 
 #include <boost/algorithm/string/predicate.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/range.hpp>
-#include <boost/tokenizer.hpp>
 
 namespace simgear
 {
 
 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.)
 
     // 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 )
     {
 
     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() )
       {
 
       if( num_vertices != _prim->getNumPrimitives() )
       {
@@ -179,16 +176,16 @@ namespace canvas
 
       // http://www.w3.org/TR/css3-background/#border-image-outset
       SGRect<float> region = _region;
 
       // 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;
       }
 
         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());
       }
       {
         setQuad(0, region.getMin(), region.getMax());
       }
@@ -215,8 +212,9 @@ namespace canvas
           -------------------- - y[3]
          */
 
           -------------------- - 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,
         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());
 
       // 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
       {
       {
         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,
         float x[4] = {
           src_rect.l(),
           src_rect.l() + slice.l,
@@ -387,21 +385,21 @@ namespace canvas
   //----------------------------------------------------------------------------
   void Image::setSlice(const std::string& slice)
   {
   //----------------------------------------------------------------------------
   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)
   {
     _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)
   {
     _attributes_dirty |= DEST_SIZE;
   }
 
   //----------------------------------------------------------------------------
   void Image::setOutset(const std::string& outset)
   {
-    _outset = parseSideOffsets(outset);
+    _outset = CSSBorder::parse(outset);
     _attributes_dirty |= DEST_SIZE;
   }
 
     _attributes_dirty |= DEST_SIZE;
   }
 
@@ -430,9 +428,10 @@ namespace canvas
                               - toOsg(_region.getMin());
 
       osg::Vec2f size(_region.width(), _region.height());
                               - 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;
 
         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);
   }
 
     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)
   {
   //----------------------------------------------------------------------------
   void Image::childChanged(SGPropertyNode* child)
   {
@@ -635,81 +606,5 @@ namespace canvas
     (*_texCoords)[i + 3].set(tl.x(), br.y());
   }
 
     (*_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
 } // namespace canvas
 } // namespace simgear
index 453f7dce5d8dab0205f311529ee219737517c135..105c628f35bbc26e6c00184695ed797f47e3aaa2 100644 (file)
@@ -22,7 +22,7 @@
 #include "CanvasElement.hxx"
 
 #include <simgear/canvas/canvas_fwd.hxx>
 #include "CanvasElement.hxx"
 
 #include <simgear/canvas/canvas_fwd.hxx>
-#include <simgear/math/SGRect.hxx>
+#include <simgear/misc/CSSBorder.hxx>
 #include <osg/Texture2D>
 
 namespace simgear
 #include <osg/Texture2D>
 
 namespace simgear
@@ -91,33 +91,6 @@ namespace canvas
         DEST_SIZE      = SRC_RECT << 1        // Element size
       };
 
         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();
       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);
 
       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;
       osg::ref_ptr<osg::Texture2D> _texture;
       // TODO optionally forward events to canvas
       CanvasWeakPtr _src_canvas;
index f92602857bea5271d2e009b9b529d54d72cbd519..11c43d6f1205c897778b4f81e7b9eac689d69759 100644 (file)
@@ -2,6 +2,7 @@
 include (SimGearComponent)
 
 set(HEADERS 
 include (SimGearComponent)
 
 set(HEADERS 
+    CSSBorder.hxx
     ResourceManager.hxx
     interpolator.hxx
     make_new.hxx
     ResourceManager.hxx
     interpolator.hxx
     make_new.hxx
@@ -17,7 +18,8 @@ set(HEADERS
     gzcontainerfile.hxx
     )
 
     gzcontainerfile.hxx
     )
 
-set(SOURCES 
+set(SOURCES
+    CSSBorder.cxx
     ResourceManager.cxx
     interpolator.cxx
     sg_dir.cxx
     ResourceManager.cxx
     interpolator.cxx
     sg_dir.cxx
@@ -34,6 +36,10 @@ simgear_component(misc misc "${SOURCES}" "${HEADERS}")
 
 if(ENABLE_TESTS)
 
 
 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})
 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 (file)
index 0000000..cf4dd9e
--- /dev/null
@@ -0,0 +1,158 @@
+// 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
diff --git a/simgear/misc/CSSBorder.hxx b/simgear/misc/CSSBorder.hxx
new file mode 100644 (file)
index 0000000..6ccf7cb
--- /dev/null
@@ -0,0 +1,71 @@
+// 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_ */
diff --git a/simgear/misc/CSSBorder_test.cxx b/simgear/misc/CSSBorder_test.cxx
new file mode 100644 (file)
index 0000000..29d4336
--- /dev/null
@@ -0,0 +1,91 @@
+#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;
+}