Android: Reinstated working simple cliping functionality based on an

improved Fl_Rect_Region class instead of Fl_Rect. Commented out 
 complex clipping.
 
 Android lib and apps now use C++11 because they can (and I like it).

git-svn-id: file:///fltk/svn/fltk/branches/branch-1.4@12741 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Matthias Melcher 2018-03-12 12:57:28 +00:00
parent 371cfd1476
commit 1b52ead802
7 changed files with 202 additions and 182 deletions

View File

@ -40,42 +40,29 @@ public:
/** The default constructor creates an empty rectangle (x = y = w = h = 0). */
Fl_Rect()
: x_(0), y_(0), w_(0), h_(0) {}
: x_(0), y_(0), w_(0), h_(0) {}
/** This constructor creates a rectangle with x = y = 0 and
the given width and height. */
Fl_Rect(int W, int H)
: x_(0), y_(0), w_(W), h_(H) {}
: x_(0), y_(0), w_(W), h_(H) {}
/** This constructor creates a rectangle with the given x,y coordinates
and the given width and height. */
Fl_Rect(int X, int Y, int W, int H)
: x_(X), y_(Y), w_(W), h_(H) {}
: x_(X), y_(Y), w_(W), h_(H) {}
/** This constructor creates a rectangle based on a widget's position and size. */
/** Copy constructor. */
Fl_Rect (const Fl_Rect& r)
: x_(r.x()), y_(r.y()), w_(r.w()), h_(r.h()) {}
/** This constructor creates a rectangle based a widget's position and size. */
Fl_Rect (const Fl_Widget& widget)
: x_(widget.x()), y_(widget.y()), w_(widget.w()), h_(widget.h()) {}
: x_(widget.x()), y_(widget.y()), w_(widget.w()), h_(widget.h()) {}
/** This constructor creates a rectangle based on a widget's position and size. */
/** This constructor creates a rectangle based a widget's position and size. */
Fl_Rect (const Fl_Widget* const widget)
: x_(widget->x()), y_(widget->y()), w_(widget->w()), h_(widget->h()) {}
virtual ~Fl_Rect() { }
/** Return 1 if the rectangle is empty, width or height are 0 */
int is_empty() { return (w_<=0)||(h_<=0); }
/** Set the position and size */
virtual void set(int x, int y, int w, int h) { x_=x; y_=y; w_=w; h_=h; }
/** Clone another rectangle */
virtual void set(Fl_Rect *r) { x_=r->x_; y_=r->y_; w_=r->w_; h_=r->h_; }
/** return 0 if the rectangles are different, or 1 if they are the same */
int equals(int x, int y, int w, int h) { return ( (x_==x) && (y_==y) && (w_==w) && (h_==h) ); }
/** Set the position and size to zero, making this rect empty */
void clear() { x_ = y_ = w_ = h_ = 0; }
: x_(widget->x()), y_(widget->y()), w_(widget->w()), h_(widget->h()) {}
int x() const { return x_; } ///< gets the x coordinate (left edge)
int y() const { return y_; } ///< gets the y coordinate (top edge)

View File

@ -17,6 +17,8 @@
cmake_minimum_required(VERSION 3.4.1)
set(FLTK_DIR ../../../../../..)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# FIXME: add as a second argument the binary build dir
# so that the first argument can link directly to FLTK

View File

