/* * GDI region objects * * Copyright 1993, 1994 Alexandre Julliard * static char Copyright[] = "Copyright Alexandre Julliard, 1993, 1994"; */ #include #include #include "region.h" #include "stddebug.h" /* #define DEBUG_REGION */ #include "debug.h" /* GC used for region operations */ static GC regionGC = 0; /*********************************************************************** * REGION_Init */ BOOL REGION_Init(void) { Pixmap tmpPixmap; /* CreateGC needs a drawable */ tmpPixmap = XCreatePixmap( display, rootWindow, 1, 1, 1 ); if (tmpPixmap) { regionGC = XCreateGC( display, tmpPixmap, 0, NULL ); XFreePixmap( display, tmpPixmap ); if (!regionGC) return FALSE; XSetForeground( display, regionGC, 1 ); XSetGraphicsExposures( display, regionGC, False ); return TRUE; } else return FALSE; } /*********************************************************************** * REGION_MakePixmap * * Make a pixmap of an X region. */ static BOOL REGION_MakePixmap( REGION *region ) { int width = region->box.right - region->box.left; int height = region->box.bottom - region->box.top; if (!region->xrgn) return TRUE; /* Null region */ region->pixmap = XCreatePixmap( display, rootWindow, width, height, 1 ); if (!region->pixmap) return FALSE; XSetRegion( display, regionGC, region->xrgn ); XSetClipOrigin( display, regionGC, -region->box.left, -region->box.top ); XSetFunction( display, regionGC, GXset ); XFillRectangle( display, region->pixmap, regionGC, 0, 0, width, height ); XSetClipMask( display, regionGC, None ); /* Clear clip region */ return TRUE; } /*********************************************************************** * REGION_SetRect * * Set the bounding box of the region and create the pixmap (or the X rgn). * The hrgn must be valid. */ static BOOL REGION_SetRect( HRGN hrgn, LPRECT rect, BOOL createXrgn ) { int width, height; /* Fill region */ REGION * region = &((RGNOBJ *)GDI_HEAP_ADDR( hrgn ))->region; width = rect->right - rect->left; height = rect->bottom - rect->top; if ((width <= 0) || (height <= 0)) { region->type = NULLREGION; region->box.left = 0; region->box.right = 0; region->box.top = 0; region->box.bottom = 0; region->pixmap = 0; region->xrgn = 0; return TRUE; } region->type = SIMPLEREGION; region->box = *rect; region->xrgn = 0; region->pixmap = 0; if (createXrgn) /* Create and set the X region */ { XRectangle xrect = { region->box.left, region->box.top, width, height}; if (!(region->xrgn = XCreateRegion())) return FALSE; XUnionRectWithRegion( &xrect, region->xrgn, region->xrgn ); } else /* Create the pixmap */ { region->pixmap = XCreatePixmap( display, rootWindow, width, height, 1); if (!region->pixmap) return FALSE; /* Fill the pixmap */ XSetFunction( display, regionGC, GXclear ); XFillRectangle(display, region->pixmap, regionGC, 0, 0, width, height); } return TRUE; } /*********************************************************************** * REGION_DeleteObject */ BOOL REGION_DeleteObject( HRGN hrgn, RGNOBJ * obj ) { if (obj->region.pixmap) XFreePixmap( display, obj->region.pixmap ); if (obj->region.xrgn) XDestroyRegion( obj->region.xrgn ); return GDI_FreeObject( hrgn ); } /*********************************************************************** * OffsetRgn (GDI.101) */ int OffsetRgn( HRGN hrgn, short x, short y ) { RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ); if (!obj) return ERROR; dprintf_region(stddeb, "OffsetRgn: %d %d,%d\n", hrgn, x, y ); OffsetRect( &obj->region.box, x, y ); if (obj->region.xrgn) XOffsetRegion( obj->region.xrgn, x, y ); return obj->region.type; } /*********************************************************************** * GetRgnBox (GDI.134) */ int GetRgnBox( HRGN hrgn, LPRECT rect ) { RGNOBJ * obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ); if (!obj) return ERROR; dprintf_region(stddeb, "GetRgnBox: %d\n", hrgn ); *rect = obj->region.box; return obj->region.type; } /*********************************************************************** * CreateRectRgn (GDI.64) */ HRGN CreateRectRgn( short left, short top, short right, short bottom ) { RECT rect = { left, top, right, bottom }; return CreateRectRgnIndirect( &rect ); } /*********************************************************************** * CreateRectRgnIndirect (GDI.65) */ HRGN CreateRectRgnIndirect( LPRECT rect ) { HRGN hrgn; dprintf_region(stddeb, "CreateRectRgnIndirect: %d,%d-%d,%d\n", rect->left, rect->top, rect->right, rect->bottom ); /* Create region */ if (!(hrgn = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC ))) return 0; if (!REGION_SetRect( hrgn, rect, TRUE )) { GDI_FreeObject( hrgn ); return 0; } return hrgn; } /*********************************************************************** * CreateRoundRectRgn (GDI.444) */ HRGN CreateRoundRectRgn( short left, short top, short right, short bottom, short ellipse_width, short ellipse_height ) { RECT rect = { left, top, right, bottom }; RGNOBJ * rgnObj; HRGN hrgn; dprintf_region(stddeb, "CreateRoundRectRgn: %d,%d-%d,%d %dx%d\n", left, top, right, bottom, ellipse_width, ellipse_height ); /* Create region */ if (!(hrgn = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC ))) return 0; if (!REGION_SetRect( hrgn, &rect, FALSE )) { GDI_FreeObject( hrgn ); return 0; } rgnObj = (RGNOBJ *) GDI_HEAP_ADDR( hrgn ); /* Fill pixmap */ if (rgnObj->region.type != NULLREGION) { int width = rgnObj->region.box.right - rgnObj->region.box.left; int height = rgnObj->region.box.bottom - rgnObj->region.box.top; XSetFunction( display, regionGC, GXcopy ); XFillRectangle( display, rgnObj->region.pixmap, regionGC, 0, ellipse_height / 2, width, height - ellipse_height ); XFillRectangle( display, rgnObj->region.pixmap, regionGC, ellipse_width / 2, 0, width - ellipse_width, height ); XFillArc( display, rgnObj->region.pixmap, regionGC, 0, 0, ellipse_width, ellipse_height, 0, 360*64 ); XFillArc( display, rgnObj->region.pixmap, regionGC, width - ellipse_width, 0, ellipse_width, ellipse_height, 0, 360*64 ); XFillArc( display, rgnObj->region.pixmap, regionGC, 0, height - ellipse_height, ellipse_width, ellipse_height, 0, 360*64 ); XFillArc( display, rgnObj->region.pixmap, regionGC, width - ellipse_width, height - ellipse_height, ellipse_width, ellipse_height, 0, 360*64 ); } return hrgn; } /*********************************************************************** * SetRectRgn (GDI.172) */ void SetRectRgn( HRGN hrgn, short left, short top, short right, short bottom ) { RECT rect = { left, top, right, bottom }; RGNOBJ * rgnObj; dprintf_region(stddeb, "SetRectRgn: %d %d,%d-%d,%d\n", hrgn, left, top, right, bottom ); /* Free previous pixmap */ if (!(rgnObj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return; if (rgnObj->region.pixmap) XFreePixmap( display, rgnObj->region.pixmap ); if (rgnObj->region.xrgn) XDestroyRegion( rgnObj->region.xrgn ); REGION_SetRect( hrgn, &rect, TRUE ); } /*********************************************************************** * CreateEllipticRgn (GDI.54) */ HRGN CreateEllipticRgn( short left, short top, short right, short bottom ) { RECT rect = { left, top, right, bottom }; return CreateEllipticRgnIndirect( &rect ); } /*********************************************************************** * CreateEllipticRgnIndirect (GDI.55) */ HRGN CreateEllipticRgnIndirect( LPRECT rect ) { RGNOBJ * rgnObj; HRGN hrgn; dprintf_region(stddeb, "CreateEllipticRgnIndirect: %d,%d-%d,%d\n", rect->left, rect->top, rect->right, rect->bottom ); /* Create region */ if (!(hrgn = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC ))) return 0; if (!REGION_SetRect( hrgn, rect, FALSE )) { GDI_FreeObject( hrgn ); return 0; } rgnObj = (RGNOBJ *) GDI_HEAP_ADDR( hrgn ); /* Fill pixmap */ if (rgnObj->region.type != NULLREGION) { int width = rgnObj->region.box.right - rgnObj->region.box.left; int height = rgnObj->region.box.bottom - rgnObj->region.box.top; XSetFunction( display, regionGC, GXcopy ); XFillArc( display, rgnObj->region.pixmap, regionGC, 0, 0, width, height, 0, 360*64 ); } return hrgn; } /*********************************************************************** * CreatePolygonRgn (GDI.63) */ HRGN CreatePolygonRgn( POINT * points, short count, short mode ) { return CreatePolyPolygonRgn( points, &count, 1, mode ); } /*********************************************************************** * CreatePolyPolygonRgn (GDI.451) */ HRGN CreatePolyPolygonRgn( POINT * points, short * count, short nbpolygons, short mode ) { RGNOBJ * rgnObj; HRGN hrgn; int i, j, maxPoints; XPoint *xpoints, *pt; XRectangle rect; Region xrgn; dprintf_region(stddeb, "CreatePolyPolygonRgn: %d polygons\n", nbpolygons ); /* Allocate points array */ if (!nbpolygons) return 0; for (i = maxPoints = 0; i < nbpolygons; i++) if (maxPoints < count[i]) maxPoints = count[i]; if (!maxPoints) return 0; if (!(xpoints = (XPoint *) malloc( sizeof(XPoint) * maxPoints ))) return 0; /* Allocate region */ if (!(hrgn = GDI_AllocObject( sizeof(RGNOBJ), REGION_MAGIC ))) { free( xpoints ); return 0; } rgnObj = (RGNOBJ *) GDI_HEAP_ADDR( hrgn ); rgnObj->region.type = SIMPLEREGION; rgnObj->region.xrgn = 0; rgnObj->region.pixmap = 0; /* Create X region */ for (i = 0; i < nbpolygons; i++, count++) { for (j = *count, pt = xpoints; j > 0; j--, points++, pt++) { pt->x = points->x; pt->y = points->y; } xrgn = XPolygonRegion( xpoints, *count, (mode == WINDING) ? WindingRule : EvenOddRule ); if (!xrgn) { if (rgnObj->region.xrgn) XDestroyRegion( rgnObj->region.xrgn ); free( xpoints ); GDI_FreeObject( hrgn ); return 0; } if (i > 0) { Region tmprgn = XCreateRegion(); if (mode == WINDING) XUnionRegion(xrgn,rgnObj->region.xrgn,tmprgn); else XXorRegion( xrgn, rgnObj->region.xrgn, tmprgn ); XDestroyRegion( rgnObj->region.xrgn ); rgnObj->region.xrgn = tmprgn; } else rgnObj->region.xrgn = xrgn; } free( xpoints ); XClipBox( rgnObj->region.xrgn, &rect ); SetRect( &rgnObj->region.box, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height); return hrgn; } /*********************************************************************** * PtInRegion (GDI.161) */ BOOL PtInRegion( HRGN hrgn, short x, short y ) { BOOL res; RGNOBJ * obj; POINT pt = { x, y }; if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return FALSE; if (!PtInRect( &obj->region.box, pt )) return FALSE; if (obj->region.xrgn) { return XPointInRegion( obj->region.xrgn, x, y ); } else { XImage *image = XGetImage( display, obj->region.pixmap, x - obj->region.box.left, y - obj->region.box.top, 1, 1, AllPlanes, ZPixmap ); if (!image) return FALSE; res = (XGetPixel( image, 0, 0 ) != 0); XDestroyImage( image ); } return res; } /*********************************************************************** * RectInRegion (GDI.181) */ BOOL RectInRegion( HRGN hrgn, LPRECT rect ) { XImage * image; RGNOBJ * obj; RECT intersect; int x, y; if (!(obj = (RGNOBJ *) GDI_GetObjPtr( hrgn, REGION_MAGIC ))) return FALSE; if (obj->region.xrgn) { return (XRectInRegion( obj->region.xrgn, rect->left, rect->top, rect->right-rect->left, rect->bottom-rect->top ) != RectangleOut); } else { if (!IntersectRect( &intersect, &obj->region.box, rect )) return FALSE; image = XGetImage( display, obj->region.pixmap, intersect.left - obj->region.box.left, intersect.top - obj->region.box.top, intersect.right - intersect.left, intersect.bottom - intersect.top, AllPlanes, ZPixmap ); if (!image) return FALSE; for (y = 0; y < image->height; y++) for (x = 0; x < image->width; x++) if (XGetPixel( image, x, y ) != 0) { XDestroyImage( image ); return TRUE; } XDestroyImage( image ); } return FALSE; } /*********************************************************************** * EqualRgn (GDI.72) */ BOOL EqualRgn( HRGN rgn1, HRGN rgn2 ) { RGNOBJ *obj1, *obj2; XImage *image1, *image2; Pixmap pixmap1, pixmap2; int width, height, x, y; /* Compare bounding boxes */ if (!(obj1 = (RGNOBJ *) GDI_GetObjPtr( rgn1, REGION_MAGIC ))) return FALSE; if (!(obj2 = (RGNOBJ *) GDI_GetObjPtr( rgn2, REGION_MAGIC ))) return FALSE; if (obj1->region.type == NULLREGION) return (obj2->region.type == NULLREGION); else if (obj2->region.type == NULLREGION) return FALSE; if (!EqualRect( &obj1->region.box, &obj2->region.box )) return FALSE; if (obj1->region.xrgn && obj2->region.xrgn) { return XEqualRegion( obj1->region.xrgn, obj2->region.xrgn ); } /* Get pixmap contents */ if (!(pixmap1 = obj1->region.pixmap) && !REGION_MakePixmap( &obj1->region )) return FALSE; if (!(pixmap2 = obj2->region.pixmap) && !REGION_MakePixmap( &obj2->region )) return FALSE; width = obj1->region.box.right - obj1->region.box.left; height = obj1->region.box.bottom - obj1->region.box.top; image1 = XGetImage( display, obj1->region.pixmap, 0, 0, width, height, AllPlanes, ZPixmap ); image2 = XGetImage( display, obj2->region.pixmap, 0, 0, width, height, AllPlanes, ZPixmap ); if (!image1 || !image2) { if (image1) XDestroyImage( image1 ); if (image2) XDestroyImage( image2 ); return FALSE; } /* Compare pixmaps */ for (y = 0; y < height; y++) for (x = 0; x < width; x++) if (XGetPixel( image1, x, y ) != XGetPixel( image2, x, y)) { XDestroyImage( image1 ); XDestroyImage( image2 ); return FALSE; } XDestroyImage( image1 ); XDestroyImage( image2 ); return TRUE; } /*********************************************************************** * REGION_CopyIntersection * * Copy to dest->pixmap the area of src->pixmap delimited by * the intersection of dest and src regions, using the current GC function. */ void REGION_CopyIntersection( REGION * dest, REGION * src ) { RECT inter; if (!IntersectRect( &inter, &dest->box, &src->box )) return; XCopyArea( display, src->pixmap, dest->pixmap, regionGC, inter.left - src->box.left, inter.top - src->box.top, inter.right - inter.left, inter.bottom - inter.top, inter.left - dest->box.left, inter.top - dest->box.top ); } /*********************************************************************** * REGION_CopyRegion * * Copy region src into dest. */ static int REGION_CopyRegion( RGNOBJ *src, RGNOBJ *dest ) { if (dest->region.pixmap) XFreePixmap( display, dest->region.pixmap ); dest->region.type = src->region.type; dest->region.box = src->region.box; dest->region.pixmap = 0; if (src->region.xrgn) /* Copy only the X region */ { Region tmprgn = XCreateRegion(); if (!dest->region.xrgn) dest->region.xrgn = XCreateRegion(); XUnionRegion( tmprgn, src->region.xrgn, dest->region.xrgn ); XDestroyRegion( tmprgn ); } else /* Copy the pixmap (if any) */ { if (dest->region.xrgn) { XDestroyRegion( dest->region.xrgn ); dest->region.xrgn = 0; } if (src->region.pixmap) { int width = src->region.box.right - src->region.box.left; int height = src->region.box.bottom - src->region.box.top; dest->region.pixmap = XCreatePixmap( display, rootWindow, width, height, 1 ); XSetFunction( display, regionGC, GXcopy ); XCopyArea( display, src->region.pixmap, dest->region.pixmap, regionGC, 0, 0, width, height, 0, 0 ); } } return dest->region.type; } /*********************************************************************** * CombineRgn (GDI.451) */ int CombineRgn( HRGN hDest, HRGN hSrc1, HRGN hSrc2, short mode ) { RGNOBJ *destObj, *src1Obj, *src2Obj; REGION * region; int width, height; BOOL res; dprintf_region(stddeb, "CombineRgn: %d %d %d %d\n", hDest, hSrc1, hSrc2, mode ); if (!(destObj = (RGNOBJ *) GDI_GetObjPtr( hDest, REGION_MAGIC ))) return ERROR; if (!(src1Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc1, REGION_MAGIC ))) return ERROR; if (mode == RGN_COPY) return REGION_CopyRegion( src1Obj, destObj ); if (!(src2Obj = (RGNOBJ *) GDI_GetObjPtr( hSrc2, REGION_MAGIC ))) return ERROR; region = &destObj->region; /* Some optimizations for null regions */ if (src1Obj->region.type == NULLREGION) { switch(mode) { case RGN_AND: case RGN_DIFF: if (region->xrgn) XDestroyRegion( region->xrgn ); if (region->pixmap) XFreePixmap( display, region->pixmap ); region->type = NULLREGION; region->xrgn = 0; return NULLREGION; case RGN_OR: case RGN_XOR: return REGION_CopyRegion( src2Obj, destObj ); default: return ERROR; } } else if (src2Obj->region.type == NULLREGION) { switch(mode) { case RGN_AND: if (region->xrgn) XDestroyRegion( region->xrgn ); if (region->pixmap) XFreePixmap( display, region->pixmap ); region->type = NULLREGION; region->xrgn = 0; return NULLREGION; case RGN_OR: case RGN_XOR: case RGN_DIFF: return REGION_CopyRegion( src1Obj, destObj ); default: return ERROR; } } if (src1Obj->region.xrgn && src2Obj->region.xrgn) { /* Perform the operation with X regions */ if (region->pixmap) XFreePixmap( display, region->pixmap ); region->pixmap = 0; if (!region->xrgn) region->xrgn = XCreateRegion(); switch(mode) { case RGN_AND: XIntersectRegion( src1Obj->region.xrgn, src2Obj->region.xrgn, region->xrgn ); break; case RGN_OR: XUnionRegion( src1Obj->region.xrgn, src2Obj->region.xrgn, region->xrgn ); break; case RGN_XOR: XXorRegion( src1Obj->region.xrgn, src2Obj->region.xrgn, region->xrgn ); break; case RGN_DIFF: XSubtractRegion( src1Obj->region.xrgn, src2Obj->region.xrgn, region->xrgn ); break; default: return ERROR; } if (XEmptyRegion(region->xrgn)) { XDestroyRegion( region->xrgn ); region->type = NULLREGION; region->xrgn = 0; return NULLREGION; } else { XRectangle rect; XClipBox( region->xrgn, &rect ); region->type = COMPLEXREGION; region->box.left = rect.x; region->box.top = rect.y; region->box.right = rect.x + rect.width; region->box.bottom = rect.y + rect.height; return COMPLEXREGION; } } else /* Create pixmaps if needed */ { if (!src1Obj->region.pixmap) if (!REGION_MakePixmap( &src1Obj->region )) return ERROR; if (!src2Obj->region.pixmap) if (!REGION_MakePixmap( &src2Obj->region )) return ERROR; } switch(mode) { case RGN_AND: res = IntersectRect( ®ion->box, &src1Obj->region.box, &src2Obj->region.box ); region->type = COMPLEXREGION; break; case RGN_OR: case RGN_XOR: res = UnionRect( ®ion->box, &src1Obj->region.box, &src2Obj->region.box ); region->type = COMPLEXREGION; break; case RGN_DIFF: res = SubtractRect( ®ion->box, &src1Obj->region.box, &src2Obj->region.box ); region->type = COMPLEXREGION; break; default: return ERROR; } if (region->pixmap) XFreePixmap( display, region->pixmap ); if (region->xrgn) XDestroyRegion( region->xrgn ); if (!res) { region->type = NULLREGION; region->pixmap = 0; region->xrgn = 0; return NULLREGION; } width = region->box.right - region->box.left; height = region->box.bottom - region->box.top; if (!width || !height) { fprintf(stderr, "CombineRgn: width or height is 0. Please report this.\n" ); fprintf(stderr, "src1=%d,%d-%d,%d src2=%d,%d-%d,%d dst=%d,%d-%d,%d op=%d\n", src1Obj->region.box.left, src1Obj->region.box.top, src1Obj->region.box.right, src1Obj->region.box.bottom, src2Obj->region.box.left, src2Obj->region.box.top, src2Obj->region.box.right, src2Obj->region.box.bottom, region->box.left, region->box.top, region->box.right, region->box.bottom, mode ); exit(1); } region->pixmap = XCreatePixmap( display, rootWindow, width, height, 1 ); region->xrgn = 0; switch(mode) { case RGN_AND: XSetFunction( display, regionGC, GXcopy ); REGION_CopyIntersection( region, &src1Obj->region ); XSetFunction( display, regionGC, GXand ); REGION_CopyIntersection( region, &src2Obj->region ); break; case RGN_OR: case RGN_XOR: XSetFunction( display, regionGC, GXclear ); XFillRectangle( display, region->pixmap, regionGC, 0, 0, width, height ); XSetFunction( display, regionGC, (mode == RGN_OR) ? GXor : GXxor); REGION_CopyIntersection( region, &src1Obj->region ); REGION_CopyIntersection( region, &src2Obj->region ); break; case RGN_DIFF: XSetFunction( display, regionGC, GXclear ); XFillRectangle( display, region->pixmap, regionGC, 0, 0, width, height ); XSetFunction( display, regionGC, GXcopy ); REGION_CopyIntersection( region, &src1Obj->region ); XSetFunction( display, regionGC, GXandInverted ); REGION_CopyIntersection( region, &src2Obj->region ); break; } return region->type; }