diff options
Diffstat (limited to 'Src/tataki/region')
-rw-r--r-- | Src/tataki/region/api_region.cpp | 1 | ||||
-rw-r--r-- | Src/tataki/region/api_region.h | 201 | ||||
-rw-r--r-- | Src/tataki/region/mac/osx_region_hishape.cpp | 217 | ||||
-rw-r--r-- | Src/tataki/region/mac/region.h | 116 | ||||
-rw-r--r-- | Src/tataki/region/region.h | 5 | ||||
-rw-r--r-- | Src/tataki/region/win/region.h | 137 | ||||
-rw-r--r-- | Src/tataki/region/win/win32_region.cpp | 813 |
7 files changed, 1490 insertions, 0 deletions
diff --git a/Src/tataki/region/api_region.cpp b/Src/tataki/region/api_region.cpp new file mode 100644 index 00000000..f9c263ef --- /dev/null +++ b/Src/tataki/region/api_region.cpp @@ -0,0 +1 @@ +#include "api_region.h"
\ No newline at end of file diff --git a/Src/tataki/region/api_region.h b/Src/tataki/region/api_region.h new file mode 100644 index 00000000..2dbcd51f --- /dev/null +++ b/Src/tataki/region/api_region.h @@ -0,0 +1,201 @@ +#ifndef __WASABI_API_REGION_H +#define __WASABI_API_REGION_H + +#include <bfc/dispatch.h> +#include <bfc/platform/export.h> + +class NOVTABLE api_region : public Dispatchable +{ +protected: + api_region() {} + virtual ~api_region() {} + +public: + DISPATCH_CODES + { + REGION_GETOSHANDLE = 50, + REGION_CLONE = 100, + REGION_DISPOSECLONE = 110, + REGION_PTINREGION = 120, + REGION_OFFSET = 130, + REGION_GETBOX = 140, + REGION_SUBTRACTRGN = 150, + REGION_SUBTRACTRECT = 160, + REGION_ADDRECT = 170, + REGION_ADD = 180, + REGION_AND = 190, + REGION_SETRECT = 200, + REGION_EMPTY = 210, + REGION_ISEMPTY = 220, + REGION_EQUALS = 230, + REGION_ENCLOSED = 240, + REGION_INTERSECTRGN = 250, + REGION_DOESINTERSECTRGN = 251, + REGION_INTERSECTRECT = 260, + REGION_ISRECT = 270, + REGION_SCALE = 280, + REGION_DEBUG = 290, + REGION_MAKEWNDREGION = 300, + REGION_GETNUMRECTS = 310, + REGION_ENUMRECT = 320, + }; +public: + OSREGIONHANDLE getOSHandle(); // avoid as much as you can, should be used only when you need to call the OS api + + api_region *clone(); + void disposeClone(api_region *r); + bool ptInRegion(const POINT *pt); + void offset(int x, int y); + void getBox(RECT *r); + void subtractRegion(const api_region *r); + void subtractRgn(const api_region *r) { subtractRegion(r); } //DEPRECATED + void subtractRect(const RECT *r); + void addRect(const RECT *r); + void addRegion(const api_region *r); + void andRegion(const api_region *r); + void setRect(const RECT *r); + void empty(); + int isEmpty(); + int equals(const api_region *r); + int enclosed(const api_region *r, api_region *outside = NULL); + int intersectRgn(const api_region *r, api_region *intersection); + int doesIntersectRgn(const api_region *r); + int intersectRect(const RECT *r, api_region *intersection); + + int isRect(); + void scale(double sx, double sy, bool round = 0); + void debug(int async = 0); + OSREGIONHANDLE makeWindowRegion(); // gives you a handle to a clone of the OSREGION object so you can insert it into a window's region with SetWindowRgn. ANY other use is prohibited + + // this is how you can enumerate the subrects that compose to make up the + // entire region + int getNumRects(); + int enumRect(int n, RECT *r); +}; + +inline OSREGIONHANDLE api_region::getOSHandle() +{ + return _call(REGION_GETOSHANDLE, (OSREGIONHANDLE)NULL); +} + +inline api_region *api_region::clone() +{ + return _call(REGION_CLONE, (api_region *)NULL); +} + +inline void api_region::disposeClone(api_region *r) +{ + _voidcall(REGION_DISPOSECLONE, r); +} + +inline bool api_region::ptInRegion(const POINT *pt) +{ + return _call(REGION_PTINREGION, false, pt); +} + +inline void api_region::offset(int x, int y) +{ + _voidcall(REGION_OFFSET, x, y); +} + +inline void api_region::getBox(RECT *r) +{ + _voidcall(REGION_GETBOX, r); +} + +inline void api_region::subtractRegion(const api_region *reg) +{ + _voidcall(REGION_SUBTRACTRGN, reg); +} + +inline void api_region::subtractRect(const RECT *r) +{ + _voidcall(REGION_SUBTRACTRECT, r); +} + +inline void api_region::addRect(const RECT *r) +{ + _voidcall(REGION_ADDRECT, r); +} + +inline void api_region::addRegion(const api_region *r) +{ + _voidcall(REGION_ADD, r); +} + +inline void api_region::andRegion(const api_region *r) +{ + _voidcall(REGION_AND, r); +} + +inline void api_region::setRect(const RECT *r) +{ + _voidcall(REGION_SETRECT, r); +} + +inline void api_region::empty() +{ + _voidcall(REGION_EMPTY); +} + +inline int api_region::isEmpty() +{ + return _call(REGION_ISEMPTY, 0); +} + +inline int api_region::equals(const api_region *r) +{ + return _call(REGION_EQUALS, 0, r); +} + +inline int api_region::enclosed(const api_region *r, api_region *outside) +{ + return _call(REGION_ENCLOSED, 0, r, outside); +} + +inline int api_region::intersectRgn(const api_region *r, api_region *intersection) +{ + return _call(REGION_INTERSECTRGN, 0, r, intersection); +} + +inline int api_region::doesIntersectRgn(const api_region *r) +{ + return _call(REGION_DOESINTERSECTRGN, 0, r); +} + +inline int api_region::intersectRect(const RECT *r, api_region *intersection) +{ + return _call(REGION_INTERSECTRECT, 0, r, intersection); +} + +inline int api_region::isRect() +{ + return _call(REGION_ISRECT, 0); +} + +inline void api_region::scale(double sx, double sy, bool round) +{ + _voidcall(REGION_SCALE, sx, sy, round); +} + +inline void api_region::debug(int async) +{ + _voidcall(REGION_DEBUG, async); +} + +inline OSREGIONHANDLE api_region::makeWindowRegion() +{ + return _call(REGION_MAKEWNDREGION, (OSREGIONHANDLE)NULL); +} + +inline int api_region::getNumRects() +{ + return _call(REGION_GETNUMRECTS, 0); +} + +inline int api_region::enumRect(int n, RECT *r) +{ + return _call(REGION_ENUMRECT, 0, n, r); +} + +#endif diff --git a/Src/tataki/region/mac/osx_region_hishape.cpp b/Src/tataki/region/mac/osx_region_hishape.cpp new file mode 100644 index 00000000..348ba1de --- /dev/null +++ b/Src/tataki/region/mac/osx_region_hishape.cpp @@ -0,0 +1,217 @@ +#include <Carbon/Carbon.h> +#include <tataki/region/region.h> +#include <tataki/bitmap/bitmap.h> + +RegionI::RegionI() : rgn(0) +{ +} + +RegionI::RegionI(const RECT *r) : rgn(0) +{ + setRect(r); +} + +RegionI::RegionI(HIMutableShapeRef _rgn) : rgn(_rgn) +{ +} + +RegionI::RegionI(HIShapeRef _rgn) +{ + rgn=HIShapeCreateMutableCopy(_rgn); +} + +RegionI::~RegionI() +{ + if (rgn) + CFRelease(rgn); +} + +RegionI::RegionI(RgnHandle qdrgn) +{ + HIShapeRef shape = HIShapeCreateWithQDRgn(qdrgn); + rgn = HIShapeCreateMutableCopy(shape); + CFRelease(shape); +} + +RegionI::RegionI(SkinBitmap *bitmap) +{ + // TODO: we need to find a much better way to do this + RECT r; + r.left=0; + r.top=0; + r.right=bitmap->getWidth(); + r.bottom=bitmap->getHeight(); + setRect(&r); +} + + +OSREGIONHANDLE RegionI::getOSHandle() +{ + if (!rgn) + rgn = HIShapeCreateMutable(); + return rgn; +} + +api_region *RegionI::clone() +{ + if (!rgn) + return new RegionI(); + else + return new RegionI(HIShapeCreateMutableCopy(rgn)); +} + +void RegionI::disposeClone(api_region *r) +{ + if (r) // yes we need to check for NULL here because r != static_cast<>(r) + delete static_cast<RegionI *>(r); +} + +bool RegionI::ptInRegion(const POINT *pt) +{ + if (!rgn) + return false; + HIPoint hipt = HIPointFromPOINT(pt); + return !!HIShapeContainsPoint(rgn, &hipt); +} + +void RegionI::offset(int x, int y) +{ + if (!rgn) + rgn = HIShapeCreateMutable(); + + HIShapeOffset(rgn, x, y); +} + +void RegionI::getBox(RECT *r) +{ + if (!rgn) // TODO: we could manually set r to 0,0,0,0 + rgn = HIShapeCreateMutable(); + + HIRect rect; + HIShapeGetBounds(rgn, &rect); + *r = RECTFromHIRect(&rect); +} + +void RegionI::subtractRegion(const api_region *r) +{ + if (rgn) + { + api_region *reg = const_cast<api_region *>(r); + HIShapeRef sub = reg->getOSHandle(); + HIShapeDifference(rgn,sub, rgn); + } +} + +void RegionI::subtractRect(const RECT *r) +{ + if (rgn) + { + HIRect rect = HIRectFromRECT(r); + HIShapeRef sub = HIShapeCreateWithRect(&rect); + HIShapeDifference(rgn, sub, rgn); + } +} + +void RegionI::addRect(const RECT *r) +{ + if (!rgn) + rgn = HIShapeCreateMutable(); + HIRect rect = HIRectFromRECT(r); + HIShapeRef add = HIShapeCreateWithRect(&rect); + HIShapeUnion(rgn, add, rgn); +} + +void RegionI::addRegion(const api_region *r) +{ + if (!rgn) + rgn = HIShapeCreateMutable(); + api_region *reg = const_cast<api_region *>(r); + HIShapeRef add = reg->getOSHandle(); + HIShapeUnion(rgn, add, rgn); +} + +void RegionI::andRegion(const api_region *r) +{ + if (rgn) // intersection with empty region will always be empty + { + api_region *reg = const_cast<api_region *>(r); + HIShapeRef intersection = reg->getOSHandle(); + HIShapeIntersect(rgn, intersection, rgn); + } +} + +void RegionI::setRect(const RECT *r) +{ + if (rgn) + CFRelease(rgn); + HIRect rect = HIRectFromRECT(r); + HIShapeRef rectRgn = HIShapeCreateWithRect(&rect); + rgn = HIShapeCreateMutableCopy(rectRgn); + CFRelease(rectRgn); +} + +void RegionI::empty() +{ + if (rgn) + CFRelease(rgn); + rgn=0; +} + +int RegionI::isEmpty() +{ + if (!rgn) + return 1; + return !!HIShapeIsEmpty(rgn); +} + +int RegionI::isRect() +{ + if (!rgn) + return 1; + return !!HIShapeIsRectangular(rgn); +} + +int RegionI::intersectRgn(const api_region *r, api_region *intersection) +{ + intersection->empty(); + intersection->addRegion(this); + intersection->andRegion(r); + return !intersection->isEmpty(); +} + +int RegionI::intersectRect(const RECT *r, api_region *intersection) +{ + intersection->setRect(r); + intersection->andRegion(this); + return !intersection->isEmpty(); +} + +#define CBCLASS RegionI +START_DISPATCH; +CB(REGION_GETOSHANDLE, getOSHandle); +CB(REGION_CLONE, clone); +VCB(REGION_DISPOSECLONE, disposeClone); +CB(REGION_PTINREGION, ptInRegion); +VCB(REGION_OFFSET, offset); +VCB(REGION_GETBOX, getBox); +VCB(REGION_SUBTRACTRGN, subtractRegion); +VCB(REGION_SUBTRACTRECT, subtractRect); +VCB(REGION_ADDRECT, addRect); +VCB(REGION_ADD, addRegion); +VCB(REGION_AND, andRegion); +VCB(REGION_SETRECT, setRect); +VCB(REGION_EMPTY, empty); +CB(REGION_ISEMPTY, isEmpty); +CB(REGION_ISRECT, isRect); +CB(REGION_INTERSECTRGN, intersectRgn); +CB(REGION_INTERSECTRECT, intersectRect); +END_DISPATCH; +#undef CBCLASS + +#define CBCLASS RegionServerI +START_DISPATCH; +VCB(REGIONSERVER_ADDREF, addRef); +VCB(REGIONSERVER_DELREF, delRef); +CB(REGIONSERVER_GETREGION, getRegion); +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/tataki/region/mac/region.h b/Src/tataki/region/mac/region.h new file mode 100644 index 00000000..f3404ea2 --- /dev/null +++ b/Src/tataki/region/mac/region.h @@ -0,0 +1,116 @@ +#ifndef __REGION_H +#define __REGION_H + +#include <tataki/export.h> +#include <Carbon/Carbon.h> +#include <bfc/platform/platform.h> +#include <tataki/region/api_region.h> + +class SkinBitmap; + +class TATAKIAPI RegionI : public api_region +{ +public: + RegionI(); + RegionI(const RECT *r); + RegionI(RgnHandle qdrgn); + RegionI(HIShapeRef _rgn); + RegionI(SkinBitmap *bitmap); + ~RegionI(); + + // api_region + OSREGIONHANDLE getOSHandle(); + api_region *clone(); + void disposeClone(api_region *r); + bool ptInRegion(const POINT *pt); + void offset(int x, int y); + void getBox(RECT *r); + void subtractRegion(const api_region *r); + void subtractRect(const RECT *r); + void addRect(const RECT *r); + void addRegion(const api_region *r); + void andRegion(const api_region *r); + void setRect(const RECT *r); + void empty(); + int isEmpty(); + int equals(const api_region *r); + int enclosed(const api_region *r, api_region *outside = NULL); + int intersectRgn(const api_region *r, api_region *intersection); + int doesIntersectRgn(const api_region *r); + int intersectRect(const RECT *r, api_region *intersection); + + int isRect(); + void scale(double sx, double sy, bool round = 0); + void debug(int async = 0); + OSREGIONHANDLE makeWindowRegion(); // gives you a handle to a clone of the OSREGION object so you can insert it into a window's region with SetWindowRgn. ANY other use is prohibited + + // this is how you can enumerate the subrects that compose to make up the + // entire region + int getNumRects(); + int enumRect(int n, RECT *r); + + +private: + RegionI(HIMutableShapeRef _rgn); + HIMutableShapeRef rgn; + +protected: + RECVS_DISPATCH; +}; + + +// TODO: we could take of advantage of HIShapeRef's built in reference counting to implement this +class RegionServer : public Dispatchable { + +protected: + RegionServer() {} + virtual ~RegionServer() {} + +public: + + void addRef(void *client); + void delRef(void *client); + api_region *getRegion(); + + enum { + REGIONSERVER_ADDREF = 500, + REGIONSERVER_DELREF = 550, + REGIONSERVER_GETREGION = 600, + }; +}; + +inline void RegionServer::addRef(void *client) { + _voidcall(REGIONSERVER_ADDREF, (api_region *)NULL, client); +} + +inline void RegionServer::delRef(void *client) { + _voidcall(REGIONSERVER_DELREF, client); +} + +inline api_region * RegionServer::getRegion() { + return _call(REGIONSERVER_GETREGION, (api_region *)NULL); +} + +class TATAKIAPI RegionServerI : public RegionServer { +public : + + RegionServerI() { numrefs = 0; } + virtual ~RegionServerI() {} + + virtual void addRef(void *client) { numrefs++; } + virtual void delRef(void *client) { numrefs--; } + virtual api_region *getRegion()=0; + + virtual int getNumRefs() { return numrefs; } + +protected: + + RECVS_DISPATCH; + +private: + + int numrefs; +}; +#endif + + diff --git a/Src/tataki/region/region.h b/Src/tataki/region/region.h new file mode 100644 index 00000000..cf03c01c --- /dev/null +++ b/Src/tataki/region/region.h @@ -0,0 +1,5 @@ +#if defined _WIN64 || defined _WIN32 +#include "win/region.h" +#elif defined(__APPLE__) +#include "mac/region.h" +#endif diff --git a/Src/tataki/region/win/region.h b/Src/tataki/region/win/region.h new file mode 100644 index 00000000..849fba3e --- /dev/null +++ b/Src/tataki/region/win/region.h @@ -0,0 +1,137 @@ +#ifndef __REGION_H +#define __REGION_H + +#include <tataki/bitmap/bitmap.h> +#include <bfc/dispatch.h> +#include <tataki/export.h> + +class BaseWnd; +class Canvas; +class api_region; +class RegionServer; + +#include <tataki/region/api_region.h> + +class TATAKIAPI RegionI : public api_region +{ +public: + RegionI(); + RegionI(const RegionI *copy); + RegionI(const RECT *r); + RegionI(int l, int t, int r, int b); + RegionI(OSREGIONHANDLE region); + RegionI(SkinBitmap *bitmap, RECT *r=NULL, int xoffset=0, int yoffset=0, bool inverted=false, int dothreshold=0, __int8 threshold=0, int threversed=0, int minalpha=1); + RegionI(Canvas *c, RECT *defboundbox=NULL); + virtual ~RegionI(); + + api_region *clone(); + void disposeClone(api_region *r); + bool ptInRegion(const POINT *pt); + void offset(int x, int y); + void getBox(RECT *r); + void subtractRegion(const api_region *reg); + void subtractRect(const RECT *r); + void addRect(const RECT *r); + void addRegion(const api_region *r); + void andRegion(const api_region *r); + void setRect(const RECT *r); + void empty(); + int isEmpty(); + int equals(const api_region *r); + int enclosed(const api_region *r, api_region *outside=NULL); + int intersectRgn(const api_region *r, api_region *intersection); + int doesIntersectRgn(const api_region *r); + int intersectRect(const RECT *r, api_region *intersection); + int doesIntersectRect(const RECT *r); + int isRect(); + void scale(double sx, double sy, bool round=0); + void debug(int async=0); + + // NONPORTABLE + + OSREGIONHANDLE makeWindowRegion(); // gives you a handle to a clone of the OSREGION object so you can insert it into a window's region with SetWindowRgn. ANY other use is prohibited + OSREGIONHANDLE getOSHandle(); // avoid as much as you can, should be used only by WIN32-dependant classes + + // END NONPORTABLE + + int getNumRects(); + int enumRect(int n, RECT *r); + + OSREGIONHANDLE alphaToRegionRect(void *pbits32, int bmX, int bmY, int bmWidth, int bmHeight, int fullw, int fullh, int xoffset, int yoffset, bool portion, int _x, int _y, int _w, int _h, bool inverted, int dothreshold, unsigned __int8 threshold, int thinverse, int minalpha); + +private: + + inline void init(); + void optimize(); + void deoptimize(); + + OSREGIONHANDLE hrgn; + OSREGIONHANDLE alphaToRegionRect(SkinBitmap *bitmap, int xoffset, int yoffset, bool portion, int _x, int _y, int _w, int _h, bool inverted=false, int dothreshold=0, unsigned __int8 threshold=0, int thinverse=0, int minalpha=1/* 1..255*/); + RECT overlay; + int clonecount; + RegionI *lastdebug; + RegionServer *srv; + RECT optrect; + int optimized; + +protected: + + RECVS_DISPATCH; +}; + +class RegionServer : public Dispatchable { + + protected: + RegionServer() {} + virtual ~RegionServer() {} + + public: + + void addRef(void *client); + void delRef(void *client); + api_region *getRegion(); + + enum { + REGIONSERVER_ADDREF = 500, + REGIONSERVER_DELREF = 550, + REGIONSERVER_GETREGION = 600, + }; +}; + +inline void RegionServer::addRef(void *client) { + _voidcall(REGIONSERVER_ADDREF, (api_region *)NULL, client); +} + +inline void RegionServer::delRef(void *client) { + _voidcall(REGIONSERVER_DELREF, client); +} + +inline api_region * RegionServer::getRegion() { + return _call(REGIONSERVER_GETREGION, (api_region *)NULL); +} + +class TATAKIAPI RegionServerI : public RegionServer +{ + public : + + RegionServerI() { numrefs = 0; } + virtual ~RegionServerI() {} + + virtual void addRef(void *client) { numrefs++; } + virtual void delRef(void *client) { numrefs--; } + virtual api_region *getRegion()=0; + + virtual int getNumRefs() { return numrefs; } + + protected: + + RECVS_DISPATCH; + + private: + + int numrefs; +}; + +#endif + + diff --git a/Src/tataki/region/win/win32_region.cpp b/Src/tataki/region/win/win32_region.cpp new file mode 100644 index 00000000..5fb3c3c5 --- /dev/null +++ b/Src/tataki/region/win/win32_region.cpp @@ -0,0 +1,813 @@ +#if defined _WIN64 || defined _WIN32 +#include <tataki/api__tataki.h> +#include "region.h" +#include <api/imgldr/api_imgldr.h> +#include <tataki/region/api_region.h> +#include <tataki/canvas/ifc_canvas.h> +#include <api/wnd/basewnd.h> + + +#define GETOSHANDLE(x) (const_cast<api_region *>(x)->getOSHandle()) + +#define CBCLASS RegionI +START_DISPATCH; +CB(REGION_GETOSHANDLE, getOSHandle); +CB(REGION_CLONE, clone); +VCB(REGION_DISPOSECLONE, disposeClone); +CB(REGION_PTINREGION, ptInRegion); +VCB(REGION_OFFSET, offset); +VCB(REGION_GETBOX, getBox); +VCB(REGION_SUBTRACTRGN, subtractRegion); +VCB(REGION_SUBTRACTRECT, subtractRect); +VCB(REGION_ADDRECT, addRect); +VCB(REGION_ADD, addRegion); +VCB(REGION_AND, andRegion); +VCB(REGION_SETRECT, setRect); +VCB(REGION_EMPTY, empty); +CB(REGION_ISEMPTY, isEmpty); +CB(REGION_EQUALS, equals); +CB(REGION_ENCLOSED, enclosed); +CB(REGION_INTERSECTRECT, intersectRect); +CB(REGION_DOESINTERSECTRGN, doesIntersectRgn); +CB(REGION_INTERSECTRGN, intersectRgn); +CB(REGION_ISRECT, isRect); +VCB(REGION_SCALE, scale); +VCB(REGION_DEBUG, debug); +CB(REGION_MAKEWNDREGION, makeWindowRegion); +CB(REGION_GETNUMRECTS, getNumRects); +CB(REGION_ENUMRECT, enumRect); +END_DISPATCH; +#undef CBCLASS + +#define CHECK_REGION \ + if (hrgn == NULL) hrgn = CreateRectRgn(0,0,0,0); + +RegionI::RegionI() +{ + hrgn = CreateRectRgn(0, 0, 0, 0); + init(); +} + +RegionI::RegionI(const RECT *r) +{ + hrgn = 0; + init(); + optrect = *r; + optimized = 1; + //hrgn = CreateRectRgn(r->left,r->top,r->right,r->bottom); + //if (!hrgn) hrgn = CreateRectRgn(0,0,0,0); + //init(); + //optimize(); +} + +RegionI::RegionI(int l, int t, int r, int b) +{ + hrgn = 0; + init(); + optrect.left = l; + optrect.top = t; + optrect.right = r; + optrect.bottom = b; + optimized = 1; + + //hrgn = CreateRectRgn(l,t,r,b); + //if (!hrgn) hrgn = CreateRectRgn(0,0,0,0); + //init(); + //optimize(); +} + +RegionI::RegionI(OSREGIONHANDLE r) +{ + OSREGIONHANDLE R = CreateRectRgn(0, 0, 0, 0); + CombineRgn(R, r, r, RGN_COPY); + hrgn = R; + init(); + optimize(); +} + +RegionI::RegionI(const RegionI *copy) +{ + init(); + if (copy->optimized) + { + optrect = copy->optrect; + optimized = copy->optimized; + hrgn = 0; + } + else + { + hrgn = CreateRectRgn(0, 0, 0, 0); + CombineRgn(hrgn, copy->hrgn, copy->hrgn, RGN_COPY); + } +} + +RegionI::RegionI(Canvas *c, RECT *defbounds) +{ + hrgn = CreateRectRgn(0, 0, 0, 0); + if (!GetClipRgn(c->getHDC(), hrgn)) + { + if (defbounds != NULL) + { + SetRectRgn(hrgn, defbounds->left, defbounds->top, defbounds->right, defbounds->bottom); + optrect=*defbounds; + optimized=1; + } + } + init(); + optimize(); +} + +RegionI::~RegionI() +{ + delete lastdebug; + if (srv != NULL) srv->delRef(this); + ASSERT(clonecount == 0); + if (srv == NULL && hrgn != NULL) DeleteObject(hrgn); +} + +void RegionI::init() +{ + srv = NULL; + clonecount = 0; + lastdebug = NULL; + optimized = 0; +} + +api_region *RegionI::clone() +{ + api_region *newregion = new RegionI(this); + clonecount++; + return newregion; +} + +void RegionI::disposeClone(api_region *r) +{ + RegionI *ri = static_cast<RegionI *>(r); + delete ri; // todo: validate pointer before deleting + clonecount--; +} + +// returns a handle that SetWindowRgn understands (non portable). We should NOT delete this handle, windows will delete +// it by itself upon setting a new region of destroying the window +OSREGIONHANDLE RegionI::makeWindowRegion() +{ + deoptimize(); + OSREGIONHANDLE R = CreateRectRgn(0, 0, 0, 0); + CombineRgn(R, hrgn, hrgn, RGN_COPY); + optimize(); + return R; +} + +RegionI::RegionI(SkinBitmap *bitmap, RECT *r, int xoffset, int yoffset, bool inverted, int dothreshold, char threshold, int thinverse, int minalpha) +{ + init(); + const wchar_t *id = bitmap->getBitmapName(); + + if (xoffset == 0 && yoffset == 0 && r == NULL && !inverted && !dothreshold && minalpha == 1 && id != NULL && *id != 0) + { + srv = WASABI_API_IMGLDR->imgldr_requestSkinRegion(id); + if (srv != NULL) + { + srv->addRef(this); + hrgn = srv->getRegion()->getOSHandle(); + } + } + + if (srv == NULL) + { + if (r) + hrgn = alphaToRegionRect(bitmap, xoffset, yoffset, TRUE, r->left, r->top, r->right - r->left, r->bottom - r->top, inverted, dothreshold, threshold, thinverse, minalpha); + else + hrgn = alphaToRegionRect(bitmap, xoffset, yoffset, FALSE, 0, 0, 0, 0, inverted, dothreshold, threshold, thinverse, minalpha); + + if (id != NULL && *id != 0) + { + if (xoffset == 0 && yoffset == 0 && r == NULL && !inverted && !dothreshold && minalpha == 1) + { + WASABI_API_IMGLDR->imgldr_cacheSkinRegion(id, this); + srv = WASABI_API_IMGLDR->imgldr_requestSkinRegion(id); + if (srv != NULL) + { + srv->addRef(this); + DeleteObject(hrgn); + hrgn = srv->getRegion()->getOSHandle(); + } + } + } + } + optimize(); + +} + +OSREGIONHANDLE RegionI::alphaToRegionRect(SkinBitmap *bitmap, int xoffset, int yoffset, bool portion, int _x, int _y, int _w, int _h, bool inverted, int dothreshold, unsigned char threshold, int thinverse, int minalpha) +{ + return alphaToRegionRect(bitmap->getBits(), bitmap->getX(), bitmap->getY(), bitmap->getWidth(), bitmap->getHeight(), bitmap->getFullWidth(), bitmap->getFullHeight(), xoffset, yoffset, portion, _x, _y, _w, _h, inverted, dothreshold, threshold, thinverse, minalpha); +} + +OSREGIONHANDLE RegionI::alphaToRegionRect(void *pbits32, int bmX, int bmY, int bmWidth, int bmHeight, int fullw, int fullh, int xoffset, int yoffset, bool portion, int _x, int _y, int _w, int _h, bool inverted, int dothreshold, unsigned char threshold, int thinverse, int minalpha) +{ + OSREGIONHANDLE hRgn = NULL; + if (!pbits32) return NULL; + + RGNDATA *pData; + int y, x; + + // For better performances, we will use the ExtCreateRegion() function to create the + // region. This function take a RGNDATA structure on entry. We will add rectangles by + // amount of ALLOC_UNIT number in this structure. + // JF> rects are 8 bytes, so this allocates just under 16kb of memory, no need to REALLOC +#define MAXRECTS 2000 + __int8 regionMemory[sizeof(RGNDATAHEADER) + (sizeof(RECT) * MAXRECTS)] = {0}; + //pData = (RGNDATA *)MALLOC(sizeof(RGNDATAHEADER) + (sizeof(RECT) * MAXRECTS)); + pData = (RGNDATA *)regionMemory; + //if (!pData) return NULL; + + pData->rdh.dwSize = sizeof(RGNDATAHEADER); + pData->rdh.iType = RDH_RECTANGLES; + pData->rdh.nCount = pData->rdh.nRgnSize = 0; + + SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0); + + int x_end = (portion ? _w + _x : bmWidth); + int y_end = (portion ? _h + _y : bmHeight); + int x_start = (portion ? _x : 0); + int y_start = (portion ? _y : 0); + + x_start += bmX; + x_end += bmX; + y_start += bmY; + y_end += bmY; + + unsigned int iv = minalpha << 24; //inverted?0xff000000:0; + + int shiftx = xoffset - bmX; + int shifty = yoffset - bmY; + + for (y = y_start; y < y_end; y++) + { + // Scan each bitmap pixel from left to right + unsigned int *lineptr = ((unsigned int *)pbits32) + fullw * y; + for (x = x_start; x < x_end; x++) + { + // Search for a continuous range of "non transparent pixels" + int x0 = x; + unsigned int *p = lineptr; + if (dothreshold) + { + if (inverted) + { + if (thinverse) + { + while (x < x_end) + { + unsigned int a = p[x]; + if ((a&0xff000000) >= iv || + (((((a & 0xFF) > threshold || ((a & 0xFF00) >> 8) > threshold || ((a & 0xFF0000) >> 16) > threshold))))) + break; + x++; + } + } + else + { + while (x < x_end) + { + unsigned int a = p[x]; + if ((a&0xff000000) >= iv || + (((((a & 0xFF) < threshold || ((a & 0xFF00) >> 8) < threshold || ((a & 0xFF0000) >> 16) < threshold))))) + break; + x++; + } + } + } + else + { + if (thinverse) + { + while (x < x_end) + { + unsigned int a = p[x]; + if ((a&0xff000000) < iv || + (((((a & 0xFF) > threshold || ((a & 0xFF00) >> 8) > threshold || ((a & 0xFF0000) >> 16) > threshold))))) + break; + x++; + } + } + else + { + while (x < x_end) + { + unsigned int a = p[x]; + if ((a&0xff000000) < iv || + (((((a & 0xFF) < threshold || ((a & 0xFF00) >> 8) < threshold || ((a & 0xFF0000) >> 16) < threshold))))) + break; + x++; + } + } + } + } + else + { + if (inverted) + { + while (x < x_end) + { + if ((p[x] & 0xFF000000) >= iv) break; + x++; + } + } + else + { + while (x < x_end) + { + if ((p[x] & 0xFF000000) < iv) break; + x++; + } + } + } + + if (x > x0) + { + SetRect(((RECT *)&pData->Buffer) + pData->rdh.nCount, x0 + shiftx, y + shifty, x + shiftx, y + 1 + shifty); + + pData->rdh.nCount++; + + if (x0 + shiftx < pData->rdh.rcBound.left) pData->rdh.rcBound.left = x0 + shiftx; + if (y + shifty < pData->rdh.rcBound.top) pData->rdh.rcBound.top = y + shifty; + if (x + shiftx > pData->rdh.rcBound.right) pData->rdh.rcBound.right = x + shiftx; + if (y + 1 + shifty > pData->rdh.rcBound.bottom) pData->rdh.rcBound.bottom = y + 1 + shifty; + + // On Windows98, ExtCreateRegion() may fail if the number of rectangles is too + // large (ie: > 4000). Therefore, we have to create the region by multiple steps. + if (pData->rdh.nCount == MAXRECTS) + { + OSREGIONHANDLE h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * pData->rdh.nCount), pData); + if (hRgn) + { + CombineRgn(hRgn, hRgn, h, RGN_OR); + DeleteObject(h); + } + else hRgn = h; + pData->rdh.nCount = 0; + SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0); + } + } + } + } + + // Create or extend the region with the remaining rectangles + OSREGIONHANDLE h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * pData->rdh.nCount), pData); + if (hRgn) + { + CombineRgn(hRgn, hRgn, h, RGN_OR); + DeleteObject(h); + } + else + hRgn = h; + + // Clean up + //FREE(pData); + + return hRgn; +} + +bool RegionI::ptInRegion(const POINT *pt) +{ + if (optimized) return !!PtInRect(&optrect, *pt); + CHECK_REGION + return !!PtInRegion(hrgn, pt->x, pt->y); +} + +void RegionI::offset(int x, int y) +{ + if (optimized) + { + optrect.left += x; + optrect.top += y; + optrect.right += x; + optrect.bottom += y; + return ; + } + CHECK_REGION + if (srv) + { + hrgn = CreateRectRgn(0, 0, 0, 0); + RegionServer *s = srv; + srv = NULL; + addRegion(s->getRegion()); + s->delRef(this); + } + if (x == 0 && y == 0) return ; + deoptimize(); // because addregion may have optimized it + OffsetRgn(hrgn, x, y); + optimize(); +} + +void RegionI::getBox(RECT *r) +{ + if (optimized) + { + *r = optrect; + return ; + } + CHECK_REGION + GetRgnBox(hrgn, r); +} + +OSREGIONHANDLE RegionI::getOSHandle() +{ + deoptimize(); + CHECK_REGION + return hrgn; +} + +void RegionI::subtractRect(const RECT *r) +{ + RegionI s(r); + subtractRegion(&s); +} + +void RegionI::subtractRegion(const api_region *reg) +{ + if (srv) + { + hrgn = CreateRectRgn(0, 0, 0, 0); + RegionServer *s = srv; + srv = NULL; + addRegion(s->getRegion()); + s->delRef(this); + } + deoptimize(); + CombineRgn(hrgn, hrgn, GETOSHANDLE(reg), RGN_DIFF); + optimize(); +} + +void RegionI::andRegion(const api_region *reg) +{ + if (srv) + { + hrgn = CreateRectRgn(0, 0, 0, 0); + RegionServer *s = srv; + srv = NULL; + addRegion(s->getRegion()); + s->delRef(this); + } + + deoptimize(); + CombineRgn(hrgn, hrgn, GETOSHANDLE(reg), RGN_AND); + optimize(); +} + +void RegionI::addRect(const RECT *r) +{ + RegionI a(r); + addRegion(&a); +} + +void RegionI::addRegion(const api_region *reg) +{ + if (srv) + { + hrgn = CreateRectRgn(0, 0, 0, 0); + RegionServer *s = srv; + srv = NULL; + addRegion(s->getRegion()); + s->delRef(this); + } + deoptimize(); + ASSERT(reg != NULL); + CombineRgn(hrgn, hrgn, GETOSHANDLE(reg), RGN_OR); + optimize(); +} + +int RegionI::isEmpty() +{ + RECT r; + getBox(&r); + if (r.left == r.right || r.bottom == r.top) return 1; + return 0; +} + +int RegionI::enclosed(const api_region *r, api_region *outside) +{ + deoptimize(); + OSREGIONHANDLE del = NULL; + if (!outside) + del = CreateRectRgn(0, 0, 0, 0); + int rs = CombineRgn(outside ? outside->getOSHandle() : del, hrgn, GETOSHANDLE(r), RGN_DIFF); + if (del != NULL) DeleteObject(del); + optimize(); + return rs == NULLREGION; +} + +#define IntersectRgn(hrgnResult, hrgnA, hrgnB) CombineRgn(hrgnResult, hrgnA, hrgnB, RGN_AND) + +int RegionI::intersectRgn(const api_region *r, api_region *intersection) +{ + ASSERT(intersection != NULL); + ASSERT(intersection != this); + int rs; + if (optimized) + { + deoptimize(); + rs = IntersectRgn(intersection->getOSHandle(), hrgn, GETOSHANDLE(r)); + DeleteObject(hrgn); + hrgn=NULL; + optimized=1; + } + else + { + rs = IntersectRgn(intersection->getOSHandle(), hrgn, GETOSHANDLE(r)); + } + + return (rs != NULLREGION && rs != ERROR); +} + +int RegionI::doesIntersectRgn(const api_region *r) +{ + if (optimized) + { + return RectInRegion(GETOSHANDLE(r), &optrect); + } + else + { + CHECK_REGION + HRGN del = CreateRectRgn(0, 0, 0, 0); + int rs = IntersectRgn(del, hrgn, GETOSHANDLE(r)); + DeleteObject(del); + return (rs != NULLREGION && rs != ERROR); + } + +} + +int RegionI::intersectRect(const RECT *r, api_region *intersection) +{ + int rs; + ASSERT(intersection != NULL); + ASSERT(intersection != this); + if (optimized) + { + RECT temp = optrect; + rs = IntersectRect(&temp, &optrect, r); + intersection->setRect(&temp); + return rs; + } + else + { + CHECK_REGION + + OSREGIONHANDLE iRgn = intersection->getOSHandle(); + SetRectRgn(iRgn, r->left, r->top, r->right, r->bottom); + rs = IntersectRgn(iRgn, hrgn, iRgn); + } + return (rs != NULLREGION && rs != ERROR); +} + +int RegionI::doesIntersectRect(const RECT *r) +{ + return RectInRegion(hrgn, r); +} + +void RegionI::empty() +{ + if (srv) + { + hrgn = CreateRectRgn(0, 0, 0, 0); + ASSERT(hrgn != NULL); + srv->delRef(this); + srv = NULL; + optimize(); + return ; + } + //deoptimize(); + if (hrgn != NULL) + DeleteObject(hrgn); + hrgn=NULL; + //hrgn = CreateRectRgn(0, 0, 0, 0); + optrect.left=0; + optrect.top=0; + optrect.right=0; + optrect.bottom=0; + optimized=1; + //ASSERT(hrgn != NULL); + //optimize(); +} + +void RegionI::setRect(const RECT *r) +{ + if (srv) + { + hrgn = CreateRectRgnIndirect(r); + srv->delRef(this); + srv = NULL; + optimize(); + return ; + } + //deoptimize(); + //CHECK_REGION + if (hrgn) + DeleteObject(hrgn); + hrgn=NULL; + //SetRectRgn(hrgn, r->left, r->top, r->right, r->bottom); + optrect = *r; + optimized = 1; + //optimize(); +} + +int RegionI::equals(const api_region *r) +{ + ASSERT(r); + api_region *cl = const_cast<api_region*>(r)->clone(); + cl->subtractRegion(this); + int ret = cl->isEmpty(); + const_cast<api_region*>(r)->disposeClone(cl); + cl = clone(); + cl->subtractRegion(r); + ret &= cl->isEmpty(); + disposeClone(cl); + return ret; +} + +int RegionI::isRect() +{ + if (optimized) return 1; + RECT r; + getBox(&r); + RegionI n(&r); + return equals(&n); +} + +void RegionI::scale(double sx, double sy, bool round) +{ + if (srv) + { + hrgn = CreateRectRgn(0, 0, 0, 0); + RegionServer *s = srv; + srv = NULL; + addRegion(s->getRegion()); + s->delRef(this); + } + deoptimize(); + CHECK_REGION + DWORD size = 0; + RECT box; + getBox(&box); + size = GetRegionData(hrgn, size, NULL); + if (!size) return ; + RGNDATA *data = (RGNDATA *)MALLOC(size); + RECT *r = (RECT *)data->Buffer; + + GetRegionData(hrgn, size, (RGNDATA *)data); + double adj = round ? 0.99999 : 0.0; + int iadj = round ? 1 : 0; + + if (data->rdh.nCount == 1) + { + RECT nr = box; + nr.left = (int)((double)nr.left * sx - iadj); + nr.top = (int)((double)nr.top * sy - iadj); + nr.right = (int)((double)nr.right * sx + adj); + nr.bottom = (int)((double)nr.bottom * sy + adj); + setRect(&nr); + FREE(data); + return ; + } + + for (int i = 0;i < (int)data->rdh.nCount;i++) + { + r[i].left = (int)((double)r[i].left * sx - iadj); + r[i].top = (int)((double)r[i].top * sy - iadj); + r[i].right = (int)((double)r[i].right * sx + adj); + r[i].bottom = (int)((double)r[i].bottom * sy + adj); + } + + OSREGIONHANDLE nhrgn = ExtCreateRegion(NULL, size, data); + if (!nhrgn) + { + nhrgn = CreateRectRgn(0, 0, 0, 0); + } + FREE(data); + DeleteObject(hrgn); + hrgn = nhrgn; + optimize(); +} + +void RegionI::debug(int async) +{ + if (!async) + { + SysCanvas c; + RECT r; + getBox(&r); + // c.fillRect(&r, 0); + InvertRgn(c.getHDC(), getOSHandle()); + Sleep(200); + InvertRgn(c.getHDC(), getOSHandle()); + } + else + { + SysCanvas c; + RECT r; + getBox(&r); + // c.fillRect(&r, 0); + if (lastdebug) + InvertRgn(c.getHDC(), lastdebug->getOSHandle()); + delete lastdebug; + lastdebug = new RegionI(); + lastdebug->addRegion(this); + InvertRgn(c.getHDC(), getOSHandle()); + } +} + +// later we can cache this data or something if needed +int RegionI::getNumRects() +{ + if (optimized) return 1; + int bytes_needed = GetRegionData(hrgn, 0, NULL) + sizeof(RGNDATA); + MemBlock<unsigned char> data(bytes_needed); + GetRegionData(hrgn, bytes_needed, (LPRGNDATA)data.getMemory()); + RGNDATA *rgndata = reinterpret_cast<RGNDATA *>(data.getMemory()); + return rgndata->rdh.nCount; +} + +int RegionI::enumRect(int n, RECT *r) +{ + if (optimized) + { + if (n == 0) + { + if (r != NULL) *r = optrect; + return 1; + } + return 0; + } + if (n < 0) return 0; + int bytes_needed = GetRegionData(hrgn, 0, NULL) + sizeof(RGNDATA); + MemBlock<unsigned char> data(bytes_needed); + GetRegionData(hrgn, bytes_needed, (LPRGNDATA)data.getMemory()); + RGNDATA *rgndata = reinterpret_cast<RGNDATA *>(data.getMemory()); + int nrects = rgndata->rdh.nCount; + if (n >= nrects) return 0; + RECT *rectlist = reinterpret_cast<RECT*>(rgndata->Buffer); + *r = rectlist[n]; + return 1; +} + +void RegionI::optimize() +{ + if (optimized) return ; + if (srv != NULL) return ; // region is cached and shared, do not optimize + CHECK_REGION + getBox(&optrect); + + if (IsRectEmpty(&optrect)) + return; + + RECT br; + OSREGIONHANDLE gr = CreateRectRgnIndirect(&optrect); + OSREGIONHANDLE res = CreateRectRgn(0, 0, 0, 0); + +/* + // if they don't intersect, we may be offset + IntersectRgn(res, gr, hrgn); + GetRgnBox(res, &br); + if (br.left == br.right || br.bottom == br.top) + { + DeleteObject(gr); + DeleteObject(res); + return ; + } + */ + + // if they intersect, but when subtracting the region from the rect, we get nothing, they're the same, let's optimize + CombineRgn(res, gr, hrgn, RGN_DIFF); + DeleteObject(gr); + GetRgnBox(res, &br); + DeleteObject(res); + if (br.left == br.right || br.bottom == br.top) + { + optimized = 1; + DeleteObject(hrgn); + hrgn = NULL; + } +} + +void RegionI::deoptimize() +{ + if (!optimized) return ; + CHECK_REGION + SetRectRgn(hrgn, optrect.left, optrect.top, optrect.right, optrect.bottom); + //if (hrgn != NULL) { DeleteObject(hrgn); hrgn = NULL; } + //hrgn = CreateRectRgnIndirect(&optrect); + //CHECK_REGION + optimized = 0; +} + + +#define CBCLASS RegionServerI +START_DISPATCH; +VCB(REGIONSERVER_ADDREF, addRef); +VCB(REGIONSERVER_DELREF, delRef); +CB(REGIONSERVER_GETREGION, getRegion); +END_DISPATCH; + +#endif//WIN32 |