@ -22,7 +22,7 @@
#include <FL/fl_draw.H>
Fl_Window *win, *win2, *win3;
Fl_Window *win;
Fl_Button *btn;
@ -31,30 +31,20 @@ class MyButton : public Fl_Button
public:
MyButton(int x, int y, int w, int h, const char *l) : Fl_Button(x, y, w, h, l) { }
void draw() {
fl_push_clip(x(), y(), w()*2/3, h()*2/3);
//fl_push_clip(x(), y(), w()*2/3, h()*2/3);
Fl_Button::draw();
fl_pop_clip();
//fl_pop_clip();
}
};
int main(int argc, char **argv)
{
win2 = new Fl_Window(100, 50, 150, 200, "on bottom");
win2->color(FL_BLUE);
win2->end();
win2->show();
win = new Fl_Window(50, 150, 500, 400, "Hallo");
btn = new MyButton((win->w()-280)/2, 200, 280, 35, "Hello, Android!");
btn->color(FL_LIGHT2);
win->show(argc, argv);
/*
win3 = new Fl_Window(300, 50, 150, 200, "on top");
win3->color(FL_RED);
win3->end();
win3->show();
*/
Fl::run();
return 0;

View File

@ -296,6 +296,8 @@ elseif (ANDROID)
# Android
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set (DRIVER_FILES
drivers/Android/Fl_Android_Application.cxx
drivers/Android/Fl_Android_System_Driver.cxx

View File

@ -26,7 +26,6 @@
#define FL_ANDROID_GRAPHICS_DRIVER_H
#include <FL/Fl_Graphics_Driver.H>
#include <FL/Fl_Rect.H>
#include <limits.h>
@ -34,34 +33,53 @@ class Fl_Android_Window_Driver;
/**
* The Fl_Rect_Region is based on Fl_Rect with additional functionality for clipping.
* The Fl_Rect_Region describes a rectangular clipping region.
*
* Contrary to common FLTK convention, rectangles are stored with coordinates
* instead of their width and height to accelerate calculations. The discreet
* constructor however uses the old convention for convenience.
*/
class Fl_Rect_Region : public Fl_Rect
class Fl_Rect_Region
{
public:
enum {
EMPTY = 0, SAME, LESS, MORE
enum Type {
EMPTY = 0, SAME, LESS, MORE, INFINITE
};
/**
* Create an empty clipping region.
*/
Fl_Rect_Region() {}
Fl_Rect_Region();
Fl_Rect_Region(int x, int y, int w, int h);
Fl_Rect_Region(const Fl_Rect_Region&);
Fl_Rect_Region(enum Type what);
virtual ~Fl_Rect_Region() { }
/**
* Create a clipping region based on position and size.
* @param x, y position
* @param w, h size
*/
Fl_Rect_Region(int x, int y, int w, int h) : Fl_Rect(x, y, w, h) {}
int intersect_with(Fl_Rect_Region *r);
virtual void print();
int x() const { return pLeft; }
int y() const { return pTop; }
int w() const { return pRight - pLeft; }
int h() const { return pBottom - pTop; }
static int min(int a, int b) { return (a<b) ? a : b; }
static int max(int a, int b) { return (a>b) ? a : b; }
int left() const { return pLeft; }
int top() const { return pTop; }
int right() const { return pRight; }
int bottom() const { return pBottom; }
bool is_empty() const;
bool is_infinite() const;
void set_empty();
void set(int x, int y, int w, int h);
void set(const Fl_Rect_Region &r);
int intersect_with(const Fl_Rect_Region &r);
virtual void print(const char*) const;
protected:
int pLeft, pTop, pRight, pBottom;
private:
Fl_Rect_Region& operator = (const Fl_Rect_Region& other);
};
#if 0
/**
* The Fl_Complex_Region represents a clipping region of any shape.
*
@ -85,9 +103,9 @@ public:
Fl_Complex_Region(int x, int y, int w, int h) : Fl_Rect_Region(x, y, w, h), pSubregion(0L), pNext(0L) { }
~Fl_Complex_Region();
virtual void set(int x, int y, int w, int h);
virtual void set(Fl_Rect *rect);
void subtract(Fl_Rect*);
void intersect(Fl_Rect*);
virtual void set(Fl_Rect_Region*);
void subtract(Fl_Rect_Region*);
void intersect(Fl_Rect_Region*);
void clone(Fl_Complex_Region*);
char is_simple() { return pSubregion==0; }
char is_complex() { return pSubregion!=0; }
@ -97,6 +115,7 @@ protected:
Fl_Complex_Region *pSubregion;
Fl_Complex_Region *pNext;
};
#endif
/**
@ -244,10 +263,8 @@ protected:
int32_t pStride;
uint16_t *pBits;
// Fl_Rect_Region pScreenRegion;
Fl_Rect_Region *pWindowRegion;
Fl_Complex_Region *pDesktopRegion;
Fl_Complex_Region *pClippingRegion;
Fl_Rect_Region pWindowRegion;
Fl_Rect_Region pClippingRegion;
};

View File

@ -37,10 +37,7 @@ Fl_Graphics_Driver *Fl_Graphics_Driver::newMainGraphicsDriver()
Fl_Android_Graphics_Driver::Fl_Android_Graphics_Driver() :
pStride(0), pBits(0),
pWindowRegion(new Fl_Rect_Region()),
pDesktopRegion(new Fl_Complex_Region()),
pClippingRegion(new Fl_Complex_Region())
pStride(0), pBits(0)
{
}
@ -52,8 +49,6 @@ Fl_Android_Graphics_Driver::~Fl_Android_Graphics_Driver()
void Fl_Android_Graphics_Driver::make_current(Fl_Window *win)
{
Fl_Android_Application::log_i("------------ make current \"%s\"", win->label());
// The Stride is the offset between lines in the graphics buffer
pStride = Fl_Android_Application::graphics_buffer().stride;
// Bits is the memory address of the top left corner of the window
@ -62,13 +57,10 @@ void Fl_Android_Graphics_Driver::make_current(Fl_Window *win)
// TODO: set the clipping area
// set the clipping area to the physical screen size in window coordinates
pWindowRegion->set(-win->x(), -win->y(), 600, 800);
Fl_Rect_Region wr(0, 0, win->w(), win->h());
pWindowRegion->intersect_with(&wr);
pWindowRegion->print();
pDesktopRegion->set(pWindowRegion);
pWindowRegion.set(-win->x(), -win->y(), 600, 800);
pWindowRegion.intersect_with(Fl_Rect_Region(0, 0, win->w(), win->h()));
#if 0
// remove all window rectangles that are positioned on top of this window
// TODO: this region is expensive to calculate. Cache it for each window and recalculate when windows move, show, hide, or change order
Fl_Window *wTop = Fl::first_window();
@ -78,11 +70,9 @@ void Fl_Android_Graphics_Driver::make_current(Fl_Window *win)
pDesktopRegion->subtract(&r);
wTop = Fl::next_window(wTop);
}
#endif
// TODO: we can optimize this by using some "copy on write" system
pClippingRegion->clone(pDesktopRegion);
pClippingRegion->print();
Fl_Android_Application::log_i("------------ make current done");
pClippingRegion.set(pWindowRegion);
}
@ -106,7 +96,7 @@ static uint16_t make565(Fl_Color crgba)
void Fl_Android_Graphics_Driver::rectf_unscaled(float x, float y, float w, float h)
{
Fl_Rect_Region r(x, y, w, h);
if (r.intersect_with((Fl_Rect_Region*)pClippingRegion)) {
if (r.intersect_with(pClippingRegion)) {
rectf_unclipped(r.x(), r.y(), r.w(), r.h());
}
// TODO: create a complex region by intersecting r with the pClippingRegion
@ -135,8 +125,8 @@ void Fl_Android_Graphics_Driver::rectf_unscaled(float x, float y, float w, float
*/
}
void Fl_Android_Graphics_Driver::rectf_unclipped(float x, float y, float w, float h) {
Fl_Android_Application::log_w("rectf unclipped %g %g %g %g", x, y, w, h);
void Fl_Android_Graphics_Driver::rectf_unclipped(float x, float y, float w, float h)
{
if (w<=0 || h<=0) return;
// TODO: clip the rectangle to the window outline

View File

@ -23,41 +23,132 @@
#include <FL/platform.H>
// return 0 for empty, 1 for same, 2 if intersecting
int Fl_Rect_Region::intersect_with(Fl_Rect_Region *a)
/**
* 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);
}
void Fl_Rect_Region::set_empty()
{
pLeft = pTop = pRight = pBottom = 0;
}
void Fl_Rect_Region::set(int x, int y, int w, int h)
{
pLeft = x;
pTop = y;
pRight = x+w;
pBottom = y+h;
}
void Fl_Rect_Region::set(const Fl_Rect_Region &r)
{
pLeft = r.pLeft;
pTop = r.pTop;
pRight = r.pRight;
pBottom = r.pBottom;
}
int Fl_Rect_Region::intersect_with(const Fl_Rect_Region &r)
{
if (is_empty()) {
return EMPTY;
}
if (a->is_empty()) {
clear();
if (r.is_empty()) {
set_empty();
return EMPTY;
}
int lx = max(x(), a->x());
int ly = max(y(), a->y());
int lr = min(r(), a->r());
int lb = min(b(), a->b());
int lw = lr-lx;
int lh = lb-ly;
if (equals(lx, ly, lw, lh)) {
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;
}
set(lx, ly, lw, lh);
if ( (w()<=0) || (h()<=0) ) {
clear();
if (is_empty())
return EMPTY;
}
return LESS;
}
void Fl_Rect_Region::print()
void Fl_Rect_Region::print(const char *label) const
{
Fl_Android_Application::log_i("-------- begin rect");
Fl_Android_Application::log_i("---> Fl_Rect_Region: %s", label);
Fl_Android_Application::log_i("Rect %d %d %d %d", x(), y(), w(), h());
}
#if 0
Fl_Complex_Region::~Fl_Complex_Region()
{
@ -151,6 +242,7 @@ void Fl_Complex_Region::print()
print_data(0);
}
#endif
@ -158,18 +250,12 @@ void Fl_Android_Graphics_Driver::restore_clip()
{
fl_clip_state_number++;
// TODO: we can optimize this by using some "copy on write" system
Fl_Android_Application::log_i("------------ restore_clip");
pDesktopRegion->print();
//pClippingRegion->set(pWindowRegion);
pClippingRegion->clone(pDesktopRegion);
pClippingRegion.set(pWindowRegion);
Fl_Region b = rstack[rstackptr];
if (b) {
pClippingRegion->intersect_with(b);
pClippingRegion->print();
pClippingRegion.intersect_with(*b);
}
Fl_Android_Application::log_i("------------ restore_clip done");
}
@ -193,10 +279,10 @@ 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);
r = new Fl_Rect_Region(x, y, w, h);
Fl_Region current = rstack[rstackptr];
if (current) {
r->intersect_with(current);
r->intersect_with(*current);
}
} else { // make empty clip region:
r = new Fl_Rect_Region();
@ -239,19 +325,18 @@ void Fl_Android_Graphics_Driver::pop_clip()
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) {
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;
}
Fl_Rect_Region a(x, y, w, h);
int ret = a.intersect_with(r); // return 0 for empty, 1 for same, 2 if intersecting
X = a.x();
Y = a.y();
W = a.w();
H = a.h();
return (ret!=1);
}
/*
@ -264,72 +349,19 @@ int Fl_Android_Graphics_Driver::clip_box(int x, int y, int w, int h, int& X, int
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 (x+w <= 0 || y+h <= 0) return 0;
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) return 1;
Fl_Rect_Region a(x, y, w, h); // return 0 for empty, 1 for same, 2 if intersecting
return a.intersect_with(r);
}
#if 0
// --- clipping
int Fl_GDI_Graphics_Driver::clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H){
X = x; Y = y; W = w; H = h;
Fl_Region r = rstack[rstackptr];
if (!r) return 0;
// The win32 API makes no distinction between partial and complete
// intersection, so we have to check for partial intersection ourselves.
// However, given that the regions may be composite, we have to do
// some voodoo stuff...
Fl_Region rr = XRectangleRegion(x,y,w,h);
Fl_Region temp = CreateRectRgn(0,0,0,0);
int ret;
if (CombineRgn(temp, rr, r, RGN_AND) == NULLREGION) { // disjoint
W = H = 0;
ret = 2;
} else if (EqualRgn(temp, rr)) { // complete
ret = 0;
} else { // partial intersection
RECT rect;
GetRgnBox(temp, &rect);
if (Fl_Surface_Device::surface() != Fl_Display_Device::display_device()) { // if print context, convert coords from device to logical
POINT pt[2] = { {rect.left, rect.top}, {rect.right, rect.bottom} };
DPtoLP(gc_, pt, 2);
X = pt[0].x; Y = pt[0].y; W = pt[1].x - X; H = pt[1].y - Y;
}
else {
X = rect.left; Y = rect.top; W = rect.right - X; H = rect.bottom - Y;
}
ret = 1;
}
DeleteObject(temp);
DeleteObject(rr);
return ret;
}
int Fl_GDI_Graphics_Driver::not_clipped(int x, int y, int w, int h) {
if (x+w <= 0 || y+h <= 0) return 0;
Fl_Region r = rstack[rstackptr];
if (!r) return 1;
RECT rect;
if (Fl_Surface_Device::surface() != Fl_Display_Device::display_device()) { // in case of print context, convert coords from logical to device
POINT pt[2] = { {x, y}, {x + w, y + h} };
LPtoDP(gc_, pt, 2);
rect.left = pt[0].x; rect.top = pt[0].y; rect.right = pt[1].x; rect.bottom = pt[1].y;
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 {
rect.left = x; rect.top = y; rect.right = x+w; rect.bottom = y+h;
return 1;
}
return RectInRegion(r,&rect);
}
#endif
//
// End of "$Id$".
//