summaryrefslogtreecommitdiff
path: root/src/drivers/Android/Fl_Android_Graphics_Clipping.cxx
diff options
context:
space:
mode:
authorMatthias Melcher <fltk@matthiasm.com>2018-03-16 12:48:29 +0000
committerMatthias Melcher <fltk@matthiasm.com>2018-03-16 12:48:29 +0000
commitbdb63b3f07cba389ce3fb584218389349561321c (patch)
treec2a99faa16c1446eea3b2872bcd5d09c5513e3ba /src/drivers/Android/Fl_Android_Graphics_Clipping.cxx
parent0e4b7bbb49e89ed3808c36eeba3881dba62a2094 (diff)
Android: clipping regions code grew so big, they moved into their own
header and source file. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.4@12758 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src/drivers/Android/Fl_Android_Graphics_Clipping.cxx')
-rw-r--r--src/drivers/Android/Fl_Android_Graphics_Clipping.cxx715
1 files changed, 715 insertions, 0 deletions
diff --git a/src/drivers/Android/Fl_Android_Graphics_Clipping.cxx b/src/drivers/Android/Fl_Android_Graphics_Clipping.cxx
new file mode 100644
index 000000000..000ef929f
--- /dev/null
+++ b/src/drivers/Android/Fl_Android_Graphics_Clipping.cxx
@@ -0,0 +1,715 @@
+//
+// "$Id$"
+//
+// Clipping region routines for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2018 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// http://www.fltk.org/COPYING.php
+//
+// Please report all bugs and problems on the following page:
+//
+// http://www.fltk.org/str.php
+//
+
+
+#include "../../config_lib.h"
+#include "Fl_Android_Graphics_Driver.H"
+#include "Fl_Android_Application.H"
+#include <FL/platform.H>
+
+
+/**
+ * Create an empty clipping region.
+ */
+Fl_Rect_Region::Fl_Rect_Region() :
+ pLeft(0), pTop(0), pRight(0), pBottom(0)
+{
+}
+
+/**
+ * Create a clipping region based on position and size.
+ * @param x, y position
+ * @param w, h size
+ */
+Fl_Rect_Region::Fl_Rect_Region(int x, int y, int w, int h) :
+ pLeft(x), pTop(y), pRight(x+w), pBottom(y+h)
+{
+}
+
+/**
+ * Clone a clipping rectangle.
+ */
+Fl_Rect_Region::Fl_Rect_Region(const Fl_Rect_Region &r) :
+ pLeft(r.pLeft), pTop(r.pTop),
+ pRight(r.pRight), pBottom(r.pBottom)
+{
+}
+
+/**
+ * Clone a clipping rectangle.
+ * The pointer can be NULL if an empty rectangle is needed.
+ */
+Fl_Rect_Region::Fl_Rect_Region(enum Type what)
+{
+ if (what==INFINITE) {
+ pLeft = pTop = INT_MIN;
+ pRight = pBottom = INT_MAX;
+ } else {
+ pLeft = pTop = pRight = pBottom = 0;
+ }
+}
+
+/**
+ * If the rectangle has no width or height, it's considered empty.
+ * @return true, if everything will be clipped and there is nothing to draw
+ */
+bool Fl_Rect_Region::is_empty() const
+{
+ return (pRight<=pLeft || pBottom<=pTop);
+}
+
+/**
+ * Return true, if the rectangle is of unlimited size and nothing should be clipped.
+ * @return treu, if there is no clipping
+ */
+bool Fl_Rect_Region::is_infinite() const
+{
+ return (pLeft==INT_MIN);
+}
+
+/**
+ * Set an empty clipping rect.
+ */
+void Fl_Rect_Region::set_empty()
+{
+ pLeft = pTop = pRight = pBottom = 0;
+}
+
+/**
+ * Set a clipping rect using position and size
+ * @param x, y position
+ * @param w, h size
+ */
+void Fl_Rect_Region::set(int x, int y, int w, int h)
+{
+ pLeft = x;
+ pTop = y;
+ pRight = x+w;
+ pBottom = y+h;
+}
+
+/**
+ * Set a rectangle using the coordinates of two points, top left and bottom right.
+ * @param l, t left and top coordinate
+ * @param r, b right and bottom coordinate
+ */
+void Fl_Rect_Region::set_ltrb(int l, int t, int r, int b)
+{
+ pLeft = l;
+ pTop = t;
+ pRight = r;
+ pBottom = b;
+}
+
+/**
+ * Copy the corrdinates from another rect.
+ * @param r source rectangle
+ */
+void Fl_Rect_Region::set(const Fl_Rect_Region &r)
+{
+ pLeft = r.pLeft;
+ pTop = r.pTop;
+ pRight = r.pRight;
+ pBottom = r.pBottom;
+}
+
+/**
+ * Set this rect to be the intersecting area between the original rect and another rect.
+ * @param r another rectangular region
+ * @return EMPTY, if rectangles are not intersecting, SAME if this and rect are
+ * equal, LESS if the new rect is smaller than the original rect
+ */
+int Fl_Rect_Region::intersect_with(const Fl_Rect_Region &r)
+{
+ if (is_empty()) {
+ return EMPTY;
+ }
+ if (r.is_empty()) {
+ set_empty();
+ return EMPTY;
+ }
+ bool same = true;
+ if ( pLeft != r.pLeft ) {
+ same = false;
+ if ( r.pLeft > pLeft ) pLeft = r.pLeft;
+ }
+ if ( pTop != r.pTop ) {
+ same = false;
+ if ( r.pTop > pTop ) pTop = r.pTop;
+ }
+ if ( pRight != r.pRight ) {
+ same = false;
+ if ( r.pRight < pRight ) pRight = r.pRight;
+ }
+ if ( pBottom != r.pBottom ) {
+ same = false;
+ if ( r.pBottom < pBottom ) pBottom = r.pBottom;
+ }
+ if (same)
+ return SAME;
+ if (is_empty())
+ return EMPTY;
+ return LESS;
+}
+
+/**
+ * Print the coordinates of the rect to the log.
+ * @param label some text that is logged with this message.
+ */
+void Fl_Rect_Region::print(const char *label) const
+{
+ Fl_Android_Application::log_i("---> Fl_Rect_Region: %s", label);
+ Fl_Android_Application::log_i("Rect l:%d t:%d r:%d b:%d", left(), top(), right(), bottom());
+}
+
+// =============================================================================
+
+/**
+ * Create an empty complex region.
+ */
+Fl_Complex_Region::Fl_Complex_Region() :
+ Fl_Rect_Region()
+{
+}
+
+/**
+ * Create a complex region with the same bounds as the give rect.
+ * @param r region size
+ */
+Fl_Complex_Region::Fl_Complex_Region(const Fl_Rect_Region &r) :
+ Fl_Rect_Region(r)
+{
+}
+
+/**
+ * Delete this region, all subregions recursively, and all following regions.
+ */
+Fl_Complex_Region::~Fl_Complex_Region()
+{
+ delete pSubregion; // recursively delete all subregions
+ delete pNext; // recursively delete all following regions
+}
+
+/**
+ * Print the entire content of this region recursively.
+ */
+void Fl_Complex_Region::print(const char *label) const
+{
+ Fl_Android_Application::log_i("---> Fl_Complex_Region: %s", label);
+ print_data(0);
+}
+
+/*
+ * Print the rectangular data only.
+ */
+void Fl_Complex_Region::print_data(int indent) const
+{
+ static const char *space = " ";
+ if (pSubregion) {
+ Fl_Android_Application::log_i("%sBBox l:%d t:%d r:%d b:%d", space+16-indent, left(), top(), right(), bottom());
+ pSubregion->print_data(indent+1);
+ } else {
+ Fl_Android_Application::log_i("%sRect l:%d t:%d r:%d b:%d", space+16-indent, left(), top(), right(), bottom());
+ }
+ if (pNext) {
+ pNext->print_data(indent);
+ }
+}
+
+/**
+ * Replace this region with a rectangle.
+ * @param r the source rectangle
+ */
+void Fl_Complex_Region::set(const Fl_Rect_Region &r)
+{
+ delete pSubregion; pSubregion = 0;
+ Fl_Rect_Region::set(r);
+}
+
+/**
+ * Replace this region with a copy of another region.
+ * This operation can be expensive for very complex regions.
+ * @param r the source region
+ */
+void Fl_Complex_Region::set(const Fl_Complex_Region &r)
+{
+ // outline:
+ // clear this region and copy the coordinates from r
+ delete pSubregion; pSubregion = 0;
+ Fl_Rect_Region::set((const Fl_Rect_Region&)r);
+ if (r.pSubregion) {
+ pSubregion = new Fl_Complex_Region();
+ pSubregion->set(*r.subregion());
+ }
+ if (r.pNext) {
+ pNext = new Fl_Complex_Region();
+ pNext->set(*r.next());
+ }
+}
+
+/**
+ * Set this region to the intersection of the original region and some rect.
+ * @param r intersect with this rectangle
+ * @return EMPTY, SAME, LESS
+ */
+int Fl_Complex_Region::intersect_with(const Fl_Rect_Region &r)
+{
+ delete pSubregion; pSubregion = 0;
+ // FIXME: handle complex regions!
+ int ret = Fl_Rect_Region::intersect_with(r);
+ return ret;
+}
+
+/**
+ * Subtract a rectangular region from this region.
+ * @param r the rect that we want removed
+ * @return currently 0, but could return something meaningful
+ */
+int Fl_Complex_Region::subtract(const Fl_Rect_Region &r)
+{
+ if (pSubregion) {
+ pSubregion->subtract(r);
+ } else {
+ // Check if we overlap at all
+ Fl_Rect_Region s(r);
+ int intersects = s.intersect_with(*this);
+ switch (intersects) {
+ case EMPTY:
+ // nothing to do
+ break;
+ case SAME:
+ set_empty(); // FIXME: delete this Rect!
+ break;
+ case LESS:
+ // split this rect into 1, 2, 3, or 4 new ones
+ subtract_smaller_region(s);
+ break;
+ default:
+ Fl_Android_Application::log_e("Invalid case in %s:%d", __FUNCTION__, __LINE__);
+ break;
+ }
+ if (pNext) {
+ pNext->subtract(r);
+ }
+ }
+ return 0;
+}
+
+/**
+ * Subtract a rect from another rect, potentially creating four new rectangles.
+ * This assumes that the calling region is NOT complex.
+ * @param r subtract the area of this rectangle.
+ * @return currently 0, but this may change
+ */
+int Fl_Complex_Region::subtract_smaller_region(const Fl_Rect_Region &r)
+{
+ // subtract a smaller rect from a larger rect and create subrects as needed
+ // FIXME: make sure that the bbox of the parent region is shrunk to the size of all children
+ // if there is only one single coordinte different, we can reuse this container
+ if (left()==r.left() && top()==r.top() && right()==r.right() && bottom()==r.bottom()) {
+ // this should not happen
+ set_empty();
+ } else if (left()!=r.left() && top()==r.top() && right()==r.right() && bottom()==r.bottom()) {
+ pRight = r.left();
+ } else if (left()==r.left() && top()!=r.top() && right()==r.right() && bottom()==r.bottom()) {
+ pBottom = r.top();
+ } else if (left()==r.left() && top()==r.top() && right()!=r.right() && bottom()==r.bottom()) {
+ pLeft = r.right();
+ } else if (left()==r.left() && top()==r.top() && right()==r.right() && bottom()!=r.bottom()) {
+ pTop = r.bottom();
+ } else {
+ // create multiple regions
+ if (pTop!=r.top()) {
+ Fl_Complex_Region *s = add_subregion();
+ s->set_ltrb(pLeft, pTop, pRight, r.top());
+ }
+ if (pBottom!=r.bottom()) {
+ Fl_Complex_Region *s = add_subregion();
+ s->set_ltrb(pLeft, r.bottom(), pRight, pBottom);
+ }
+ if (pLeft!=r.left()) {
+ Fl_Complex_Region *s = add_subregion();
+ s->set_ltrb(pLeft, r.top(), r.left(), r.bottom());
+ }
+ if (pRight!=r.right()) {
+ Fl_Complex_Region *s = add_subregion();
+ s->set_ltrb(r.right(), r.top(), pRight, r.bottom());
+ }
+ }
+ return 0;
+}
+
+/**
+ * Add an empty subregion to the current region.
+ * @return a pointer to the newly created region.
+ */
+Fl_Complex_Region *Fl_Complex_Region::add_subregion()
+{
+ Fl_Complex_Region *r = new Fl_Complex_Region();
+ r->pParent = this;
+ r->pNext = pSubregion;
+ pSubregion = r;
+ return r;
+}
+
+
+// -----------------------------------------------------------------------------
+
+/**
+ * Returns an interator object for loops that traverse the entire region tree.
+ * C++11 interface to range-based loops.
+ * @return Iterator pointing to the first element.
+ */
+Fl_Complex_Region::Iterator Fl_Complex_Region::begin()
+{
+ return Iterator(this);
+}
+
+/**
+ * Returns an interator object to mark the end of travesing the tree.
+ * C++11 interface to range-based loops.
+ * @return
+ */
+Fl_Complex_Region::Iterator Fl_Complex_Region::end()
+{
+ return Iterator(0L);
+}
+
+/**
+ * Create an iterator to walk the entire tree.
+ * @param r Iterate through this region, r must not have a parent().
+ */
+Fl_Complex_Region::Iterator::Iterator(Fl_Complex_Region *r) :
+ pRegion(r)
+{
+}
+
+/**
+ * Compare two iterators.
+ * C++11 needs this to find the end of a for loop.
+ * @param other
+ * @return
+ */
+bool Fl_Complex_Region::Iterator::operator!=(const Iterator &other) const
+{
+ return pRegion != other.pRegion;
+}
+
+/**
+ * Set the iterator to the next object in the tree, down first.
+ * C++11 needs this to iterate in a for loop.
+ * @return
+ */
+const Fl_Complex_Region::Iterator &Fl_Complex_Region::Iterator::operator++()
+{
+ if (pRegion->subregion()) {
+ pRegion = pRegion->subregion();
+ } else if (pRegion->next()) {
+ pRegion = pRegion->next();
+ } else {
+ pRegion = pRegion->parent();
+ }
+ return *this;
+}
+
+/**
+ * Return the current object while iterating through the tree.
+ * @return
+ */
+Fl_Complex_Region *Fl_Complex_Region::Iterator::operator*() const
+{
+ return pRegion;
+}
+
+// -----------------------------------------------------------------------------
+
+/**
+ * Use this to iterate through a region, hitting only nodes that intersect with this rect.
+ * @param r find all parts of the region that intersect with this rect.
+ * @return an object that can be used in range-based for loops in C++11.
+ */
+Fl_Complex_Region::Overlapping Fl_Complex_Region::overlapping(const Fl_Rect_Region &r)
+{
+ return Overlapping(this, r);
+}
+
+/**
+ * A helper object for iterating through a region, finding only overlapping rects.
+ * @param rgn
+ * @param rect
+ */
+Fl_Complex_Region::Overlapping::Overlapping(Fl_Complex_Region *rgn,
+ const Fl_Rect_Region &rect) :
+ pRegion(rgn),
+ pOriginalRect(rect),
+ pClippedRect(rect)
+{
+}
+
+/**
+ * Return an itertor for the first clipping rectangle inside the region.
+ * @return
+ */
+Fl_Complex_Region::Overlapping::OverlappingIterator Fl_Complex_Region::Overlapping::begin()
+{
+ find_intersecting();
+ return OverlappingIterator(this);
+}
+
+/**
+ * Return an iterator for the end of forward iteration.
+ * @return
+ */
+Fl_Complex_Region::Overlapping::OverlappingIterator Fl_Complex_Region::Overlapping::end()
+{
+ return OverlappingIterator(0L);
+}
+
+/**
+ * Return the result of intersecting the original rect with this iterator.
+ * @return
+ */
+Fl_Rect_Region &Fl_Complex_Region::Overlapping::clipped_rect()
+{
+ return pClippedRect;
+}
+
+/**
+ * Store the intersection in pClippedRect and return true if there was an intersection.
+ * @return
+ */
+bool Fl_Complex_Region::Overlapping::intersects()
+{
+ return (pClippedRect.intersect_with(*pRegion) != EMPTY);
+}
+
+/**
+ * Find the next element in the tree that actually intersects with the initial rect.
+ * Starting the search at the current object, NOT the next object.
+ * @return
+ */
+bool Fl_Complex_Region::Overlapping::find_intersecting()
+{
+ for (;;) {
+ if (!pRegion) return false;
+ pClippedRect.set(pOriginalRect);
+ if (intersects()) {
+ if (!pRegion->subregion()) {
+ return true;
+ } else {
+ pRegion = pRegion->subregion();
+ }
+ } else {
+ find_next();
+ }
+ }
+}
+
+/**
+ * Find the next object in the tree, complex, simple, intersecting or not.
+ * @return
+ */
+bool Fl_Complex_Region::Overlapping::find_next()
+{
+ if (pRegion->subregion()) {
+ pRegion = pRegion->subregion();
+ } else if (pRegion->next()) {
+ pRegion = pRegion->next();
+ } else {
+ pRegion = pRegion->parent(); // can be NULL
+ }
+ return (pRegion != 0L);
+}
+
+// -----------------------------------------------------------------------------
+
+/**
+ * Create the actual iterator for finding true clipping rects.
+ * @see Fl_Complex_Region::Overlapping
+ * @param ov
+ */
+Fl_Complex_Region::Overlapping::OverlappingIterator::OverlappingIterator(
+ Overlapping *ov) :
+ pOv(ov)
+{
+}
+
+/**
+ * Compare two iterator.
+ * This is used by C++11 range0based for loops to find the end of the range.
+ * @param other
+ * @return
+ */
+bool Fl_Complex_Region::Overlapping::OverlappingIterator::operator!=(
+ const OverlappingIterator &other) const
+{
+ auto thisRegion = pOv ? pOv->pRegion : nullptr;
+ auto otherRegion = other.pOv ? other.pOv->pRegion : nullptr;
+ return thisRegion != otherRegion;
+}
+
+/**
+ * Wrapper to find and set the next intersecting rectangle.
+ * @see Fl_Complex_Region::Overlapping::find_intersecting
+ * @see Fl_Complex_Region::Overlapping::find_next
+ * @return
+ */
+const Fl_Complex_Region::Overlapping::OverlappingIterator &
+Fl_Complex_Region::Overlapping::OverlappingIterator::operator++()
+{
+ pOv->find_next();
+ if (pOv->pRegion)
+ pOv->find_intersecting();
+ return *this;
+}
+
+/**
+ * Return the Fl_Complex_Region::Overlapping state for this iterator.
+ * This gives the user access to the current rectangular fragment of
+ * the clipping region.
+ * @return
+ */
+Fl_Complex_Region::Overlapping *
+Fl_Complex_Region::Overlapping::OverlappingIterator::operator*() const
+{
+ return pOv;
+}
+
+// =============================================================================
+
+
+void Fl_Android_Graphics_Driver::restore_clip()
+{
+ fl_clip_state_number++;
+
+ pClippingRegion.set(pDesktopWindowRegion);
+
+
+ Fl_Region b = rstack[rstackptr];
+ if (b) {
+ // FIXME: pClippingRegion.intersect_with(*b);
+ }
+}
+
+
+void Fl_Android_Graphics_Driver::clip_region(Fl_Region r)
+{
+ Fl_Region oldr = rstack[rstackptr];
+ if (oldr)
+ ::free(oldr);
+ rstack[rstackptr] = r;
+ restore_clip();
+}
+
+
+Fl_Region Fl_Android_Graphics_Driver::clip_region()
+{
+ return rstack[rstackptr];
+}
+
+
+void Fl_Android_Graphics_Driver::push_clip(int x, int y, int w, int h)
+{
+ Fl_Region r;
+ if (w > 0 && h > 0) {
+ r = new Fl_Rect_Region(x, y, w, h);
+ Fl_Region current = rstack[rstackptr];
+ if (current) {
+ r->intersect_with(*current);
+ }
+ } else { // make empty clip region:
+ r = new Fl_Rect_Region();
+ }
+ if (rstackptr < region_stack_max) rstack[++rstackptr] = r;
+ else Fl::warning("Fl_Android_Graphics_Driver::push_clip: clip stack overflow!\n");
+ restore_clip();
+}
+
+
+void Fl_Android_Graphics_Driver::push_no_clip()
+{
+ if (rstackptr < region_stack_max) rstack[++rstackptr] = 0;
+ else Fl::warning("Fl_Android_Graphics_Driver::push_no_clip: clip stack overflow!\n");
+ restore_clip();
+}
+
+
+void Fl_Android_Graphics_Driver::pop_clip()
+{
+ if (rstackptr > 0) {
+ Fl_Region oldr = rstack[rstackptr--];
+ if (oldr)
+ ::free(oldr);
+ } else Fl::warning("Fl_Android_Graphics_Driver::pop_clip: clip stack underflow!\n");
+ restore_clip();
+}
+
+/*
+ Intersects the rectangle with the current clip region and returns the
+ bounding box of the result.
+
+ Returns non-zero if the resulting rectangle is different to the original.
+ This can be used to limit the necessary drawing to a rectangle.
+ \p W and \p H are set to zero if the rectangle is completely outside the region.
+ \param[in] x,y,w,h position and size of rectangle
+ \param[out] X,Y,W,H position and size of resulting bounding box.
+ \returns Non-zero if the resulting rectangle is different to the original.
+ */
+int Fl_Android_Graphics_Driver::clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H)
+{
+ Fl_Region r = rstack[rstackptr];
+ if (r) {
+ Fl_Rect_Region a(x, y, w, h);
+ int ret = a.intersect_with(*r);
+ X = a.x();
+ Y = a.y();
+ W = a.w();
+ H = a.h();
+ return (ret!=Fl_Rect_Region::SAME);
+ } else {
+ X = x; Y = y; W = w; H = h;
+ return 0;
+ }
+}
+
+/*
+ Does the rectangle intersect the current clip region?
+ \param[in] x,y,w,h position and size of rectangle
+ \returns non-zero if any of the rectangle intersects the current clip
+ region. If this returns 0 you don't have to draw the object.
+
+ \note
+ Under X this returns 2 if the rectangle is partially clipped,
+ and 1 if it is entirely inside the clip region.
+ */
+int Fl_Android_Graphics_Driver::not_clipped(int x, int y, int w, int h)
+{
+ if (w <= 0 || h <= 0) return 0;
+ Fl_Region r = rstack[rstackptr];
+ if (r) {
+ Fl_Rect_Region a(x, y, w, h); // return 0 for empty, 1 for same, 2 if intersecting
+ return a.intersect_with(*r);
+ } else {
+ return 1;
+ }
+}
+
+
+//
+// End of "$Id$".
+//