diff options
author | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 15:54:54 +0000 |
---|---|---|
committer | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 15:54:54 +0000 |
commit | ded6147bfb5d75ff1e67c858040a628b61bc17d1 (patch) | |
tree | 82355105e93cdac89ef7d987424351c77545faf0 /mi | |
parent | cb6ef07bf01e72d1a6e6e83ceb7f76d6534da941 (diff) |
R6.6 is the Xorg base-lineXORG-MAIN
Diffstat (limited to 'mi')
-rw-r--r-- | mi/cbrt.c | 39 | ||||
-rw-r--r-- | mi/mi.h | 748 | ||||
-rw-r--r-- | mi/miarc.c | 3654 | ||||
-rw-r--r-- | mi/mibitblt.c | 818 | ||||
-rw-r--r-- | mi/mibstore.c | 3823 | ||||
-rw-r--r-- | mi/mibstore.h | 62 | ||||
-rw-r--r-- | mi/mibstorest.h | 91 | ||||
-rw-r--r-- | mi/miclipn.c | 73 | ||||
-rw-r--r-- | mi/micursor.c | 71 | ||||
-rw-r--r-- | mi/midash.c | 310 | ||||
-rw-r--r-- | mi/midispcur.c | 614 | ||||
-rw-r--r-- | mi/mieq.c | 191 | ||||
-rw-r--r-- | mi/miexpose.c | 827 | ||||
-rw-r--r-- | mi/mifillarc.c | 810 | ||||
-rw-r--r-- | mi/mifillarc.h | 223 | ||||
-rw-r--r-- | mi/mifillrct.c | 139 | ||||
-rw-r--r-- | mi/mifpoly.h | 108 | ||||
-rw-r--r-- | mi/mifpolycon.c | 277 | ||||
-rw-r--r-- | mi/migc.c | 295 | ||||
-rw-r--r-- | mi/migc.h | 113 | ||||
-rw-r--r-- | mi/miglblt.c | 248 | ||||
-rw-r--r-- | mi/miinitext.c | 246 | ||||
-rw-r--r-- | mi/miline.h | 167 | ||||
-rw-r--r-- | mi/mipointer.c | 498 | ||||
-rw-r--r-- | mi/mipointer.h | 122 | ||||
-rw-r--r-- | mi/mipointrst.h | 62 | ||||
-rw-r--r-- | mi/mipoly.c | 124 | ||||
-rw-r--r-- | mi/mipoly.h | 228 | ||||
-rw-r--r-- | mi/mipolycon.c | 245 | ||||
-rw-r--r-- | mi/mipolygen.c | 226 | ||||
-rw-r--r-- | mi/mipolypnt.c | 119 | ||||
-rw-r--r-- | mi/mipolyrect.c | 187 | ||||
-rw-r--r-- | mi/mipolyseg.c | 79 | ||||
-rw-r--r-- | mi/mipolytext.c | 196 | ||||
-rw-r--r-- | mi/mipolyutil.c | 398 | ||||
-rw-r--r-- | mi/mipushpxl.c | 197 | ||||
-rw-r--r-- | mi/miregion.c | 2419 | ||||
-rw-r--r-- | mi/miscanfill.h | 144 | ||||
-rw-r--r-- | mi/miscrinit.c | 288 | ||||
-rw-r--r-- | mi/mispans.c | 554 | ||||
-rw-r--r-- | mi/mispans.h | 132 | ||||
-rw-r--r-- | mi/misprite.c | 2067 | ||||
-rw-r--r-- | mi/misprite.h | 111 | ||||
-rw-r--r-- | mi/mispritest.h | 110 | ||||
-rw-r--r-- | mi/mistruct.h | 63 | ||||
-rw-r--r-- | mi/mivalidate.h | 51 | ||||
-rw-r--r-- | mi/mivaltree.c | 729 | ||||
-rw-r--r-- | mi/miwideline.c | 2203 | ||||
-rw-r--r-- | mi/miwideline.h | 244 | ||||
-rw-r--r-- | mi/miwindow.c | 1144 | ||||
-rw-r--r-- | mi/mizerarc.c | 848 | ||||
-rw-r--r-- | mi/mizerarc.h | 137 | ||||
-rw-r--r-- | mi/mizerline.c | 960 |
53 files changed, 28832 insertions, 0 deletions
diff --git a/mi/cbrt.c b/mi/cbrt.c new file mode 100644 index 000000000..44a017ad5 --- /dev/null +++ b/mi/cbrt.c @@ -0,0 +1,39 @@ +/* $Xorg: cbrt.c,v 1.4 2001/02/09 02:05:19 xorgcvs Exp $ */ +/* + +Copyright 1990, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + +/* simple cbrt, in case your math library doesn't have a good one */ + +double pow(); + +double +cbrt(x) + double x; +{ + return pow(x, 1.0/3.0); +} diff --git a/mi/mi.h b/mi/mi.h new file mode 100644 index 000000000..dd7dc94ed --- /dev/null +++ b/mi/mi.h @@ -0,0 +1,748 @@ +/* $Xorg: mi.h,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +#ifndef MI_H +#define MI_H +#include "X11/X.h" +#include "region.h" +#include "validate.h" +#include "window.h" +#include "gc.h" +#include "font.h" +#include "input.h" +#include "cursor.h" + +typedef struct _miDash *miDashPtr; +#define EVEN_DASH 0 +#define ODD_DASH ~0 + +/* miarc.c */ + +extern void miPolyArc( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*narcs*/, + xArc * /*parcs*/ +#endif +); + +/* mibitblt.c */ + +extern RegionPtr miCopyArea( +#if NeedFunctionPrototypes + DrawablePtr /*pSrcDrawable*/, + DrawablePtr /*pDstDrawable*/, + GCPtr /*pGC*/, + int /*xIn*/, + int /*yIn*/, + int /*widthSrc*/, + int /*heightSrc*/, + int /*xOut*/, + int /*yOut*/ +#endif +); + +extern void miOpqStipDrawable( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + RegionPtr /*prgnSrc*/, + unsigned long * /*pbits*/, + int /*srcx*/, + int /*w*/, + int /*h*/, + int /*dstx*/, + int /*dsty*/ +#endif +); + +extern RegionPtr miCopyPlane( +#if NeedFunctionPrototypes + DrawablePtr /*pSrcDrawable*/, + DrawablePtr /*pDstDrawable*/, + GCPtr /*pGC*/, + int /*srcx*/, + int /*srcy*/, + int /*width*/, + int /*height*/, + int /*dstx*/, + int /*dsty*/, + unsigned long /*bitPlane*/ +#endif +); + +extern void miGetImage( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + int /*sx*/, + int /*sy*/, + int /*w*/, + int /*h*/, + unsigned int /*format*/, + unsigned long /*planeMask*/, + char * /*pdstLine*/ +#endif +); + +extern void miPutImage( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*depth*/, + int /*x*/, + int /*y*/, + int /*w*/, + int /*h*/, + int /*leftPad*/, + int /*format*/, + char * /*pImage*/ +#endif +); + +/* miclipn.c */ + +extern void miClipNotify( +#if NeedFunctionPrototypes + void (* /*func*/)() +#endif +); + +/* micursor.c */ + +extern void miRecolorCursor( +#if NeedFunctionPrototypes + ScreenPtr /*pScr*/, + CursorPtr /*pCurs*/, + Bool /*displayed*/ +#endif +); + +/* midash.c */ + +extern miDashPtr miDashLine( +#if NeedFunctionPrototypes + int /*npt*/, + DDXPointPtr /*ppt*/, + unsigned int /*nDash*/, + unsigned char * /*pDash*/, + unsigned int /*offset*/, + int * /*pnseg*/ +#endif +); + +extern void miStepDash( +#if NeedFunctionPrototypes + int /*dist*/, + int * /*pDashIndex*/, + unsigned char * /*pDash*/, + int /*numInDashList*/, + int * /*pDashOffset*/ +#endif +); + +/* mieq.c */ + + +#ifndef INPUT_H +typedef struct _DeviceRec *DevicePtr; +#endif + +extern Bool mieqInit( +#if NeedFunctionPrototypes + DevicePtr /*pKbd*/, + DevicePtr /*pPtr*/ +#endif +); + +extern void mieqEnqueue( +#if NeedFunctionPrototypes + xEventPtr /*e*/ +#endif +); + +extern void mieqSwitchScreen( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + Bool /*fromDIX*/ +#endif +); + +extern int mieqProcessInputEvents( +#if NeedFunctionPrototypes + void +#endif +); + +/* miexpose.c */ + +extern RegionPtr miHandleExposures( +#if NeedFunctionPrototypes + DrawablePtr /*pSrcDrawable*/, + DrawablePtr /*pDstDrawable*/, + GCPtr /*pGC*/, + int /*srcx*/, + int /*srcy*/, + int /*width*/, + int /*height*/, + int /*dstx*/, + int /*dsty*/, + unsigned long /*plane*/ +#endif +); + +extern void miSendGraphicsExpose( +#if NeedFunctionPrototypes + ClientPtr /*client*/, + RegionPtr /*pRgn*/, + XID /*drawable*/, + int /*major*/, + int /*minor*/ +#endif +); + +extern void miSendExposures( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/, + RegionPtr /*pRgn*/, + int /*dx*/, + int /*dy*/ +#endif +); + +extern void miWindowExposures( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/, + RegionPtr /*prgn*/, + RegionPtr /*other_exposed*/ +#endif +); + +extern void miPaintWindow( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/, + RegionPtr /*prgn*/, + int /*what*/ +#endif +); + +extern int miClearDrawable( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/ +#endif +); + +/* mifillrct.c */ + +extern void miPolyFillRect( +#if NeedFunctionPrototypes + DrawablePtr /*pDrawable*/, + GCPtr /*pGC*/, + int /*nrectFill*/, + xRectangle * /*prectInit*/ +#endif +); + +/* miglblt.c */ + +extern void miPolyGlyphBlt( +#if NeedFunctionPrototypes + DrawablePtr /*pDrawable*/, + GCPtr /*pGC*/, + int /*x*/, + int /*y*/, + unsigned int /*nglyph*/, + CharInfoPtr * /*ppci*/, + pointer /*pglyphBase*/ +#endif +); + +extern void miImageGlyphBlt( +#if NeedFunctionPrototypes + DrawablePtr /*pDrawable*/, + GCPtr /*pGC*/, + int /*x*/, + int /*y*/, + unsigned int /*nglyph*/, + CharInfoPtr * /*ppci*/, + pointer /*pglyphBase*/ +#endif +); + +/* mipoly.c */ + +extern void miFillPolygon( +#if NeedFunctionPrototypes + DrawablePtr /*dst*/, + GCPtr /*pgc*/, + int /*shape*/, + int /*mode*/, + int /*count*/, + DDXPointPtr /*pPts*/ +#endif +); + +/* mipolycon.c */ + +extern Bool miFillConvexPoly( +#if NeedFunctionPrototypes + DrawablePtr /*dst*/, + GCPtr /*pgc*/, + int /*count*/, + DDXPointPtr /*ptsIn*/ +#endif +); + +/* mipolygen.c */ + +extern Bool miFillGeneralPoly( +#if NeedFunctionPrototypes + DrawablePtr /*dst*/, + GCPtr /*pgc*/, + int /*count*/, + DDXPointPtr /*ptsIn*/ +#endif +); + +/* mipolypnt.c */ + +extern void miPolyPoint( +#if NeedFunctionPrototypes + DrawablePtr /*pDrawable*/, + GCPtr /*pGC*/, + int /*mode*/, + int /*npt*/, + xPoint * /*pptInit*/ +#endif +); + +/* mipolyrect.c */ + +extern void miPolyRectangle( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*nrects*/, + xRectangle * /*pRects*/ +#endif +); + +/* mipolyseg.c */ + +extern void miPolySegment( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*nseg*/, + xSegment * /*pSegs*/ +#endif +); + +/* mipolytext.c */ + +extern int miPolyText( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*x*/, + int /*y*/, + int /*count*/, + char * /*chars*/, + FontEncoding /*fontEncoding*/ +#endif +); + +extern int miPolyText8( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*x*/, + int /*y*/, + int /*count*/, + char * /*chars*/ +#endif +); + +extern int miPolyText16( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*x*/, + int /*y*/, + int /*count*/, + unsigned short * /*chars*/ +#endif +); + +extern int miImageText( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*x*/, + int /*y*/, + int /*count*/, + char * /*chars*/, + FontEncoding /*fontEncoding*/ +#endif +); + +extern void miImageText8( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*x*/, + int /*y*/, + int /*count*/, + char * /*chars*/ +#endif +); + +extern void miImageText16( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*x*/, + int /*y*/, + int /*count*/, + unsigned short * /*chars*/ +#endif +); + +/* mipushpxl.c */ + +extern void miPushPixels( +#if NeedFunctionPrototypes + GCPtr /*pGC*/, + PixmapPtr /*pBitMap*/, + DrawablePtr /*pDrawable*/, + int /*dx*/, + int /*dy*/, + int /*xOrg*/, + int /*yOrg*/ +#endif +); + +/* miregion.c */ + +/* see also region.h */ + +extern Bool miRectAlloc( +#if NeedFunctionPrototypes + RegionPtr /*pRgn*/, + int /*n*/ +#endif +); + +extern void miSetExtents( +#if NeedFunctionPrototypes + RegionPtr /*pReg*/ +#endif +); + +extern int miFindMaxBand( +#if NeedFunctionPrototypes + RegionPtr /*prgn*/ +#endif +); + +#ifdef DEBUG +extern Bool miValidRegion( +#if NeedFunctionPrototypes + RegionPtr /*prgn*/ +#endif +); +#endif + +/* miscrinit.c */ + +extern Bool miModifyPixmapHeader( +#if NeedFunctionPrototypes + PixmapPtr /*pPixmap*/, + int /*width*/, + int /*height*/, + int /*depth*/, + int /*bitsPerPixel*/, + int /*devKind*/, + pointer /*pPixData*/ +#endif +); + +extern Bool miCloseScreen( +#if NeedFunctionPrototypes + int /*index*/, + ScreenPtr /*pScreen*/ +#endif +); + +extern Bool miCreateScreenResources( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/ +#endif +); + +extern Bool miScreenDevPrivateInit( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + int /*width*/, + pointer /*pbits*/ +#endif +); + +#ifndef _XTYPEDEF_MIBSFUNCPTR +typedef struct _miBSFuncRec *miBSFuncPtr; +#define _XTYPEDEF_MIBSFUNCPTR +#endif + +extern Bool miScreenInit( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + pointer /*pbits*/, + int /*xsize*/, + int /*ysize*/, + int /*dpix*/, + int /*dpiy*/, + int /*width*/, + int /*rootDepth*/, + int /*numDepths*/, + DepthPtr /*depths*/, + VisualID /*rootVisual*/, + int /*numVisuals*/, + VisualPtr /*visuals*/, + miBSFuncPtr /*bsfuncs*/ +#endif +); + +/* mivaltree.c */ + +extern int miShapedWindowIn( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + RegionPtr /*universe*/, + RegionPtr /*bounding*/, + BoxPtr /*rect*/, + int /*x*/, + int /*y*/ +#endif +); + +extern int miValidateTree( +#if NeedFunctionPrototypes + WindowPtr /*pParent*/, + WindowPtr /*pChild*/, + VTKind /*kind*/ +#endif +); + +extern void miWideLine( +#if NeedFunctionPrototypes + DrawablePtr /*pDrawable*/, + GCPtr /*pGC*/, + int /*mode*/, + int /*npt*/, + DDXPointPtr /*pPts*/ +#endif +); + +extern void miWideDash( +#if NeedFunctionPrototypes + DrawablePtr /*pDrawable*/, + GCPtr /*pGC*/, + int /*mode*/, + int /*npt*/, + DDXPointPtr /*pPts*/ +#endif +); + +extern void miMiter( +#if NeedFunctionPrototypes + void +#endif +); + +extern void miNotMiter( +#if NeedFunctionPrototypes + void +#endif +); + +/* miwindow.c */ + +extern void miClearToBackground( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/, + int /*x*/, + int /*y*/, + int /*w*/, + int /*h*/, + Bool /*generateExposures*/ +#endif +); + +extern Bool miChangeSaveUnder( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/, + WindowPtr /*first*/ +#endif +); + +extern void miPostChangeSaveUnder( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/, + WindowPtr /*pFirst*/ +#endif +); + +extern void miMarkWindow( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/ +#endif +); + +extern Bool miMarkOverlappedWindows( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/, + WindowPtr /*pFirst*/, + WindowPtr * /*ppLayerWin*/ +#endif +); + +extern void miHandleValidateExposures( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/ +#endif +); + +extern void miMoveWindow( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/, + int /*x*/, + int /*y*/, + WindowPtr /*pNextSib*/, + VTKind /*kind*/ +#endif +); + +extern void miSlideAndSizeWindow( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/, + int /*x*/, + int /*y*/, + unsigned int /*w*/, + unsigned int /*h*/, + WindowPtr /*pSib*/ +#endif +); + +extern WindowPtr miGetLayerWindow( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/ +#endif +); + +extern void miSetShape( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/ +#endif +); + +extern void miChangeBorderWidth( +#if NeedFunctionPrototypes + WindowPtr /*pWin*/, + unsigned int /*width*/ +#endif +); + +extern void miMarkUnrealizedWindow( +#if NeedFunctionPrototypes + WindowPtr /*pChild*/, + WindowPtr /*pWin*/, + Bool /*fromConfigure*/ +#endif +); + +extern void miZeroPolyArc( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*narcs*/, + xArc * /*parcs*/ +#endif +); + +/* mizerline.c */ + +extern void miZeroLine( +#if NeedFunctionPrototypes + DrawablePtr /*dst*/, + GCPtr /*pgc*/, + int /*mode*/, + int /*nptInit*/, + DDXPointRec * /*pptInit*/ +#endif +); + +extern void miZeroDashLine( +#if NeedFunctionPrototypes + DrawablePtr /*dst*/, + GCPtr /*pgc*/, + int /*mode*/, + int /*nptInit*/, + DDXPointRec * /*pptInit*/ +#endif +); + +extern void miPolyFillArc( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + int /*narcs*/, + xArc * /*parcs*/ +#endif +); + +#endif /* MI_H */ diff --git a/mi/miarc.c b/mi/miarc.c new file mode 100644 index 000000000..db569fa41 --- /dev/null +++ b/mi/miarc.c @@ -0,0 +1,3654 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: miarc.c,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ +/* Author: Keith Packard and Bob Scheifler */ +/* Warning: this code is toxic, do not dally very long here. */ + +#ifdef _XOPEN_SOURCE +#include <math.h> +#else +#define _XOPEN_SOURCE /* to get prototype for hypot on some systems */ +#include <math.h> +#undef _XOPEN_SOURCE +#endif +#include "X.h" +#include "Xprotostr.h" +#include "misc.h" +#include "gcstruct.h" +#include "scrnintstr.h" +#include "pixmapstr.h" +#include "windowstr.h" +#include "mifpoly.h" +#include "mi.h" +#include "mifillarc.h" +#include "Xfuncproto.h" + +static double miDsin(), miDcos(), miDasin(), miDatan2(); +double cbrt( +#if NeedFunctionPrototypes + double +#endif +); + +#ifdef ICEILTEMPDECL +ICEILTEMPDECL +#endif + +/* + * some interesting sematic interpretation of the protocol: + * + * Self intersecting arcs (i.e. those spanning 360 degrees) + * never join with other arcs, and are drawn without caps + * (unless on/off dashed, in which case each dash segment + * is capped, except when the last segment meets the + * first segment, when no caps are drawn) + * + * double dash arcs are drawn in two parts, first the + * odd dashes (drawn in background) then the even dashes + * (drawn in foreground). This means that overlapping + * sections of foreground/background are drawn twice, + * first in background then in foreground. The double-draw + * occurs even when the function uses the destination values + * (e.g. xor mode). This is the same way the wide-line + * code works and should be "fixed". + * + */ + +#undef max +#undef min + +#if defined (__GNUC__) && defined (__STDC__) && !defined (__STRICT_ANSI__) +#define USE_INLINE +#endif + +#ifdef USE_INLINE +inline static const int max (const int x, const int y) +{ + return x>y? x:y; +} + +inline static const int min (const int x, const int y) +{ + return x<y? x:y; +} + +#else + +static int +max (x, y) +{ + return x>y? x:y; +} + +static int +min (x, y) +{ + return x<y? x:y; +} + +#endif + +struct bound { + double min, max; +}; + +struct ibound { + int min, max; +}; + +#define boundedLe(value, bounds)\ + ((bounds).min <= (value) && (value) <= (bounds).max) + +struct line { + double m, b; + int valid; +}; + +#define intersectLine(y,line) (line.m * (y) + line.b) + +/* + * these are all y value bounds + */ + +struct arc_bound { + struct bound ellipse; + struct bound inner; + struct bound outer; + struct bound right; + struct bound left; + struct ibound inneri; + struct ibound outeri; +}; + +struct accelerators { + double tail_y; + double h2; + double w2; + double h4; + double w4; + double h2mw2; + double h2l; + double w2l; + double fromIntX; + double fromIntY; + struct line left, right; + int yorgu; + int yorgl; + int xorg; +}; + +struct arc_def { + double w, h, l; + double a0, a1; +}; + +# define todeg(xAngle) (((double) (xAngle)) / 64.0) + +# define RIGHT_END 0 +# define LEFT_END 1 + +typedef struct _miArcJoin { + int arcIndex0, arcIndex1; + int phase0, phase1; + int end0, end1; +} miArcJoinRec, *miArcJoinPtr; + +typedef struct _miArcCap { + int arcIndex; + int end; +} miArcCapRec, *miArcCapPtr; + +typedef struct _miArcFace { + SppPointRec clock; + SppPointRec center; + SppPointRec counterClock; +} miArcFaceRec, *miArcFacePtr; + +typedef struct _miArcData { + xArc arc; + int render; /* non-zero means render after drawing */ + int join; /* related join */ + int cap; /* related cap */ + int selfJoin; /* final dash meets first dash */ + miArcFaceRec bounds[2]; + double x0, y0, x1, y1; +} miArcDataRec, *miArcDataPtr; + +/* + * This is an entire sequence of arcs, computed and categorized according + * to operation. miDashArcs generates either one or two of these. + */ + +typedef struct _miPolyArc { + int narcs; + miArcDataPtr arcs; + int ncaps; + miArcCapPtr caps; + int njoins; + miArcJoinPtr joins; +} miPolyArcRec, *miPolyArcPtr; + +#define GCValsFunction 0 +#define GCValsForeground 1 +#define GCValsBackground 2 +#define GCValsLineWidth 3 +#define GCValsCapStyle 4 +#define GCValsJoinStyle 5 +#define GCValsMask (GCFunction | GCForeground | GCBackground | \ + GCLineWidth | GCCapStyle | GCJoinStyle) +static CARD32 gcvals[6]; + +static void fillSpans(), newFinalSpan(); +static void drawArc(), drawQuadrant(), drawZeroArc(); +static void miArcJoin(), miArcCap(), miRoundCap(), miFreeArcs(); +static int computeAngleFromPath(); +static miPolyArcPtr miComputeArcs (); +static int miGetArcPts(); + +# define CUBED_ROOT_2 1.2599210498948732038115849718451499938964 +# define CUBED_ROOT_4 1.5874010519681993173435330390930175781250 + +/* + * draw one segment of the arc using the arc spans generation routines + */ + +static void +miArcSegment(pDraw, pGC, tarc, right, left) + DrawablePtr pDraw; + GCPtr pGC; + xArc tarc; + miArcFacePtr right, left; +{ + int l = pGC->lineWidth; + int a0, a1, startAngle, endAngle; + miArcFacePtr temp; + + if (!l) + l = 1; + + if (tarc.width == 0 || tarc.height == 0) { + drawZeroArc (pDraw, pGC, &tarc, l, left, right); + return; + } + + if (pGC->miTranslate) { + tarc.x += pDraw->x; + tarc.y += pDraw->y; + } + + a0 = tarc.angle1; + a1 = tarc.angle2; + if (a1 > FULLCIRCLE) + a1 = FULLCIRCLE; + else if (a1 < -FULLCIRCLE) + a1 = -FULLCIRCLE; + if (a1 < 0) { + startAngle = a0 + a1; + endAngle = a0; + temp = right; + right = left; + left = temp; + } else { + startAngle = a0; + endAngle = a0 + a1; + } + /* + * bounds check the two angles + */ + if (startAngle < 0) + startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE; + if (startAngle >= FULLCIRCLE) + startAngle = startAngle % FULLCIRCLE; + if (endAngle < 0) + endAngle = FULLCIRCLE - (-endAngle) % FULLCIRCLE; + if (endAngle > FULLCIRCLE) + endAngle = (endAngle-1) % FULLCIRCLE + 1; + if ((startAngle == endAngle) && a1) { + startAngle = 0; + endAngle = FULLCIRCLE; + } + + drawArc (&tarc, l, startAngle, endAngle, right, left); +} + +/* + +Three equations combine to describe the boundaries of the arc + +x^2/w^2 + y^2/h^2 = 1 ellipse itself +(X-x)^2 + (Y-y)^2 = r^2 circle at (x, y) on the ellipse +(Y-y) = (X-x)*w^2*y/(h^2*x) normal at (x, y) on the ellipse + +These lead to a quartic relating Y and y + +y^4 - (2Y)y^3 + (Y^2 + (h^4 - w^2*r^2)/(w^2 - h^2))y^2 + - (2Y*h^4/(w^2 - h^2))y + (Y^2*h^4)/(w^2 - h^2) = 0 + +The reducible cubic obtained from this quartic is + +z^3 - (3N)z^2 - 2V = 0 + +where + +N = (Y^2 + (h^4 - w^2*r^2/(w^2 - h^2)))/6 +V = w^2*r^2*Y^2*h^4/(4 *(w^2 - h^2)^2) + +Let + +t = z - N +p = -N^2 +q = -N^3 - V + +Then we get + +t^3 + 3pt + 2q = 0 + +The discriminant of this cubic is + +D = q^2 + p^3 + +When D > 0, a real root is obtained as + +z = N + cbrt(-q+sqrt(D)) + cbrt(-q-sqrt(D)) + +When D < 0, a real root is obtained as + +z = N - 2m*cos(acos(-q/m^3)/3) + +where + +m = sqrt(|p|) * sign(q) + +Given a real root Z of the cubic, the roots of the quartic are the roots +of the two quadratics + +y^2 + ((b+A)/2)y + (Z + (bZ - d)/A) = 0 + +where + +A = +/- sqrt(8Z + b^2 - 4c) +b, c, d are the cubic, quadratic, and linear coefficients of the quartic + +Some experimentation is then required to determine which solutions +correspond to the inner and outer boundaries. + +*/ + +typedef struct { + short lx, lw, rx, rw; +} miArcSpan; + +typedef struct { + miArcSpan *spans; + int count1, count2, k; + char top, bot, hole; +} miArcSpanData; + +typedef struct { + unsigned long lrustamp; + unsigned short lw; + unsigned short width, height; + miArcSpanData *spdata; +} arcCacheRec; + +#define CACHESIZE 25 + +static arcCacheRec arcCache[CACHESIZE]; +static unsigned long lrustamp; +static arcCacheRec *lastCacheHit = &arcCache[0]; +static RESTYPE cacheType; + +/* + * External so it can be called when low on memory. + * Call with a zero ID in that case. + */ +/*ARGSUSED*/ +int +miFreeArcCache (data, id) + pointer data; + XID id; +{ + int k; + arcCacheRec *cent; + + if (id) + cacheType = 0; + + for (k = CACHESIZE, cent = &arcCache[0]; --k >= 0; cent++) + { + if (cent->spdata) + { + cent->lrustamp = 0; + cent->lw = 0; + xfree(cent->spdata); + cent->spdata = NULL; + } + } + lrustamp = 0; + return Success; +} + +static void +miComputeCircleSpans(lw, parc, spdata) + int lw; + xArc *parc; + miArcSpanData *spdata; +{ + register miArcSpan *span; + int doinner; + register int x, y, e; + int xk, yk, xm, ym, dx, dy; + register int slw, inslw; + int inx, iny, ine; + int inxk, inyk, inxm, inym; + + doinner = -lw; + slw = parc->width - doinner; + y = parc->height >> 1; + dy = parc->height & 1; + dx = 1 - dy; + MIWIDEARCSETUP(x, y, dy, slw, e, xk, xm, yk, ym); + inslw = parc->width + doinner; + if (inslw > 0) + { + spdata->hole = spdata->top; + MIWIDEARCSETUP(inx, iny, dy, inslw, ine, inxk, inxm, inyk, inym); + } + else + { + spdata->hole = FALSE; + doinner = -y; + } + spdata->count1 = -doinner - spdata->top; + spdata->count2 = y + doinner; + span = spdata->spans; + while (y) + { + MIFILLARCSTEP(slw); + span->lx = dy - x; + if (++doinner <= 0) + { + span->lw = slw; + span->rx = 0; + span->rw = span->lx + slw; + } + else + { + MIFILLINARCSTEP(inslw); + span->lw = x - inx; + span->rx = dy - inx + inslw; + span->rw = inx - x + slw - inslw; + } + span++; + } + if (spdata->bot) + { + if (spdata->count2) + spdata->count2--; + else + { + if (lw > (int)parc->height) + span[-1].rx = span[-1].rw = -((lw - (int)parc->height) >> 1); + else + span[-1].rw = 0; + spdata->count1--; + } + } +} + +static void +miComputeEllipseSpans(lw, parc, spdata) + int lw; + xArc *parc; + miArcSpanData *spdata; +{ + register miArcSpan *span; + double w, h, r, xorg; + double Hs, Hf, WH, K, Vk, Nk, Fk, Vr, N, Nc, Z, rs; + double A, T, b, d, x, y, t, inx, outx, hepp, hepm; + int flip, solution; + + w = (double)parc->width / 2.0; + h = (double)parc->height / 2.0; + r = lw / 2.0; + rs = r * r; + Hs = h * h; + WH = w * w - Hs; + Nk = w * r; + Vk = (Nk * Hs) / (WH + WH); + Hf = Hs * Hs; + Nk = (Hf - Nk * Nk) / WH; + Fk = Hf / WH; + hepp = h + EPSILON; + hepm = h - EPSILON; + K = h + ((lw - 1) >> 1); + span = spdata->spans; + if (parc->width & 1) + xorg = .5; + else + xorg = 0.0; + if (spdata->top) + { + span->lx = 0; + span->lw = 1; + span++; + } + spdata->count1 = 0; + spdata->count2 = 0; + spdata->hole = (spdata->top && + (int)parc->height * lw <= (int)(parc->width * parc->width) && + lw < (int)parc->height); + for (; K > 0.0; K -= 1.0) + { + N = (K * K + Nk) / 6.0; + Nc = N * N * N; + Vr = Vk * K; + t = Nc + Vr * Vr; + d = Nc + t; + if (d < 0.0) { + d = Nc; + b = N; + if ( (b < 0.0) == (t < 0.0) ) + { + b = -b; + d = -d; + } + Z = N - 2.0 * b * cos(acos(-t / d) / 3.0); + if ( (Z < 0.0) == (Vr < 0.0) ) + flip = 2; + else + flip = 1; + } + else + { + d = Vr * sqrt(d); + Z = N + cbrt(t + d) + cbrt(t - d); + flip = 0; + } + A = sqrt((Z + Z) - Nk); + T = (Fk - Z) * K / A; + inx = 0.0; + solution = FALSE; + b = -A + K; + d = b * b - 4 * (Z + T); + if (d >= 0) + { + d = sqrt(d); + y = (b + d) / 2; + if ((y >= 0.0) && (y < hepp)) + { + solution = TRUE; + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + t = sqrt(rs - (t * t)); + if (flip == 2) + inx = x - t; + else + outx = x + t; + } + } + b = A + K; + d = b * b - 4 * (Z - T); + /* Because of the large magnitudes involved, we lose enough precision + * that sometimes we end up with a negative value near the axis, when + * it should be positive. This is a workaround. + */ + if (d < 0 && !solution) + d = 0.0; + if (d >= 0) { + d = sqrt(d); + y = (b + d) / 2; + if (y < hepp) + { + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + inx = x - sqrt(rs - (t * t)); + } + y = (b - d) / 2; + if (y >= 0.0) + { + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + t = sqrt(rs - (t * t)); + if (flip == 1) + inx = x - t; + else + outx = x + t; + } + } + span->lx = ICEIL(xorg - outx); + if (inx <= 0.0) + { + spdata->count1++; + span->lw = ICEIL(xorg + outx) - span->lx; + span->rx = ICEIL(xorg + inx); + span->rw = -ICEIL(xorg - inx); + } + else + { + spdata->count2++; + span->lw = ICEIL(xorg - inx) - span->lx; + span->rx = ICEIL(xorg + inx); + span->rw = ICEIL(xorg + outx) - span->rx; + } + span++; + } + if (spdata->bot) + { + outx = w + r; + if (r >= h && r <= w) + inx = 0.0; + else if (Nk < 0.0 && -Nk < Hs) + { + inx = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk); + if (inx > w - r) + inx = w - r; + } + else + inx = w - r; + span->lx = ICEIL(xorg - outx); + if (inx <= 0.0) + { + span->lw = ICEIL(xorg + outx) - span->lx; + span->rx = ICEIL(xorg + inx); + span->rw = -ICEIL(xorg - inx); + } + else + { + span->lw = ICEIL(xorg - inx) - span->lx; + span->rx = ICEIL(xorg + inx); + span->rw = ICEIL(xorg + outx) - span->rx; + } + } + if (spdata->hole) + { + span = &spdata->spans[spdata->count1]; + span->lw = -span->lx; + span->rx = 1; + span->rw = span->lw; + spdata->count1--; + spdata->count2++; + } +} + +static double +tailX(K, def, bounds, acc) + double K; + struct arc_def *def; + struct arc_bound *bounds; + struct accelerators *acc; +{ + double w, h, r; + double Hs, Hf, WH, Vk, Nk, Fk, Vr, N, Nc, Z, rs; + double A, T, b, d, x, y, t, hepp, hepm; + int flip, solution; + double xs[2]; + double *xp; + + w = def->w; + h = def->h; + r = def->l; + rs = r * r; + Hs = acc->h2; + WH = -acc->h2mw2; + Nk = def->w * r; + Vk = (Nk * Hs) / (WH + WH); + Hf = acc->h4; + Nk = (Hf - Nk * Nk) / WH; + if (K == 0.0) { + if (Nk < 0.0 && -Nk < Hs) { + xs[0] = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk); + xs[1] = w - r; + if (acc->left.valid && boundedLe(K, bounds->left) && + !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0) + return xs[1]; + if (acc->right.valid && boundedLe(K, bounds->right) && + !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0) + return xs[1]; + return xs[0]; + } + return w - r; + } + Fk = Hf / WH; + hepp = h + EPSILON; + hepm = h - EPSILON; + N = (K * K + Nk) / 6.0; + Nc = N * N * N; + Vr = Vk * K; + xp = xs; + xs[0] = 0.0; + t = Nc + Vr * Vr; + d = Nc + t; + if (d < 0.0) { + d = Nc; + b = N; + if ( (b < 0.0) == (t < 0.0) ) + { + b = -b; + d = -d; + } + Z = N - 2.0 * b * cos(acos(-t / d) / 3.0); + if ( (Z < 0.0) == (Vr < 0.0) ) + flip = 2; + else + flip = 1; + } + else + { + d = Vr * sqrt(d); + Z = N + cbrt(t + d) + cbrt(t - d); + flip = 0; + } + A = sqrt((Z + Z) - Nk); + T = (Fk - Z) * K / A; + solution = FALSE; + b = -A + K; + d = b * b - 4 * (Z + T); + if (d >= 0 && flip == 2) + { + d = sqrt(d); + y = (b + d) / 2; + if ((y >= 0.0) && (y < hepp)) + { + solution = TRUE; + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + t = sqrt(rs - (t * t)); + *xp++ = x - t; + } + } + b = A + K; + d = b * b - 4 * (Z - T); + /* Because of the large magnitudes involved, we lose enough precision + * that sometimes we end up with a negative value near the axis, when + * it should be positive. This is a workaround. + */ + if (d < 0 && !solution) + d = 0.0; + if (d >= 0) { + d = sqrt(d); + y = (b + d) / 2; + if (y < hepp) + { + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + *xp++ = x - sqrt(rs - (t * t)); + } + y = (b - d) / 2; + if (y >= 0.0 && flip == 1) + { + if (y > hepm) + y = h; + t = y / h; + x = w * sqrt(1 - (t * t)); + t = K - y; + t = sqrt(rs - (t * t)); + *xp++ = x - t; + } + } + if (xp > &xs[1]) { + if (acc->left.valid && boundedLe(K, bounds->left) && + !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0) + return xs[1]; + if (acc->right.valid && boundedLe(K, bounds->right) && + !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0) + return xs[1]; + } + return xs[0]; +} + +static miArcSpanData * +miComputeWideEllipse(lw, parc, mustFree) + int lw; + register xArc *parc; + Bool *mustFree; +{ + register miArcSpanData *spdata; + register arcCacheRec *cent, *lruent; + register int k; + arcCacheRec fakeent; + + if (!lw) + lw = 1; + if (parc->height <= 1500) + { + *mustFree = FALSE; + cent = lastCacheHit; + if (cent->lw == lw && + cent->width == parc->width && cent->height == parc->height) + { + cent->lrustamp = ++lrustamp; + return cent->spdata; + } + lruent = &arcCache[0]; + for (k = CACHESIZE, cent = lruent; --k >= 0; cent++) + { + if (cent->lw == lw && + cent->width == parc->width && cent->height == parc->height) + { + cent->lrustamp = ++lrustamp; + lastCacheHit = cent; + return cent->spdata; + } + if (cent->lrustamp < lruent->lrustamp) + lruent = cent; + } + if (!cacheType) + { + cacheType = CreateNewResourceType(miFreeArcCache); + (void) AddResource(FakeClientID(0), cacheType, NULL); + } + } else { + lruent = &fakeent; + lruent->spdata = NULL; + *mustFree = TRUE; + } + k = (parc->height >> 1) + ((lw - 1) >> 1); + spdata = lruent->spdata; + if (!spdata || spdata->k != k) + { + if (spdata) + xfree(spdata); + spdata = (miArcSpanData *)xalloc(sizeof(miArcSpanData) + + sizeof(miArcSpan) * (k + 2)); + lruent->spdata = spdata; + if (!spdata) + { + lruent->lrustamp = 0; + lruent->lw = 0; + return spdata; + } + spdata->spans = (miArcSpan *)(spdata + 1); + spdata->k = k; + } + spdata->top = !(lw & 1) && !(parc->width & 1); + spdata->bot = !(parc->height & 1); + lruent->lrustamp = ++lrustamp; + lruent->lw = lw; + lruent->width = parc->width; + lruent->height = parc->height; + if (lruent != &fakeent) + lastCacheHit = lruent; + if (parc->width == parc->height) + miComputeCircleSpans(lw, parc, spdata); + else + miComputeEllipseSpans(lw, parc, spdata); + return spdata; +} + +static void +miFillWideEllipse(pDraw, pGC, parc) + DrawablePtr pDraw; + GCPtr pGC; + xArc *parc; +{ + DDXPointPtr points; + register DDXPointPtr pts; + int *widths; + register int *wids; + miArcSpanData *spdata; + Bool mustFree; + register miArcSpan *span; + register int xorg, yorgu, yorgl; + register int n; + + yorgu = parc->height + pGC->lineWidth; + n = (sizeof(int) * 2) * yorgu; + widths = (int *)ALLOCATE_LOCAL(n + (sizeof(DDXPointRec) * 2) * yorgu); + if (!widths) + return; + points = (DDXPointPtr)((char *)widths + n); + spdata = miComputeWideEllipse((int)pGC->lineWidth, parc, &mustFree); + if (!spdata) + { + DEALLOCATE_LOCAL(widths); + return; + } + pts = points; + wids = widths; + span = spdata->spans; + xorg = parc->x + (parc->width >> 1); + yorgu = parc->y + (parc->height >> 1); + yorgl = yorgu + (parc->height & 1); + if (pGC->miTranslate) + { + xorg += pDraw->x; + yorgu += pDraw->y; + yorgl += pDraw->y; + } + yorgu -= spdata->k; + yorgl += spdata->k; + if (spdata->top) + { + pts->x = xorg; + pts->y = yorgu - 1; + pts++; + *wids++ = 1; + span++; + } + for (n = spdata->count1; --n >= 0; ) + { + pts[0].x = xorg + span->lx; + pts[0].y = yorgu; + wids[0] = span->lw; + pts[1].x = pts[0].x; + pts[1].y = yorgl; + wids[1] = wids[0]; + yorgu++; + yorgl--; + pts += 2; + wids += 2; + span++; + } + if (spdata->hole) + { + pts[0].x = xorg; + pts[0].y = yorgl; + wids[0] = 1; + pts++; + wids++; + } + for (n = spdata->count2; --n >= 0; ) + { + pts[0].x = xorg + span->lx; + pts[0].y = yorgu; + wids[0] = span->lw; + pts[1].x = xorg + span->rx; + pts[1].y = pts[0].y; + wids[1] = span->rw; + pts[2].x = pts[0].x; + pts[2].y = yorgl; + wids[2] = wids[0]; + pts[3].x = pts[1].x; + pts[3].y = pts[2].y; + wids[3] = wids[1]; + yorgu++; + yorgl--; + pts += 4; + wids += 4; + span++; + } + if (spdata->bot) + { + if (span->rw <= 0) + { + pts[0].x = xorg + span->lx; + pts[0].y = yorgu; + wids[0] = span->lw; + pts++; + wids++; + } + else + { + pts[0].x = xorg + span->lx; + pts[0].y = yorgu; + wids[0] = span->lw; + pts[1].x = xorg + span->rx; + pts[1].y = pts[0].y; + wids[1] = span->rw; + pts += 2; + wids += 2; + } + } + if (mustFree) + xfree(spdata); + (*pGC->ops->FillSpans)(pDraw, pGC, pts - points, points, widths, FALSE); + + DEALLOCATE_LOCAL(widths); +} + +/* + * miPolyArc strategy: + * + * If arc is zero width and solid, we don't have to worry about the rasterop + * or join styles. For wide solid circles, we use a fast integer algorithm. + * For wide solid ellipses, we use special case floating point code. + * Otherwise, we set up pDrawTo and pGCTo according to the rasterop, then + * draw using pGCTo and pDrawTo. If the raster-op was "tricky," that is, + * if it involves the destination, then we use PushPixels to move the bits + * from the scratch drawable to pDraw. (See the wide line code for a + * fuller explanation of this.) + */ + +void +miPolyArc(pDraw, pGC, narcs, parcs) + DrawablePtr pDraw; + GCPtr pGC; + int narcs; + xArc *parcs; +{ + register int i; + xArc *parc; + int xMin, xMax, yMin, yMax; + int pixmapWidth, pixmapHeight; + int xOrg, yOrg; + int width; + Bool fTricky; + DrawablePtr pDrawTo; + CARD32 fg, bg; + GCPtr pGCTo; + miPolyArcPtr polyArcs; + int cap[2], join[2]; + int iphase; + int halfWidth; + + width = pGC->lineWidth; + if(width == 0 && pGC->lineStyle == LineSolid) + { + for(i = narcs, parc = parcs; --i >= 0; parc++) + miArcSegment( pDraw, pGC, *parc, + (miArcFacePtr) 0, (miArcFacePtr) 0 ); + fillSpans (pDraw, pGC); + } + else + { + if ((pGC->lineStyle == LineSolid) && narcs) + { + while (parcs->width && parcs->height && + (parcs->angle2 >= FULLCIRCLE || + parcs->angle2 <= -FULLCIRCLE)) + { + miFillWideEllipse(pDraw, pGC, parcs); + if (!--narcs) + return; + parcs++; + } + } + + /* Set up pDrawTo and pGCTo based on the rasterop */ + switch(pGC->alu) + { + case GXclear: /* 0 */ + case GXcopy: /* src */ + case GXcopyInverted: /* NOT src */ + case GXset: /* 1 */ + fTricky = FALSE; + pDrawTo = pDraw; + pGCTo = pGC; + break; + default: + fTricky = TRUE; + + /* find bounding box around arcs */ + xMin = yMin = MAXSHORT; + xMax = yMax = MINSHORT; + + for(i = narcs, parc = parcs; --i >= 0; parc++) + { + xMin = min (xMin, parc->x); + yMin = min (yMin, parc->y); + xMax = max (xMax, (parc->x + (int) parc->width)); + yMax = max (yMax, (parc->y + (int) parc->height)); + } + + /* expand box to deal with line widths */ + halfWidth = (width + 1)/2; + xMin -= halfWidth; + yMin -= halfWidth; + xMax += halfWidth; + yMax += halfWidth; + + /* compute pixmap size; limit it to size of drawable */ + xOrg = max(xMin, 0); + yOrg = max(yMin, 0); + pixmapWidth = min(xMax, pDraw->width) - xOrg; + pixmapHeight = min(yMax, pDraw->height) - yOrg; + + /* if nothing left, return */ + if ( (pixmapWidth <= 0) || (pixmapHeight <= 0) ) return; + + for(i = narcs, parc = parcs; --i >= 0; parc++) + { + parc->x -= xOrg; + parc->y -= yOrg; + } + if (pGC->miTranslate) + { + xOrg += pDraw->x; + yOrg += pDraw->y; + } + + /* set up scratch GC */ + + pGCTo = GetScratchGC(1, pDraw->pScreen); + if (!pGCTo) + return; + gcvals[GCValsFunction] = GXcopy; + gcvals[GCValsForeground] = 1; + gcvals[GCValsBackground] = 0; + gcvals[GCValsLineWidth] = pGC->lineWidth; + gcvals[GCValsCapStyle] = pGC->capStyle; + gcvals[GCValsJoinStyle] = pGC->joinStyle; + dixChangeGC(NullClient, pGCTo, GCValsMask, gcvals, NULL); + + /* allocate a 1 bit deep pixmap of the appropriate size, and + * validate it */ + pDrawTo = (DrawablePtr)(*pDraw->pScreen->CreatePixmap) + (pDraw->pScreen, pixmapWidth, pixmapHeight, 1); + if (!pDrawTo) + { + FreeScratchGC(pGCTo); + return; + } + ValidateGC(pDrawTo, pGCTo); + miClearDrawable(pDrawTo, pGCTo); + } + + fg = pGC->fgPixel; + bg = pGC->bgPixel; + if ((pGC->fillStyle == FillTiled) || + (pGC->fillStyle == FillOpaqueStippled)) + bg = fg; /* the protocol sez these don't cause color changes */ + + polyArcs = miComputeArcs (parcs, narcs, pGC); + + if (!polyArcs) + { + if (fTricky) { + (*pDraw->pScreen->DestroyPixmap) ((PixmapPtr)pDrawTo); + FreeScratchGC (pGCTo); + } + return; + } + + cap[0] = cap[1] = 0; + join[0] = join[1] = 0; + for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0); + iphase >= 0; + iphase--) + { + if (iphase == 1) { + dixChangeGC (NullClient, pGC, GCForeground, &bg, NULL); + ValidateGC (pDraw, pGC); + } else if (pGC->lineStyle == LineDoubleDash) { + dixChangeGC (NullClient, pGC, GCForeground, &fg, NULL); + ValidateGC (pDraw, pGC); + } + for (i = 0; i < polyArcs[iphase].narcs; i++) { + miArcDataPtr arcData; + + arcData = &polyArcs[iphase].arcs[i]; + miArcSegment(pDrawTo, pGCTo, arcData->arc, + &arcData->bounds[RIGHT_END], + &arcData->bounds[LEFT_END]); + if (polyArcs[iphase].arcs[i].render) { + fillSpans (pDrawTo, pGCTo); + /* + * don't cap self-joining arcs + */ + if (polyArcs[iphase].arcs[i].selfJoin && + cap[iphase] < polyArcs[iphase].arcs[i].cap) + cap[iphase]++; + while (cap[iphase] < polyArcs[iphase].arcs[i].cap) { + int arcIndex, end; + miArcDataPtr arcData0; + + arcIndex = polyArcs[iphase].caps[cap[iphase]].arcIndex; + end = polyArcs[iphase].caps[cap[iphase]].end; + arcData0 = &polyArcs[iphase].arcs[arcIndex]; + miArcCap (pDrawTo, pGCTo, + &arcData0->bounds[end], end, + arcData0->arc.x, arcData0->arc.y, + (double) arcData0->arc.width / 2.0, + (double) arcData0->arc.height / 2.0); + ++cap[iphase]; + } + while (join[iphase] < polyArcs[iphase].arcs[i].join) { + int arcIndex0, arcIndex1, end0, end1; + int phase0, phase1; + miArcDataPtr arcData0, arcData1; + miArcJoinPtr joinp; + + joinp = &polyArcs[iphase].joins[join[iphase]]; + arcIndex0 = joinp->arcIndex0; + end0 = joinp->end0; + arcIndex1 = joinp->arcIndex1; + end1 = joinp->end1; + phase0 = joinp->phase0; + phase1 = joinp->phase1; + arcData0 = &polyArcs[phase0].arcs[arcIndex0]; + arcData1 = &polyArcs[phase1].arcs[arcIndex1]; + miArcJoin (pDrawTo, pGCTo, + &arcData0->bounds[end0], + &arcData1->bounds[end1], + arcData0->arc.x, arcData0->arc.y, + (double) arcData0->arc.width / 2.0, + (double) arcData0->arc.height / 2.0, + arcData1->arc.x, arcData1->arc.y, + (double) arcData1->arc.width / 2.0, + (double) arcData1->arc.height / 2.0); + ++join[iphase]; + } + if (fTricky) { + if (pGC->serialNumber != pDraw->serialNumber) + ValidateGC (pDraw, pGC); + (*pGC->ops->PushPixels) (pGC, (PixmapPtr)pDrawTo, + pDraw, pixmapWidth, pixmapHeight, xOrg, yOrg); + miClearDrawable ((DrawablePtr) pDrawTo, pGCTo); + } + } + } + } + miFreeArcs(polyArcs, pGC); + + if(fTricky) + { + (*pGCTo->pScreen->DestroyPixmap)((PixmapPtr)pDrawTo); + FreeScratchGC(pGCTo); + } + } +} + +static double +angleBetween (center, point1, point2) + SppPointRec center, point1, point2; +{ + double a1, a2, a; + + /* + * reflect from X coordinates back to ellipse + * coordinates -- y increasing upwards + */ + a1 = miDatan2 (- (point1.y - center.y), point1.x - center.x); + a2 = miDatan2 (- (point2.y - center.y), point2.x - center.x); + a = a2 - a1; + if (a <= -180.0) + a += 360.0; + else if (a > 180.0) + a -= 360.0; + return a; +} + +static void +translateBounds (b, x, y, fx, fy) +miArcFacePtr b; +int x, y; +double fx, fy; +{ + fx += x; + fy += y; + b->clock.x -= fx; + b->clock.y -= fy; + b->center.x -= fx; + b->center.y -= fy; + b->counterClock.x -= fx; + b->counterClock.y -= fy; +} + +static void +miArcJoin (pDraw, pGC, pLeft, pRight, + xOrgLeft, yOrgLeft, xFtransLeft, yFtransLeft, + xOrgRight, yOrgRight, xFtransRight, yFtransRight) + DrawablePtr pDraw; + GCPtr pGC; + miArcFacePtr pRight, pLeft; + int xOrgRight, yOrgRight; + double xFtransRight, yFtransRight; + int xOrgLeft, yOrgLeft; + double xFtransLeft, yFtransLeft; +{ + SppPointRec center, corner, otherCorner; + SppPointRec poly[5], e; + SppPointPtr pArcPts; + int cpt; + SppArcRec arc; + miArcFaceRec Right, Left; + int polyLen; + int xOrg, yOrg; + double xFtrans, yFtrans; + double a; + double ae, ac2, ec2, bc2, de; + double width; + + xOrg = (xOrgRight + xOrgLeft) / 2; + yOrg = (yOrgRight + yOrgLeft) / 2; + xFtrans = (xFtransLeft + xFtransRight) / 2; + yFtrans = (yFtransLeft + yFtransRight) / 2; + Right = *pRight; + translateBounds (&Right, xOrg - xOrgRight, yOrg - yOrgRight, + xFtrans - xFtransRight, yFtrans - yFtransRight); + Left = *pLeft; + translateBounds (&Left, xOrg - xOrgLeft, yOrg - yOrgLeft, + xFtrans - xFtransLeft, yFtrans - yFtransLeft); + pRight = &Right; + pLeft = &Left; + + if (pRight->clock.x == pLeft->counterClock.x && + pRight->clock.y == pLeft->counterClock.y) + return; + center = pRight->center; + if (0 <= (a = angleBetween (center, pRight->clock, pLeft->counterClock)) + && a <= 180.0) + { + corner = pRight->clock; + otherCorner = pLeft->counterClock; + } else { + a = angleBetween (center, pLeft->clock, pRight->counterClock); + corner = pLeft->clock; + otherCorner = pRight->counterClock; + } + switch (pGC->joinStyle) { + case JoinRound: + width = (pGC->lineWidth ? (double)pGC->lineWidth : (double)1); + + arc.x = center.x - width/2; + arc.y = center.y - width/2; + arc.width = width; + arc.height = width; + arc.angle1 = -miDatan2 (corner.y - center.y, corner.x - center.x); + arc.angle2 = a; + pArcPts = (SppPointPtr) xalloc (3 * sizeof (SppPointRec)); + if (!pArcPts) + return; + pArcPts[0].x = otherCorner.x; + pArcPts[0].y = otherCorner.y; + pArcPts[1].x = center.x; + pArcPts[1].y = center.y; + pArcPts[2].x = corner.x; + pArcPts[2].y = corner.y; + if( (cpt = miGetArcPts(&arc, 3, &pArcPts)) ) + { + /* by drawing with miFillSppPoly and setting the endpoints of the arc + * to be the corners, we assure that the cap will meet up with the + * rest of the line */ + miFillSppPoly(pDraw, pGC, cpt, pArcPts, xOrg, yOrg, xFtrans, yFtrans); + } + xfree(pArcPts); + return; + case JoinMiter: + /* + * don't miter arcs with less than 11 degrees between them + */ + if (a < 169.0) { + poly[0] = corner; + poly[1] = center; + poly[2] = otherCorner; + bc2 = (corner.x - otherCorner.x) * (corner.x - otherCorner.x) + + (corner.y - otherCorner.y) * (corner.y - otherCorner.y); + ec2 = bc2 / 4; + ac2 = (corner.x - center.x) * (corner.x - center.x) + + (corner.y - center.y) * (corner.y - center.y); + ae = sqrt (ac2 - ec2); + de = ec2 / ae; + e.x = (corner.x + otherCorner.x) / 2; + e.y = (corner.y + otherCorner.y) / 2; + poly[3].x = e.x + de * (e.x - center.x) / ae; + poly[3].y = e.y + de * (e.y - center.y) / ae; + poly[4] = corner; + polyLen = 5; + break; + } + case JoinBevel: + poly[0] = corner; + poly[1] = center; + poly[2] = otherCorner; + poly[3] = corner; + polyLen = 4; + break; + } + miFillSppPoly (pDraw, pGC, polyLen, poly, xOrg, yOrg, xFtrans, yFtrans); +} + +/*ARGSUSED*/ +static void +miArcCap (pDraw, pGC, pFace, end, xOrg, yOrg, xFtrans, yFtrans) + DrawablePtr pDraw; + GCPtr pGC; + miArcFacePtr pFace; + int end; + int xOrg, yOrg; + double xFtrans, yFtrans; +{ + SppPointRec corner, otherCorner, center, endPoint, poly[5]; + + corner = pFace->clock; + otherCorner = pFace->counterClock; + center = pFace->center; + switch (pGC->capStyle) { + case CapProjecting: + poly[0].x = otherCorner.x; + poly[0].y = otherCorner.y; + poly[1].x = corner.x; + poly[1].y = corner.y; + poly[2].x = corner.x - + (center.y - corner.y); + poly[2].y = corner.y + + (center.x - corner.x); + poly[3].x = otherCorner.x - + (otherCorner.y - center.y); + poly[3].y = otherCorner.y + + (otherCorner.x - center.x); + poly[4].x = otherCorner.x; + poly[4].y = otherCorner.y; + miFillSppPoly (pDraw, pGC, 5, poly, xOrg, yOrg, xFtrans, yFtrans); + break; + case CapRound: + /* + * miRoundCap just needs these to be unequal. + */ + endPoint = center; + endPoint.x = endPoint.x + 100; + miRoundCap (pDraw, pGC, center, endPoint, corner, otherCorner, 0, + -xOrg, -yOrg, xFtrans, yFtrans); + break; + } +} + +/* MIROUNDCAP -- a private helper function + * Put Rounded cap on end. pCenter is the center of this end of the line + * pEnd is the center of the other end of the line. pCorner is one of the + * two corners at this end of the line. + * NOTE: pOtherCorner must be counter-clockwise from pCorner. + */ +/*ARGSUSED*/ +static void +miRoundCap(pDraw, pGC, pCenter, pEnd, pCorner, pOtherCorner, fLineEnd, + xOrg, yOrg, xFtrans, yFtrans) + DrawablePtr pDraw; + GCPtr pGC; + SppPointRec pCenter, pEnd; + SppPointRec pCorner, pOtherCorner; + int fLineEnd, xOrg, yOrg; + double xFtrans, yFtrans; +{ + int cpt; + double width; + double miDatan2 (); + SppArcRec arc; + SppPointPtr pArcPts; + + width = (pGC->lineWidth ? (double)pGC->lineWidth : (double)1); + + arc.x = pCenter.x - width/2; + arc.y = pCenter.y - width/2; + arc.width = width; + arc.height = width; + arc.angle1 = -miDatan2 (pCorner.y - pCenter.y, pCorner.x - pCenter.x); + if(PTISEQUAL(pCenter, pEnd)) + arc.angle2 = - 180.0; + else { + arc.angle2 = -miDatan2 (pOtherCorner.y - pCenter.y, pOtherCorner.x - pCenter.x) - arc.angle1; + if (arc.angle2 < 0) + arc.angle2 += 360.0; + } + pArcPts = (SppPointPtr) NULL; + if( (cpt = miGetArcPts(&arc, 0, &pArcPts)) ) + { + /* by drawing with miFillSppPoly and setting the endpoints of the arc + * to be the corners, we assure that the cap will meet up with the + * rest of the line */ + miFillSppPoly(pDraw, pGC, cpt, pArcPts, -xOrg, -yOrg, xFtrans, yFtrans); + } + xfree(pArcPts); +} + +/* + * To avoid inaccuracy at the cardinal points, use trig functions + * which are exact for those angles + */ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 +#endif + +# define Dsin(d) ((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0))) +# define Dcos(d) ((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0))) +# define mod(a,b) ((a) >= 0 ? (a) % (b) : (b) - (-a) % (b)) + +static double +miDcos (a) +double a; +{ + int i; + + if (floor (a/90) == a/90) { + i = (int) (a/90.0); + switch (mod (i, 4)) { + case 0: return 1; + case 1: return 0; + case 2: return -1; + case 3: return 0; + } + } + return cos (a * M_PI / 180.0); +} + +static double +miDsin (a) +double a; +{ + int i; + + if (floor (a/90) == a/90) { + i = (int) (a/90.0); + switch (mod (i, 4)) { + case 0: return 0; + case 1: return 1; + case 2: return 0; + case 3: return -1; + } + } + return sin (a * M_PI / 180.0); +} + +static double +miDasin (v) +double v; +{ + if (v == 0) + return 0.0; + if (v == 1.0) + return 90.0; + if (v == -1.0) + return -90.0; + return asin(v) * (180.0 / M_PI); +} + +static double +miDatan2 (dy, dx) +double dy, dx; +{ + if (dy == 0) { + if (dx >= 0) + return 0.0; + return 180.0; + } else if (dx == 0) { + if (dy > 0) + return 90.0; + return -90.0; + } else if (Fabs (dy) == Fabs (dx)) { + if (dy > 0) { + if (dx > 0) + return 45.0; + return 135.0; + } else { + if (dx > 0) + return 315.0; + return 225.0; + } + } else { + return atan2 (dy, dx) * (180.0 / M_PI); + } +} + +/* MIGETARCPTS -- Converts an arc into a set of line segments -- a helper + * routine for filled arc and line (round cap) code. + * Returns the number of points in the arc. Note that it takes a pointer + * to a pointer to where it should put the points and an index (cpt). + * This procedure allocates the space necessary to fit the arc points. + * Sometimes it's convenient for those points to be at the end of an existing + * array. (For example, if we want to leave a spare point to make sectors + * instead of segments.) So we pass in the Xalloc()ed chunk that contains the + * array and an index saying where we should start stashing the points. + * If there isn't an array already, we just pass in a null pointer and + * count on Xrealloc() to handle the null pointer correctly. + */ +static int +miGetArcPts(parc, cpt, ppPts) + SppArcPtr parc; /* points to an arc */ + int cpt; /* number of points already in arc list */ + SppPointPtr *ppPts; /* pointer to pointer to arc-list -- modified */ +{ + double st, /* Start Theta, start angle */ + et, /* End Theta, offset from start theta */ + dt, /* Delta Theta, angle to sweep ellipse */ + cdt, /* Cos Delta Theta, actually 2 cos(dt) */ + x0, y0, /* the recurrence formula needs two points to start */ + x1, y1, + x2, y2, /* this will be the new point generated */ + xc, yc; /* the center point */ + int count, i; + SppPointPtr poly; + DDXPointRec last; /* last point on integer boundaries */ + + /* The spec says that positive angles indicate counterclockwise motion. + * Given our coordinate system (with 0,0 in the upper left corner), + * the screen appears flipped in Y. The easiest fix is to negate the + * angles given */ + + st = - parc->angle1; + + et = - parc->angle2; + + /* Try to get a delta theta that is within 1/2 pixel. Then adjust it + * so that it divides evenly into the total. + * I'm just using cdt 'cause I'm lazy. + */ + cdt = parc->width; + if (parc->height > cdt) + cdt = parc->height; + cdt /= 2.0; + if(cdt <= 0) + return 0; + if (cdt < 1.0) + cdt = 1.0; + dt = miDasin ( 1.0 / cdt ); /* minimum step necessary */ + count = et/dt; + count = abs(count) + 1; + dt = et/count; + count++; + + cdt = 2 * miDcos(dt); + if (!(poly = (SppPointPtr) xrealloc((pointer)*ppPts, + (cpt + count) * sizeof(SppPointRec)))) + return(0); + *ppPts = poly; + + xc = parc->width/2.0; /* store half width and half height */ + yc = parc->height/2.0; + + x0 = xc * miDcos(st); + y0 = yc * miDsin(st); + x1 = xc * miDcos(st + dt); + y1 = yc * miDsin(st + dt); + xc += parc->x; /* by adding initial point, these become */ + yc += parc->y; /* the center point */ + + poly[cpt].x = (xc + x0); + poly[cpt].y = (yc + y0); + last.x = ROUNDTOINT( poly[cpt + 1].x = (xc + x1) ); + last.y = ROUNDTOINT( poly[cpt + 1].y = (yc + y1) ); + + for(i = 2; i < count; i++) + { + x2 = cdt * x1 - x0; + y2 = cdt * y1 - y0; + + poly[cpt + i].x = (xc + x2); + poly[cpt + i].y = (yc + y2); + + x0 = x1; y0 = y1; + x1 = x2; y1 = y2; + } + /* adjust the last point */ + if (abs(parc->angle2) >= 360.0) + poly[cpt +i -1] = poly[0]; + else { + poly[cpt +i -1].x = (miDcos(st + et) * parc->width/2.0 + xc); + poly[cpt +i -1].y = (miDsin(st + et) * parc->height/2.0 + yc); + } + + return(count); +} + +struct arcData { + double x0, y0, x1, y1; + int selfJoin; +}; + +# define ADD_REALLOC_STEP 20 + +static void +addCap (capsp, ncapsp, sizep, end, arcIndex) + miArcCapPtr *capsp; + int *ncapsp, *sizep; + int end, arcIndex; +{ + int newsize; + miArcCapPtr cap; + + if (*ncapsp == *sizep) + { + newsize = *sizep + ADD_REALLOC_STEP; + cap = (miArcCapPtr) xrealloc (*capsp, + newsize * sizeof (**capsp)); + if (!cap) + return; + *sizep = newsize; + *capsp = cap; + } + cap = &(*capsp)[*ncapsp]; + cap->end = end; + cap->arcIndex = arcIndex; + ++*ncapsp; +} + +static void +addJoin (joinsp, njoinsp, sizep, end0, index0, phase0, end1, index1, phase1) + miArcJoinPtr *joinsp; + int *njoinsp, *sizep; + int end0, index0, phase0, end1, index1, phase1; +{ + int newsize; + miArcJoinPtr join; + + if (*njoinsp == *sizep) + { + newsize = *sizep + ADD_REALLOC_STEP; + join = (miArcJoinPtr) xrealloc (*joinsp, + newsize * sizeof (**joinsp)); + if (!join) + return; + *sizep = newsize; + *joinsp = join; + } + join = &(*joinsp)[*njoinsp]; + join->end0 = end0; + join->arcIndex0 = index0; + join->phase0 = phase0; + join->end1 = end1; + join->arcIndex1 = index1; + join->phase1 = phase1; + ++*njoinsp; +} + +static miArcDataPtr +addArc (arcsp, narcsp, sizep, xarc) + miArcDataPtr *arcsp; + int *narcsp, *sizep; + xArc *xarc; +{ + int newsize; + miArcDataPtr arc; + + if (*narcsp == *sizep) + { + newsize = *sizep + ADD_REALLOC_STEP; + arc = (miArcDataPtr) xrealloc (*arcsp, + newsize * sizeof (**arcsp)); + if (!arc) + return (miArcDataPtr)NULL; + *sizep = newsize; + *arcsp = arc; + } + arc = &(*arcsp)[*narcsp]; + arc->arc = *xarc; + ++*narcsp; + return arc; +} + +static void +miFreeArcs(arcs, pGC) + miPolyArcPtr arcs; + GCPtr pGC; +{ + int iphase; + + for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0); + iphase >= 0; + iphase--) + { + if (arcs[iphase].narcs > 0) + xfree(arcs[iphase].arcs); + if (arcs[iphase].njoins > 0) + xfree(arcs[iphase].joins); + if (arcs[iphase].ncaps > 0) + xfree(arcs[iphase].caps); + } + xfree(arcs); +} + +/* + * map angles to radial distance. This only deals with the first quadrant + */ + +/* + * a polygonal approximation to the arc for computing arc lengths + */ + +# define DASH_MAP_SIZE 91 + +# define dashIndexToAngle(di) ((((double) (di)) * 90.0) / ((double) DASH_MAP_SIZE - 1)) +# define xAngleToDashIndex(xa) ((((long) (xa)) * (DASH_MAP_SIZE - 1)) / (90 * 64)) +# define dashIndexToXAngle(di) ((((long) (di)) * (90 * 64)) / (DASH_MAP_SIZE - 1)) +# define dashXAngleStep (((double) (90 * 64)) / ((double) (DASH_MAP_SIZE - 1))) + +typedef struct { + double map[DASH_MAP_SIZE]; +} dashMap; + +static void +computeDashMap (arcp, map) + xArc *arcp; + dashMap *map; +{ + int di; + double a, x, y, prevx, prevy, dist; + + for (di = 0; di < DASH_MAP_SIZE; di++) { + a = dashIndexToAngle (di); + x = ((double) arcp->width / 2.0) * miDcos (a); + y = ((double) arcp->height / 2.0) * miDsin (a); + if (di == 0) { + map->map[di] = 0.0; + } else { + dist = hypot (x - prevx, y - prevy); + map->map[di] = map->map[di - 1] + dist; + } + prevx = x; + prevy = y; + } +} + +typedef enum {HORIZONTAL, VERTICAL, OTHER} arcTypes; + +/* this routine is a bit gory */ + +static miPolyArcPtr +miComputeArcs (parcs, narcs, pGC) + xArc *parcs; + int narcs; + GCPtr pGC; +{ + int isDashed, isDoubleDash; + int dashOffset; + miPolyArcPtr arcs; + int start, i, j, k, nexti, nextk; + int joinSize[2]; + int capSize[2]; + int arcSize[2]; + int angle2; + double a0, a1; + struct arcData *data; + miArcDataPtr arc; + xArc xarc; + int iphase, prevphase, joinphase; + int arcsJoin; + int selfJoin; + + int iDash, dashRemaining; + int iDashStart, dashRemainingStart, iphaseStart; + int startAngle, spanAngle, endAngle, backwards; + int prevDashAngle, dashAngle; + dashMap map; + + isDashed = !(pGC->lineStyle == LineSolid); + isDoubleDash = (pGC->lineStyle == LineDoubleDash); + dashOffset = pGC->dashOffset; + + data = (struct arcData *) ALLOCATE_LOCAL (narcs * sizeof (struct arcData)); + if (!data) + return (miPolyArcPtr)NULL; + arcs = (miPolyArcPtr) xalloc (sizeof (*arcs) * (isDoubleDash ? 2 : 1)); + if (!arcs) + { + DEALLOCATE_LOCAL(data); + return (miPolyArcPtr)NULL; + } + for (i = 0; i < narcs; i++) { + a0 = todeg (parcs[i].angle1); + angle2 = parcs[i].angle2; + if (angle2 > FULLCIRCLE) + angle2 = FULLCIRCLE; + else if (angle2 < -FULLCIRCLE) + angle2 = -FULLCIRCLE; + data[i].selfJoin = angle2 == FULLCIRCLE || angle2 == -FULLCIRCLE; + a1 = todeg (parcs[i].angle1 + angle2); + data[i].x0 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a0)); + data[i].y0 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a0)); + data[i].x1 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a1)); + data[i].y1 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a1)); + } + + for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) { + arcs[iphase].njoins = 0; + arcs[iphase].joins = 0; + joinSize[iphase] = 0; + + arcs[iphase].ncaps = 0; + arcs[iphase].caps = 0; + capSize[iphase] = 0; + + arcs[iphase].narcs = 0; + arcs[iphase].arcs = 0; + arcSize[iphase] = 0; + } + + iphase = 0; + if (isDashed) { + iDash = 0; + dashRemaining = pGC->dash[0]; + while (dashOffset > 0) { + if (dashOffset >= dashRemaining) { + dashOffset -= dashRemaining; + iphase = iphase ? 0 : 1; + iDash++; + if (iDash == pGC->numInDashList) + iDash = 0; + dashRemaining = pGC->dash[iDash]; + } else { + dashRemaining -= dashOffset; + dashOffset = 0; + } + } + iDashStart = iDash; + dashRemainingStart = dashRemaining; + } + iphaseStart = iphase; + + for (i = narcs - 1; i >= 0; i--) { + j = i + 1; + if (j == narcs) + j = 0; + if (data[i].selfJoin || i == j || + (UNEQUAL (data[i].x1, data[j].x0) || + UNEQUAL (data[i].y1, data[j].y0))) + { + if (iphase == 0 || isDoubleDash) + addCap (&arcs[iphase].caps, &arcs[iphase].ncaps, + &capSize[iphase], RIGHT_END, 0); + break; + } + } + start = i + 1; + if (start == narcs) + start = 0; + i = start; + for (;;) { + j = i + 1; + if (j == narcs) + j = 0; + nexti = i+1; + if (nexti == narcs) + nexti = 0; + if (isDashed) { + /* + ** deal with dashed arcs. Use special rules for certain 0 area arcs. + ** Presumably, the other 0 area arcs still aren't done right. + */ + arcTypes arcType = OTHER; + CARD16 thisLength; + + if (parcs[i].height == 0 + && (parcs[i].angle1 % FULLCIRCLE) == 0x2d00 + && parcs[i].angle2 == 0x2d00) + arcType = HORIZONTAL; + else if (parcs[i].width == 0 + && (parcs[i].angle1 % FULLCIRCLE) == 0x1680 + && parcs[i].angle2 == 0x2d00) + arcType = VERTICAL; + if (arcType == OTHER) { + /* + * precompute an approximation map + */ + computeDashMap (&parcs[i], &map); + /* + * compute each individual dash segment using the path + * length function + */ + startAngle = parcs[i].angle1; + spanAngle = parcs[i].angle2; + if (spanAngle > FULLCIRCLE) + spanAngle = FULLCIRCLE; + else if (spanAngle < -FULLCIRCLE) + spanAngle = -FULLCIRCLE; + if (startAngle < 0) + startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE; + if (startAngle >= FULLCIRCLE) + startAngle = startAngle % FULLCIRCLE; + endAngle = startAngle + spanAngle; + backwards = spanAngle < 0; + } else { + xarc = parcs[i]; + if (arcType == VERTICAL) { + xarc.angle1 = 0x1680; + startAngle = parcs[i].y; + endAngle = startAngle + parcs[i].height; + } else { + xarc.angle1 = 0x2d00; + startAngle = parcs[i].x; + endAngle = startAngle + parcs[i].width; + } + } + dashAngle = startAngle; + selfJoin = data[i].selfJoin && + (iphase == 0 || isDoubleDash); + /* + * add dashed arcs to each bucket + */ + arc = 0; + while (dashAngle != endAngle) { + prevDashAngle = dashAngle; + if (arcType == OTHER) { + dashAngle = computeAngleFromPath (prevDashAngle, endAngle, + &map, &dashRemaining, backwards); + /* avoid troubles with huge arcs and small dashes */ + if (dashAngle == prevDashAngle) { + if (backwards) + dashAngle--; + else + dashAngle++; + } + } else { + thisLength = (dashAngle + dashRemaining <= endAngle) ? + dashRemaining : endAngle - dashAngle; + if (arcType == VERTICAL) { + xarc.y = dashAngle; + xarc.height = thisLength; + } else { + xarc.x = dashAngle; + xarc.width = thisLength; + } + dashAngle += thisLength; + dashRemaining -= thisLength; + } + if (iphase == 0 || isDoubleDash) { + if (arcType == OTHER) { + xarc = parcs[i]; + spanAngle = prevDashAngle; + if (spanAngle < 0) + spanAngle = FULLCIRCLE - (-spanAngle) % FULLCIRCLE; + if (spanAngle >= FULLCIRCLE) + spanAngle = spanAngle % FULLCIRCLE; + xarc.angle1 = spanAngle; + spanAngle = dashAngle - prevDashAngle; + if (backwards) { + if (dashAngle > prevDashAngle) + spanAngle = - FULLCIRCLE + spanAngle; + } else { + if (dashAngle < prevDashAngle) + spanAngle = FULLCIRCLE + spanAngle; + } + if (spanAngle > FULLCIRCLE) + spanAngle = FULLCIRCLE; + if (spanAngle < -FULLCIRCLE) + spanAngle = -FULLCIRCLE; + xarc.angle2 = spanAngle; + } + arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs, + &arcSize[iphase], &xarc); + if (!arc) + goto arcfail; + /* + * cap each end of an on/off dash + */ + if (!isDoubleDash) { + if (prevDashAngle != startAngle) { + addCap (&arcs[iphase].caps, + &arcs[iphase].ncaps, + &capSize[iphase], RIGHT_END, + arc - arcs[iphase].arcs); + + } + if (dashAngle != endAngle) { + addCap (&arcs[iphase].caps, + &arcs[iphase].ncaps, + &capSize[iphase], LEFT_END, + arc - arcs[iphase].arcs); + } + } + arc->cap = arcs[iphase].ncaps; + arc->join = arcs[iphase].njoins; + arc->render = 0; + arc->selfJoin = 0; + if (dashAngle == endAngle) + arc->selfJoin = selfJoin; + } + prevphase = iphase; + if (dashRemaining <= 0) { + ++iDash; + if (iDash == pGC->numInDashList) + iDash = 0; + iphase = iphase ? 0:1; + dashRemaining = pGC->dash[iDash]; + } + } + /* + * make sure a place exists for the position data when + * drawing a zero-length arc + */ + if (startAngle == endAngle) { + prevphase = iphase; + if (!isDoubleDash && iphase == 1) + prevphase = 0; + arc = addArc (&arcs[prevphase].arcs, &arcs[prevphase].narcs, + &arcSize[prevphase], &parcs[i]); + if (!arc) + goto arcfail; + arc->join = arcs[prevphase].njoins; + arc->cap = arcs[prevphase].ncaps; + arc->selfJoin = data[i].selfJoin; + } + } else { + arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs, + &arcSize[iphase], &parcs[i]); + if (!arc) + goto arcfail; + arc->join = arcs[iphase].njoins; + arc->cap = arcs[iphase].ncaps; + arc->selfJoin = data[i].selfJoin; + prevphase = iphase; + } + if (prevphase == 0 || isDoubleDash) + k = arcs[prevphase].narcs - 1; + if (iphase == 0 || isDoubleDash) + nextk = arcs[iphase].narcs; + if (nexti == start) { + nextk = 0; + if (isDashed) { + iDash = iDashStart; + iphase = iphaseStart; + dashRemaining = dashRemainingStart; + } + } + arcsJoin = narcs > 1 && i != j && + ISEQUAL (data[i].x1, data[j].x0) && + ISEQUAL (data[i].y1, data[j].y0) && + !data[i].selfJoin && !data[j].selfJoin; + if (arc) + { + if (arcsJoin) + arc->render = 0; + else + arc->render = 1; + } + if (arcsJoin && + (prevphase == 0 || isDoubleDash) && + (iphase == 0 || isDoubleDash)) + { + joinphase = iphase; + if (isDoubleDash) { + if (nexti == start) + joinphase = iphaseStart; + /* + * if the join is right at the dash, + * draw the join in foreground + * This is because the foreground + * arcs are computed second, the results + * of which are needed to draw the join + */ + if (joinphase != prevphase) + joinphase = 0; + } + if (joinphase == 0 || isDoubleDash) { + addJoin (&arcs[joinphase].joins, + &arcs[joinphase].njoins, + &joinSize[joinphase], + LEFT_END, k, prevphase, + RIGHT_END, nextk, iphase); + arc->join = arcs[prevphase].njoins; + } + } else { + /* + * cap the left end of this arc + * unless it joins itself + */ + if ((prevphase == 0 || isDoubleDash) && + !arc->selfJoin) + { + addCap (&arcs[prevphase].caps, &arcs[prevphase].ncaps, + &capSize[prevphase], LEFT_END, k); + arc->cap = arcs[prevphase].ncaps; + } + if (isDashed && !arcsJoin) { + iDash = iDashStart; + iphase = iphaseStart; + dashRemaining = dashRemainingStart; + } + nextk = arcs[iphase].narcs; + if (nexti == start) { + nextk = 0; + iDash = iDashStart; + iphase = iphaseStart; + dashRemaining = dashRemainingStart; + } + /* + * cap the right end of the next arc. If the + * next arc is actually the first arc, only + * cap it if it joins with this arc. This + * case will occur when the final dash segment + * of an on/off dash is off. Of course, this + * cap will be drawn at a strange time, but that + * hardly matters... + */ + if ((iphase == 0 || isDoubleDash) && + (nexti != start || (arcsJoin && isDashed))) + addCap (&arcs[iphase].caps, &arcs[iphase].ncaps, + &capSize[iphase], RIGHT_END, nextk); + } + i = nexti; + if (i == start) + break; + } + /* + * make sure the last section is rendered + */ + for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) + if (arcs[iphase].narcs > 0) { + arcs[iphase].arcs[arcs[iphase].narcs-1].render = 1; + arcs[iphase].arcs[arcs[iphase].narcs-1].join = + arcs[iphase].njoins; + arcs[iphase].arcs[arcs[iphase].narcs-1].cap = + arcs[iphase].ncaps; + } + DEALLOCATE_LOCAL(data); + return arcs; +arcfail: + miFreeArcs(arcs, pGC); + DEALLOCATE_LOCAL(data); + return (miPolyArcPtr)NULL; +} + +static double +angleToLength (angle, map) + int angle; + dashMap *map; +{ + double len, excesslen, sidelen = map->map[DASH_MAP_SIZE - 1], totallen; + int di; + int excess; + Bool oddSide = FALSE; + + totallen = 0; + if (angle >= 0) { + while (angle >= 90 * 64) { + angle -= 90 * 64; + totallen += sidelen; + oddSide = !oddSide; + } + } else { + while (angle < 0) { + angle += 90 * 64; + totallen -= sidelen; + oddSide = !oddSide; + } + } + if (oddSide) + angle = 90 * 64 - angle; + + di = xAngleToDashIndex (angle); + excess = angle - dashIndexToXAngle (di); + + len = map->map[di]; + /* + * linearly interpolate between this point and the next + */ + if (excess > 0) { + excesslen = (map->map[di + 1] - map->map[di]) * + ((double) excess) / dashXAngleStep; + len += excesslen; + } + if (oddSide) + totallen += (sidelen - len); + else + totallen += len; + return totallen; +} + +/* + * len is along the arc, but may be more than one rotation + */ + +static int +lengthToAngle (len, map) + double len; + dashMap *map; +{ + double sidelen = map->map[DASH_MAP_SIZE - 1]; + int angle, angleexcess; + Bool oddSide = FALSE; + int a0, a1, a; + + angle = 0; + /* + * step around the ellipse, subtracting sidelens and + * adding 90 degrees. oddSide will tell if the + * map should be interpolated in reverse + */ + if (len >= 0) { + if (sidelen == 0) + return 2 * FULLCIRCLE; /* infinity */ + while (len >= sidelen) { + angle += 90 * 64; + len -= sidelen; + oddSide = !oddSide; + } + } else { + if (sidelen == 0) + return -2 * FULLCIRCLE; /* infinity */ + while (len < 0) { + angle -= 90 * 64; + len += sidelen; + oddSide = !oddSide; + } + } + if (oddSide) + len = sidelen - len; + a0 = 0; + a1 = DASH_MAP_SIZE - 1; + /* + * binary search for the closest pre-computed length + */ + while (a1 - a0 > 1) { + a = (a0 + a1) / 2; + if (len > map->map[a]) + a0 = a; + else + a1 = a; + } + angleexcess = dashIndexToXAngle (a0); + /* + * linearly interpolate to the next point + */ + angleexcess += (len - map->map[a0]) / + (map->map[a0+1] - map->map[a0]) * dashXAngleStep; + if (oddSide) + angle += (90 * 64) - angleexcess; + else + angle += angleexcess; + return angle; +} + +/* + * compute the angle of an ellipse which cooresponds to + * the given path length. Note that the correct solution + * to this problem is an eliptic integral, we'll punt and + * approximate (it's only for dashes anyway). This + * approximation uses a polygon. + * + * The remaining portion of len is stored in *lenp - + * this will be negative if the arc extends beyond + * len and positive if len extends beyond the arc. + */ + +static int +computeAngleFromPath (startAngle, endAngle, map, lenp, backwards) + int startAngle, endAngle; /* normalized absolute angles in *64 degrees */ + dashMap *map; + int *lenp; + int backwards; +{ + int a0, a1, a; + double len0; + int len; + + a0 = startAngle; + a1 = endAngle; + len = *lenp; + if (backwards) { + /* + * flip the problem around to always be + * forwards + */ + a0 = FULLCIRCLE - a0; + a1 = FULLCIRCLE - a1; + } + if (a1 < a0) + a1 += FULLCIRCLE; + len0 = angleToLength (a0, map); + a = lengthToAngle (len0 + len, map); + if (a > a1) { + a = a1; + len -= angleToLength (a1, map) - len0; + } else + len = 0; + if (backwards) + a = FULLCIRCLE - a; + *lenp = len; + return a; +} + +/* + * scan convert wide arcs. + */ + +/* + * draw zero width/height arcs + */ + +static void +drawZeroArc (pDraw, pGC, tarc, lw, left, right) + DrawablePtr pDraw; + GCPtr pGC; + xArc *tarc; + int lw; + miArcFacePtr right, left; +{ + double x0, y0, x1, y1, w, h, x, y; + double xmax, ymax, xmin, ymin; + int a0, a1; + double a, startAngle, endAngle; + double l, lx, ly; + + l = lw / 2.0; + a0 = tarc->angle1; + a1 = tarc->angle2; + if (a1 > FULLCIRCLE) + a1 = FULLCIRCLE; + else if (a1 < -FULLCIRCLE) + a1 = -FULLCIRCLE; + w = (double)tarc->width / 2.0; + h = (double)tarc->height / 2.0; + /* + * play in X coordinates right away + */ + startAngle = - ((double) a0 / 64.0); + endAngle = - ((double) (a0 + a1) / 64.0); + + xmax = -w; + xmin = w; + ymax = -h; + ymin = h; + a = startAngle; + for (;;) + { + x = w * miDcos(a); + y = h * miDsin(a); + if (a == startAngle) + { + x0 = x; + y0 = y; + } + if (a == endAngle) + { + x1 = x; + y1 = y; + } + if (x > xmax) + xmax = x; + if (x < xmin) + xmin = x; + if (y > ymax) + ymax = y; + if (y < ymin) + ymin = y; + if (a == endAngle) + break; + if (a1 < 0) /* clockwise */ + { + if (floor (a / 90.0) == floor (endAngle / 90.0)) + a = endAngle; + else + a = 90 * (floor (a/90.0) + 1); + } + else + { + if (ceil (a / 90.0) == ceil (endAngle / 90.0)) + a = endAngle; + else + a = 90 * (ceil (a/90.0) - 1); + } + } + lx = ly = l; + if ((x1 - x0) + (y1 - y0) < 0) + lx = ly = -l; + if (h) + { + ly = 0.0; + lx = -lx; + } + else + lx = 0.0; + if (right) + { + right->center.x = x0; + right->center.y = y0; + right->clock.x = x0 - lx; + right->clock.y = y0 - ly; + right->counterClock.x = x0 + lx; + right->counterClock.y = y0 + ly; + } + if (left) + { + left->center.x = x1; + left->center.y = y1; + left->clock.x = x1 + lx; + left->clock.y = y1 + ly; + left->counterClock.x = x1 - lx; + left->counterClock.y = y1 - ly; + } + + x0 = xmin; + x1 = xmax; + y0 = ymin; + y1 = ymax; + if (ymin != y1) { + xmin = -l; + xmax = l; + } else { + ymin = -l; + ymax = l; + } + if (xmax != xmin && ymax != ymin) { + int minx, maxx, miny, maxy; + xRectangle rect; + + minx = ICEIL (xmin + w) + tarc->x; + maxx = ICEIL (xmax + w) + tarc->x; + miny = ICEIL (ymin + h) + tarc->y; + maxy = ICEIL (ymax + h) + tarc->y; + rect.x = minx; + rect.y = miny; + rect.width = maxx - minx; + rect.height = maxy - miny; + (*pGC->ops->PolyFillRect) (pDraw, pGC, 1, &rect); + } +} + +/* + * this computes the ellipse y value associated with the + * bottom of the tail. + */ + +static void +tailEllipseY (def, acc) + struct arc_def *def; + struct accelerators *acc; +{ + double t; + + acc->tail_y = 0.0; + if (def->w == def->h) + return; + t = def->l * def->w; + if (def->w > def->h) { + if (t < acc->h2) + return; + } else { + if (t > acc->h2) + return; + } + t = 2.0 * def->h * t; + t = (CUBED_ROOT_4 * acc->h2 - cbrt(t * t)) / acc->h2mw2; + if (t > 0.0) + acc->tail_y = def->h / CUBED_ROOT_2 * sqrt(t); +} + +/* + * inverse functions -- compute edge coordinates + * from the ellipse + */ + +static double +outerXfromXY (x, y, def, acc) + double x, y; + struct arc_def *def; + struct accelerators *acc; +{ + return x + (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4); +} + +static double +outerYfromXY (x, y, def, acc) + double x, y; + struct arc_def *def; + struct accelerators *acc; +{ + return y + (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4); +} + +static double +innerXfromXY (x, y, def, acc) + double x, y; + struct arc_def *def; + struct accelerators *acc; +{ + return x - (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4); +} + +static double +innerYfromXY (x, y, def, acc) + double x, y; + struct arc_def *def; + struct accelerators *acc; +{ + return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4); +} + +static double +innerYfromY (y, def, acc) + double y; + struct arc_def *def; + struct accelerators *acc; +{ + double x; + + x = (def->w / def->h) * sqrt (acc->h2 - y*y); + + return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4); +} + +static void +computeLine (x1, y1, x2, y2, line) + double x1, y1, x2, y2; + struct line *line; +{ + if (y1 == y2) + line->valid = 0; + else { + line->m = (x1 - x2) / (y1 - y2); + line->b = x1 - y1 * line->m; + line->valid = 1; + } +} + +/* + * compute various accelerators for an ellipse. These + * are simply values that are used repeatedly in + * the computations + */ + +static void +computeAcc (tarc, lw, def, acc) + xArc *tarc; + int lw; + struct arc_def *def; + struct accelerators *acc; +{ + def->w = ((double) tarc->width) / 2.0; + def->h = ((double) tarc->height) / 2.0; + def->l = ((double) lw) / 2.0; + acc->h2 = def->h * def->h; + acc->w2 = def->w * def->w; + acc->h4 = acc->h2 * acc->h2; + acc->w4 = acc->w2 * acc->w2; + acc->h2l = acc->h2 * def->l; + acc->w2l = acc->w2 * def->l; + acc->h2mw2 = acc->h2 - acc->w2; + acc->fromIntX = (tarc->width & 1) ? 0.5 : 0.0; + acc->fromIntY = (tarc->height & 1) ? 0.5 : 0.0; + acc->xorg = tarc->x + (tarc->width >> 1); + acc->yorgu = tarc->y + (tarc->height >> 1); + acc->yorgl = acc->yorgu + (tarc->height & 1); + tailEllipseY (def, acc); +} + +/* + * compute y value bounds of various portions of the arc, + * the outer edge, the ellipse and the inner edge. + */ + +static void +computeBound (def, bound, acc, right, left) + struct arc_def *def; + struct arc_bound *bound; + struct accelerators *acc; + miArcFacePtr right, left; +{ + double t; + double innerTaily; + double tail_y; + struct bound innerx, outerx; + struct bound ellipsex; + + bound->ellipse.min = Dsin (def->a0) * def->h; + bound->ellipse.max = Dsin (def->a1) * def->h; + if (def->a0 == 45 && def->w == def->h) + ellipsex.min = bound->ellipse.min; + else + ellipsex.min = Dcos (def->a0) * def->w; + if (def->a1 == 45 && def->w == def->h) + ellipsex.max = bound->ellipse.max; + else + ellipsex.max = Dcos (def->a1) * def->w; + bound->outer.min = outerYfromXY (ellipsex.min, bound->ellipse.min, def, acc); + bound->outer.max = outerYfromXY (ellipsex.max, bound->ellipse.max, def, acc); + bound->inner.min = innerYfromXY (ellipsex.min, bound->ellipse.min, def, acc); + bound->inner.max = innerYfromXY (ellipsex.max, bound->ellipse.max, def, acc); + + outerx.min = outerXfromXY (ellipsex.min, bound->ellipse.min, def, acc); + outerx.max = outerXfromXY (ellipsex.max, bound->ellipse.max, def, acc); + innerx.min = innerXfromXY (ellipsex.min, bound->ellipse.min, def, acc); + innerx.max = innerXfromXY (ellipsex.max, bound->ellipse.max, def, acc); + + /* + * save the line end points for the + * cap code to use. Careful here, these are + * in cartesean coordinates (y increasing upwards) + * while the cap code uses inverted coordinates + * (y increasing downwards) + */ + + if (right) { + right->counterClock.y = bound->outer.min; + right->counterClock.x = outerx.min; + right->center.y = bound->ellipse.min; + right->center.x = ellipsex.min; + right->clock.y = bound->inner.min; + right->clock.x = innerx.min; + } + + if (left) { + left->clock.y = bound->outer.max; + left->clock.x = outerx.max; + left->center.y = bound->ellipse.max; + left->center.x = ellipsex.max; + left->counterClock.y = bound->inner.max; + left->counterClock.x = innerx.max; + } + + bound->left.min = bound->inner.max; + bound->left.max = bound->outer.max; + bound->right.min = bound->inner.min; + bound->right.max = bound->outer.min; + + computeLine (innerx.min, bound->inner.min, outerx.min, bound->outer.min, + &acc->right); + computeLine (innerx.max, bound->inner.max, outerx.max, bound->outer.max, + &acc->left); + + if (bound->inner.min > bound->inner.max) { + t = bound->inner.min; + bound->inner.min = bound->inner.max; + bound->inner.max = t; + } + tail_y = acc->tail_y; + if (tail_y > bound->ellipse.max) + tail_y = bound->ellipse.max; + else if (tail_y < bound->ellipse.min) + tail_y = bound->ellipse.min; + innerTaily = innerYfromY (tail_y, def, acc); + if (bound->inner.min > innerTaily) + bound->inner.min = innerTaily; + if (bound->inner.max < innerTaily) + bound->inner.max = innerTaily; + bound->inneri.min = ICEIL(bound->inner.min - acc->fromIntY); + bound->inneri.max = floor(bound->inner.max - acc->fromIntY); + bound->outeri.min = ICEIL(bound->outer.min - acc->fromIntY); + bound->outeri.max = floor(bound->outer.max - acc->fromIntY); +} + +/* + * this section computes the x value of the span at y + * intersected with the specified face of the ellipse. + * + * this is the min/max X value over the set of normal + * lines to the entire ellipse, the equation of the + * normal lines is: + * + * ellipse_x h^2 h^2 + * x = ------------ y + ellipse_x (1 - --- ) + * ellipse_y w^2 w^2 + * + * compute the derivative with-respect-to ellipse_y and solve + * for zero: + * + * (w^2 - h^2) ellipse_y^3 + h^4 y + * 0 = - ---------------------------------- + * h w ellipse_y^2 sqrt (h^2 - ellipse_y^2) + * + * ( h^4 y ) + * ellipse_y = ( ---------- ) ^ (1/3) + * ( (h^2 - w^2) ) + * + * The other two solutions to the equation are imaginary. + * + * This gives the position on the ellipse which generates + * the normal with the largest/smallest x intersection point. + * + * Now compute the second derivative to check whether + * the intersection is a minimum or maximum: + * + * h (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2)) + * - ------------------------------------------- + * w y0^3 (sqrt (h^2 - y^2)) ^ 3 + * + * as we only care about the sign, + * + * - (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2)) + * + * or (to use accelerators), + * + * y0^3 (h^2 - w^2) - h^2 y (3y0^2 - 2h^2) + * + */ + +/* + * computes the position on the ellipse whose normal line + * intersects the given scan line maximally + */ + +static double +hookEllipseY (scan_y, bound, acc, left) + double scan_y; + struct arc_bound *bound; + struct accelerators *acc; + int left; +{ + double ret; + + if (acc->h2mw2 == 0) { + if ( (scan_y > 0 && !left) || (scan_y < 0 && left) ) + return bound->ellipse.min; + return bound->ellipse.max; + } + ret = (acc->h4 * scan_y) / (acc->h2mw2); + if (ret >= 0) + return cbrt (ret); + else + return -cbrt (-ret); +} + +/* + * computes the X value of the intersection of the + * given scan line with the right side of the lower hook + */ + +static double +hookX (scan_y, def, bound, acc, left) + double scan_y; + struct arc_def *def; + struct arc_bound *bound; + struct accelerators *acc; + int left; +{ + double ellipse_y, x; + double maxMin; + + if (def->w != def->h) { + ellipse_y = hookEllipseY (scan_y, bound, acc, left); + if (boundedLe (ellipse_y, bound->ellipse)) { + /* + * compute the value of the second + * derivative + */ + maxMin = ellipse_y*ellipse_y*ellipse_y * acc->h2mw2 - + acc->h2 * scan_y * (3 * ellipse_y*ellipse_y - 2*acc->h2); + if ((left && maxMin > 0) || (!left && maxMin < 0)) { + if (ellipse_y == 0) + return def->w + left ? -def->l : def->l; + x = (acc->h2 * scan_y - ellipse_y * acc->h2mw2) * + sqrt (acc->h2 - ellipse_y * ellipse_y) / + (def->h * def->w * ellipse_y); + return x; + } + } + } + if (left) { + if (acc->left.valid && boundedLe (scan_y, bound->left)) { + x = intersectLine (scan_y, acc->left); + } else { + if (acc->right.valid) + x = intersectLine (scan_y, acc->right); + else + x = def->w - def->l; + } + } else { + if (acc->right.valid && boundedLe (scan_y, bound->right)) { + x = intersectLine (scan_y, acc->right); + } else { + if (acc->left.valid) + x = intersectLine (scan_y, acc->left); + else + x = def->w - def->l; + } + } + return x; +} + +/* + * generate the set of spans with + * the given y coordinate + */ + +static void +arcSpan (y, lx, lw, rx, rw, def, bounds, acc, mask) + int y; + int lx; + int lw; + int rx; + int rw; + struct arc_def *def; + struct arc_bound *bounds; + struct accelerators *acc; + int mask; +{ + int linx, loutx, rinx, routx; + double x, altx; + + if (boundedLe (y, bounds->inneri)) { + linx = -(lx + lw); + rinx = rx; + } else { + /* + * intersection with left face + */ + x = hookX (y + acc->fromIntY, def, bounds, acc, 1); + if (acc->right.valid && + boundedLe (y + acc->fromIntY, bounds->right)) + { + altx = intersectLine (y + acc->fromIntY, acc->right); + if (altx < x) + x = altx; + } + linx = -ICEIL(acc->fromIntX - x); + rinx = ICEIL(acc->fromIntX + x); + } + if (boundedLe (y, bounds->outeri)) { + loutx = -lx; + routx = rx + rw; + } else { + /* + * intersection with right face + */ + x = hookX (y + acc->fromIntY, def, bounds, acc, 0); + if (acc->left.valid && + boundedLe (y + acc->fromIntY, bounds->left)) + { + altx = x; + x = intersectLine (y + acc->fromIntY, acc->left); + if (x < altx) + x = altx; + } + loutx = -ICEIL(acc->fromIntX - x); + routx = ICEIL(acc->fromIntX + x); + } + if (routx > rinx) { + if (mask & 1) + newFinalSpan (acc->yorgu - y, + acc->xorg + rinx, acc->xorg + routx); + if (mask & 8) + newFinalSpan (acc->yorgl + y, + acc->xorg + rinx, acc->xorg + routx); + } + if (loutx > linx) { + if (mask & 2) + newFinalSpan (acc->yorgu - y, + acc->xorg - loutx, acc->xorg - linx); + if (mask & 4) + newFinalSpan (acc->yorgl + y, + acc->xorg - loutx, acc->xorg - linx); + } +} + +static void +arcSpan0 (lx, lw, rx, rw, def, bounds, acc, mask) + int lx; + int lw; + int rx; + int rw; + struct arc_def *def; + struct arc_bound *bounds; + struct accelerators *acc; + int mask; +{ + double x; + + if (boundedLe (0, bounds->inneri) && + acc->left.valid && boundedLe (0, bounds->left) && + acc->left.b > 0) + { + x = def->w - def->l; + if (acc->left.b < x) + x = acc->left.b; + lw = ICEIL(acc->fromIntX - x) - lx; + rw += rx; + rx = ICEIL(acc->fromIntX + x); + rw -= rx; + } + arcSpan (0, lx, lw, rx, rw, def, bounds, acc, mask); +} + +static void +tailSpan (y, lw, rw, def, bounds, acc, mask) + int y; + int lw; + int rw; + struct arc_def *def; + struct arc_bound *bounds; + struct accelerators *acc; + int mask; +{ + double yy, xalt, x, lx, rx; + int n; + + if (boundedLe(y, bounds->outeri)) + arcSpan (y, 0, lw, -rw, rw, def, bounds, acc, mask); + else if (def->w != def->h) { + yy = y + acc->fromIntY; + x = tailX(yy, def, bounds, acc); + if (yy == 0.0 && x == -rw - acc->fromIntX) + return; + if (acc->right.valid && boundedLe (yy, bounds->right)) { + rx = x; + lx = -x; + xalt = intersectLine (yy, acc->right); + if (xalt >= -rw - acc->fromIntX && xalt <= rx) + rx = xalt; + n = ICEIL(acc->fromIntX + lx); + if (lw > n) { + if (mask & 2) + newFinalSpan (acc->yorgu - y, + acc->xorg + n, acc->xorg + lw); + if (mask & 4) + newFinalSpan (acc->yorgl + y, + acc->xorg + n, acc->xorg + lw); + } + n = ICEIL(acc->fromIntX + rx); + if (n > -rw) { + if (mask & 1) + newFinalSpan (acc->yorgu - y, + acc->xorg - rw, acc->xorg + n); + if (mask & 8) + newFinalSpan (acc->yorgl + y, + acc->xorg - rw, acc->xorg + n); + } + } + arcSpan (y, + ICEIL(acc->fromIntX - x), 0, + ICEIL(acc->fromIntX + x), 0, + def, bounds, acc, mask); + } +} + +/* + * create whole arcs out of pieces. This code is + * very bad. + */ + +static struct finalSpan **finalSpans = NULL; +static int finalMiny = 0, finalMaxy = -1; +static int finalSize = 0; + +static int nspans = 0; /* total spans, not just y coords */ + +struct finalSpan { + struct finalSpan *next; + int min, max; /* x values */ +}; + +static struct finalSpan *freeFinalSpans, *tmpFinalSpan; + +# define allocFinalSpan() (freeFinalSpans ?\ + ((tmpFinalSpan = freeFinalSpans), \ + (freeFinalSpans = freeFinalSpans->next), \ + (tmpFinalSpan->next = 0), \ + tmpFinalSpan) : \ + realAllocSpan ()) + +# define SPAN_CHUNK_SIZE 128 + +struct finalSpanChunk { + struct finalSpan data[SPAN_CHUNK_SIZE]; + struct finalSpanChunk *next; +}; + +static struct finalSpanChunk *chunks; + +struct finalSpan * +realAllocSpan () +{ + register struct finalSpanChunk *newChunk; + register struct finalSpan *span; + register int i; + + newChunk = (struct finalSpanChunk *) xalloc (sizeof (struct finalSpanChunk)); + if (!newChunk) + return (struct finalSpan *) NULL; + newChunk->next = chunks; + chunks = newChunk; + freeFinalSpans = span = newChunk->data + 1; + for (i = 1; i < SPAN_CHUNK_SIZE-1; i++) { + span->next = span+1; + span++; + } + span->next = 0; + span = newChunk->data; + span->next = 0; + return span; +} + +static void +disposeFinalSpans () +{ + struct finalSpanChunk *chunk, *next; + + for (chunk = chunks; chunk; chunk = next) { + next = chunk->next; + xfree (chunk); + } + chunks = 0; + freeFinalSpans = 0; + xfree(finalSpans); + finalSpans = 0; +} + +static void +fillSpans (pDrawable, pGC) + DrawablePtr pDrawable; + GCPtr pGC; +{ + register struct finalSpan *span; + register DDXPointPtr xSpan; + register int *xWidth; + register int i; + register struct finalSpan **f; + register int spany; + DDXPointPtr xSpans; + int *xWidths; + + if (nspans == 0) + return; + xSpan = xSpans = (DDXPointPtr) xalloc (nspans * sizeof (DDXPointRec)); + xWidth = xWidths = (int *) xalloc (nspans * sizeof (int)); + if (xSpans && xWidths) + { + i = 0; + f = finalSpans; + for (spany = finalMiny; spany <= finalMaxy; spany++, f++) { + for (span = *f; span; span=span->next) { + if (span->max <= span->min) + continue; + xSpan->x = span->min; + xSpan->y = spany; + ++xSpan; + *xWidth++ = span->max - span->min; + ++i; + } + } + (*pGC->ops->FillSpans) (pDrawable, pGC, i, xSpans, xWidths, TRUE); + } + disposeFinalSpans (); + xfree (xSpans); + xfree (xWidths); + finalMiny = 0; + finalMaxy = -1; + finalSize = 0; + nspans = 0; +} + +# define SPAN_REALLOC 100 + +# define findSpan(y) ((finalMiny <= (y) && (y) <= finalMaxy) ? \ + &finalSpans[(y) - finalMiny] : \ + realFindSpan (y)) + +static struct finalSpan ** +realFindSpan (y) + int y; +{ + struct finalSpan **newSpans; + int newSize, newMiny, newMaxy; + int change; + int i; + + if (y < finalMiny || y > finalMaxy) { + if (!finalSize) { + finalMiny = y; + finalMaxy = y - 1; + } + if (y < finalMiny) + change = finalMiny - y; + else + change = y - finalMaxy; + if (change >= SPAN_REALLOC) + change += SPAN_REALLOC; + else + change = SPAN_REALLOC; + newSize = finalSize + change; + newSpans = (struct finalSpan **) xalloc + (newSize * sizeof (struct finalSpan *)); + if (!newSpans) + return (struct finalSpan **)NULL; + newMiny = finalMiny; + newMaxy = finalMaxy; + if (y < finalMiny) + newMiny = finalMiny - change; + else + newMaxy = finalMaxy + change; + if (finalSpans) { + memmove(((char *) newSpans) + (finalMiny-newMiny) * sizeof (struct finalSpan *), + (char *) finalSpans, + finalSize * sizeof (struct finalSpan *)); + xfree (finalSpans); + } + if ((i = finalMiny - newMiny) > 0) + bzero ((char *)newSpans, i * sizeof (struct finalSpan *)); + if ((i = newMaxy - finalMaxy) > 0) + bzero ((char *)(newSpans + newSize - i), + i * sizeof (struct finalSpan *)); + finalSpans = newSpans; + finalMaxy = newMaxy; + finalMiny = newMiny; + finalSize = newSize; + } + return &finalSpans[y - finalMiny]; +} + +static void +newFinalSpan (y, xmin, xmax) + int y; + register int xmin, xmax; +{ + register struct finalSpan *x; + register struct finalSpan **f; + struct finalSpan *oldx; + struct finalSpan *prev; + + f = findSpan (y); + if (!f) + return; + oldx = 0; + for (;;) { + prev = 0; + for (x = *f; x; x=x->next) { + if (x == oldx) { + prev = x; + continue; + } + if (x->min <= xmax && xmin <= x->max) { + if (oldx) { + oldx->min = min (x->min, xmin); + oldx->max = max (x->max, xmax); + if (prev) + prev->next = x->next; + else + *f = x->next; + --nspans; + } else { + x->min = min (x->min, xmin); + x->max = max (x->max, xmax); + oldx = x; + } + xmin = oldx->min; + xmax = oldx->max; + break; + } + prev = x; + } + if (!x) + break; + } + if (!oldx) { + x = allocFinalSpan (); + if (x) + { + x->min = xmin; + x->max = xmax; + x->next = *f; + *f = x; + ++nspans; + } + } +} + +static void +mirrorSppPoint (quadrant, sppPoint) + int quadrant; + SppPointPtr sppPoint; +{ + switch (quadrant) { + case 0: + break; + case 1: + sppPoint->x = -sppPoint->x; + break; + case 2: + sppPoint->x = -sppPoint->x; + sppPoint->y = -sppPoint->y; + break; + case 3: + sppPoint->y = -sppPoint->y; + break; + } + /* + * and translate to X coordinate system + */ + sppPoint->y = -sppPoint->y; +} + +/* + * split an arc into pieces which are scan-converted + * in the first-quadrant and mirrored into position. + * This is necessary as the scan-conversion code can + * only deal with arcs completely contained in the + * first quadrant. + */ + +static void +drawArc (tarc, l, a0, a1, right, left) + xArc *tarc; + int l, a0, a1; + miArcFacePtr right, left; /* save end line points */ +{ + struct arc_def def; + struct accelerators acc; + int startq, endq, curq; + int rightq, leftq, righta, lefta; + miArcFacePtr passRight, passLeft; + int q0, q1, mask; + struct band { + int a0, a1; + int mask; + } band[5], sweep[20]; + int bandno, sweepno; + int i, j; + int flipRight = 0, flipLeft = 0; + int copyEnd = 0; + miArcSpanData *spdata; + Bool mustFree; + + spdata = miComputeWideEllipse(l, tarc, &mustFree); + if (!spdata) + return; + + if (a1 < a0) + a1 += 360 * 64; + startq = a0 / (90 * 64); + if (a0 == a1) + endq = startq; + else + endq = (a1-1) / (90 * 64); + bandno = 0; + curq = startq; + rightq = -1; + for (;;) { + switch (curq) { + case 0: + if (a0 > 90 * 64) + q0 = 0; + else + q0 = a0; + if (a1 < 360 * 64) + q1 = min (a1, 90 * 64); + else + q1 = 90 * 64; + if (curq == startq && a0 == q0 && rightq < 0) { + righta = q0; + rightq = curq; + } + if (curq == endq && a1 == q1) { + lefta = q1; + leftq = curq; + } + break; + case 1: + if (a1 < 90 * 64) + q0 = 0; + else + q0 = 180 * 64 - min (a1, 180 * 64); + if (a0 > 180 * 64) + q1 = 90 * 64; + else + q1 = 180 * 64 - max (a0, 90 * 64); + if (curq == startq && 180 * 64 - a0 == q1) { + righta = q1; + rightq = curq; + } + if (curq == endq && 180 * 64 - a1 == q0) { + lefta = q0; + leftq = curq; + } + break; + case 2: + if (a0 > 270 * 64) + q0 = 0; + else + q0 = max (a0, 180 * 64) - 180 * 64; + if (a1 < 180 * 64) + q1 = 90 * 64; + else + q1 = min (a1, 270 * 64) - 180 * 64; + if (curq == startq && a0 - 180*64 == q0) { + righta = q0; + rightq = curq; + } + if (curq == endq && a1 - 180 * 64 == q1) { + lefta = q1; + leftq = curq; + } + break; + case 3: + if (a1 < 270 * 64) + q0 = 0; + else + q0 = 360 * 64 - min (a1, 360 * 64); + q1 = 360 * 64 - max (a0, 270 * 64); + if (curq == startq && 360 * 64 - a0 == q1) { + righta = q1; + rightq = curq; + } + if (curq == endq && 360 * 64 - a1 == q0) { + lefta = q0; + leftq = curq; + } + break; + } + band[bandno].a0 = q0; + band[bandno].a1 = q1; + band[bandno].mask = 1 << curq; + bandno++; + if (curq == endq) + break; + curq++; + if (curq == 4) { + a0 = 0; + a1 -= 360 * 64; + curq = 0; + endq -= 4; + } + } + sweepno = 0; + for (;;) { + q0 = 90 * 64; + mask = 0; + /* + * find left-most point + */ + for (i = 0; i < bandno; i++) + if (band[i].a0 <= q0) { + q0 = band[i].a0; + q1 = band[i].a1; + mask = band[i].mask; + } + if (!mask) + break; + /* + * locate next point of change + */ + for (i = 0; i < bandno; i++) + if (!(mask & band[i].mask)) { + if (band[i].a0 == q0) { + if (band[i].a1 < q1) + q1 = band[i].a1; + mask |= band[i].mask; + } else if (band[i].a0 < q1) + q1 = band[i].a0; + } + /* + * create a new sweep + */ + sweep[sweepno].a0 = q0; + sweep[sweepno].a1 = q1; + sweep[sweepno].mask = mask; + sweepno++; + /* + * subtract the sweep from the affected bands + */ + for (i = 0; i < bandno; i++) + if (band[i].a0 == q0) { + band[i].a0 = q1; + /* + * check if this band is empty + */ + if (band[i].a0 == band[i].a1) + band[i].a1 = band[i].a0 = 90 * 64 + 1; + } + } + computeAcc (tarc, l, &def, &acc); + for (j = 0; j < sweepno; j++) { + mask = sweep[j].mask; + passRight = passLeft = 0; + if (mask & (1 << rightq)) { + if (sweep[j].a0 == righta) + passRight = right; + else if (sweep[j].a1 == righta) { + passLeft = right; + flipRight = 1; + } + } + if (mask & (1 << leftq)) { + if (sweep[j].a1 == lefta) + { + if (passLeft) + copyEnd = 1; + passLeft = left; + } + else if (sweep[j].a0 == lefta) { + if (passRight) + copyEnd = 1; + passRight = left; + flipLeft = 1; + } + } + drawQuadrant (&def, &acc, sweep[j].a0, sweep[j].a1, mask, + passRight, passLeft, spdata); + } + /* + * when copyEnd is set, both ends of the arc were computed + * at the same time; drawQuadrant only takes one end though, + * so the left end will be the only one holding the data. Copy + * it from there. + */ + if (copyEnd) + *right = *left; + /* + * mirror the coordinates generated for the + * faces of the arc + */ + if (right) { + mirrorSppPoint (rightq, &right->clock); + mirrorSppPoint (rightq, &right->center); + mirrorSppPoint (rightq, &right->counterClock); + if (flipRight) { + SppPointRec temp; + + temp = right->clock; + right->clock = right->counterClock; + right->counterClock = temp; + } + } + if (left) { + mirrorSppPoint (leftq, &left->counterClock); + mirrorSppPoint (leftq, &left->center); + mirrorSppPoint (leftq, &left->clock); + if (flipLeft) { + SppPointRec temp; + + temp = left->clock; + left->clock = left->counterClock; + left->counterClock = temp; + } + } + if (mustFree) + xfree(spdata); +} + +static void +drawQuadrant (def, acc, a0, a1, mask, right, left, spdata) + struct arc_def *def; + struct accelerators *acc; + int a0, a1; + int mask; + miArcFacePtr right, left; + miArcSpanData *spdata; +{ + struct arc_bound bound; + double yy, x, xalt; + int y, miny, maxy; + int n; + miArcSpan *span; + + def->a0 = ((double) a0) / 64.0; + def->a1 = ((double) a1) / 64.0; + computeBound (def, &bound, acc, right, left); + yy = bound.inner.min; + if (bound.outer.min < yy) + yy = bound.outer.min; + miny = ICEIL(yy - acc->fromIntY); + yy = bound.inner.max; + if (bound.outer.max > yy) + yy = bound.outer.max; + maxy = floor(yy - acc->fromIntY); + y = spdata->k; + span = spdata->spans; + if (spdata->top) + { + if (a1 == 90 * 64 && (mask & 1)) + newFinalSpan (acc->yorgu - y - 1, acc->xorg, acc->xorg + 1); + span++; + } + for (n = spdata->count1; --n >= 0; ) + { + if (y < miny) + return; + if (y <= maxy) { + arcSpan (y, + span->lx, -span->lx, 0, span->lx + span->lw, + def, &bound, acc, mask); + if (span->rw + span->rx) + tailSpan (y, -span->rw, -span->rx, def, &bound, acc, mask); + } + y--; + span++; + } + if (y < miny) + return; + if (spdata->hole) + { + if (y <= maxy) + arcSpan (y, 0, 0, 0, 1, def, &bound, acc, mask & 0xc); + } + for (n = spdata->count2; --n >= 0; ) + { + if (y < miny) + return; + if (y <= maxy) + arcSpan (y, span->lx, span->lw, span->rx, span->rw, + def, &bound, acc, mask); + y--; + span++; + } + if (spdata->bot && miny <= y && y <= maxy) + { + n = mask; + if (y == miny) + n &= 0xc; + if (span->rw <= 0) { + arcSpan0 (span->lx, -span->lx, 0, span->lx + span->lw, + def, &bound, acc, n); + if (span->rw + span->rx) + tailSpan (y, -span->rw, -span->rx, def, &bound, acc, n); + } + else + arcSpan0 (span->lx, span->lw, span->rx, span->rw, + def, &bound, acc, n); + y--; + } + while (y >= miny) { + yy = y + acc->fromIntY; + if (def->w == def->h) { + xalt = def->w - def->l; + x = -sqrt(xalt * xalt - yy * yy); + } else { + x = tailX(yy, def, &bound, acc); + if (acc->left.valid && boundedLe (yy, bound.left)) { + xalt = intersectLine (yy, acc->left); + if (xalt < x) + x = xalt; + } + if (acc->right.valid && boundedLe (yy, bound.right)) { + xalt = intersectLine (yy, acc->right); + if (xalt < x) + x = xalt; + } + } + arcSpan (y, + ICEIL(acc->fromIntX - x), 0, + ICEIL(acc->fromIntX + x), 0, + def, &bound, acc, mask); + y--; + } +} diff --git a/mi/mibitblt.c b/mi/mibitblt.c new file mode 100644 index 000000000..762e7effa --- /dev/null +++ b/mi/mibitblt.c @@ -0,0 +1,818 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mibitblt.c,v 1.5 2001/02/09 02:05:20 xorgcvs Exp $ */ +/* Author: Todd Newman (aided and abetted by Mr. Drewry) */ + +#include "X.h" +#include "Xprotostr.h" + +#include "misc.h" +#include "gcstruct.h" +#include "pixmapstr.h" +#include "windowstr.h" +#include "scrnintstr.h" +#include "mi.h" +#include "regionstr.h" +#include "Xmd.h" +#include "servermd.h" + +/* MICOPYAREA -- public entry for the CopyArea request + * For each rectangle in the source region + * get the pixels with GetSpans + * set them in the destination with SetSpans + * We let SetSpans worry about clipping to the destination. + */ +RegionPtr +miCopyArea(pSrcDrawable, pDstDrawable, + pGC, xIn, yIn, widthSrc, heightSrc, xOut, yOut) + register DrawablePtr pSrcDrawable; + register DrawablePtr pDstDrawable; + GCPtr pGC; + int xIn, yIn; + int widthSrc, heightSrc; + int xOut, yOut; +{ + DDXPointPtr ppt, pptFirst; + unsigned int *pwidthFirst, *pwidth, *pbits; + BoxRec srcBox, *prect; + /* may be a new region, or just a copy */ + RegionPtr prgnSrcClip; + /* non-0 if we've created a src clip */ + RegionPtr prgnExposed; + int realSrcClip = 0; + int srcx, srcy, dstx, dsty, i, j, y, width, height, + xMin, xMax, yMin, yMax; + unsigned int *ordering; + int numRects; + BoxPtr boxes; + + srcx = xIn + pSrcDrawable->x; + srcy = yIn + pSrcDrawable->y; + + /* If the destination isn't realized, this is easy */ + if (pDstDrawable->type == DRAWABLE_WINDOW && + !((WindowPtr)pDstDrawable)->realized) + return (RegionPtr)NULL; + + /* clip the source */ + if (pSrcDrawable->type == DRAWABLE_PIXMAP) + { + BoxRec box; + + box.x1 = pSrcDrawable->x; + box.y1 = pSrcDrawable->y; + box.x2 = pSrcDrawable->x + (int) pSrcDrawable->width; + box.y2 = pSrcDrawable->y + (int) pSrcDrawable->height; + + prgnSrcClip = REGION_CREATE(pGC->pScreen, &box, 1); + realSrcClip = 1; + } + else + { + if (pGC->subWindowMode == IncludeInferiors) { + prgnSrcClip = NotClippedByChildren ((WindowPtr) pSrcDrawable); + realSrcClip = 1; + } else + prgnSrcClip = &((WindowPtr)pSrcDrawable)->clipList; + } + + /* If the src drawable is a window, we need to translate the srcBox so + * that we can compare it with the window's clip region later on. */ + srcBox.x1 = srcx; + srcBox.y1 = srcy; + srcBox.x2 = srcx + widthSrc; + srcBox.y2 = srcy + heightSrc; + + dstx = xOut; + dsty = yOut; + if (pGC->miTranslate) + { + dstx += pDstDrawable->x; + dsty += pDstDrawable->y; + } + + pptFirst = ppt = (DDXPointPtr) + ALLOCATE_LOCAL(heightSrc * sizeof(DDXPointRec)); + pwidthFirst = pwidth = (unsigned int *) + ALLOCATE_LOCAL(heightSrc * sizeof(unsigned int)); + numRects = REGION_NUM_RECTS(prgnSrcClip); + boxes = REGION_RECTS(prgnSrcClip); + ordering = (unsigned int *) + ALLOCATE_LOCAL(numRects * sizeof(unsigned int)); + if(!pptFirst || !pwidthFirst || !ordering) + { + if (ordering) + DEALLOCATE_LOCAL(ordering); + if (pwidthFirst) + DEALLOCATE_LOCAL(pwidthFirst); + if (pptFirst) + DEALLOCATE_LOCAL(pptFirst); + return (RegionPtr)NULL; + } + + /* If not the same drawable then order of move doesn't matter. + Following assumes that boxes are sorted from top + to bottom and left to right. + */ + if ((pSrcDrawable != pDstDrawable) && + ((pGC->subWindowMode != IncludeInferiors) || + (pSrcDrawable->type == DRAWABLE_PIXMAP) || + (pDstDrawable->type == DRAWABLE_PIXMAP))) + for (i=0; i < numRects; i++) + ordering[i] = i; + else { /* within same drawable, must sequence moves carefully! */ + if (dsty <= srcBox.y1) { /* Scroll up or stationary vertical. + Vertical order OK */ + if (dstx <= srcBox.x1) /* Scroll left or stationary horizontal. + Horizontal order OK as well */ + for (i=0; i < numRects; i++) + ordering[i] = i; + else { /* scroll right. must reverse horizontal banding of rects. */ + for (i=0, j=1, xMax=0; i < numRects; j=i+1, xMax=i) { + /* find extent of current horizontal band */ + y=boxes[i].y1; /* band has this y coordinate */ + while ((j < numRects) && (boxes[j].y1 == y)) + j++; + /* reverse the horizontal band in the output ordering */ + for (j-- ; j >= xMax; j--, i++) + ordering[i] = j; + } + } + } + else { /* Scroll down. Must reverse vertical banding. */ + if (dstx < srcBox.x1) { /* Scroll left. Horizontal order OK. */ + for (i=numRects-1, j=i-1, yMin=i, yMax=0; + i >= 0; + j=i-1, yMin=i) { + /* find extent of current horizontal band */ + y=boxes[i].y1; /* band has this y coordinate */ + while ((j >= 0) && (boxes[j].y1 == y)) + j--; + /* reverse the horizontal band in the output ordering */ + for (j++ ; j <= yMin; j++, i--, yMax++) + ordering[yMax] = j; + } + } + else /* Scroll right or horizontal stationary. + Reverse horizontal order as well (if stationary, horizontal + order can be swapped without penalty and this is faster + to compute). */ + for (i=0, j=numRects-1; i < numRects; i++, j--) + ordering[i] = j; + } + } + + for(i = 0; i < numRects; i++) + { + prect = &boxes[ordering[i]]; + xMin = max(prect->x1, srcBox.x1); + xMax = min(prect->x2, srcBox.x2); + yMin = max(prect->y1, srcBox.y1); + yMax = min(prect->y2, srcBox.y2); + /* is there anything visible here? */ + if(xMax <= xMin || yMax <= yMin) + continue; + + ppt = pptFirst; + pwidth = pwidthFirst; + y = yMin; + height = yMax - yMin; + width = xMax - xMin; + + for(j = 0; j < height; j++) + { + /* We must untranslate before calling GetSpans */ + ppt->x = xMin; + ppt++->y = y++; + *pwidth++ = width; + } + pbits = (unsigned int *)xalloc(height * PixmapBytePad(width, + pSrcDrawable->depth)); + if (pbits) + { + (*pSrcDrawable->pScreen->GetSpans)(pSrcDrawable, width, pptFirst, + (int *)pwidthFirst, height, (char *)pbits); + ppt = pptFirst; + pwidth = pwidthFirst; + xMin -= (srcx - dstx); + y = yMin - (srcy - dsty); + for(j = 0; j < height; j++) + { + ppt->x = xMin; + ppt++->y = y++; + *pwidth++ = width; + } + + (*pGC->ops->SetSpans)(pDstDrawable, pGC, (char *)pbits, pptFirst, + (int *)pwidthFirst, height, TRUE); + xfree(pbits); + } + } + prgnExposed = miHandleExposures(pSrcDrawable, pDstDrawable, pGC, xIn, yIn, + widthSrc, heightSrc, xOut, yOut, (unsigned long)0); + if(realSrcClip) + REGION_DESTROY(pGC->pScreen, prgnSrcClip); + + DEALLOCATE_LOCAL(ordering); + DEALLOCATE_LOCAL(pwidthFirst); + DEALLOCATE_LOCAL(pptFirst); + return prgnExposed; +} + +/* MIGETPLANE -- gets a bitmap representing one plane of pDraw + * A helper used for CopyPlane and XY format GetImage + * No clever strategy here, we grab a scanline at a time, pull out the + * bits and then stuff them in a 1 bit deep map. + */ +static +unsigned long * +miGetPlane(pDraw, planeNum, sx, sy, w, h, result) + DrawablePtr pDraw; + int planeNum; /* number of the bitPlane */ + int sx, sy, w, h; + unsigned long *result; +{ + int i, j, k, width, bitsPerPixel, widthInBytes; + DDXPointRec pt; + unsigned long pixel; + unsigned long bit; + unsigned char *pCharsOut; + +#if BITMAP_SCANLINE_UNIT == 8 +#define OUT_TYPE unsigned char +#endif +#if BITMAP_SCANLINE_UNIT == 16 +#define OUT_TYPE CARD16 +#endif +#if BITMAP_SCANLINE_UNIT == 32 +#define OUT_TYPE CARD32 +#endif +#if BITMAP_SCANLINE_UNIT == 64 +#define OUT_TYPE CARD64 +#endif + + OUT_TYPE *pOut; + int delta; + + sx += pDraw->x; + sy += pDraw->y; + widthInBytes = BitmapBytePad(w); + if(!result) + result = (unsigned long *)xalloc(h * widthInBytes); + if (!result) + return (unsigned long *)NULL; + bitsPerPixel = pDraw->bitsPerPixel; + bzero((char *)result, h * widthInBytes); + pOut = (OUT_TYPE *) result; + if(bitsPerPixel == 1) + { + pCharsOut = (unsigned char *) result; + width = w; + } + else + { + delta = (widthInBytes / (BITMAP_SCANLINE_UNIT / 8)) - + (w / BITMAP_SCANLINE_UNIT); + width = 1; +#if IMAGE_BYTE_ORDER == MSBFirst + planeNum += (32 - bitsPerPixel); +#endif + } + pt.y = sy; + for (i = h; --i >= 0; pt.y++) + { + pt.x = sx; + if(bitsPerPixel == 1) + { + (*pDraw->pScreen->GetSpans)(pDraw, width, &pt, &width, 1, + (char *)pCharsOut); + pCharsOut += widthInBytes; + } + else + { + k = 0; + for(j = w; --j >= 0; pt.x++) + { + /* Fetch the next pixel */ + (*pDraw->pScreen->GetSpans)(pDraw, width, &pt, &width, 1, + (char *)&pixel); + /* + * Now get the bit and insert into a bitmap in XY format. + */ + bit = (pixel >> planeNum) & 1; + /* XXX assuming bit order == byte order */ +#if BITMAP_BIT_ORDER == LSBFirst + bit <<= k; +#else + bit <<= ((BITMAP_SCANLINE_UNIT - 1) - k); +#endif + *pOut |= (OUT_TYPE) bit; + k++; + if (k == BITMAP_SCANLINE_UNIT) + { + pOut++; + k = 0; + } + } + pOut += delta; + } + } + return(result); + +} + +/* MIOPQSTIPDRAWABLE -- use pbits as an opaque stipple for pDraw. + * Drawing through the clip mask we SetSpans() the bits into a + * bitmap and stipple those bits onto the destination drawable by doing a + * PolyFillRect over the whole drawable, + * then we invert the bitmap by copying it onto itself with an alu of + * GXinvert, invert the foreground/background colors of the gc, and draw + * the background bits. + * Note how the clipped out bits of the bitmap are always the background + * color so that the stipple never causes FillRect to draw them. + */ +void +miOpqStipDrawable(pDraw, pGC, prgnSrc, pbits, srcx, w, h, dstx, dsty) + DrawablePtr pDraw; + GCPtr pGC; + RegionPtr prgnSrc; + unsigned long *pbits; + int srcx, w, h, dstx, dsty; +{ + int oldfill, i; + unsigned long oldfg; + int *pwidth, *pwidthFirst; + ChangeGCVal gcv[6]; + PixmapPtr pStipple, pPixmap; + DDXPointRec oldOrg; + GCPtr pGCT; + DDXPointPtr ppt, pptFirst; + xRectangle rect; + RegionPtr prgnSrcClip; + + pPixmap = (*pDraw->pScreen->CreatePixmap) + (pDraw->pScreen, w + srcx, h, 1); + if (!pPixmap) + return; + + /* Put the image into a 1 bit deep pixmap */ + pGCT = GetScratchGC(1, pDraw->pScreen); + if (!pGCT) + { + (*pDraw->pScreen->DestroyPixmap)(pPixmap); + return; + } + /* First set the whole pixmap to 0 */ + gcv[0].val = 0; + dixChangeGC(NullClient, pGCT, GCBackground, NULL, gcv); + ValidateGC((DrawablePtr)pPixmap, pGCT); + miClearDrawable((DrawablePtr)pPixmap, pGCT); + ppt = pptFirst = (DDXPointPtr)ALLOCATE_LOCAL(h * sizeof(DDXPointRec)); + pwidth = pwidthFirst = (int *)ALLOCATE_LOCAL(h * sizeof(int)); + if(!pptFirst || !pwidthFirst) + { + if (pwidthFirst) DEALLOCATE_LOCAL(pwidthFirst); + if (pptFirst) DEALLOCATE_LOCAL(pptFirst); + FreeScratchGC(pGCT); + return; + } + + /* we need a temporary region because ChangeClip must be assumed + to destroy what it's sent. note that this means we don't + have to free prgnSrcClip ourselves. + */ + prgnSrcClip = REGION_CREATE(pGCT->pScreen, NULL, 0); + REGION_COPY(pGCT->pScreen, prgnSrcClip, prgnSrc); + REGION_TRANSLATE(pGCT->pScreen, prgnSrcClip, srcx, 0); + (*pGCT->funcs->ChangeClip)(pGCT, CT_REGION, prgnSrcClip, 0); + ValidateGC((DrawablePtr)pPixmap, pGCT); + + /* Since we know pDraw is always a pixmap, we never need to think + * about translation here */ + for(i = 0; i < h; i++) + { + ppt->x = 0; + ppt++->y = i; + *pwidth++ = w + srcx; + } + + (*pGCT->ops->SetSpans)((DrawablePtr)pPixmap, pGCT, (char *)pbits, + pptFirst, pwidthFirst, h, TRUE); + DEALLOCATE_LOCAL(pwidthFirst); + DEALLOCATE_LOCAL(pptFirst); + + + /* Save current values from the client GC */ + oldfill = pGC->fillStyle; + pStipple = pGC->stipple; + if(pStipple) + pStipple->refcnt++; + oldOrg = pGC->patOrg; + + /* Set a new stipple in the drawable */ + gcv[0].val = FillStippled; + gcv[1].ptr = pPixmap; + gcv[2].val = dstx - srcx; + gcv[3].val = dsty; + + dixChangeGC(NullClient, pGC, + GCFillStyle | GCStipple | GCTileStipXOrigin | GCTileStipYOrigin, + NULL, gcv); + ValidateGC(pDraw, pGC); + + /* Fill the drawable with the stipple. This will draw the + * foreground color whereever 1 bits are set, leaving everything + * with 0 bits untouched. Note that the part outside the clip + * region is all 0s. */ + rect.x = dstx; + rect.y = dsty; + rect.width = w; + rect.height = h; + (*pGC->ops->PolyFillRect)(pDraw, pGC, 1, &rect); + + /* Invert the tiling pixmap. This sets 0s for 1s and 1s for 0s, only + * within the clipping region, the part outside is still all 0s */ + gcv[0].val = GXinvert; + dixChangeGC(NullClient, pGCT, GCFunction, NULL, gcv); + ValidateGC((DrawablePtr)pPixmap, pGCT); + (*pGCT->ops->CopyArea)((DrawablePtr)pPixmap, (DrawablePtr)pPixmap, + pGCT, 0, 0, w + srcx, h, 0, 0); + + /* Swap foreground and background colors on the GC for the drawable. + * Now when we fill the drawable, we will fill in the "Background" + * values */ + oldfg = pGC->fgPixel; + gcv[0].val = pGC->bgPixel; + gcv[1].val = oldfg; + gcv[2].ptr = pPixmap; + dixChangeGC(NullClient, pGC, GCForeground | GCBackground | GCStipple, + NULL, gcv); + ValidateGC(pDraw, pGC); + /* PolyFillRect might have bashed the rectangle */ + rect.x = dstx; + rect.y = dsty; + rect.width = w; + rect.height = h; + (*pGC->ops->PolyFillRect)(pDraw, pGC, 1, &rect); + + /* Now put things back */ + if(pStipple) + pStipple->refcnt--; + gcv[0].val = oldfg; + gcv[1].val = pGC->fgPixel; + gcv[2].val = oldfill; + gcv[3].ptr = pStipple; + gcv[4].val = oldOrg.x; + gcv[5].val = oldOrg.y; + dixChangeGC(NullClient, pGC, + GCForeground | GCBackground | GCFillStyle | GCStipple | + GCTileStipXOrigin | GCTileStipYOrigin, NULL, gcv); + + ValidateGC(pDraw, pGC); + /* put what we hope is a smaller clip region back in the scratch gc */ + (*pGCT->funcs->ChangeClip)(pGCT, CT_NONE, NULL, 0); + FreeScratchGC(pGCT); + (*pDraw->pScreen->DestroyPixmap)(pPixmap); + +} + +/* MICOPYPLANE -- public entry for the CopyPlane request. + * strategy: + * First build up a bitmap out of the bits requested + * build a source clip + * Use the bitmap we've built up as a Stipple for the destination + */ +RegionPtr +miCopyPlane(pSrcDrawable, pDstDrawable, + pGC, srcx, srcy, width, height, dstx, dsty, bitPlane) + DrawablePtr pSrcDrawable; + DrawablePtr pDstDrawable; + GCPtr pGC; + int srcx, srcy; + int width, height; + int dstx, dsty; + unsigned long bitPlane; +{ + unsigned long *ptile; + BoxRec box; + RegionPtr prgnSrc, prgnExposed; + + /* incorporate the source clip */ + + box.x1 = srcx + pSrcDrawable->x; + box.y1 = srcy + pSrcDrawable->y; + box.x2 = box.x1 + width; + box.y2 = box.y1 + height; + /* clip to visible drawable */ + if (box.x1 < pSrcDrawable->x) + box.x1 = pSrcDrawable->x; + if (box.y1 < pSrcDrawable->y) + box.y1 = pSrcDrawable->y; + if (box.x2 > pSrcDrawable->x + (int) pSrcDrawable->width) + box.x2 = pSrcDrawable->x + (int) pSrcDrawable->width; + if (box.y2 > pSrcDrawable->y + (int) pSrcDrawable->height) + box.y2 = pSrcDrawable->y + (int) pSrcDrawable->height; + if (box.x1 > box.x2) + box.x2 = box.x1; + if (box.y1 > box.y2) + box.y2 = box.y1; + prgnSrc = REGION_CREATE(pGC->pScreen, &box, 1); + + if (pSrcDrawable->type != DRAWABLE_PIXMAP) { + /* clip to visible drawable */ + + if (pGC->subWindowMode == IncludeInferiors) + { + RegionPtr clipList = NotClippedByChildren ((WindowPtr) pSrcDrawable); + REGION_INTERSECT(pGC->pScreen, prgnSrc, prgnSrc, clipList); + REGION_DESTROY(pGC->pScreen, clipList); + } else + REGION_INTERSECT(pGC->pScreen, prgnSrc, prgnSrc, + &((WindowPtr)pSrcDrawable)->clipList); + } + + box = *REGION_EXTENTS(pGC->pScreen, prgnSrc); + REGION_TRANSLATE(pGC->pScreen, prgnSrc, -box.x1, -box.y1); + + if ((box.x2 > box.x1) && (box.y2 > box.y1)) + { + /* minimize the size of the data extracted */ + /* note that we convert the plane mask bitPlane into a plane number */ + box.x1 -= pSrcDrawable->x; + box.x2 -= pSrcDrawable->x; + box.y1 -= pSrcDrawable->y; + box.y2 -= pSrcDrawable->y; + ptile = miGetPlane(pSrcDrawable, ffs(bitPlane) - 1, + box.x1, box.y1, + box.x2 - box.x1, box.y2 - box.y1, + (unsigned long *) NULL); + if (ptile) + { + miOpqStipDrawable(pDstDrawable, pGC, prgnSrc, ptile, 0, + box.x2 - box.x1, box.y2 - box.y1, + dstx + box.x1 - srcx, dsty + box.y1 - srcy); + xfree(ptile); + } + } + prgnExposed = miHandleExposures(pSrcDrawable, pDstDrawable, pGC, srcx, srcy, + width, height, dstx, dsty, bitPlane); + REGION_DESTROY(pGC->pScreen, prgnSrc); + return prgnExposed; +} + +/* MIGETIMAGE -- public entry for the GetImage Request + * We're getting the image into a memory buffer. While we have to use GetSpans + * to read a line from the device (since we don't know what that looks like), + * we can just write into the destination buffer + * + * two different strategies are used, depending on whether we're getting the + * image in Z format or XY format + * Z format: + * Line at a time, GetSpans a line into the destination buffer, then if the + * planemask is not all ones, we do a SetSpans into a temporary buffer (to get + * bits turned off) and then another GetSpans to get stuff back (because + * pixmaps are opaque, and we are passed in the memory to write into). This is + * pretty ugly and slow but works. Life is hard. + * XY format: + * get the single plane specified in planemask + */ +void +miGetImage(pDraw, sx, sy, w, h, format, planeMask, pDst) + DrawablePtr pDraw; + int sx, sy, w, h; + unsigned int format; + unsigned long planeMask; + char * pDst; +{ + unsigned char depth; + int i, linelength, width, srcx, srcy; + DDXPointRec pt; + XID gcv[2]; + PixmapPtr pPixmap = (PixmapPtr)NULL; + GCPtr pGC; + + depth = pDraw->depth; + if(format == ZPixmap) + { + if ( (((1<<depth)-1)&planeMask) != (1<<depth)-1 ) + { + xPoint pt; + + pGC = GetScratchGC(depth, pDraw->pScreen); + if (!pGC) + return; + pPixmap = (*pDraw->pScreen->CreatePixmap) + (pDraw->pScreen, w, 1, depth); + if (!pPixmap) + { + FreeScratchGC(pGC); + return; + } + /* + * Clear the pixmap before doing anything else + */ + ValidateGC((DrawablePtr)pPixmap, pGC); + pt.x = pt.y = 0; + width = w; + (*pGC->ops->FillSpans)((DrawablePtr)pPixmap, pGC, 1, &pt, &width, + TRUE); + + /* alu is already GXCopy */ + gcv[0] = (XID)planeMask; + DoChangeGC(pGC, GCPlaneMask, gcv, 0); + ValidateGC((DrawablePtr)pPixmap, pGC); + } + + linelength = PixmapBytePad(w, depth); + srcx = sx + pDraw->x; + srcy = sy + pDraw->y; + for(i = 0; i < h; i++) + { + pt.x = srcx; + pt.y = srcy + i; + width = w; + (*pDraw->pScreen->GetSpans)(pDraw, w, &pt, &width, 1, pDst); + if (pPixmap) + { + pt.x = 0; + pt.y = 0; + width = w; + (*pGC->ops->SetSpans)((DrawablePtr)pPixmap, pGC, pDst, + &pt, &width, 1, TRUE); + (*pDraw->pScreen->GetSpans)((DrawablePtr)pPixmap, w, &pt, + &width, 1, pDst); + } + pDst += linelength; + } + if (pPixmap) + { + (*pGC->pScreen->DestroyPixmap)(pPixmap); + FreeScratchGC(pGC); + } + } + else + { + (void) miGetPlane(pDraw, ffs(planeMask) - 1, sx, sy, w, h, + (unsigned long *)pDst); + } +} + + +/* MIPUTIMAGE -- public entry for the PutImage request + * Here we benefit from knowing the format of the bits pointed to by pImage, + * even if we don't know how pDraw represents them. + * Three different strategies are used depending on the format + * XYBitmap Format: + * we just use the Opaque Stipple helper function to cover the destination + * Note that this covers all the planes of the drawable with the + * foreground color (masked with the GC planemask) where there are 1 bits + * and the background color (masked with the GC planemask) where there are + * 0 bits + * XYPixmap format: + * what we're called with is a series of XYBitmaps, but we only want + * each XYPixmap to update 1 plane, instead of updating all of them. + * we set the foreground color to be all 1s and the background to all 0s + * then for each plane, we set the plane mask to only effect that one + * plane and recursive call ourself with the format set to XYBitmap + * (This clever idea courtesy of RGD.) + * ZPixmap format: + * This part is simple, just call SetSpans + */ +void +miPutImage(pDraw, pGC, depth, x, y, w, h, leftPad, format, pImage) + DrawablePtr pDraw; + GCPtr pGC; + int depth, x, y, w, h, leftPad; + int format; + char *pImage; +{ + DDXPointPtr pptFirst, ppt; + int *pwidthFirst, *pwidth; + RegionPtr prgnSrc; + BoxRec box; + unsigned long oldFg, oldBg; + XID gcv[3]; + unsigned long oldPlanemask; + unsigned long i; + long bytesPer; + + if (!w || !h) + return; + switch(format) + { + case XYBitmap: + + box.x1 = 0; + box.y1 = 0; + box.x2 = w; + box.y2 = h; + prgnSrc = REGION_CREATE(pGC->pScreen, &box, 1); + + miOpqStipDrawable(pDraw, pGC, prgnSrc, (unsigned long *) pImage, + leftPad, w, h, x, y); + REGION_DESTROY(pGC->pScreen, prgnSrc); + break; + + case XYPixmap: + depth = pGC->depth; + oldPlanemask = pGC->planemask; + oldFg = pGC->fgPixel; + oldBg = pGC->bgPixel; + gcv[0] = (XID)~0; + gcv[1] = (XID)0; + DoChangeGC(pGC, GCForeground | GCBackground, gcv, 0); + bytesPer = (long)h * BitmapBytePad(w + leftPad); + + for (i = 1 << (depth-1); i != 0; i >>= 1, pImage += bytesPer) + { + if (i & oldPlanemask) + { + gcv[0] = (XID)i; + DoChangeGC(pGC, GCPlaneMask, gcv, 0); + ValidateGC(pDraw, pGC); + (*pGC->ops->PutImage)(pDraw, pGC, 1, x, y, w, h, leftPad, + XYBitmap, (char *)pImage); + } + } + gcv[0] = (XID)oldPlanemask; + gcv[1] = (XID)oldFg; + gcv[2] = (XID)oldBg; + DoChangeGC(pGC, GCPlaneMask | GCForeground | GCBackground, gcv, 0); + break; + + case ZPixmap: + ppt = pptFirst = (DDXPointPtr)ALLOCATE_LOCAL(h * sizeof(DDXPointRec)); + pwidth = pwidthFirst = (int *)ALLOCATE_LOCAL(h * sizeof(int)); + if(!pptFirst || !pwidthFirst) + { + if (pwidthFirst) + DEALLOCATE_LOCAL(pwidthFirst); + if (pptFirst) + DEALLOCATE_LOCAL(pptFirst); + return; + } + if (pGC->miTranslate) + { + x += pDraw->x; + y += pDraw->y; + } + + for(i = 0; i < h; i++) + { + ppt->x = x; + ppt->y = y + i; + ppt++; + *pwidth++ = w; + } + + (*pGC->ops->SetSpans)(pDraw, pGC, (char *)pImage, pptFirst, + pwidthFirst, h, TRUE); + DEALLOCATE_LOCAL(pwidthFirst); + DEALLOCATE_LOCAL(pptFirst); + break; + } +} diff --git a/mi/mibstore.c b/mi/mibstore.c new file mode 100644 index 000000000..f8a4cc891 --- /dev/null +++ b/mi/mibstore.c @@ -0,0 +1,3823 @@ +/* $Xorg: mibstore.c,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by the Regents of the University of California + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name The Open Group not be used in advertising or publicity +pertaining to distribution of the software without specific, written prior +permission. + +The University of California makes no representations about the suitability +of this software for any purpose. It is provided "as is" without express or +implied warranty. + +******************************************************************/ + +#define NEED_EVENTS +#include "X.h" +#include "Xmd.h" +#include "Xproto.h" +#include "misc.h" +#include "regionstr.h" +#include "scrnintstr.h" +#include "gcstruct.h" +#include "extnsionst.h" +#include "windowstr.h" +#include "pixmapstr.h" +#include "fontstruct.h" +#include "dixfontstr.h" +#include "dixstruct.h" /* For requestingClient */ +#include "mi.h" +#include "mibstorest.h" + +/* + * When the server fails to allocate a backing store pixmap, if you want + * it to dynamically retry to allocate backing store on every subsequent + * graphics op, you can enable BSEAGER; otherwise, backing store will be + * disabled on the window until it is unmapped and then remapped. + */ +/* #define BSEAGER */ + +/*- + * NOTES ON USAGE: + * + * The functions in this file implement a machine-independent backing-store + * scheme. To use it, the output library must do the following: + * - Provide a SaveAreas function that takes a destination pixmap, a + * region of the areas to save (in the pixmap's coordinate system) + * and the screen origin of the region. It should copy the areas from + * the screen into the pixmap. + * - Provide a RestoreAreas function that takes a source pixmap, a region + * of the areas to restore (in the screen's coordinate system) and the + * origin of the pixmap on the screen. It should copy the areas from + * the pixmap into the screen. + * - Provide a SetClipmaskRgn function that takes a gc and a region + * and merges the region into any CT_PIXMAP client clip that + * is specified in the GC. This routine is only needed if + * miValidateBackingStore will see CT_PIXMAP clip lists; not + * true for any of the sample servers (which convert the PIXMAP + * clip lists into CT_REGION clip lists; an expensive but simple + * to code option). + * - The function placed in a window's ClearToBackground vector must call + * pScreen->ClearBackingStore with the window, followed by + * the window-relative x and y coordinates, followed by the width and + * height of the area to be cleared, followed by the generateExposures + * flag. This has been taken care of in miClearToBackground. + * - Whatever determines GraphicsExpose events for the CopyArea and + * CopyPlane requests should call pWin->backStorage->ExposeCopy + * with the source and destination drawables, the GC used, a source- + * window-relative region of exposed areas, the source and destination + * coordinates and the bitplane copied, if CopyPlane, or 0, if + * CopyArea. + * + * JUSTIFICATION + * This is a cross between saving everything and just saving the + * obscued areas (as in Pike's layers.) This method has the advantage + * of only doing each output operation once per pixel, visible or + * invisible, and avoids having to do all the crufty storage + * management of keeping several separate rectangles. Since the + * ddx layer ouput primitives are required to draw through clipping + * rectangles anyway, sending multiple drawing requests for each of + * several rectangles isn't necessary. (Of course, it could be argued + * that the ddx routines should just take one rectangle each and + * get called multiple times, but that would make taking advantage of + * smart hardware harder, and probably be slower as well.) + */ + +#define SETUP_BACKING_TERSE(pGC) \ + miBSGCPtr pGCPrivate = (miBSGCPtr)(pGC)->devPrivates[miBSGCIndex].ptr; \ + GCFuncs *oldFuncs = pGC->funcs; + +#define SETUP_BACKING(pDrawable,pGC) \ + miBSWindowPtr pBackingStore = \ + (miBSWindowPtr)((WindowPtr)(pDrawable))->backStorage; \ + DrawablePtr pBackingDrawable = (DrawablePtr) \ + pBackingStore->pBackingPixmap; \ + SETUP_BACKING_TERSE(pGC) \ + GCPtr pBackingGC = pGCPrivate->pBackingGC; + +#define PROLOGUE(pGC) { \ + pGC->ops = pGCPrivate->wrapOps;\ + pGC->funcs = pGCPrivate->wrapFuncs; \ + } + +#define EPILOGUE(pGC) { \ + pGCPrivate->wrapOps = (pGC)->ops; \ + (pGC)->ops = &miBSGCOps; \ + (pGC)->funcs = oldFuncs; \ + } + +static void miCreateBSPixmap(); +static void miDestroyBSPixmap(); +static void miTileVirtualBS(); +static void miBSAllocate(), miBSFree(); +static Bool miBSCreateGCPrivate (); +static void miBSClearBackingRegion (); + +#define MoreCopy0 ; +#define MoreCopy2 *dstCopy++ = *srcCopy++; *dstCopy++ = *srcCopy++; +#define MoreCopy4 MoreCopy2 MoreCopy2 + +#define copyData(src,dst,n,morecopy) \ +{ \ + register short *srcCopy = (short *)(src); \ + register short *dstCopy = (short *)(dst); \ + register int i; \ + register int bsx = pBackingStore->x; \ + register int bsy = pBackingStore->y; \ + for (i = n; --i >= 0; ) \ + { \ + *dstCopy++ = *srcCopy++ - bsx; \ + *dstCopy++ = *srcCopy++ - bsy; \ + morecopy \ + } \ +} + +#define copyPoints(src,dst,n,mode) \ +if (mode == CoordModeOrigin) \ +{ \ + copyData(src,dst,n,MoreCopy0); \ +} \ +else \ +{ \ + memmove((char *)(dst), (char *)(src), (n) << 2); \ + *((short *)(dst)) -= pBackingStore->x; \ + *((short *)(dst) + 1) -= pBackingStore->y; \ +} + +/* + * wrappers for screen funcs + */ + +static int miBSScreenIndex; +static unsigned long miBSGeneration = 0; + +static Bool miBSCloseScreen(); +static void miBSGetImage(); +static void miBSGetSpans(); +static Bool miBSChangeWindowAttributes(); +static Bool miBSCreateGC(); +static Bool miBSDestroyWindow(); + +/* + * backing store screen functions + */ + +static void miBSSaveDoomedAreas(); +static RegionPtr miBSRestoreAreas(); +static void miBSExposeCopy(); +static RegionPtr miBSTranslateBackingStore(), miBSClearBackingStore(); +static void miBSDrawGuarantee(); + +/* + * wrapper vectors for GC funcs and ops + */ + +static int miBSGCIndex; + +static void miBSValidateGC (), miBSCopyGC (), miBSDestroyGC(); +static void miBSChangeGC(); +static void miBSChangeClip(), miBSDestroyClip(), miBSCopyClip(); + +static GCFuncs miBSGCFuncs = { + miBSValidateGC, + miBSChangeGC, + miBSCopyGC, + miBSDestroyGC, + miBSChangeClip, + miBSDestroyClip, + miBSCopyClip, +}; + +static void miBSFillSpans(), miBSSetSpans(), miBSPutImage(); +static RegionPtr miBSCopyArea(), miBSCopyPlane(); +static void miBSPolyPoint(), miBSPolylines(), miBSPolySegment(); +static void miBSPolyRectangle(),miBSPolyArc(), miBSFillPolygon(); +static void miBSPolyFillRect(), miBSPolyFillArc(); +static int miBSPolyText8(), miBSPolyText16(); +static void miBSImageText8(), miBSImageText16(); +static void miBSImageGlyphBlt(),miBSPolyGlyphBlt(); +static void miBSPushPixels(); +#ifdef NEED_LINEHELPER +static void miBSLineHelper(); +#endif + +static GCOps miBSGCOps = { + miBSFillSpans, miBSSetSpans, miBSPutImage, + miBSCopyArea, miBSCopyPlane, miBSPolyPoint, + miBSPolylines, miBSPolySegment, miBSPolyRectangle, + miBSPolyArc, miBSFillPolygon, miBSPolyFillRect, + miBSPolyFillArc, miBSPolyText8, miBSPolyText16, + miBSImageText8, miBSImageText16, miBSImageGlyphBlt, + miBSPolyGlyphBlt, miBSPushPixels +#ifdef NEED_LINEHELPER + , miBSLineHelper +#endif +}; + +#define FUNC_PROLOGUE(pGC, pPriv) \ + ((pGC)->funcs = pPriv->wrapFuncs),\ + ((pGC)->ops = pPriv->wrapOps) + +#define FUNC_EPILOGUE(pGC, pPriv) \ + ((pGC)->funcs = &miBSGCFuncs),\ + ((pGC)->ops = &miBSGCOps) + +/* + * every GC in the server is initially wrapped with these + * "cheap" functions. This allocates no memory and is used + * to discover GCs used with windows which have backing + * store enabled + */ + +static void miBSCheapValidateGC(), miBSCheapCopyGC(), miBSCheapDestroyGC(); +static void miBSCheapChangeGC (); +static void miBSCheapChangeClip(), miBSCheapDestroyClip(); +static void miBSCheapCopyClip(); + +static GCFuncs miBSCheapGCFuncs = { + miBSCheapValidateGC, + miBSCheapChangeGC, + miBSCheapCopyGC, + miBSCheapDestroyGC, + miBSCheapChangeClip, + miBSCheapDestroyClip, + miBSCheapCopyClip, +}; + +#define CHEAP_FUNC_PROLOGUE(pGC) \ + ((pGC)->funcs = (GCFuncs *) (pGC)->devPrivates[miBSGCIndex].ptr) + +#define CHEAP_FUNC_EPILOGUE(pGC) \ + ((pGC)->funcs = &miBSCheapGCFuncs) + +/* + * called from device screen initialization proc. Gets a GCPrivateIndex + * and wraps appropriate per-screen functions + */ + +void +miInitializeBackingStore (pScreen, funcs) + ScreenPtr pScreen; + miBSFuncPtr funcs; +{ + miBSScreenPtr pScreenPriv; + + if (miBSGeneration != serverGeneration) + { + miBSScreenIndex = AllocateScreenPrivateIndex (); + if (miBSScreenIndex < 0) + return; + miBSGCIndex = AllocateGCPrivateIndex (); + miBSGeneration = serverGeneration; + } + if (!AllocateGCPrivate(pScreen, miBSGCIndex, 0)) + return; + pScreenPriv = (miBSScreenPtr) xalloc (sizeof (miBSScreenRec)); + if (!pScreenPriv) + return; + + pScreenPriv->CloseScreen = pScreen->CloseScreen; + pScreenPriv->GetImage = pScreen->GetImage; + pScreenPriv->GetSpans = pScreen->GetSpans; + pScreenPriv->ChangeWindowAttributes = pScreen->ChangeWindowAttributes; + pScreenPriv->CreateGC = pScreen->CreateGC; + pScreenPriv->DestroyWindow = pScreen->DestroyWindow; + pScreenPriv->funcs = funcs; + + pScreen->CloseScreen = miBSCloseScreen; + pScreen->GetImage = miBSGetImage; + pScreen->GetSpans = miBSGetSpans; + pScreen->ChangeWindowAttributes = miBSChangeWindowAttributes; + pScreen->CreateGC = miBSCreateGC; + pScreen->DestroyWindow = miBSDestroyWindow; + + pScreen->SaveDoomedAreas = miBSSaveDoomedAreas; + pScreen->RestoreAreas = miBSRestoreAreas; + pScreen->ExposeCopy = miBSExposeCopy; + pScreen->TranslateBackingStore = miBSTranslateBackingStore; + pScreen->ClearBackingStore = miBSClearBackingStore; + pScreen->DrawGuarantee = miBSDrawGuarantee; + + pScreen->devPrivates[miBSScreenIndex].ptr = (pointer) pScreenPriv; +} + +/* + * Screen function wrappers + */ + +#define SCREEN_PROLOGUE(pScreen, field)\ + ((pScreen)->field = \ + ((miBSScreenPtr) \ + (pScreen)->devPrivates[miBSScreenIndex].ptr)->field) + +#define SCREEN_EPILOGUE(pScreen, field, wrapper)\ + ((pScreen)->field = wrapper) + +/* + * CloseScreen wrapper -- unwrap everything, free the private data + * and call the wrapped function + */ + +static Bool +miBSCloseScreen (i, pScreen) + int i; + ScreenPtr pScreen; +{ + miBSScreenPtr pScreenPriv; + + pScreenPriv = (miBSScreenPtr) pScreen->devPrivates[miBSScreenIndex].ptr; + + pScreen->CloseScreen = pScreenPriv->CloseScreen; + pScreen->GetImage = pScreenPriv->GetImage; + pScreen->GetSpans = pScreenPriv->GetSpans; + pScreen->ChangeWindowAttributes = pScreenPriv->ChangeWindowAttributes; + pScreen->CreateGC = pScreenPriv->CreateGC; + + xfree ((pointer) pScreenPriv); + + return (*pScreen->CloseScreen) (i, pScreen); +} + +static void miBSFillVirtualBits(); + +static void +miBSGetImage (pDrawable, sx, sy, w, h, format, planemask, pdstLine) + DrawablePtr pDrawable; + int sx, sy, w, h; + unsigned int format; + unsigned long planemask; + char *pdstLine; +{ + ScreenPtr pScreen = pDrawable->pScreen; + BoxRec bounds; + unsigned char depth; + + SCREEN_PROLOGUE (pScreen, GetImage); + + if (pDrawable->type != DRAWABLE_PIXMAP && + ((WindowPtr) pDrawable)->visibility != VisibilityUnobscured) + { + PixmapPtr pPixmap; + miBSWindowPtr pWindowPriv; + GCPtr pGC; + WindowPtr pWin, pSrcWin; + int xoff, yoff; + RegionRec Remaining; + RegionRec Border; + RegionRec Inside; + BoxPtr pBox; + int n; + + pWin = (WindowPtr) pDrawable; + pPixmap = 0; + depth = pDrawable->depth; + bounds.x1 = sx + pDrawable->x; + bounds.y1 = sy + pDrawable->y; + bounds.x2 = bounds.x1 + w; + bounds.y2 = bounds.y1 + h; + REGION_INIT(pScreen, &Remaining, &bounds, 0); + for (;;) + { + bounds.x1 = sx + pDrawable->x - pWin->drawable.x; + bounds.y1 = sy + pDrawable->y - pWin->drawable.y; + bounds.x2 = bounds.x1 + w; + bounds.y2 = bounds.y1 + h; + if (pWin->viewable && pWin->backStorage && + pWin->drawable.depth == depth && + (RECT_IN_REGION(pScreen, &(pWindowPriv = + (miBSWindowPtr) pWin->backStorage)->SavedRegion, + &bounds) != rgnOUT || + RECT_IN_REGION(pScreen, &Remaining, + REGION_EXTENTS(pScreen, &pWin->borderSize)) != rgnOUT)) + { + if (!pPixmap) + { + XID subWindowMode = IncludeInferiors; + int x, y; + + pPixmap = (*pScreen->CreatePixmap) (pScreen, w, h, depth); + if (!pPixmap) + goto punt; + pGC = GetScratchGC (depth, pScreen); + if (!pGC) + { + (*pScreen->DestroyPixmap) (pPixmap); + goto punt; + } + ChangeGC (pGC, GCSubwindowMode, &subWindowMode); + ValidateGC ((DrawablePtr)pPixmap, pGC); + REGION_INIT(pScreen, &Border, NullBox, 0); + REGION_INIT(pScreen, &Inside, NullBox, 0); + pSrcWin = (WindowPtr) pDrawable; + x = sx; + y = sy; + if (pSrcWin->parent) + { + x += pSrcWin->origin.x; + y += pSrcWin->origin.y; + pSrcWin = pSrcWin->parent; + } + (*pGC->ops->CopyArea) ((DrawablePtr)pSrcWin, + (DrawablePtr)pPixmap, pGC, + x, y, w, h, + 0, 0); + REGION_SUBTRACT(pScreen, &Remaining, &Remaining, + &((WindowPtr) pDrawable)->borderClip); + } + + REGION_INTERSECT(pScreen, &Inside, &Remaining, &pWin->winSize); + REGION_TRANSLATE(pScreen, &Inside, + -pWin->drawable.x, + -pWin->drawable.y); + REGION_INTERSECT(pScreen, &Inside, &Inside, + &pWindowPriv->SavedRegion); + + /* offset of sub-window in GetImage pixmap */ + xoff = pWin->drawable.x - pDrawable->x - sx; + yoff = pWin->drawable.y - pDrawable->y - sy; + + if (REGION_NUM_RECTS(&Inside) > 0) + { + switch (pWindowPriv->status) + { + case StatusContents: + pBox = REGION_RECTS(&Inside); + for (n = REGION_NUM_RECTS(&Inside); --n >= 0;) + { + (*pGC->ops->CopyArea) ( + (DrawablePtr)pWindowPriv->pBackingPixmap, + (DrawablePtr)pPixmap, pGC, + pBox->x1 - pWindowPriv->x, + pBox->y1 - pWindowPriv->y, + pBox->x2 - pBox->x1, + pBox->y2 - pBox->y1, + pBox->x1 + xoff, + pBox->y1 + yoff); + ++pBox; + } + break; + case StatusVirtual: + case StatusVDirty: + if (pWindowPriv->backgroundState == BackgroundPixmap || + pWindowPriv->backgroundState == BackgroundPixel) + miBSFillVirtualBits ((DrawablePtr) pPixmap, pGC, &Inside, + xoff, yoff, + (int) pWindowPriv->backgroundState, + pWindowPriv->background, ~0L); + break; + } + } + REGION_SUBTRACT(pScreen, &Border, &pWin->borderSize, + &pWin->winSize); + REGION_INTERSECT(pScreen, &Border, &Border, &Remaining); + if (REGION_NUM_RECTS(&Border) > 0) + { + REGION_TRANSLATE(pScreen, &Border, -pWin->drawable.x, + -pWin->drawable.y); + miBSFillVirtualBits ((DrawablePtr) pPixmap, pGC, &Border, + xoff, yoff, + pWin->borderIsPixel ? (int)BackgroundPixel : (int)BackgroundPixmap, + pWin->border, ~0L); + } + } + + if (pWin->viewable && pWin->firstChild) + pWin = pWin->firstChild; + else + { + while (!pWin->nextSib && pWin != (WindowPtr) pDrawable) + pWin = pWin->parent; + if (pWin == (WindowPtr) pDrawable) + break; + pWin = pWin->nextSib; + } + } + + REGION_UNINIT(pScreen, &Remaining); + + if (pPixmap) + { + REGION_UNINIT(pScreen, &Border); + REGION_UNINIT(pScreen, &Inside); + (*pScreen->GetImage) ((DrawablePtr) pPixmap, + 0, 0, w, h, format, planemask, pdstLine); + (*pScreen->DestroyPixmap) (pPixmap); + FreeScratchGC (pGC); + } + else + { + goto punt; + } + } + else + { +punt: ; + (*pScreen->GetImage) (pDrawable, sx, sy, w, h, + format, planemask, pdstLine); + } + + SCREEN_EPILOGUE (pScreen, GetImage, miBSGetImage); +} + +static void +miBSGetSpans (pDrawable, wMax, ppt, pwidth, nspans, pdstStart) + DrawablePtr pDrawable; + int wMax; + DDXPointPtr ppt; + int *pwidth; + int nspans; + char *pdstStart; +{ + ScreenPtr pScreen = pDrawable->pScreen; + BoxRec bounds; + int i; + WindowPtr pWin; + int dx, dy; + + SCREEN_PROLOGUE (pScreen, GetSpans); + + if (pDrawable->type != DRAWABLE_PIXMAP && ((WindowPtr) pDrawable)->backStorage) + { + PixmapPtr pPixmap; + miBSWindowPtr pWindowPriv; + GCPtr pGC; + + pWin = (WindowPtr) pDrawable; + pWindowPriv = (miBSWindowPtr) pWin->backStorage; + pPixmap = pWindowPriv->pBackingPixmap; + + bounds.x1 = ppt->x; + bounds.y1 = ppt->y; + bounds.x2 = bounds.x1 + *pwidth; + bounds.y2 = ppt->y; + for (i = 0; i < nspans; i++) + { + if (ppt[i].x < bounds.x1) + bounds.x1 = ppt[i].x; + if (ppt[i].x + pwidth[i] > bounds.x2) + bounds.x2 = ppt[i].x + pwidth[i]; + if (ppt[i].y < bounds.y1) + bounds.y1 = ppt[i].y; + else if (ppt[i].y > bounds.y2) + bounds.y2 = ppt[i].y; + } + + switch (RECT_IN_REGION(pScreen, &pWindowPriv->SavedRegion, &bounds)) + { + case rgnPART: + if (!pPixmap) + { + miCreateBSPixmap (pWin, NullBox); + if (!(pPixmap = pWindowPriv->pBackingPixmap)) + break; + } + pWindowPriv->status = StatusNoPixmap; + pGC = GetScratchGC(pPixmap->drawable.depth, + pPixmap->drawable.pScreen); + if (pGC) + { + ValidateGC ((DrawablePtr) pPixmap, pGC); + (*pGC->ops->CopyArea) + (pDrawable, (DrawablePtr) pPixmap, pGC, + bounds.x1, bounds.y1, + bounds.x2 - bounds.x1, bounds.y2 - bounds.y1, + bounds.x1 + pPixmap->drawable.x - pWin->drawable.x - + pWindowPriv->x, + bounds.y1 + pPixmap->drawable.y - pWin->drawable.y - + pWindowPriv->y); + FreeScratchGC(pGC); + } + pWindowPriv->status = StatusContents; + /* fall through */ + case rgnIN: + if (!pPixmap) + { + miCreateBSPixmap (pWin, NullBox); + if (!(pPixmap = pWindowPriv->pBackingPixmap)) + break; + } + dx = pPixmap->drawable.x - pWin->drawable.x - pWindowPriv->x; + dy = pPixmap->drawable.y - pWin->drawable.y - pWindowPriv->y; + for (i = 0; i < nspans; i++) + { + ppt[i].x += dx; + ppt[i].y += dy; + } + (*pScreen->GetSpans) ((DrawablePtr) pPixmap, wMax, ppt, pwidth, + nspans, pdstStart); + break; + case rgnOUT: + (*pScreen->GetSpans) (pDrawable, wMax, ppt, pwidth, nspans, + pdstStart); + break; + } + } + else + { + (*pScreen->GetSpans) (pDrawable, wMax, ppt, pwidth, nspans, pdstStart); + } + + SCREEN_EPILOGUE (pScreen, GetSpans, miBSGetSpans); +} + +static Bool +miBSChangeWindowAttributes (pWin, mask) + WindowPtr pWin; + unsigned long mask; +{ + ScreenPtr pScreen; + Bool ret; + + pScreen = pWin->drawable.pScreen; + + SCREEN_PROLOGUE (pScreen, ChangeWindowAttributes); + + ret = (*pScreen->ChangeWindowAttributes) (pWin, mask); + + if (ret && (mask & CWBackingStore)) + { + if (pWin->backingStore != NotUseful || pWin->DIXsaveUnder) + miBSAllocate (pWin); + else + miBSFree (pWin); + } + + SCREEN_EPILOGUE (pScreen, ChangeWindowAttributes, miBSChangeWindowAttributes); + + return ret; +} + +/* + * GC Create wrapper. Set up the cheap GC func wrappers to track + * GC validation on BackingStore windows + */ + +static Bool +miBSCreateGC (pGC) + GCPtr pGC; +{ + ScreenPtr pScreen = pGC->pScreen; + Bool ret; + + SCREEN_PROLOGUE (pScreen, CreateGC); + + if ( (ret = (*pScreen->CreateGC) (pGC)) ) + { + pGC->devPrivates[miBSGCIndex].ptr = (pointer) pGC->funcs; + pGC->funcs = &miBSCheapGCFuncs; + } + + SCREEN_EPILOGUE (pScreen, CreateGC, miBSCreateGC); + + return ret; +} + +static Bool +miBSDestroyWindow (pWin) + WindowPtr pWin; +{ + ScreenPtr pScreen = pWin->drawable.pScreen; + Bool ret; + + SCREEN_PROLOGUE (pScreen, DestroyWindow); + + ret = (*pScreen->DestroyWindow) (pWin); + + miBSFree (pWin); + + SCREEN_EPILOGUE (pScreen, DestroyWindow, miBSDestroyWindow); + + return ret; +} + +/* + * cheap GC func wrappers. Simply track validation on windows + * with backing store to enable the real func/op wrappers + */ + +static void +miBSCheapValidateGC (pGC, stateChanges, pDrawable) + GCPtr pGC; + unsigned long stateChanges; + DrawablePtr pDrawable; +{ + CHEAP_FUNC_PROLOGUE (pGC); + + if (pDrawable->type != DRAWABLE_PIXMAP && + ((WindowPtr) pDrawable)->backStorage != NULL && + miBSCreateGCPrivate (pGC)) + { + (*pGC->funcs->ValidateGC) (pGC, stateChanges, pDrawable); + } + else + { + (*pGC->funcs->ValidateGC) (pGC, stateChanges, pDrawable); + + /* rewrap funcs as Validate may have changed them */ + pGC->devPrivates[miBSGCIndex].ptr = (pointer) pGC->funcs; + + CHEAP_FUNC_EPILOGUE (pGC); + } +} + +static void +miBSCheapChangeGC (pGC, mask) + GCPtr pGC; + unsigned long mask; +{ + CHEAP_FUNC_PROLOGUE (pGC); + + (*pGC->funcs->ChangeGC) (pGC, mask); + + CHEAP_FUNC_EPILOGUE (pGC); +} + +static void +miBSCheapCopyGC (pGCSrc, mask, pGCDst) + GCPtr pGCSrc, pGCDst; + unsigned long mask; +{ + CHEAP_FUNC_PROLOGUE (pGCDst); + + (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst); + + CHEAP_FUNC_EPILOGUE (pGCDst); +} + +static void +miBSCheapDestroyGC (pGC) + GCPtr pGC; +{ + CHEAP_FUNC_PROLOGUE (pGC); + + (*pGC->funcs->DestroyGC) (pGC); + + /* leave it unwrapped */ +} + +static void +miBSCheapChangeClip (pGC, type, pvalue, nrects) + GCPtr pGC; + int type; + pointer pvalue; + int nrects; +{ + CHEAP_FUNC_PROLOGUE (pGC); + + (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects); + + CHEAP_FUNC_EPILOGUE (pGC); +} + +static void +miBSCheapCopyClip(pgcDst, pgcSrc) + GCPtr pgcDst, pgcSrc; +{ + CHEAP_FUNC_PROLOGUE (pgcDst); + + (* pgcDst->funcs->CopyClip)(pgcDst, pgcSrc); + + CHEAP_FUNC_EPILOGUE (pgcDst); +} + +static void +miBSCheapDestroyClip(pGC) + GCPtr pGC; +{ + CHEAP_FUNC_PROLOGUE (pGC); + + (* pGC->funcs->DestroyClip)(pGC); + + CHEAP_FUNC_EPILOGUE (pGC); +} + +/* + * create the full func/op wrappers for a GC + */ + +static Bool +miBSCreateGCPrivate (pGC) + GCPtr pGC; +{ + miBSGCRec *pPriv; + + pPriv = (miBSGCRec *) xalloc (sizeof (miBSGCRec)); + if (!pPriv) + return FALSE; + pPriv->pBackingGC = NULL; + pPriv->guarantee = GuaranteeNothing; + pPriv->serialNumber = 0; + pPriv->stateChanges = (1 << (GCLastBit + 1)) - 1; + pPriv->wrapOps = pGC->ops; + pPriv->wrapFuncs = pGC->funcs; + pGC->funcs = &miBSGCFuncs; + pGC->ops = &miBSGCOps; + pGC->devPrivates[miBSGCIndex].ptr = (pointer) pPriv; + return TRUE; +} + +static void +miBSDestroyGCPrivate (pGC) + GCPtr pGC; +{ + miBSGCRec *pPriv; + + pPriv = (miBSGCRec *) pGC->devPrivates[miBSGCIndex].ptr; + if (pPriv) + { + pGC->devPrivates[miBSGCIndex].ptr = (pointer) pPriv->wrapFuncs; + pGC->funcs = &miBSCheapGCFuncs; + pGC->ops = pPriv->wrapOps; + if (pPriv->pBackingGC) + FreeGC (pPriv->pBackingGC, (GContext) 0); + xfree ((pointer) pPriv); + } +} + +/* + * GC ops -- wrap each GC operation with our own function + */ + +/*- + *----------------------------------------------------------------------- + * miBSFillSpans -- + * Perform a FillSpans, routing output to backing-store as needed. + * + * Results: + * None. + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSFillSpans(pDrawable, pGC, nInit, pptInit, pwidthInit, fSorted) + DrawablePtr pDrawable; + GCPtr pGC; + int nInit; /* number of spans to fill */ + DDXPointPtr pptInit; /* pointer to list of start points */ + int *pwidthInit; /* pointer to list of n widths */ + int fSorted; +{ + DDXPointPtr pptCopy, pptReset; + int *pwidthCopy; + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + pptCopy = (DDXPointPtr)ALLOCATE_LOCAL(nInit*sizeof(DDXPointRec)); + pwidthCopy=(int *)ALLOCATE_LOCAL(nInit*sizeof(int)); + if (pptCopy && pwidthCopy) + { + copyData(pptInit, pptCopy, nInit, MoreCopy0); + memmove((char *)pwidthCopy,(char *)pwidthInit,nInit*sizeof(int)); + + (* pGC->ops->FillSpans)(pDrawable, pGC, nInit, pptInit, + pwidthInit, fSorted); + if (pGC->miTranslate) + { + int dx, dy; + int nReset; + + pptReset = pptCopy; + dx = pDrawable->x - pBackingDrawable->x; + dy = pDrawable->y - pBackingDrawable->y; + nReset = nInit; + while (nReset--) + { + pptReset->x -= dx; + pptReset->y -= dy; + ++pptReset; + } + } + (* pBackingGC->ops->FillSpans)(pBackingDrawable, + pBackingGC, nInit, pptCopy, pwidthCopy, + fSorted); + } + if (pwidthCopy) DEALLOCATE_LOCAL(pwidthCopy); + if (pptCopy) DEALLOCATE_LOCAL(pptCopy); + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSSetSpans -- + * Perform a SetSpans, routing output to backing-store as needed. + * + * Results: + * None. + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSSetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted) + DrawablePtr pDrawable; + GCPtr pGC; + char *psrc; + register DDXPointPtr ppt; + int *pwidth; + int nspans; + int fSorted; +{ + DDXPointPtr pptCopy, pptReset; + int *pwidthCopy; + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + pptCopy = (DDXPointPtr)ALLOCATE_LOCAL(nspans*sizeof(DDXPointRec)); + pwidthCopy=(int *)ALLOCATE_LOCAL(nspans*sizeof(int)); + if (pptCopy && pwidthCopy) + { + copyData(ppt, pptCopy, nspans, MoreCopy0); + memmove((char *)pwidthCopy,(char *)pwidth,nspans*sizeof(int)); + + (* pGC->ops->SetSpans)(pDrawable, pGC, psrc, ppt, pwidth, + nspans, fSorted); + if (pGC->miTranslate) + { + int dx, dy; + int nReset; + + pptReset = pptCopy; + dx = pDrawable->x - pBackingDrawable->x; + dy = pDrawable->y - pBackingDrawable->y; + nReset = nspans; + while (nReset--) + { + pptReset->x -= dx; + pptReset->y -= dy; + ++pptReset; + } + } + (* pBackingGC->ops->SetSpans)(pBackingDrawable, pBackingGC, + psrc, pptCopy, pwidthCopy, nspans, fSorted); + } + if (pwidthCopy) DEALLOCATE_LOCAL(pwidthCopy); + if (pptCopy) DEALLOCATE_LOCAL(pptCopy); + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSPutImage -- + * Perform a PutImage, routing output to backing-store as needed. + * + * Results: + * None. + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSPutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format, pBits) + DrawablePtr pDrawable; + GCPtr pGC; + int depth; + int x; + int y; + int w; + int h; + int leftPad; + int format; + char *pBits; +{ + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + (*pGC->ops->PutImage)(pDrawable, pGC, + depth, x, y, w, h, leftPad, format, pBits); + (*pBackingGC->ops->PutImage)(pBackingDrawable, pBackingGC, + depth, x - pBackingStore->x, y - pBackingStore->y, + w, h, leftPad, format, pBits); + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSDoCopy -- + * Perform a CopyArea or CopyPlane within a window that has backing + * store enabled. + * + * Results: + * TRUE if the copy was performed or FALSE if a regular one should + * be done. + * + * Side Effects: + * Things are copied (no s***!) + * + * Notes: + * The idea here is to form two regions that cover the source box. + * One contains the exposed rectangles while the other contains + * the obscured ones. An array of <box, drawable> pairs is then + * formed where the <box> indicates the area to be copied and the + * <drawable> indicates from where it is to be copied (exposed regions + * come from the screen while obscured ones come from the backing + * pixmap). The array 'sequence' is then filled with the indices of + * the pairs in the order in which they should be copied to prevent + * things from getting screwed up. A call is also made through the + * backingGC to take care of any copying into the backing pixmap. + * + *----------------------------------------------------------------------- + */ +static Bool +miBSDoCopy(pWin, pGC, srcx, srcy, w, h, dstx, dsty, plane, copyProc, ppRgn) + WindowPtr pWin; /* Window being scrolled */ + GCPtr pGC; /* GC we're called through */ + int srcx; /* X of source rectangle */ + int srcy; /* Y of source rectangle */ + int w; /* Width of source rectangle */ + int h; /* Height of source rectangle */ + int dstx; /* X of destination rectangle */ + int dsty; /* Y of destination rectangle */ + unsigned long plane; /* Plane to copy (0 for CopyArea) */ + RegionPtr (*copyProc)(); /* Procedure to call to perform the copy */ + RegionPtr *ppRgn; /* resultant Graphics Expose region */ +{ + RegionPtr pRgnExp; /* Exposed region */ + RegionPtr pRgnObs; /* Obscured region */ + BoxRec box; /* Source box (screen coord) */ + struct BoxDraw { + BoxPtr pBox; /* Source box */ + enum { + win, pix + } source; /* Place from which to copy */ + } *boxes; /* Array of box/drawable pairs covering + * source box. */ + int *sequence; /* Sequence of boxes to move */ + register int i, j, k, l, y; + register BoxPtr pBox; + int dx, dy, nrects; + Bool graphicsExposures; + RegionPtr (*pixCopyProc)(); + int numRectsExp, numRectsObs; + BoxPtr pBoxExp, pBoxObs; + + SETUP_BACKING (pWin, pGC); + + /* + * Create a region of exposed boxes in pRgnExp. + */ + box.x1 = srcx + pWin->drawable.x; + box.x2 = box.x1 + w; + box.y1 = srcy + pWin->drawable.y; + box.y2 = box.y1 + h; + + pRgnExp = REGION_CREATE(pGC->pScreen, &box, 1); + REGION_INTERSECT(pGC->pScreen, pRgnExp, pRgnExp, &pWin->clipList); + pRgnObs = REGION_CREATE(pGC->pScreen, NULL, 1); + REGION_INVERSE( pGC->pScreen, pRgnObs, pRgnExp, &box); + + /* + * Translate regions into window coordinates for proper calls + * to the copyProc, then make sure none of the obscured region sticks + * into invalid areas of the backing pixmap. + */ + REGION_TRANSLATE(pGC->pScreen, pRgnExp, + -pWin->drawable.x, + -pWin->drawable.y); + REGION_TRANSLATE(pGC->pScreen, pRgnObs, + -pWin->drawable.x, + -pWin->drawable.y); + REGION_INTERSECT(pGC->pScreen, pRgnObs, pRgnObs, &pBackingStore->SavedRegion); + + /* + * If the obscured region is empty, there's no point being fancy. + */ + if (!REGION_NOTEMPTY(pGC->pScreen, pRgnObs)) + { + REGION_DESTROY(pGC->pScreen, pRgnExp); + REGION_DESTROY(pGC->pScreen, pRgnObs); + + return (FALSE); + } + + numRectsExp = REGION_NUM_RECTS(pRgnExp); + pBoxExp = REGION_RECTS(pRgnExp); + pBoxObs = REGION_RECTS(pRgnObs); + numRectsObs = REGION_NUM_RECTS(pRgnObs); + nrects = numRectsExp + numRectsObs; + + boxes = (struct BoxDraw *)ALLOCATE_LOCAL(nrects * sizeof(struct BoxDraw)); + sequence = (int *) ALLOCATE_LOCAL(nrects * sizeof(int)); + *ppRgn = NULL; + + if (!boxes || !sequence) + { + if (sequence) DEALLOCATE_LOCAL(sequence); + if (boxes) DEALLOCATE_LOCAL(boxes); + REGION_DESTROY(pGC->pScreen, pRgnExp); + REGION_DESTROY(pGC->pScreen, pRgnObs); + + return(TRUE); + } + + /* + * Order the boxes in the two regions so we know from which drawable + * to copy which box, storing the result in the boxes array + */ + for (i = 0, j = 0, k = 0; + (i < numRectsExp) && (j < numRectsObs); + k++) + { + if (pBoxExp[i].y1 < pBoxObs[j].y1) + { + boxes[k].pBox = &pBoxExp[i]; + boxes[k].source = win; + i++; + } + else if ((pBoxObs[j].y1 < pBoxExp[i].y1) || + (pBoxObs[j].x1 < pBoxExp[i].x1)) + { + boxes[k].pBox = &pBoxObs[j]; + boxes[k].source = pix; + j++; + } + else + { + boxes[k].pBox = &pBoxExp[i]; + boxes[k].source = win; + i++; + } + } + + /* + * Catch any leftover boxes from either region (note that only + * one can have leftover boxes...) + */ + if (i != numRectsExp) + { + do + { + boxes[k].pBox = &pBoxExp[i]; + boxes[k].source = win; + i++; + k++; + } while (i < numRectsExp); + + } + else + { + do + { + boxes[k].pBox = &pBoxObs[j]; + boxes[k].source = pix; + j++; + k++; + } while (j < numRectsObs); + } + + if (dsty <= srcy) + { + /* + * Scroll up or vertically stationary, so vertical order is ok. + */ + if (dstx <= srcx) + { + /* + * Scroll left or horizontally stationary, so horizontal order + * is ok as well. + */ + for (i = 0; i < nrects; i++) + { + sequence[i] = i; + } + } + else + { + /* + * Scroll right. Need to reverse the rectangles within each + * band. + */ + for (i = 0, j = 1, k = 0; + i < nrects; + j = i + 1, k = i) + { + y = boxes[i].pBox->y1; + while ((j < nrects) && (boxes[j].pBox->y1 == y)) + { + j++; + } + for (j--; j >= k; j--, i++) + { + sequence[i] = j; + } + } + } + } + else + { + /* + * Scroll down. Must reverse vertical banding, at least. + */ + if (dstx < srcx) + { + /* + * Scroll left. Horizontal order is ok. + */ + for (i = nrects - 1, j = i - 1, k = i, l = 0; + i >= 0; + j = i - 1, k = i) + { + /* + * Find extent of current horizontal band, then reverse + * the order of the whole band. + */ + y = boxes[i].pBox->y1; + while ((j >= 0) && (boxes[j].pBox->y1 == y)) + { + j--; + } + for (j++; j <= k; j++, i--, l++) + { + sequence[l] = j; + } + } + } + else + { + /* + * Scroll right or horizontal stationary. + * Reverse horizontal order as well (if stationary, horizontal + * order can be swapped without penalty and this is faster + * to compute). + */ + for (i = 0, j = nrects - 1; i < nrects; i++, j--) + { + sequence[i] = j; + } + } + } + + /* + * XXX: To avoid getting multiple NoExpose events from this operation, + * we turn OFF graphicsExposures in the gc and deal with any uncopied + * areas later, if there's something not in backing-store. + */ + + graphicsExposures = pGC->graphicsExposures; + pGC->graphicsExposures = FALSE; + + dx = dstx - srcx; + dy = dsty - srcy; + + /* + * Figure out which copy procedure to use from the backing GC. Note we + * must do this because some implementations (sun's, e.g.) have + * pBackingGC a fake GC with the real one below it, thus the devPriv for + * pBackingGC won't be what the output library expects. + */ + if (plane != 0) + { + pixCopyProc = pBackingGC->ops->CopyPlane; + } + else + { + pixCopyProc = pBackingGC->ops->CopyArea; + } + + for (i = 0; i < nrects; i++) + { + pBox = boxes[sequence[i]].pBox; + + /* + * If we're copying from the pixmap, we need to place its contents + * onto the screen before scrolling the pixmap itself. If we're copying + * from the window, we need to copy its contents into the pixmap before + * we scroll the window itself. + */ + if (boxes[sequence[i]].source == pix) + { + (void) (* copyProc) (pBackingDrawable, pWin, pGC, + pBox->x1 - pBackingStore->x, + pBox->y1 - pBackingStore->y, + pBox->x2 - pBox->x1, pBox->y2 - pBox->y1, + pBox->x1 + dx, pBox->y1 + dy, plane); + (void) (* pixCopyProc) (pBackingDrawable, pBackingDrawable, pBackingGC, + pBox->x1 - pBackingStore->x, + pBox->y1 - pBackingStore->y, + pBox->x2 - pBox->x1, pBox->y2 - pBox->y1, + pBox->x1 + dx - pBackingStore->x, + pBox->y1 + dy - pBackingStore->y, plane); + } + else + { + (void) (* pixCopyProc) (pWin, pBackingDrawable, pBackingGC, + pBox->x1, pBox->y1, + pBox->x2 - pBox->x1, pBox->y2 - pBox->y1, + pBox->x1 + dx - pBackingStore->x, + pBox->y1 + dy - pBackingStore->y, plane); + (void) (* copyProc) (pWin, pWin, pGC, + pBox->x1, pBox->y1, + pBox->x2 - pBox->x1, pBox->y2 - pBox->y1, + pBox->x1 + dx, pBox->y1 + dy, plane); + } + } + DEALLOCATE_LOCAL(sequence); + DEALLOCATE_LOCAL(boxes); + + pGC->graphicsExposures = graphicsExposures; + /* + * Form union of rgnExp and rgnObs and see if covers entire area + * to be copied. Store the resultant region for miBSCopyArea + * to return to dispatch which will send the appropriate expose + * events. + */ + REGION_UNION(pGC->pScreen, pRgnExp, pRgnExp, pRgnObs); + box.x1 = srcx; + box.x2 = srcx + w; + box.y1 = srcy; + box.y2 = srcy + h; + if (RECT_IN_REGION(pGC->pScreen, pRgnExp, &box) == rgnIN) + { + REGION_EMPTY(pGC->pScreen, pRgnExp); + } + else + { + REGION_INVERSE( pGC->pScreen, pRgnExp, pRgnExp, &box); + REGION_TRANSLATE( pGC->pScreen, pRgnExp, + dx + pWin->drawable.x, + dy + pWin->drawable.y); + REGION_INTERSECT( pGC->pScreen, pRgnObs, pRgnExp, &pWin->clipList); + (*pWin->drawable.pScreen->PaintWindowBackground) (pWin, + pRgnObs, PW_BACKGROUND); + REGION_TRANSLATE( pGC->pScreen, pRgnExp, + -pWin->drawable.x, + -pWin->drawable.y); + miBSClearBackingRegion (pWin, pRgnExp); + } + if (graphicsExposures) + *ppRgn = pRgnExp; + else + REGION_DESTROY(pGC->pScreen, pRgnExp); + REGION_DESTROY(pGC->pScreen, pRgnObs); + + return (TRUE); +} + +/*- + *----------------------------------------------------------------------- + * miBSCopyArea -- + * Perform a CopyArea from the source to the destination, extracting + * from the source's backing-store and storing into the destination's + * backing-store without messing anything up. If the source and + * destination are different, there's not too much to worry about: + * we can just issue several calls to the regular CopyArea function. + * + * Results: + * None. + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static RegionPtr +miBSCopyArea (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty) + DrawablePtr pSrc; + DrawablePtr pDst; + GCPtr pGC; + int srcx; + int srcy; + int w; + int h; + int dstx; + int dsty; +{ + BoxPtr pExtents; + long dx, dy; + int bsrcx, bsrcy, bw, bh, bdstx, bdsty; + RegionPtr pixExposed = 0, winExposed = 0; + + SETUP_BACKING(pDst, pGC); + + PROLOGUE(pGC); + + if ((pSrc != pDst) || + (!miBSDoCopy((WindowPtr)pSrc, pGC, srcx, srcy, w, h, dstx, dsty, + (unsigned long) 0, pGC->ops->CopyArea, &winExposed))) + { + /* + * always copy to the backing store first, miBSDoCopy + * returns FALSE if the *source* region is disjoint + * from the backing store saved region. So, copying + * *to* the backing store is always safe + */ + if (pGC->clientClipType != CT_PIXMAP) + { + /* + * adjust srcx, srcy, w, h, dstx, dsty to be clipped to + * the backing store. An unnecessary optimisation, + * but a useful one when GetSpans is slow. + */ + pExtents = REGION_EXTENTS(pDst->pScreen, + (RegionPtr)pBackingGC->clientClip); + bsrcx = srcx; + bsrcy = srcy; + bw = w; + bh = h; + bdstx = dstx; + bdsty = dsty; + dx = pExtents->x1 - bdstx; + if (dx > 0) + { + bsrcx += dx; + bdstx += dx; + bw -= dx; + } + dy = pExtents->y1 - bdsty; + if (dy > 0) + { + bsrcy += dy; + bdsty += dy; + bh -= dy; + } + dx = (bdstx + bw) - pExtents->x2; + if (dx > 0) + bw -= dx; + dy = (bdsty + bh) - pExtents->y2; + if (dy > 0) + bh -= dy; + if (bw > 0 && bh > 0) + pixExposed = (* pBackingGC->ops->CopyArea) (pSrc, + pBackingDrawable, pBackingGC, + bsrcx, bsrcy, bw, bh, bdstx - pBackingStore->x, + bdsty - pBackingStore->y); + } + else + pixExposed = (* pBackingGC->ops->CopyArea) (pSrc, + pBackingDrawable, pBackingGC, + srcx, srcy, w, h, + dstx - pBackingStore->x, dsty - pBackingStore->y); + + winExposed = (* pGC->ops->CopyArea) (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty); + } + + /* + * compute the composite graphics exposure region + */ + if (winExposed) + { + if (pixExposed){ + REGION_UNION(pDst->pScreen, winExposed, winExposed, pixExposed); + REGION_DESTROY(pDst->pScreen, pixExposed); + } + } else + winExposed = pixExposed; + + EPILOGUE (pGC); + + return winExposed; +} + +/*- + *----------------------------------------------------------------------- + * miBSCopyPlane -- + * + * Results: + * None. + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static RegionPtr +miBSCopyPlane (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, plane) + DrawablePtr pSrc; + DrawablePtr pDst; + register GC *pGC; + int srcx, + srcy; + int w, + h; + int dstx, + dsty; + unsigned long plane; +{ + BoxPtr pExtents; + long dx, dy; + int bsrcx, bsrcy, bw, bh, bdstx, bdsty; + RegionPtr winExposed = 0, pixExposed = 0; + SETUP_BACKING(pDst, pGC); + + PROLOGUE(pGC); + + if ((pSrc != pDst) || + (!miBSDoCopy((WindowPtr)pSrc, pGC, srcx, srcy, w, h, dstx, dsty, + plane, pGC->ops->CopyPlane, &winExposed))) + { + /* + * always copy to the backing store first, miBSDoCopy + * returns FALSE if the *source* region is disjoint + * from the backing store saved region. So, copying + * *to* the backing store is always safe + */ + if (pGC->clientClipType != CT_PIXMAP) + { + /* + * adjust srcx, srcy, w, h, dstx, dsty to be clipped to + * the backing store. An unnecessary optimisation, + * but a useful one when GetSpans is slow. + */ + pExtents = REGION_EXTENTS(pDst->pScreen, + (RegionPtr)pBackingGC->clientClip); + bsrcx = srcx; + bsrcy = srcy; + bw = w; + bh = h; + bdstx = dstx; + bdsty = dsty; + dx = pExtents->x1 - bdstx; + if (dx > 0) + { + bsrcx += dx; + bdstx += dx; + bw -= dx; + } + dy = pExtents->y1 - bdsty; + if (dy > 0) + { + bsrcy += dy; + bdsty += dy; + bh -= dy; + } + dx = (bdstx + bw) - pExtents->x2; + if (dx > 0) + bw -= dx; + dy = (bdsty + bh) - pExtents->y2; + if (dy > 0) + bh -= dy; + if (bw > 0 && bh > 0) + pixExposed = (* pBackingGC->ops->CopyPlane) (pSrc, + pBackingDrawable, + pBackingGC, bsrcx, bsrcy, bw, bh, + bdstx - pBackingStore->x, + bdsty - pBackingStore->y, plane); + } + else + pixExposed = (* pBackingGC->ops->CopyPlane) (pSrc, + pBackingDrawable, + pBackingGC, srcx, srcy, w, h, + dstx - pBackingStore->x, + dsty - pBackingStore->y, plane); + + winExposed = (* pGC->ops->CopyPlane) (pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty, plane); + + } + + /* + * compute the composite graphics exposure region + */ + if (winExposed) + { + if (pixExposed) + { + REGION_UNION(pDst->pScreen, winExposed, winExposed, pixExposed); + REGION_DESTROY(pDst->pScreen, pixExposed); + } + } else + winExposed = pixExposed; + + EPILOGUE (pGC); + + return winExposed; +} + +/*- + *----------------------------------------------------------------------- + * miBSPolyPoint -- + * Perform a PolyPoint, routing output to backing-store as needed. + * + * Results: + * None. + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSPolyPoint (pDrawable, pGC, mode, npt, pptInit) + DrawablePtr pDrawable; + GCPtr pGC; + int mode; /* Origin or Previous */ + int npt; + xPoint *pptInit; +{ + xPoint *pptCopy; + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + pptCopy = (xPoint *)ALLOCATE_LOCAL(npt*sizeof(xPoint)); + if (pptCopy) + { + copyPoints(pptInit, pptCopy, npt, mode); + + (* pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pptInit); + + (* pBackingGC->ops->PolyPoint) (pBackingDrawable, + pBackingGC, mode, npt, pptCopy); + + DEALLOCATE_LOCAL(pptCopy); + } + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSPolyLines -- + * Perform a Polylines, routing output to backing-store as needed. + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSPolylines (pDrawable, pGC, mode, npt, pptInit) + DrawablePtr pDrawable; + GCPtr pGC; + int mode; + int npt; + DDXPointPtr pptInit; +{ + DDXPointPtr pptCopy; + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + pptCopy = (DDXPointPtr)ALLOCATE_LOCAL(npt*sizeof(DDXPointRec)); + if (pptCopy) + { + copyPoints(pptInit, pptCopy, npt, mode); + + (* pGC->ops->Polylines)(pDrawable, pGC, mode, npt, pptInit); + (* pBackingGC->ops->Polylines)(pBackingDrawable, + pBackingGC, mode, npt, pptCopy); + DEALLOCATE_LOCAL(pptCopy); + } + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSPolySegment -- + * Perform a PolySegment, routing output to backing-store as needed. + * + * Results: + * None. + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSPolySegment(pDrawable, pGC, nseg, pSegs) + DrawablePtr pDrawable; + GCPtr pGC; + int nseg; + xSegment *pSegs; +{ + xSegment *pSegsCopy; + + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + pSegsCopy = (xSegment *)ALLOCATE_LOCAL(nseg*sizeof(xSegment)); + if (pSegsCopy) + { + copyData(pSegs, pSegsCopy, nseg << 1, MoreCopy0); + + (* pGC->ops->PolySegment)(pDrawable, pGC, nseg, pSegs); + (* pBackingGC->ops->PolySegment)(pBackingDrawable, + pBackingGC, nseg, pSegsCopy); + + DEALLOCATE_LOCAL(pSegsCopy); + } + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSPolyRectangle -- + * Perform a PolyRectangle, routing output to backing-store as needed. + * + * Results: + * None + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSPolyRectangle(pDrawable, pGC, nrects, pRects) + DrawablePtr pDrawable; + GCPtr pGC; + int nrects; + xRectangle *pRects; +{ + xRectangle *pRectsCopy; + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + pRectsCopy =(xRectangle *)ALLOCATE_LOCAL(nrects*sizeof(xRectangle)); + if (pRectsCopy) + { + copyData(pRects, pRectsCopy, nrects, MoreCopy2); + + (* pGC->ops->PolyRectangle)(pDrawable, pGC, nrects, pRects); + (* pBackingGC->ops->PolyRectangle)(pBackingDrawable, + pBackingGC, nrects, pRectsCopy); + + DEALLOCATE_LOCAL(pRectsCopy); + } + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSPolyArc -- + * Perform a PolyArc, routing output to backing-store as needed. + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSPolyArc(pDrawable, pGC, narcs, parcs) + DrawablePtr pDrawable; + GCPtr pGC; + int narcs; + xArc *parcs; +{ + xArc *pArcsCopy; + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + pArcsCopy = (xArc *)ALLOCATE_LOCAL(narcs*sizeof(xArc)); + if (pArcsCopy) + { + copyData(parcs, pArcsCopy, narcs, MoreCopy4); + + (* pGC->ops->PolyArc)(pDrawable, pGC, narcs, parcs); + (* pBackingGC->ops->PolyArc)(pBackingDrawable, pBackingGC, + narcs, pArcsCopy); + + DEALLOCATE_LOCAL(pArcsCopy); + } + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSFillPolygon -- + * Perform a FillPolygon, routing output to backing-store as needed. + * + * Results: + * None. + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSFillPolygon(pDrawable, pGC, shape, mode, count, pPts) + DrawablePtr pDrawable; + register GCPtr pGC; + int shape, mode; + register int count; + DDXPointPtr pPts; +{ + DDXPointPtr pPtsCopy; + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + pPtsCopy = (DDXPointPtr)ALLOCATE_LOCAL(count*sizeof(DDXPointRec)); + if (pPtsCopy) + { + copyPoints(pPts, pPtsCopy, count, mode); + (* pGC->ops->FillPolygon)(pDrawable, pGC, shape, mode, count, pPts); + (* pBackingGC->ops->FillPolygon)(pBackingDrawable, + pBackingGC, shape, mode, + count, pPtsCopy); + + DEALLOCATE_LOCAL(pPtsCopy); + } + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSPolyFillRect -- + * Perform a PolyFillRect, routing output to backing-store as needed. + * + * Results: + * None. + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSPolyFillRect(pDrawable, pGC, nrectFill, prectInit) + DrawablePtr pDrawable; + GCPtr pGC; + int nrectFill; /* number of rectangles to fill */ + xRectangle *prectInit; /* Pointer to first rectangle to fill */ +{ + xRectangle *pRectCopy; + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + pRectCopy = + (xRectangle *)ALLOCATE_LOCAL(nrectFill*sizeof(xRectangle)); + if (pRectCopy) + { + copyData(prectInit, pRectCopy, nrectFill, MoreCopy2); + + (* pGC->ops->PolyFillRect)(pDrawable, pGC, nrectFill, prectInit); + (* pBackingGC->ops->PolyFillRect)(pBackingDrawable, + pBackingGC, nrectFill, pRectCopy); + + DEALLOCATE_LOCAL(pRectCopy); + } + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSPolyFillArc -- + * Perform a PolyFillArc, routing output to backing-store as needed. + * + * Results: + * None. + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSPolyFillArc(pDrawable, pGC, narcs, parcs) + DrawablePtr pDrawable; + GCPtr pGC; + int narcs; + xArc *parcs; +{ + xArc *pArcsCopy; + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + pArcsCopy = (xArc *)ALLOCATE_LOCAL(narcs*sizeof(xArc)); + if (pArcsCopy) + { + copyData(parcs, pArcsCopy, narcs, MoreCopy4); + (* pGC->ops->PolyFillArc)(pDrawable, pGC, narcs, parcs); + (* pBackingGC->ops->PolyFillArc)(pBackingDrawable, + pBackingGC, narcs, pArcsCopy); + DEALLOCATE_LOCAL(pArcsCopy); + } + + EPILOGUE (pGC); +} + + +/*- + *----------------------------------------------------------------------- + * miBSPolyText8 -- + * Perform a PolyText8, routing output to backing-store as needed. + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static int +miBSPolyText8(pDrawable, pGC, x, y, count, chars) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + int count; + char *chars; +{ + int result; + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + result = (* pGC->ops->PolyText8)(pDrawable, pGC, x, y, count, chars); + (* pBackingGC->ops->PolyText8)(pBackingDrawable, pBackingGC, + x - pBackingStore->x, y - pBackingStore->y, + count, chars); + + EPILOGUE (pGC); + return result; +} + +/*- + *----------------------------------------------------------------------- + * miBSPolyText16 -- + * Perform a PolyText16, routing output to backing-store as needed. + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static int +miBSPolyText16(pDrawable, pGC, x, y, count, chars) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + int count; + unsigned short *chars; +{ + int result; + SETUP_BACKING (pDrawable, pGC); + + PROLOGUE(pGC); + + result = (* pGC->ops->PolyText16)(pDrawable, pGC, x, y, count, chars); + (* pBackingGC->ops->PolyText16)(pBackingDrawable, pBackingGC, + x - pBackingStore->x, y - pBackingStore->y, + count, chars); + + EPILOGUE (pGC); + + return result; +} + +/*- + *----------------------------------------------------------------------- + * miBSImageText8 -- + * Perform a ImageText8, routing output to backing-store as needed. + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSImageText8(pDrawable, pGC, x, y, count, chars) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + int count; + char *chars; +{ + SETUP_BACKING (pDrawable, pGC); + PROLOGUE(pGC); + + (* pGC->ops->ImageText8)(pDrawable, pGC, x, y, count, chars); + (* pBackingGC->ops->ImageText8)(pBackingDrawable, pBackingGC, + x - pBackingStore->x, y - pBackingStore->y, + count, chars); + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSImageText16 -- + * Perform a ImageText16, routing output to backing-store as needed. + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSImageText16(pDrawable, pGC, x, y, count, chars) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + int count; + unsigned short *chars; +{ + SETUP_BACKING (pDrawable, pGC); + PROLOGUE(pGC); + + (* pGC->ops->ImageText16)(pDrawable, pGC, x, y, count, chars); + (* pBackingGC->ops->ImageText16)(pBackingDrawable, pBackingGC, + x - pBackingStore->x, y - pBackingStore->y, + count, chars); + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSImageGlyphBlt -- + * Perform a ImageGlyphBlt, routing output to backing-store as needed. + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + unsigned int nglyph; + CharInfoPtr *ppci; /* array of character info */ + pointer pglyphBase; /* start of array of glyphs */ +{ + SETUP_BACKING (pDrawable, pGC); + PROLOGUE(pGC); + + (* pGC->ops->ImageGlyphBlt)(pDrawable, pGC, x, y, nglyph, ppci, + pglyphBase); + (* pBackingGC->ops->ImageGlyphBlt)(pBackingDrawable, pBackingGC, + x - pBackingStore->x, y - pBackingStore->y, + nglyph, ppci, pglyphBase); + + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSPolyGlyphBlt -- + * Perform a PolyGlyphBlt, routing output to backing-store as needed. + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSPolyGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + unsigned int nglyph; + CharInfoPtr *ppci; /* array of character info */ + pointer pglyphBase; /* start of array of glyphs */ +{ + SETUP_BACKING (pDrawable, pGC); + PROLOGUE(pGC); + + (* pGC->ops->PolyGlyphBlt)(pDrawable, pGC, x, y, nglyph, + ppci, pglyphBase); + (* pBackingGC->ops->PolyGlyphBlt)(pBackingDrawable, pBackingGC, + x - pBackingStore->x, y - pBackingStore->y, + nglyph, ppci, pglyphBase); + EPILOGUE (pGC); +} + +/*- + *----------------------------------------------------------------------- + * miBSPushPixels -- + * Perform a PushPixels, routing output to backing-store as needed. + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +miBSPushPixels(pGC, pBitMap, pDst, w, h, x, y) + GCPtr pGC; + PixmapPtr pBitMap; + DrawablePtr pDst; + int w, h, x, y; +{ + SETUP_BACKING (pDst, pGC); + PROLOGUE(pGC); + + (* pGC->ops->PushPixels)(pGC, pBitMap, pDst, w, h, x, y); + if (pGC->miTranslate) { + x -= pDst->x; + y -= pDst->y; + } + (* pBackingGC->ops->PushPixels)(pBackingGC, pBitMap, + pBackingDrawable, w, h, + x - pBackingStore->x, y - pBackingStore->y); + + EPILOGUE (pGC); +} + +#ifdef NEED_LINEHELPER +/*- + *----------------------------------------------------------------------- + * miBSLineHelper -- + * + * Results: should never be called + * + * Side Effects: server dies + * + *----------------------------------------------------------------------- + */ +static void +miBSLineHelper() +{ + FatalError("miBSLineHelper called\n"); +} +#endif + +/*- + *----------------------------------------------------------------------- + * miBSClearBackingStore -- + * Clear the given area of the backing pixmap with the background of + * the window, whatever it is. If generateExposures is TRUE, generate + * exposure events for the area. Note that if the area has any + * part outside the saved portions of the window, we do not allow the + * count in the expose events to be 0, since there will be more + * expose events to come. + * + * Results: + * None. + * + * Side Effects: + * Areas of pixmap are cleared and Expose events are generated. + * + *----------------------------------------------------------------------- + */ +static RegionPtr +miBSClearBackingStore(pWin, x, y, w, h, generateExposures) + WindowPtr pWin; + int x; + int y; + int w; + int h; + Bool generateExposures; +{ + RegionPtr pRgn; + int i; + miBSWindowPtr pBackingStore; + ScreenPtr pScreen; + GCPtr pGC; + int ts_x_origin, + ts_y_origin; + pointer gcvalues[4]; + unsigned long gcmask; + xRectangle *rects; + BoxPtr pBox; + BoxRec box; + PixUnion background; + char backgroundState; + int numRects; + + pBackingStore = (miBSWindowPtr)pWin->backStorage; + pScreen = pWin->drawable.pScreen; + + if ((pBackingStore->status == StatusNoPixmap) || + (pBackingStore->status == StatusBadAlloc)) + return NullRegion; + + if (w == 0) + w = (int) pWin->drawable.width - x; + if (h == 0) + h = (int) pWin->drawable.height - y; + + box.x1 = x; + box.y1 = y; + box.x2 = x + w; + box.y2 = y + h; + pRgn = REGION_CREATE(pWin->drawable.pScreen, &box, 1); + if (!pRgn) + return NullRegion; + REGION_INTERSECT( pScreen, pRgn, pRgn, &pBackingStore->SavedRegion); + + if (REGION_NOTEMPTY( pScreen, pRgn)) + { + /* + * if clearing entire window, simply make new virtual + * tile. For the root window, we also destroy the pixmap + * to save a pile of memory + */ + if (x == 0 && y == 0 && + w == pWin->drawable.width && + h == pWin->drawable.height) + { + if (!pWin->parent) + miDestroyBSPixmap (pWin); + if (pBackingStore->status != StatusContents) + miTileVirtualBS (pWin); + } + + ts_x_origin = ts_y_origin = 0; + + backgroundState = pWin->backgroundState; + background = pWin->background; + if (backgroundState == ParentRelative) { + WindowPtr pParent; + + pParent = pWin; + while (pParent->backgroundState == ParentRelative) { + ts_x_origin -= pParent->origin.x; + ts_y_origin -= pParent->origin.y; + pParent = pParent->parent; + } + backgroundState = pParent->backgroundState; + background = pParent->background; + } + + if ((backgroundState != None) && + ((pBackingStore->status == StatusContents) || + !SameBackground (pBackingStore->backgroundState, + pBackingStore->background, + backgroundState, + background))) + { + if (!pBackingStore->pBackingPixmap) + miCreateBSPixmap(pWin, NullBox); + + pGC = GetScratchGC(pWin->drawable.depth, pScreen); + if (pGC && pBackingStore->pBackingPixmap) + { + /* + * First take care of any ParentRelative stuff by altering the + * tile/stipple origin to match the coordinates of the upper-left + * corner of the first ancestor without a ParentRelative background. + * This coordinate is, of course, negative. + */ + + if (backgroundState == BackgroundPixel) + { + gcvalues[0] = (pointer) background.pixel; + gcvalues[1] = (pointer)FillSolid; + gcmask = GCForeground|GCFillStyle; + } + else + { + gcvalues[0] = (pointer)FillTiled; + gcvalues[1] = (pointer) background.pixmap; + gcmask = GCFillStyle|GCTile; + } + gcvalues[2] = (pointer)(ts_x_origin - pBackingStore->x); + gcvalues[3] = (pointer)(ts_y_origin - pBackingStore->y); + gcmask |= GCTileStipXOrigin|GCTileStipYOrigin; + DoChangeGC(pGC, gcmask, (XID *)gcvalues, TRUE); + ValidateGC((DrawablePtr)pBackingStore->pBackingPixmap, pGC); + + /* + * Figure out the array of rectangles to fill and fill them with + * PolyFillRect in the proper mode, as set in the GC above. + */ + numRects = REGION_NUM_RECTS(pRgn); + rects = (xRectangle *)ALLOCATE_LOCAL(numRects*sizeof(xRectangle)); + + if (rects) + { + for (i = 0, pBox = REGION_RECTS(pRgn); + i < numRects; + i++, pBox++) + { + rects[i].x = pBox->x1 - pBackingStore->x; + rects[i].y = pBox->y1 - pBackingStore->y; + rects[i].width = pBox->x2 - pBox->x1; + rects[i].height = pBox->y2 - pBox->y1; + } + (* pGC->ops->PolyFillRect) ( + (DrawablePtr)pBackingStore->pBackingPixmap, + pGC, numRects, rects); + DEALLOCATE_LOCAL(rects); + } + FreeScratchGC(pGC); + } + } + + if (!generateExposures) + { + REGION_DESTROY(pScreen, pRgn); + pRgn = NULL; + } + else + { + /* + * result must be screen relative, but is currently + * drawable relative. + */ + REGION_TRANSLATE(pScreen, pRgn, pWin->drawable.x, + pWin->drawable.y); + } + } + else + { + REGION_DESTROY( pScreen, pRgn); + pRgn = NULL; + } + return pRgn; +} + +static void +miBSClearBackingRegion (pWin, pRgn) + WindowPtr pWin; + RegionPtr pRgn; +{ + BoxPtr pBox; + int i; + + i = REGION_NUM_RECTS(pRgn); + pBox = REGION_RECTS(pRgn); + while (i--) + { + (void) miBSClearBackingStore(pWin, pBox->x1, pBox->y1, + pBox->x2 - pBox->x1, + pBox->y2 - pBox->y1, + FALSE); + pBox++; + } +} + +/* + * fill a region of the destination with virtual bits + * + * pRgn is to be translated by (x,y) + */ + +static void +miBSFillVirtualBits (pDrawable, pGC, pRgn, x, y, state, pixunion, planeMask) + DrawablePtr pDrawable; + GCPtr pGC; + RegionPtr pRgn; + int x, y; + int state; + PixUnion pixunion; + unsigned long planeMask; +{ + int i; + BITS32 gcmask; + pointer gcval[5]; + xRectangle *pRect; + BoxPtr pBox; + WindowPtr pWin; + int numRects; + + if (state == None) + return; + numRects = REGION_NUM_RECTS(pRgn); + pRect = (xRectangle *)ALLOCATE_LOCAL(numRects * sizeof(xRectangle)); + if (!pRect) + return; + pWin = 0; + if (pDrawable->type != DRAWABLE_PIXMAP) + { + pWin = (WindowPtr) pDrawable; + if (!pWin->backStorage) + pWin = 0; + } + i = 0; + gcmask = 0; + gcval[i++] = (pointer)planeMask; + gcmask |= GCPlaneMask; + if (state == BackgroundPixel) + { + if (pGC->fgPixel != pixunion.pixel) + { + gcval[i++] = (pointer)pixunion.pixel; + gcmask |= GCForeground; + } + if (pGC->fillStyle != FillSolid) + { + gcval[i++] = (pointer)FillSolid; + gcmask |= GCFillStyle; + } + } + else + { + if (pGC->fillStyle != FillTiled) + { + gcval[i++] = (pointer)FillTiled; + gcmask |= GCFillStyle; + } + if (pGC->tileIsPixel || pGC->tile.pixmap != pixunion.pixmap) + { + gcval[i++] = (pointer)pixunion.pixmap; + gcmask |= GCTile; + } + if (pGC->patOrg.x != x) + { + gcval[i++] = (pointer)x; + gcmask |= GCTileStipXOrigin; + } + if (pGC->patOrg.y != y) + { + gcval[i++] = (pointer)y; + gcmask |= GCTileStipYOrigin; + } + } + if (gcmask) + DoChangeGC (pGC, gcmask, (XID *)gcval, 1); + + if (pWin) + (*pWin->drawable.pScreen->DrawGuarantee) (pWin, pGC, GuaranteeVisBack); + + if (pDrawable->serialNumber != pGC->serialNumber) + ValidateGC (pDrawable, pGC); + + pBox = REGION_RECTS(pRgn); + for (i = numRects; --i >= 0; pBox++, pRect++) + { + pRect->x = pBox->x1 + x; + pRect->y = pBox->y1 + y; + pRect->width = pBox->x2 - pBox->x1; + pRect->height = pBox->y2 - pBox->y1; + } + pRect -= numRects; + (*pGC->ops->PolyFillRect) (pDrawable, pGC, numRects, pRect); + if (pWin) + (*pWin->drawable.pScreen->DrawGuarantee) (pWin, pGC, GuaranteeNothing); + DEALLOCATE_LOCAL (pRect); +} + +/*- + *----------------------------------------------------------------------- + * miBSAllocate -- + * Create and install backing store info for a window + * + *----------------------------------------------------------------------- + */ + +static void +miBSAllocate(pWin) + WindowPtr pWin; +{ + register miBSWindowPtr pBackingStore; + register ScreenPtr pScreen; + + if (pWin->drawable.pScreen->backingStoreSupport == NotUseful) + return; + pScreen = pWin->drawable.pScreen; + if (!(pBackingStore = (miBSWindowPtr)pWin->backStorage)) + { + + pBackingStore = (miBSWindowPtr)xalloc(sizeof(miBSWindowRec)); + if (!pBackingStore) + return; + + pBackingStore->pBackingPixmap = NullPixmap; + pBackingStore->x = 0; + pBackingStore->y = 0; + REGION_INIT( pScreen, &pBackingStore->SavedRegion, NullBox, 1); + pBackingStore->viewable = (char)pWin->viewable; + pBackingStore->status = StatusNoPixmap; + pBackingStore->backgroundState = None; + pWin->backStorage = (pointer) pBackingStore; + } + + /* + * Now want to initialize the backing pixmap and SavedRegion if + * necessary. The initialization consists of finding all the + * currently-obscured regions, by taking the inverse of the window's + * clip list, storing the result in SavedRegion, and exposing those + * areas of the window. + */ + + if (pBackingStore->status == StatusNoPixmap && + ((pWin->backingStore == WhenMapped && pWin->viewable) || + (pWin->backingStore == Always))) + { + BoxRec box; + RegionPtr pSavedRegion; + + pSavedRegion = &pBackingStore->SavedRegion; + + box.x1 = pWin->drawable.x; + box.x2 = box.x1 + (int) pWin->drawable.width; + box.y1 = pWin->drawable.y; + box.y2 = pWin->drawable.y + (int) pWin->drawable.height; + + REGION_INVERSE( pScreen, pSavedRegion, &pWin->clipList, &box); + REGION_TRANSLATE( pScreen, pSavedRegion, + -pWin->drawable.x, + -pWin->drawable.y); +#ifdef SHAPE + if (wBoundingShape (pWin)) + REGION_INTERSECT(pScreen, pSavedRegion, pSavedRegion, + wBoundingShape (pWin)); + if (wClipShape (pWin)) + REGION_INTERSECT(pScreen, pSavedRegion, pSavedRegion, + wClipShape (pWin)); +#endif + /* if window is already on-screen, assume it has been drawn to */ + if (pWin->viewable) + pBackingStore->status = StatusVDirty; + miTileVirtualBS (pWin); + + /* + * deliver all the newly available regions + * as exposure events to the window + */ + + miSendExposures(pWin, pSavedRegion, 0, 0); + } + else if (!pWin->viewable) + { + /* + * Turn off backing store when we're not supposed to + * be saving anything + */ + if (pBackingStore->status != StatusNoPixmap) + { + REGION_EMPTY( pScreen, &pBackingStore->SavedRegion); + miDestroyBSPixmap (pWin); + } + } +} + +/*- + *----------------------------------------------------------------------- + * miBSFree -- + * Destroy and free all the stuff associated with the backing-store + * for the given window. + * + * Results: + * None. + * + * Side Effects: + * The backing pixmap and all the regions and GC's are destroyed. + * + *----------------------------------------------------------------------- + */ +static void +miBSFree(pWin) + WindowPtr pWin; +{ + miBSWindowPtr pBackingStore; + register ScreenPtr pScreen = pWin->drawable.pScreen; + + pBackingStore = (miBSWindowPtr)pWin->backStorage; + if (pBackingStore) + { + miDestroyBSPixmap (pWin); + + REGION_UNINIT( pScreen, &pBackingStore->SavedRegion); + + xfree(pBackingStore); + pWin->backStorage = NULL; + } +} + +/*- + *----------------------------------------------------------------------- + * miResizeBackingStore -- + * Alter the size of the backing pixmap as necessary when the + * SavedRegion changes size. The contents of the old pixmap are + * copied/shifted into the new/same pixmap. + * + * Results: + * The new Pixmap is created as necessary. + * + * Side Effects: + * The old pixmap is destroyed. + * + *----------------------------------------------------------------------- + */ +static void +miResizeBackingStore(pWin, dx, dy, saveBits) + WindowPtr pWin; + int dx, dy; /* bits are moving this far */ + Bool saveBits; /* bits are useful */ +{ + miBSWindowPtr pBackingStore; + PixmapPtr pBackingPixmap; + ScreenPtr pScreen; + GC *pGC; + BoxPtr extents; + PixmapPtr pNewPixmap; + int nx, ny; + int nw, nh; + + pBackingStore = (miBSWindowPtr)(pWin->backStorage); + pBackingPixmap = pBackingStore->pBackingPixmap; + if (!pBackingPixmap) + return; + pScreen = pWin->drawable.pScreen; + extents = REGION_EXTENTS(pScreen, &pBackingStore->SavedRegion); + pNewPixmap = pBackingPixmap; + + nw = extents->x2 - extents->x1; + nh = extents->y2 - extents->y1; + + /* the policy here could be more sophisticated */ + if (nw != pBackingPixmap->drawable.width || + nh != pBackingPixmap->drawable.height) + { + if (!saveBits) + { + pNewPixmap = NullPixmap; + pBackingStore->status = StatusNoPixmap; + } + else + { + pNewPixmap = (PixmapPtr)(*pScreen->CreatePixmap) + (pScreen, + nw, nh, + pWin->drawable.depth); + if (!pNewPixmap) + { +#ifdef BSEAGER + pBackingStore->status = StatusNoPixmap; +#else + pBackingStore->status = StatusBadAlloc; +#endif + } + } + } + if (!pNewPixmap) + { + pBackingStore->x = 0; + pBackingStore->y = 0; + } + else + { + nx = pBackingStore->x - extents->x1 + dx; + ny = pBackingStore->y - extents->y1 + dy; + pBackingStore->x = extents->x1; + pBackingStore->y = extents->y1; + + if (saveBits && (pNewPixmap != pBackingPixmap || nx != 0 || ny != 0)) + { + pGC = GetScratchGC(pNewPixmap->drawable.depth, pScreen); + if (pGC) + { + ValidateGC((DrawablePtr)pNewPixmap, pGC); + /* if we implement a policy where the pixmap can be larger than + * the region extents, we might want to optimize this copyarea + * by only copying the old extents, rather than the entire + * pixmap + */ + (*pGC->ops->CopyArea)((DrawablePtr)pBackingPixmap, + (DrawablePtr)pNewPixmap, pGC, + 0, 0, + pBackingPixmap->drawable.width, + pBackingPixmap->drawable.height, + nx, ny); + FreeScratchGC(pGC); + } + } + } + /* SavedRegion is used in the backingGC clip; force an update */ + pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER; + if (pNewPixmap != pBackingPixmap) + { + (* pScreen->DestroyPixmap)(pBackingPixmap); + pBackingStore->pBackingPixmap = pNewPixmap; + } +} + +/*- + *----------------------------------------------------------------------- + * miBSSaveDoomedAreas -- + * Saved the areas of the given window that are about to be + * obscured. If the window has moved, pObscured is expected to + * be at the new screen location and (dx,dy) is expected to be the offset + * to the window's previous location. + * + * Results: + * None. + * + * Side Effects: + * The region is copied from the screen into pBackingPixmap and + * SavedRegion is updated. + * + *----------------------------------------------------------------------- + */ +static void +miBSSaveDoomedAreas(pWin, pObscured, dx, dy) + register WindowPtr pWin; + RegionPtr pObscured; + int dx, dy; +{ + miBSWindowPtr pBackingStore; + ScreenPtr pScreen; + int x, y; + + pBackingStore = (miBSWindowPtr)pWin->backStorage; + pScreen = pWin->drawable.pScreen; + + /* + * If the window isn't realized, it's being unmapped, thus we don't + * want to save anything if backingStore isn't Always. + */ + if (!pWin->realized) + { + pBackingStore->viewable = (char)pWin->viewable; + if (pWin->backingStore != Always) + { + REGION_EMPTY( pScreen, &pBackingStore->SavedRegion); + miDestroyBSPixmap (pWin); + return; + } + if (pBackingStore->status == StatusBadAlloc) + pBackingStore->status = StatusNoPixmap; + } + + /* Don't even pretend to save anything for a virtual background None */ + if ((pBackingStore->status == StatusVirtual) && + (pBackingStore->backgroundState == None)) + return; + + if (REGION_NOTEMPTY(pScreen, pObscured)) + { + BoxRec oldExtents; + x = pWin->drawable.x; + y = pWin->drawable.y; + REGION_TRANSLATE(pScreen, pObscured, -x, -y); + oldExtents = *REGION_EXTENTS(pScreen, &pBackingStore->SavedRegion); + REGION_UNION( pScreen, &pBackingStore->SavedRegion, + &pBackingStore->SavedRegion, + pObscured); + /* + * only save the bits if we've actually + * started using backing store + */ + if (pBackingStore->status != StatusVirtual) + { + miBSScreenPtr pScreenPriv; + + pScreenPriv = (miBSScreenPtr) pScreen->devPrivates[miBSScreenIndex].ptr; + if (!pBackingStore->pBackingPixmap) + miCreateBSPixmap (pWin, &oldExtents); + else + miResizeBackingStore(pWin, 0, 0, TRUE); + + if (pBackingStore->pBackingPixmap) { + if (pBackingStore->x | pBackingStore->y) + { + REGION_TRANSLATE( pScreen, pObscured, + -pBackingStore->x, + -pBackingStore->y); + x += pBackingStore->x; + y += pBackingStore->y; + } + (* pScreenPriv->funcs->SaveAreas) (pBackingStore->pBackingPixmap, + pObscured, x - dx, y - dy, pWin); + } + } + REGION_TRANSLATE(pScreen, pObscured, x, y); + } +} + +/*- + *----------------------------------------------------------------------- + * miBSRestoreAreas -- + * Restore areas from backing-store that are no longer obscured. + * expects prgnExposed to contain a screen-relative area. + * + * Results: + * The region to generate exposure events on (which may be + * different from the region to paint). + * + * Side Effects: + * Areas are copied from pBackingPixmap to the screen. prgnExposed + * is altered to contain the region that could not be restored from + * backing-store. + * + * Notes: + * This is called before sending any exposure events to the client, + * and so might be called if the window has grown. Changing the backing + * pixmap doesn't require revalidating the backingGC because the + * client's next output request will result in a call to ValidateGC, + * since the window clip region has changed, which will in turn call + * miValidateBackingStore. + *----------------------------------------------------------------------- + */ +static RegionPtr +miBSRestoreAreas(pWin, prgnExposed) + register WindowPtr pWin; + RegionPtr prgnExposed; +{ + PixmapPtr pBackingPixmap; + miBSWindowPtr pBackingStore; + RegionPtr prgnSaved; + RegionPtr prgnRestored; + register ScreenPtr pScreen; + RegionPtr exposures = prgnExposed; + + pScreen = pWin->drawable.pScreen; + pBackingStore = (miBSWindowPtr)pWin->backStorage; + pBackingPixmap = pBackingStore->pBackingPixmap; + + prgnSaved = &pBackingStore->SavedRegion; + + if (pBackingStore->status == StatusContents) + { + REGION_TRANSLATE(pScreen, prgnSaved, pWin->drawable.x, + pWin->drawable.y); + + prgnRestored = REGION_CREATE( pScreen, (BoxPtr)NULL, 1); + REGION_INTERSECT( pScreen, prgnRestored, prgnExposed, prgnSaved); + + /* + * Since prgnExposed is no longer obscured, we no longer + * will have a valid copy of it in backing-store, but there is a valid + * copy of it on screen, so subtract the area we just restored from + * from the area to be exposed. + */ + + if (REGION_NOTEMPTY( pScreen, prgnRestored)) + { + miBSScreenPtr pScreenPriv; + + REGION_SUBTRACT( pScreen, prgnSaved, prgnSaved, prgnExposed); + REGION_SUBTRACT( pScreen, prgnExposed, prgnExposed, prgnRestored); + + /* + * Do the actual restoration + */ + + pScreenPriv = (miBSScreenPtr) + pScreen->devPrivates[miBSScreenIndex].ptr; + (* pScreenPriv->funcs->RestoreAreas) (pBackingPixmap, + prgnRestored, + pWin->drawable.x + pBackingStore->x, + pWin->drawable.y + pBackingStore->y, pWin); + /* + * if the saved region is completely empty, dispose of the + * backing pixmap, otherwise, retranslate the saved + * region to window relative + */ + + if (REGION_NOTEMPTY(pScreen, prgnSaved)) + { + REGION_TRANSLATE(pScreen, prgnSaved, + -pWin->drawable.x, + -pWin->drawable.y); + miResizeBackingStore(pWin, 0, 0, TRUE); + } + else + miDestroyBSPixmap (pWin); + } + else + REGION_TRANSLATE(pScreen, prgnSaved, + -pWin->drawable.x, -pWin->drawable.y); + REGION_DESTROY( pScreen, prgnRestored); + + } + else if ((pBackingStore->status == StatusVirtual) || + (pBackingStore->status == StatusVDirty)) + { + REGION_TRANSLATE(pScreen, prgnSaved, + pWin->drawable.x, pWin->drawable.y); + exposures = REGION_CREATE( pScreen, NullBox, 1); + if (SameBackground (pBackingStore->backgroundState, + pBackingStore->background, + pWin->backgroundState, + pWin->background)) + { + REGION_SUBTRACT( pScreen, exposures, prgnExposed, prgnSaved); + } + else + { + miTileVirtualBS(pWin); + + /* we need to expose all we have (virtually) retiled */ + REGION_UNION( pScreen, exposures, prgnExposed, prgnSaved); + } + REGION_SUBTRACT( pScreen, prgnSaved, prgnSaved, prgnExposed); + REGION_TRANSLATE(pScreen, prgnSaved, + -pWin->drawable.x, -pWin->drawable.y); + } + else if (pWin->viewable && !pBackingStore->viewable && + pWin->backingStore != Always) + { + /* + * The window was just mapped and nothing has been saved in + * backing-store from the last time it was mapped. We want to capture + * any output to regions that are already obscured but there are no + * bits to snag off the screen, so we initialize things just as we did + * in miBSAllocate, above. + */ + BoxRec box; + + prgnSaved = &pBackingStore->SavedRegion; + + box.x1 = pWin->drawable.x; + box.x2 = box.x1 + (int) pWin->drawable.width; + box.y1 = pWin->drawable.y; + box.y2 = box.y1 + (int) pWin->drawable.height; + + REGION_INVERSE( pScreen, prgnSaved, &pWin->clipList, &box); + REGION_TRANSLATE( pScreen, prgnSaved, + -pWin->drawable.x, + -pWin->drawable.y); +#ifdef SHAPE + if (wBoundingShape (pWin)) + REGION_INTERSECT(pScreen, prgnSaved, prgnSaved, + wBoundingShape (pWin)); + if (wClipShape (pWin)) + REGION_INTERSECT(pScreen, prgnSaved, prgnSaved, + wClipShape (pWin)); +#endif + miTileVirtualBS(pWin); + + exposures = REGION_CREATE( pScreen, &box, 1); + } + pBackingStore->viewable = (char)pWin->viewable; + return exposures; +} + + +/*- + *----------------------------------------------------------------------- + * miBSTranslateBackingStore -- + * Shift the backing-store in the given direction. Called when bit + * gravity is shifting things around. + * + * Results: + * An occluded region of the window which should be sent exposure events. + * This region should be in absolute coordinates (i.e. include + * new window position). + * + * Side Effects: + * If the window changed size as well as position, the backing pixmap + * is resized. The contents of the backing pixmap are shifted + * + * Warning: + * Bob and I have rewritten this routine quite a few times, each + * time it gets a few more cases correct, and introducing some + * interesting bugs. Naturally, I think the code is correct this + * time. + * + * Let me try to explain what this routine is for: + * + * It's called from SlideAndSizeWindow whenever a window + * with backing store is resized. There are two separate + * possibilities: + * + * a) The window has ForgetGravity + * + * In this case, windx, windy will be 0 and oldClip will + * be NULL. This indicates that all of the window contents + * currently saved offscreen should be discarded, and the + * entire window exposed. TranslateBackingStore, then, should + * prepare a completely new backing store region based on the + * new window clipList and return that region for exposure. + * + * b) The window has some other gravity + * + * In this case, windx, windy will be set to the distance + * that the bits should move within the window. oldClip + * will be set to the old visible portion of the window. + * TranslateBackingStore, then, should adjust the backing + * store to accommodate the portion of the existing backing + * store bits which coorespond to backing store bits which + * will still be occluded in the new configuration. oldx,oldy + * are set to the old position of the window on the screen. + * + * Furthermore, in this case any contents of the screen which + * are about to become occluded should be fetched from the screen + * and placed in backing store. This is to avoid the eventual + * occlusion by the win gravity shifting the child window bits around + * on top of this window, and potentially losing information + * + * It's also called from SetShape, but I think (he says not + * really knowing for sure) that this code will even work + * in that case. + *----------------------------------------------------------------------- + */ + +static RegionPtr +miBSTranslateBackingStore(pWin, windx, windy, oldClip, oldx, oldy) + WindowPtr pWin; + int windx; /* bit translation distance in window */ + int windy; + RegionPtr oldClip; /* Region being copied */ + int oldx; /* old window position */ + int oldy; +{ + register miBSWindowPtr pBackingStore; + register RegionPtr pSavedRegion; + register RegionPtr newSaved, doomed; + register ScreenPtr pScreen; + BoxRec extents; + int scrdx; /* bit translation distance on screen */ + int scrdy; + int dx; /* distance window moved on screen */ + int dy; + + pScreen = pWin->drawable.pScreen; + pBackingStore = (miBSWindowPtr)(pWin->backStorage); + if ((pBackingStore->status == StatusNoPixmap) || + (pBackingStore->status == StatusBadAlloc)) + return NullRegion; + + /* + * Compute the new saved region + */ + + newSaved = REGION_CREATE( pScreen, NullBox, 1); + extents.x1 = pWin->drawable.x; + extents.x2 = pWin->drawable.x + (int) pWin->drawable.width; + extents.y1 = pWin->drawable.y; + extents.y2 = pWin->drawable.y + (int) pWin->drawable.height; + REGION_INVERSE( pScreen, newSaved, &pWin->clipList, &extents); + + REGION_TRANSLATE( pScreen, newSaved, + -pWin->drawable.x, -pWin->drawable.y); +#ifdef SHAPE + if (wBoundingShape (pWin) || wClipShape (pWin)) { + if (wBoundingShape (pWin)) + REGION_INTERSECT( pScreen, newSaved, newSaved, + wBoundingShape (pWin)); + if (wClipShape (pWin)) + REGION_INTERSECT( pScreen, newSaved, newSaved, wClipShape (pWin)); + } +#endif + + pSavedRegion = &pBackingStore->SavedRegion; + + /* now find any visible areas we can save from the screen */ + /* and then translate newSaved to old local coordinates */ + if (oldClip) + { + /* bit gravity makes things virtually too hard, punt */ + if (((windx != 0) || (windy != 0)) && + (pBackingStore->status != StatusContents)) + miCreateBSPixmap(pWin, NullBox); + + /* + * The window is moving this far on the screen + */ + dx = pWin->drawable.x - oldx; + dy = pWin->drawable.y - oldy; + /* + * The bits will be moving on the screen by the + * amount the window is moving + the amount the + * bits are moving within the window + */ + scrdx = windx + dx; + scrdy = windy + dy; + + /* + * intersect at old bit position to discover the + * bits on the screen which can be put into the + * new backing store + */ + REGION_TRANSLATE( pScreen, oldClip, windx - oldx, windy - oldy); + doomed = REGION_CREATE( pScreen, NullBox, 1); + REGION_INTERSECT( pScreen, doomed, oldClip, newSaved); + REGION_TRANSLATE( pScreen, oldClip, oldx - windx, oldy - windy); + + /* + * Translate the old saved region to the position in the + * window where it will appear to be + */ + REGION_TRANSLATE( pScreen, pSavedRegion, windx, windy); + + /* + * Add the old saved region to the new saved region, so + * that calls to RestoreAreas will be able to fetch those + * bits back + */ + REGION_UNION( pScreen, newSaved, newSaved, pSavedRegion); + + /* + * Swap the new saved region into the window + */ + { + RegionRec tmp; + + tmp = *pSavedRegion; + *pSavedRegion = *newSaved; + *newSaved = tmp; + } + miResizeBackingStore (pWin, windx, windy, TRUE); + + /* + * Compute the newly enabled region + * of backing store. This region will be + * set to background in the backing pixmap and + * sent as exposure events to the client. + */ + REGION_SUBTRACT( pScreen, newSaved, pSavedRegion, newSaved); + + /* + * Fetch bits which will be obscured from + * the screen + */ + if (REGION_NOTEMPTY( pScreen, doomed)) + { + /* + * Don't clear regions which have bits on the + * screen + */ + REGION_SUBTRACT( pScreen, newSaved, newSaved, doomed); + + /* + * Make the region to SaveDoomedAreas absolute, instead + * of window relative. + */ + REGION_TRANSLATE( pScreen, doomed, + pWin->drawable.x, pWin->drawable.y); + (* pScreen->SaveDoomedAreas) (pWin, doomed, scrdx, scrdy); + } + + REGION_DESTROY(pScreen, doomed); + + /* + * and clear whatever there is that's new + */ + if (REGION_NOTEMPTY( pScreen, newSaved)) + { + miBSClearBackingRegion (pWin, newSaved); + /* + * Make the exposed region absolute + */ + REGION_TRANSLATE(pScreen, newSaved, + pWin->drawable.x, + pWin->drawable.y); + } + else + { + REGION_DESTROY(pScreen, newSaved); + newSaved = NullRegion; + } + } + else + { + /* + * ForgetGravity: just reset backing store and + * expose the whole mess + */ + REGION_COPY( pScreen, pSavedRegion, newSaved); + REGION_TRANSLATE( pScreen, newSaved, + pWin->drawable.x, pWin->drawable.y); + + miResizeBackingStore (pWin, 0, 0, FALSE); + (void) miBSClearBackingStore (pWin, 0, 0, 0, 0, FALSE); + } + + return newSaved; +} + +/* + * Inform the backing store layer that you are about to validate + * a gc with a window, and that subsequent output to the window + * is (or is not) guaranteed to be already clipped to the visible + * regions of the window. + */ + +static void +miBSDrawGuarantee (pWin, pGC, guarantee) + WindowPtr pWin; + GCPtr pGC; + int guarantee; +{ + miBSGCPtr pPriv; + + if (pWin->backStorage) + { + pPriv = (miBSGCPtr)pGC->devPrivates[miBSGCIndex].ptr; + if (!pPriv) + (void) miBSCreateGCPrivate (pGC); + pPriv = (miBSGCPtr)pGC->devPrivates[miBSGCIndex].ptr; + if (pPriv) + { + /* + * XXX KLUDGE ALERT + * + * when the GC is Cheap pPriv will point + * at some device's gc func structure. guarantee + * will point at the ChangeGC entry of that struct + * and will never match a valid guarantee value. + */ + switch (pPriv->guarantee) + { + case GuaranteeNothing: + case GuaranteeVisBack: + pPriv->guarantee = guarantee; + break; + } + } + } +} + +#define noBackingCopy (GCGraphicsExposures|GCClipXOrigin|GCClipYOrigin| \ + GCClipMask|GCSubwindowMode| \ + GCTileStipXOrigin|GCTileStipYOrigin) + +/*- + *----------------------------------------------------------------------- + * miBSValidateGC -- + * Wrapper around output-library's ValidateGC routine + * + * Results: + * None. + * + * Side Effects: + * + * Notes: + * The idea here is to perform several functions: + * - All the output calls must be intercepted and routed to + * backing-store as necessary. + * - pGC in the window's devBackingStore must be set up with the + * clip list appropriate for writing to pBackingPixmap (i.e. + * the inverse of the window's clipList intersected with the + * clientClip of the GC). Since the destination for this GC is + * a pixmap, it is sufficient to set the clip list as its + * clientClip. + *----------------------------------------------------------------------- + */ + +static void +miBSValidateGC (pGC, stateChanges, pDrawable) + GCPtr pGC; + unsigned long stateChanges; + DrawablePtr pDrawable; +{ + GCPtr pBackingGC; + miBSWindowPtr pWindowPriv; + miBSGCPtr pPriv; + WindowPtr pWin; + int lift_functions; + RegionPtr backingCompositeClip = NULL; + + if (pDrawable->type != DRAWABLE_PIXMAP) + { + pWin = (WindowPtr) pDrawable; + pWindowPriv = (miBSWindowPtr) pWin->backStorage; + lift_functions = (pWindowPriv == (miBSWindowPtr) NULL); + } + else + { + pWin = (WindowPtr) NULL; + lift_functions = TRUE; + } + + pPriv = (miBSGCPtr)pGC->devPrivates[miBSGCIndex].ptr; + + FUNC_PROLOGUE (pGC, pPriv); + + (*pGC->funcs->ValidateGC) (pGC, stateChanges, pDrawable); + + /* + * rewrap funcs and ops as Validate may have changed them + */ + + pPriv->wrapFuncs = pGC->funcs; + pPriv->wrapOps = pGC->ops; + + if (!lift_functions && ((pPriv->guarantee == GuaranteeVisBack) || + (pWindowPriv->status == StatusNoPixmap) || + (pWindowPriv->status == StatusBadAlloc))) + lift_functions = TRUE; + + /* + * check to see if a new backingCompositeClip region must + * be generated + */ + + if (!lift_functions && + ((pDrawable->serialNumber != pPriv->serialNumber) || + (stateChanges&(GCClipXOrigin|GCClipYOrigin|GCClipMask|GCSubwindowMode)))) + { + if (REGION_NOTEMPTY(pGC->pScreen, &pWindowPriv->SavedRegion)) + { + backingCompositeClip = REGION_CREATE(pGC->pScreen, NULL, 1); + if ((pGC->clientClipType == CT_NONE) || + (pGC->clientClipType == CT_PIXMAP)) + { + REGION_COPY(pGC->pScreen, backingCompositeClip, + &pWindowPriv->SavedRegion); + } + else + { + /* + * Make a new copy of the client clip, translated to + * its proper origin. + */ + + REGION_COPY(pGC->pScreen, backingCompositeClip, + pGC->clientClip); + REGION_TRANSLATE(pGC->pScreen, backingCompositeClip, + pGC->clipOrg.x, + pGC->clipOrg.y); + REGION_INTERSECT(pGC->pScreen, backingCompositeClip, + backingCompositeClip, + &pWindowPriv->SavedRegion); + } + if (pGC->subWindowMode == IncludeInferiors) + { + RegionPtr translatedClip; + + /* XXX + * any output in IncludeInferiors mode will not + * be redirected to Inferiors backing store. This + * can be fixed only at great cost to the shadow routines. + */ + translatedClip = NotClippedByChildren (pWin); + REGION_TRANSLATE(pGC->pScreen, translatedClip, + -pDrawable->x, + -pDrawable->y); + REGION_SUBTRACT(pGC->pScreen, backingCompositeClip, + backingCompositeClip, translatedClip); + REGION_DESTROY(pGC->pScreen, translatedClip); + } + if (!REGION_NOTEMPTY(pGC->pScreen, backingCompositeClip)) + lift_functions = TRUE; + } + else + { + lift_functions = TRUE; + } + + /* Reset the status when drawing to an unoccluded window so that + * future SaveAreas will actually copy bits from the screen. Note that + * output to root window in IncludeInferiors mode will not cause this + * to change. This causes all transient graphics by the window + * manager to the root window to not enable backing store. + */ + if (lift_functions && (pWindowPriv->status == StatusVirtual) && + (pWin->parent || pGC->subWindowMode != IncludeInferiors)) + pWindowPriv->status = StatusVDirty; + } + + /* + * if no backing store has been allocated, and it's needed, + * create it now. + */ + + if (!lift_functions && !pWindowPriv->pBackingPixmap) + { + miCreateBSPixmap (pWin, NullBox); + if (!pWindowPriv->pBackingPixmap) + lift_functions = TRUE; + } + + /* + * create the backing GC if needed, lift functions + * if the creation fails + */ + + if (!lift_functions && !pPriv->pBackingGC) + { + int status; + XID noexpose = xFalse; + + /* We never want ops with the backingGC to generate GraphicsExpose */ + pBackingGC = CreateGC ((DrawablePtr)pWindowPriv->pBackingPixmap, + GCGraphicsExposures, &noexpose, &status); + if (status != Success) + lift_functions = TRUE; + else + pPriv->pBackingGC = pBackingGC; + } + + pBackingGC = pPriv->pBackingGC; + + pPriv->stateChanges |= stateChanges; + + if (lift_functions) + { + if (backingCompositeClip) + REGION_DESTROY( pGC->pScreen, backingCompositeClip); + + /* unwrap the GC again */ + miBSDestroyGCPrivate (pGC); + + return; + } + + /* + * the rest of this function gets the pBackingGC + * into shape for possible draws + */ + + pPriv->stateChanges &= ~noBackingCopy; + if (pPriv->stateChanges) + CopyGC(pGC, pBackingGC, pPriv->stateChanges); + if ((pGC->patOrg.x - pWindowPriv->x) != pBackingGC->patOrg.x || + (pGC->patOrg.y - pWindowPriv->y) != pBackingGC->patOrg.y) + { + XID vals[2]; + vals[0] = pGC->patOrg.x - pWindowPriv->x; + vals[1] = pGC->patOrg.y - pWindowPriv->y; + DoChangeGC(pBackingGC, GCTileStipXOrigin|GCTileStipYOrigin, vals, 0); + } + pPriv->stateChanges = 0; + + if (backingCompositeClip) + { + XID vals[2]; + + if (pGC->clientClipType == CT_PIXMAP) + { + miBSScreenPtr pScreenPriv; + + (*pBackingGC->funcs->CopyClip)(pBackingGC, pGC); + REGION_TRANSLATE(pGC->pScreen, backingCompositeClip, + -pGC->clipOrg.x, -pGC->clipOrg.y); + vals[0] = pGC->clipOrg.x - pWindowPriv->x; + vals[1] = pGC->clipOrg.y - pWindowPriv->y; + DoChangeGC(pBackingGC, GCClipXOrigin|GCClipYOrigin, vals, 0); + pScreenPriv = (miBSScreenPtr) + pGC->pScreen->devPrivates[miBSScreenIndex].ptr; + (* pScreenPriv->funcs->SetClipmaskRgn) + (pBackingGC, backingCompositeClip); + REGION_DESTROY( pGC->pScreen, backingCompositeClip); + } + else + { + vals[0] = -pWindowPriv->x; + vals[1] = -pWindowPriv->y; + DoChangeGC(pBackingGC, GCClipXOrigin|GCClipYOrigin, vals, 0); + (*pBackingGC->funcs->ChangeClip) (pBackingGC, CT_REGION, backingCompositeClip, 0); + } + pPriv->serialNumber = pDrawable->serialNumber; + } + + if (pWindowPriv->pBackingPixmap->drawable.serialNumber + != pBackingGC->serialNumber) + { + ValidateGC((DrawablePtr)pWindowPriv->pBackingPixmap, pBackingGC); + } + + if (pBackingGC->clientClip == 0) + ErrorF ("backing store clip list nil"); + + FUNC_EPILOGUE (pGC, pPriv); +} + +static void +miBSChangeGC (pGC, mask) + GCPtr pGC; + unsigned long mask; +{ + miBSGCPtr pPriv = (miBSGCPtr) (pGC)->devPrivates[miBSGCIndex].ptr; + + FUNC_PROLOGUE (pGC, pPriv); + + (*pGC->funcs->ChangeGC) (pGC, mask); + + FUNC_EPILOGUE (pGC, pPriv); +} + +static void +miBSCopyGC (pGCSrc, mask, pGCDst) + GCPtr pGCSrc, pGCDst; + unsigned long mask; +{ + miBSGCPtr pPriv = (miBSGCPtr) (pGCDst)->devPrivates[miBSGCIndex].ptr; + + FUNC_PROLOGUE (pGCDst, pPriv); + + (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst); + + FUNC_EPILOGUE (pGCDst, pPriv); +} + +static void +miBSDestroyGC (pGC) + GCPtr pGC; +{ + miBSGCPtr pPriv = (miBSGCPtr) (pGC)->devPrivates[miBSGCIndex].ptr; + + FUNC_PROLOGUE (pGC, pPriv); + + if (pPriv->pBackingGC) + FreeGC(pPriv->pBackingGC, (GContext)0); + + (*pGC->funcs->DestroyGC) (pGC); + + FUNC_EPILOGUE (pGC, pPriv); + + xfree(pPriv); +} + +static void +miBSChangeClip(pGC, type, pvalue, nrects) + GCPtr pGC; + int type; + pointer pvalue; + int nrects; +{ + miBSGCPtr pPriv = (miBSGCPtr) (pGC)->devPrivates[miBSGCIndex].ptr; + + FUNC_PROLOGUE (pGC, pPriv); + + (* pGC->funcs->ChangeClip)(pGC, type, pvalue, nrects); + + FUNC_EPILOGUE (pGC, pPriv); +} + +static void +miBSCopyClip(pgcDst, pgcSrc) + GCPtr pgcDst, pgcSrc; +{ + miBSGCPtr pPriv = (miBSGCPtr) (pgcDst)->devPrivates[miBSGCIndex].ptr; + + FUNC_PROLOGUE (pgcDst, pPriv); + + (* pgcDst->funcs->CopyClip)(pgcDst, pgcSrc); + + FUNC_EPILOGUE (pgcDst, pPriv); +} + +static void +miBSDestroyClip(pGC) + GCPtr pGC; +{ + miBSGCPtr pPriv = (miBSGCPtr) (pGC)->devPrivates[miBSGCIndex].ptr; + + FUNC_PROLOGUE (pGC, pPriv); + + (* pGC->funcs->DestroyClip)(pGC); + + FUNC_EPILOGUE (pGC, pPriv); +} + +static void +miDestroyBSPixmap (pWin) + WindowPtr pWin; +{ + miBSWindowPtr pBackingStore; + ScreenPtr pScreen; + + pScreen = pWin->drawable.pScreen; + pBackingStore = (miBSWindowPtr) pWin->backStorage; + if (pBackingStore->pBackingPixmap) + (* pScreen->DestroyPixmap)(pBackingStore->pBackingPixmap); + pBackingStore->pBackingPixmap = NullPixmap; + pBackingStore->x = 0; + pBackingStore->y = 0; + if (pBackingStore->backgroundState == BackgroundPixmap) + (* pScreen->DestroyPixmap)(pBackingStore->background.pixmap); + pBackingStore->backgroundState = None; + pBackingStore->status = StatusNoPixmap; + pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER; +} + +static void +miTileVirtualBS (pWin) + WindowPtr pWin; +{ + miBSWindowPtr pBackingStore; + + pBackingStore = (miBSWindowPtr) pWin->backStorage; + if (pBackingStore->backgroundState == BackgroundPixmap) + (* pWin->drawable.pScreen->DestroyPixmap) + (pBackingStore->background.pixmap); + pBackingStore->backgroundState = pWin->backgroundState; + pBackingStore->background = pWin->background; + if (pBackingStore->backgroundState == BackgroundPixmap) + pBackingStore->background.pixmap->refcnt++; + + if (pBackingStore->status != StatusVDirty) + pBackingStore->status = StatusVirtual; + + /* + * punt parent relative tiles and do it now + */ + if (pBackingStore->backgroundState == ParentRelative) + miCreateBSPixmap (pWin, NullBox); +} + +#ifdef DEBUG +static int BSAllocationsFailed = 0; +#define FAILEDSIZE 32 +static struct { int w, h; } failedRecord[FAILEDSIZE]; +static int failedIndex; +#endif + +static void +miCreateBSPixmap (pWin, pExtents) + WindowPtr pWin; + BoxPtr pExtents; +{ + miBSWindowPtr pBackingStore; + ScreenPtr pScreen; + PixUnion background; + char backgroundState; + BoxPtr extents; + Bool backSet; + + pScreen = pWin->drawable.pScreen; + pBackingStore = (miBSWindowPtr) pWin->backStorage; + if (pBackingStore->status == StatusBadAlloc) + return; + backSet = ((pBackingStore->status == StatusVirtual) || + (pBackingStore->status == StatusVDirty)); + + extents = REGION_EXTENTS( pScreen, &pBackingStore->SavedRegion); + + if (!pBackingStore->pBackingPixmap) + { + /* the policy here could be more sophisticated */ + pBackingStore->x = extents->x1; + pBackingStore->y = extents->y1; + pBackingStore->pBackingPixmap = + (PixmapPtr)(* pScreen->CreatePixmap) + (pScreen, + extents->x2 - extents->x1, + extents->y2 - extents->y1, + pWin->drawable.depth); + } + if (!pBackingStore->pBackingPixmap) + { +#ifdef DEBUG + BSAllocationsFailed++; + /* + * record failed allocations + */ + failedRecord[failedIndex].w = pWin->drawable.width; + failedRecord[failedIndex].h = pWin->drawable.height; + failedIndex++; + if (failedIndex == FAILEDSIZE) + failedIndex = 0; +#endif +#ifdef BSEAGER + pBackingStore->status = StatusNoPixmap; +#else + pBackingStore->status = StatusBadAlloc; +#endif + return; + } + + pBackingStore->status = StatusContents; + + if (backSet) + { + backgroundState = pWin->backgroundState; + background = pWin->background; + + pWin->backgroundState = pBackingStore->backgroundState; + pWin->background = pBackingStore->background; + if (pWin->backgroundState == BackgroundPixmap) + pWin->background.pixmap->refcnt++; + } + + if (!pExtents) + pExtents = extents; + + if (pExtents->y1 != pExtents->y2) + { + RegionPtr exposed; + + exposed = miBSClearBackingStore(pWin, + pExtents->x1, pExtents->y1, + pExtents->x2 - pExtents->x1, + pExtents->y2 - pExtents->y1, + !backSet); + if (exposed) + { + miSendExposures(pWin, exposed, pWin->drawable.x, pWin->drawable.y); + REGION_DESTROY( pScreen, exposed); + } + } + + if (backSet) + { + if (pWin->backgroundState == BackgroundPixmap) + (* pScreen->DestroyPixmap) (pWin->background.pixmap); + pWin->backgroundState = backgroundState; + pWin->background = background; + if (pBackingStore->backgroundState == BackgroundPixmap) + (* pScreen->DestroyPixmap) (pBackingStore->background.pixmap); + pBackingStore->backgroundState = None; + } +} + +/*- + *----------------------------------------------------------------------- + * miBSExposeCopy -- + * Handle the restoration of areas exposed by graphics operations. + * + * Results: + * None. + * + * Side Effects: + * prgnExposed has the areas exposed from backing-store removed + * from it. + * + *----------------------------------------------------------------------- + */ +static void +miBSExposeCopy (pSrc, pDst, pGC, prgnExposed, srcx, srcy, dstx, dsty, plane) + WindowPtr pSrc; + DrawablePtr pDst; + GCPtr pGC; + RegionPtr prgnExposed; + int srcx, srcy; + int dstx, dsty; + unsigned long plane; +{ + RegionRec tempRgn; + miBSWindowPtr pBackingStore; + RegionPtr (*copyProc)(); + GCPtr pScratchGC; + register BoxPtr pBox; + register int i; + register int dx, dy; + BITS32 gcMask; + + if (!REGION_NOTEMPTY(pGC->pScreen, prgnExposed)) + return; + pBackingStore = (miBSWindowPtr)pSrc->backStorage; + + if ((pBackingStore->status == StatusNoPixmap) || + (pBackingStore->status == StatusBadAlloc)) + return; + + REGION_INIT( pGC->pScreen, &tempRgn, NullBox, 0); + REGION_INTERSECT( pGC->pScreen, &tempRgn, prgnExposed, + &pBackingStore->SavedRegion); + REGION_SUBTRACT( pGC->pScreen, prgnExposed, prgnExposed, &tempRgn); + + if (plane != 0) { + copyProc = pGC->ops->CopyPlane; + } else { + copyProc = pGC->ops->CopyArea; + } + + dx = dstx - srcx; + dy = dsty - srcy; + + switch (pBackingStore->status) { + case StatusVirtual: + case StatusVDirty: + pScratchGC = GetScratchGC (pDst->depth, pDst->pScreen); + if (pScratchGC) + { + gcMask = 0; + if (pGC->alu != pScratchGC->alu) + gcMask = GCFunction; + if (pGC->planemask != pScratchGC->planemask) + gcMask |= GCPlaneMask; + if (gcMask) + CopyGC (pGC, pScratchGC, gcMask); + miBSFillVirtualBits (pDst, pScratchGC, &tempRgn, dx, dy, + (int) pBackingStore->backgroundState, + pBackingStore->background, + ~0L); + FreeScratchGC (pScratchGC); + } + break; + case StatusContents: + for (i = REGION_NUM_RECTS(&tempRgn), pBox = REGION_RECTS(&tempRgn); + --i >= 0; + pBox++) + { + (* copyProc) (pBackingStore->pBackingPixmap, pDst, pGC, + pBox->x1 - pBackingStore->x, + pBox->y1 - pBackingStore->y, + pBox->x2 - pBox->x1, pBox->y2 - pBox->y1, + pBox->x1 + dx, pBox->y1 + dy, plane); + } + break; + } + REGION_UNINIT( pGC->pScreen, &tempRgn); +} diff --git a/mi/mibstore.h b/mi/mibstore.h new file mode 100644 index 000000000..fe6c759e0 --- /dev/null +++ b/mi/mibstore.h @@ -0,0 +1,62 @@ +/*- + * mibstore.h -- + * Header file for users of the MI backing-store scheme. + * + * Copyright (c) 1987 by the Regents of the University of California + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + * + * "$Xorg: mibstore.h,v 1.3 2000/08/17 19:53:37 cpqbld Exp $ + */ + +#ifndef _MIBSTORE_H +#define _MIBSTORE_H + +typedef struct _miBSFuncRec { + void (*SaveAreas)( +#if NeedNestedPrototypes + PixmapPtr /*pBackingPixmap*/, + RegionPtr /*pObscured*/, + int /*x*/, + int /*y*/, + WindowPtr /*pWin*/ +#endif +); + void (*RestoreAreas)( +#if NeedNestedPrototypes + PixmapPtr /*pBackingPixmap*/, + RegionPtr /*pExposed*/, + int /*x*/, + int /*y*/, + WindowPtr /*pWin*/ +#endif +); + void (*SetClipmaskRgn)( +#if NeedNestedPrototypes + GCPtr /*pBackingGC*/, + RegionPtr /*pbackingCompositeClip*/ +#endif +); + PixmapPtr (*GetImagePixmap)(); /* unused */ + PixmapPtr (*GetSpansPixmap)(); /* unused */ +} miBSFuncRec; + +#ifndef _XTYPEDEF_MIBSFUNCPTR +typedef struct _miBSFuncRec *miBSFuncPtr; +#define _XTYPEDEF_MIBSFUNCPTR +#endif + +extern void miInitializeBackingStore( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + miBSFuncPtr /*funcs*/ +#endif +); + +#endif /* _MIBSTORE_H */ diff --git a/mi/mibstorest.h b/mi/mibstorest.h new file mode 100644 index 000000000..d56ee76f6 --- /dev/null +++ b/mi/mibstorest.h @@ -0,0 +1,91 @@ +/* + * mibstorest.h + * + * internal structure definitions for mi backing store + */ + +/* $Xorg: mibstorest.h,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ + +/* + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. +*/ + +#include "mibstore.h" +#include "regionstr.h" + +/* + * One of these structures is allocated per GC used with a backing-store + * drawable. + */ + +typedef struct { + GCPtr pBackingGC; /* Copy of the GC but with graphicsExposures + * set FALSE and the clientClip set to + * clip output to the valid regions of the + * backing pixmap. */ + int guarantee; /* GuaranteeNothing, etc. */ + unsigned long serialNumber; /* clientClip computed time */ + unsigned long stateChanges; /* changes in parent gc since last copy */ + GCOps *wrapOps; /* wrapped ops */ + GCFuncs *wrapFuncs; /* wrapped funcs */ +} miBSGCRec, *miBSGCPtr; + +/* + * one of these structures is allocated per Window with backing store + */ + +typedef struct { + PixmapPtr pBackingPixmap; /* Pixmap for saved areas */ + short x; /* origin of pixmap relative to window */ + short y; + RegionRec SavedRegion; /* Valid area in pBackingPixmap */ + char viewable; /* Tracks pWin->viewable so SavedRegion may + * be initialized correctly when the window + * is first mapped */ + char status; /* StatusNoPixmap, etc. */ + char backgroundState; /* background type */ + PixUnion background; /* background pattern */ +} miBSWindowRec, *miBSWindowPtr; + +#define StatusNoPixmap 1 /* pixmap has not been created */ +#define StatusVirtual 2 /* pixmap is virtual, tiled with background */ +#define StatusVDirty 3 /* pixmap is virtual, visiblt has contents */ +#define StatusBadAlloc 4 /* pixmap create failed, do not try again */ +#define StatusContents 5 /* pixmap is created, has valid contents */ + +typedef struct { + /* + * screen func wrappers + */ + CloseScreenProcPtr CloseScreen; + GetImageProcPtr GetImage; + GetSpansProcPtr GetSpans; + ChangeWindowAttributesProcPtr ChangeWindowAttributes; + CreateGCProcPtr CreateGC; + DestroyWindowProcPtr DestroyWindow; + /* + * pointer to vector of device-specific backing store functions + */ + miBSFuncPtr funcs; +} miBSScreenRec, *miBSScreenPtr; diff --git a/mi/miclipn.c b/mi/miclipn.c new file mode 100644 index 000000000..6eca416f8 --- /dev/null +++ b/mi/miclipn.c @@ -0,0 +1,73 @@ +/* $Xorg: miclipn.c,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ +/* + +Copyright 1990, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + +#include "X.h" +#include "windowstr.h" +#include "scrnintstr.h" + +static void (*clipNotify)() = 0; +static void (*ClipNotifies[MAXSCREENS])(); + +static void +miClipNotifyWrapper(pWin, dx, dy) + WindowPtr pWin; + int dx, dy; +{ + if (clipNotify) + (*clipNotify)(pWin, dx, dy); + if (ClipNotifies[pWin->drawable.pScreen->myNum]) + (*ClipNotifies[pWin->drawable.pScreen->myNum])(pWin, dx, dy); +} + +/* + * miClipNotify -- + * Hook to let DDX request notification when the clipList of + * a window is recomputed on any screen. For R4 compatibility; + * better if you wrap the ClipNotify screen proc yourself. + */ + +static unsigned long clipGeneration = 0; + +void +miClipNotify (func) + void (*func)(); +{ + int i; + + clipNotify = func; + if (clipGeneration != serverGeneration) + { + clipGeneration = serverGeneration; + for (i = 0; i < screenInfo.numScreens; i++) + { + ClipNotifies[i] = screenInfo.screens[i]->ClipNotify; + screenInfo.screens[i]->ClipNotify = miClipNotifyWrapper; + } + } +} diff --git a/mi/micursor.c b/mi/micursor.c new file mode 100644 index 000000000..b23bcddf6 --- /dev/null +++ b/mi/micursor.c @@ -0,0 +1,71 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: micursor.c,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ +#include "scrnintstr.h" +#include "cursor.h" +#include "misc.h" + +extern Bool Must_have_memory; + +void +miRecolorCursor( pScr, pCurs, displayed) + ScreenPtr pScr; + CursorPtr pCurs; + Bool displayed; +{ + /* + * This is guaranteed to correct any color-dependent state which may have + * been bound up in private state created by RealizeCursor + */ + (* pScr->UnrealizeCursor)( pScr, pCurs); + Must_have_memory = TRUE; /* XXX */ + (* pScr->RealizeCursor)( pScr, pCurs); + Must_have_memory = FALSE; /* XXX */ + if ( displayed) + (* pScr->DisplayCursor)( pScr, pCurs); + +} diff --git a/mi/midash.c b/mi/midash.c new file mode 100644 index 000000000..cc7e31b28 --- /dev/null +++ b/mi/midash.c @@ -0,0 +1,310 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: midash.c,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ +#include "miscstruct.h" +#include "mistruct.h" +#include "mifpoly.h" + +static miDashPtr CheckDashStorage(); + +/* return a list of DashRec. there will be an extra +entry at the end holding the last point of the polyline. + this means that the code that actually draws dashes can +get a pair of points for every dash. only the point in the last +dash record is useful; the other fields are not used. + nseg is the number of segments, not the number of points. + +example: + + dash1.start + dash2.start + dash3.start + last-point + +defines a list of segments + (dash1.pt, dash2.pt) + (dash2.pt, dash3.pt) + (dash3.pt, last-point) +and nseg == 3. + +NOTE: + EVEN_DASH == ~ODD_DASH + +NOTE ALSO: + miDashLines may return 0 segments, going from pt[0] to pt[0] with one dash. +*/ + +miDashPtr +miDashLine(npt, ppt, nDash, pDash, offset, pnseg) +int npt; +DDXPointPtr ppt; +unsigned int nDash; +unsigned char *pDash; +unsigned int offset; +int *pnseg; +{ + DDXPointRec pt1, pt2; + int lenCur; /* npt used from this dash */ + int lenMax; /* npt in this dash */ + int iDash = 0; /* index of current dash */ + int which; /* EVEN_DASH or ODD_DASH */ + miDashPtr pseg; /* list of dash segments */ + miDashPtr psegBase; /* start of list */ + int nseg = 0; /* number of dashes so far */ + int nsegMax = 0; /* num segs we can fit in this list */ + + int x, y, len; + int adx, ady, signdx, signdy; + int du, dv, e1, e2, e, base_e = 0; + + lenCur = offset; + which = EVEN_DASH; + while(lenCur >= pDash[iDash]) + { + lenCur -= pDash[iDash]; + iDash++; + if (iDash >= nDash) + iDash = 0; + which = ~which; + } + lenMax = pDash[iDash]; + + psegBase = (miDashPtr)NULL; + pt2 = ppt[0]; /* just in case there is only one point */ + + while(--npt) + { + if (PtEqual(ppt[0], ppt[1])) + { + ppt++; + continue; /* no duplicated points in polyline */ + } + pt1 = *ppt++; + pt2 = *ppt; + + adx = pt2.x - pt1.x; + ady = pt2.y - pt1.y; + signdx = sign(adx); + signdy = sign(ady); + adx = abs(adx); + ady = abs(ady); + + if (adx > ady) + { + du = adx; + dv = ady; + len = adx; + } + else + { + du = ady; + dv = adx; + len = ady; + } + + e1 = dv * 2; + e2 = e1 - 2*du; + e = e1 - du; + x = pt1.x; + y = pt1.y; + + nseg++; + pseg = CheckDashStorage(&psegBase, nseg, &nsegMax); + if (!pseg) + return (miDashPtr)NULL; + pseg->pt = pt1; + pseg->e1 = e1; + pseg->e2 = e2; + base_e = pseg->e = e; + pseg->which = which; + pseg->newLine = 1; + + while (len--) + { + if (adx > ady) + { + /* X_AXIS */ + if (((signdx > 0) && (e < 0)) || + ((signdx <=0) && (e <=0)) + ) + { + e += e1; + } + else + { + y += signdy; + e += e2; + } + x += signdx; + } + else + { + /* Y_AXIS */ + if (((signdx > 0) && (e < 0)) || + ((signdx <=0) && (e <=0)) + ) + { + e +=e1; + } + else + { + x += signdx; + e += e2; + } + y += signdy; + } + + lenCur++; + if (lenCur >= lenMax && (len || npt <= 1)) + { + nseg++; + pseg = CheckDashStorage(&psegBase, nseg, &nsegMax); + if (!pseg) + return (miDashPtr)NULL; + pseg->pt.x = x; + pseg->pt.y = y; + pseg->e1 = e1; + pseg->e2 = e2; + pseg->e = e; + which = ~which; + pseg->which = which; + pseg->newLine = 0; + + /* move on to next dash */ + iDash++; + if (iDash >= nDash) + iDash = 0; + lenMax = pDash[iDash]; + lenCur = 0; + } + } /* while len-- */ + } /* while --npt */ + + if (lenCur == 0 && nseg != 0) + { + nseg--; + which = ~which; + } + *pnseg = nseg; + pseg = CheckDashStorage(&psegBase, nseg+1, &nsegMax); + if (!pseg) + return (miDashPtr)NULL; + pseg->pt = pt2; + pseg->e = base_e; + pseg->which = which; + pseg->newLine = 0; + return psegBase; +} + + +#define NSEGDELTA 16 + +/* returns a pointer to the pseg[nseg-1], growing the storage as +necessary. this interface seems unnecessarily cumbersome. + +*/ + +static +miDashPtr +CheckDashStorage(ppseg, nseg, pnsegMax) +miDashPtr *ppseg; /* base pointer */ +int nseg; /* number of segment we want to write to */ +int *pnsegMax; /* size (in segments) of list so far */ +{ + if (nseg > *pnsegMax) + { + miDashPtr newppseg; + + *pnsegMax += NSEGDELTA; + newppseg = (miDashPtr)xrealloc(*ppseg, + (*pnsegMax)*sizeof(miDashRec)); + if (!newppseg) + { + xfree(*ppseg); + return (miDashPtr)NULL; + } + *ppseg = newppseg; + } + return(*ppseg+(nseg-1)); +} + +void +miStepDash (dist, pDashIndex, pDash, numInDashList, pDashOffset) + int dist; /* distance to step */ + int *pDashIndex; /* current dash */ + unsigned char *pDash; /* dash list */ + int numInDashList; /* total length of dash list */ + int *pDashOffset; /* offset into current dash */ +{ + int dashIndex, dashOffset; + int totallen; + int i; + + dashIndex = *pDashIndex; + dashOffset = *pDashOffset; + if (dist < pDash[dashIndex] - dashOffset) + { + *pDashOffset = dashOffset + dist; + return; + } + dist -= pDash[dashIndex] - dashOffset; + if (++dashIndex == numInDashList) + dashIndex = 0; + totallen = 0; + for (i = 0; i < numInDashList; i++) + totallen += pDash[i]; + if (totallen <= dist) + dist = dist % totallen; + while (dist >= pDash[dashIndex]) + { + dist -= pDash[dashIndex]; + if (++dashIndex == numInDashList) + dashIndex = 0; + } + *pDashIndex = dashIndex; + *pDashOffset = dist; +} diff --git a/mi/midispcur.c b/mi/midispcur.c new file mode 100644 index 000000000..4c33d8e1d --- /dev/null +++ b/mi/midispcur.c @@ -0,0 +1,614 @@ +/* + * midispcur.c + * + * machine independent cursor display routines + */ + +/* $Xorg: midispcur.c,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ + +/* + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. +*/ + +#define NEED_EVENTS +# include "X.h" +# include "misc.h" +# include "input.h" +# include "cursorstr.h" +# include "windowstr.h" +# include "regionstr.h" +# include "dixstruct.h" +# include "scrnintstr.h" +# include "servermd.h" +# include "mipointer.h" +# include "misprite.h" +# include "gcstruct.h" + +extern WindowPtr *WindowTable; + +/* per-screen private data */ + +static int miDCScreenIndex; +static unsigned long miDCGeneration = 0; + +static Bool miDCCloseScreen(); + +typedef struct { + GCPtr pSourceGC, pMaskGC; + GCPtr pSaveGC, pRestoreGC; + GCPtr pMoveGC; + GCPtr pPixSourceGC, pPixMaskGC; + CloseScreenProcPtr CloseScreen; + PixmapPtr pSave, pTemp; +} miDCScreenRec, *miDCScreenPtr; + +/* per-cursor per-screen private data */ +typedef struct { + PixmapPtr sourceBits; /* source bits */ + PixmapPtr maskBits; /* mask bits */ +} miDCCursorRec, *miDCCursorPtr; + +/* + * sprite/cursor method table + */ + +static Bool miDCRealizeCursor(), miDCUnrealizeCursor(); +static Bool miDCPutUpCursor(), miDCSaveUnderCursor(); +static Bool miDCRestoreUnderCursor(), miDCMoveCursor(); +static Bool miDCChangeSave(); + +static miSpriteCursorFuncRec miDCFuncs = { + miDCRealizeCursor, + miDCUnrealizeCursor, + miDCPutUpCursor, + miDCSaveUnderCursor, + miDCRestoreUnderCursor, + miDCMoveCursor, + miDCChangeSave, +}; + +Bool +miDCInitialize (pScreen, screenFuncs) + ScreenPtr pScreen; + miPointerScreenFuncPtr screenFuncs; +{ + miDCScreenPtr pScreenPriv; + + if (miDCGeneration != serverGeneration) + { + miDCScreenIndex = AllocateScreenPrivateIndex (); + if (miDCScreenIndex < 0) + return FALSE; + miDCGeneration = serverGeneration; + } + pScreenPriv = (miDCScreenPtr) xalloc (sizeof (miDCScreenRec)); + if (!pScreenPriv) + return FALSE; + + /* + * initialize the entire private structure to zeros + */ + + pScreenPriv->pSourceGC = + pScreenPriv->pMaskGC = + pScreenPriv->pSaveGC = + pScreenPriv->pRestoreGC = + pScreenPriv->pMoveGC = + pScreenPriv->pPixSourceGC = + pScreenPriv->pPixMaskGC = NULL; + + pScreenPriv->pSave = pScreenPriv->pTemp = NULL; + + pScreenPriv->CloseScreen = pScreen->CloseScreen; + pScreen->CloseScreen = miDCCloseScreen; + + pScreen->devPrivates[miDCScreenIndex].ptr = (pointer) pScreenPriv; + + if (!miSpriteInitialize (pScreen, &miDCFuncs, screenFuncs)) + { + xfree ((pointer) pScreenPriv); + return FALSE; + } + return TRUE; +} + +#define tossGC(gc) (gc ? FreeGC (gc, (GContext) 0) : 0) +#define tossPix(pix) (pix ? (*pScreen->DestroyPixmap) (pix) : TRUE) + +static Bool +miDCCloseScreen (index, pScreen) + ScreenPtr pScreen; +{ + miDCScreenPtr pScreenPriv; + + pScreenPriv = (miDCScreenPtr) pScreen->devPrivates[miDCScreenIndex].ptr; + pScreen->CloseScreen = pScreenPriv->CloseScreen; + tossGC (pScreenPriv->pSourceGC); + tossGC (pScreenPriv->pMaskGC); + tossGC (pScreenPriv->pSaveGC); + tossGC (pScreenPriv->pRestoreGC); + tossGC (pScreenPriv->pMoveGC); + tossGC (pScreenPriv->pPixSourceGC); + tossGC (pScreenPriv->pPixMaskGC); + tossPix (pScreenPriv->pSave); + tossPix (pScreenPriv->pTemp); + xfree ((pointer) pScreenPriv); + return (*pScreen->CloseScreen) (index, pScreen); +} + +static Bool +miDCRealizeCursor (pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + if (pCursor->bits->refcnt <= 1) + pCursor->bits->devPriv[pScreen->myNum] = (pointer)NULL; + return TRUE; +} + +static miDCCursorPtr +miDCRealize (pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + miDCCursorPtr pPriv; + GCPtr pGC; + XID gcvals[3]; + + pPriv = (miDCCursorPtr) xalloc (sizeof (miDCCursorRec)); + if (!pPriv) + return (miDCCursorPtr)NULL; + pPriv->sourceBits = (*pScreen->CreatePixmap) (pScreen, pCursor->bits->width, pCursor->bits->height, 1); + if (!pPriv->sourceBits) + { + xfree ((pointer) pPriv); + return (miDCCursorPtr)NULL; + } + pPriv->maskBits = (*pScreen->CreatePixmap) (pScreen, pCursor->bits->width, pCursor->bits->height, 1); + if (!pPriv->maskBits) + { + (*pScreen->DestroyPixmap) (pPriv->sourceBits); + xfree ((pointer) pPriv); + return (miDCCursorPtr)NULL; + } + pCursor->bits->devPriv[pScreen->myNum] = (pointer) pPriv; + + /* create the two sets of bits, clipping as appropriate */ + + pGC = GetScratchGC (1, pScreen); + if (!pGC) + { + (void) miDCUnrealizeCursor (pScreen, pCursor); + return (miDCCursorPtr)NULL; + } + + ValidateGC ((DrawablePtr)pPriv->sourceBits, pGC); + (*pGC->ops->PutImage) ((DrawablePtr)pPriv->sourceBits, pGC, 1, + 0, 0, pCursor->bits->width, pCursor->bits->height, + 0, XYPixmap, (char *)pCursor->bits->source); + gcvals[0] = GXand; + ChangeGC (pGC, GCFunction, gcvals); + ValidateGC ((DrawablePtr)pPriv->sourceBits, pGC); + (*pGC->ops->PutImage) ((DrawablePtr)pPriv->sourceBits, pGC, 1, + 0, 0, pCursor->bits->width, pCursor->bits->height, + 0, XYPixmap, (char *)pCursor->bits->mask); + + /* mask bits -- pCursor->mask & ~pCursor->source */ + gcvals[0] = GXcopy; + ChangeGC (pGC, GCFunction, gcvals); + ValidateGC ((DrawablePtr)pPriv->maskBits, pGC); + (*pGC->ops->PutImage) ((DrawablePtr)pPriv->maskBits, pGC, 1, + 0, 0, pCursor->bits->width, pCursor->bits->height, + 0, XYPixmap, (char *)pCursor->bits->mask); + gcvals[0] = GXandInverted; + ChangeGC (pGC, GCFunction, gcvals); + ValidateGC ((DrawablePtr)pPriv->maskBits, pGC); + (*pGC->ops->PutImage) ((DrawablePtr)pPriv->maskBits, pGC, 1, + 0, 0, pCursor->bits->width, pCursor->bits->height, + 0, XYPixmap, (char *)pCursor->bits->source); + FreeScratchGC (pGC); + return pPriv; +} + +static Bool +miDCUnrealizeCursor (pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + miDCCursorPtr pPriv; + + pPriv = (miDCCursorPtr) pCursor->bits->devPriv[pScreen->myNum]; + if (pPriv && (pCursor->bits->refcnt <= 1)) + { + (*pScreen->DestroyPixmap) (pPriv->sourceBits); + (*pScreen->DestroyPixmap) (pPriv->maskBits); + xfree ((pointer) pPriv); + pCursor->bits->devPriv[pScreen->myNum] = (pointer)NULL; + } + return TRUE; +} + +static void +miDCPutBits (pDrawable, pPriv, sourceGC, maskGC, x, y, w, h, source, mask) + DrawablePtr pDrawable; + GCPtr sourceGC, maskGC; + int x, y; + unsigned w, h; + miDCCursorPtr pPriv; + unsigned long source, mask; +{ + XID gcvals[1]; + + if (sourceGC->fgPixel != source) + { + gcvals[0] = source; + DoChangeGC (sourceGC, GCForeground, gcvals, 0); + } + if (sourceGC->serialNumber != pDrawable->serialNumber) + ValidateGC (pDrawable, sourceGC); + (*sourceGC->ops->PushPixels) (sourceGC, pPriv->sourceBits, pDrawable, w, h, x, y); + if (maskGC->fgPixel != mask) + { + gcvals[0] = mask; + DoChangeGC (maskGC, GCForeground, gcvals, 0); + } + if (maskGC->serialNumber != pDrawable->serialNumber) + ValidateGC (pDrawable, maskGC); + (*maskGC->ops->PushPixels) (maskGC, pPriv->maskBits, pDrawable, w, h, x, y); +} + +#define EnsureGC(gc,win) (gc || miDCMakeGC(&gc, win)) + +static GCPtr +miDCMakeGC(ppGC, pWin) + GCPtr *ppGC; + WindowPtr pWin; +{ + GCPtr pGC; + int status; + XID gcvals[2]; + + gcvals[0] = IncludeInferiors; + gcvals[1] = FALSE; + pGC = CreateGC((DrawablePtr)pWin, + GCSubwindowMode|GCGraphicsExposures, gcvals, &status); + if (pGC) + (*pWin->drawable.pScreen->DrawGuarantee) (pWin, pGC, GuaranteeVisBack); + *ppGC = pGC; + return pGC; +} + +static Bool +miDCPutUpCursor (pScreen, pCursor, x, y, source, mask) + ScreenPtr pScreen; + CursorPtr pCursor; + int x, y; + unsigned long source, mask; +{ + miDCScreenPtr pScreenPriv; + miDCCursorPtr pPriv; + WindowPtr pWin; + + pPriv = (miDCCursorPtr) pCursor->bits->devPriv[pScreen->myNum]; + if (!pPriv) + { + pPriv = miDCRealize(pScreen, pCursor); + if (!pPriv) + return FALSE; + } + pScreenPriv = (miDCScreenPtr) pScreen->devPrivates[miDCScreenIndex].ptr; + pWin = WindowTable[pScreen->myNum]; + if (!EnsureGC(pScreenPriv->pSourceGC, pWin)) + return FALSE; + if (!EnsureGC(pScreenPriv->pMaskGC, pWin)) + { + FreeGC (pScreenPriv->pSourceGC, (GContext) 0); + pScreenPriv->pSourceGC = 0; + return FALSE; + } + miDCPutBits ((DrawablePtr)pWin, pPriv, + pScreenPriv->pSourceGC, pScreenPriv->pMaskGC, + x, y, pCursor->bits->width, pCursor->bits->height, + source, mask); + return TRUE; +} + +static Bool +miDCSaveUnderCursor (pScreen, x, y, w, h) + ScreenPtr pScreen; + int x, y, w, h; +{ + miDCScreenPtr pScreenPriv; + PixmapPtr pSave; + WindowPtr pWin; + GCPtr pGC; + + pScreenPriv = (miDCScreenPtr) pScreen->devPrivates[miDCScreenIndex].ptr; + pSave = pScreenPriv->pSave; + pWin = WindowTable[pScreen->myNum]; + if (!pSave || pSave->drawable.width < w || pSave->drawable.height < h) + { + if (pSave) + (*pScreen->DestroyPixmap) (pSave); + pScreenPriv->pSave = pSave = + (*pScreen->CreatePixmap) (pScreen, w, h, pScreen->rootDepth); + if (!pSave) + return FALSE; + } + if (!EnsureGC(pScreenPriv->pSaveGC, pWin)) + return FALSE; + pGC = pScreenPriv->pSaveGC; + if (pSave->drawable.serialNumber != pGC->serialNumber) + ValidateGC ((DrawablePtr) pSave, pGC); + (*pGC->ops->CopyArea) ((DrawablePtr) pWin, (DrawablePtr) pSave, pGC, + x, y, w, h, 0, 0); + return TRUE; +} + +static Bool +miDCRestoreUnderCursor (pScreen, x, y, w, h) + ScreenPtr pScreen; + int x, y, w, h; +{ + miDCScreenPtr pScreenPriv; + PixmapPtr pSave; + WindowPtr pWin; + GCPtr pGC; + + pScreenPriv = (miDCScreenPtr) pScreen->devPrivates[miDCScreenIndex].ptr; + pSave = pScreenPriv->pSave; + pWin = WindowTable[pScreen->myNum]; + if (!pSave) + return FALSE; + if (!EnsureGC(pScreenPriv->pRestoreGC, pWin)) + return FALSE; + pGC = pScreenPriv->pRestoreGC; + if (pWin->drawable.serialNumber != pGC->serialNumber) + ValidateGC ((DrawablePtr) pWin, pGC); + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pWin, pGC, + 0, 0, w, h, x, y); + return TRUE; +} + +static Bool +miDCChangeSave (pScreen, x, y, w, h, dx, dy) + ScreenPtr pScreen; + int x, y, w, h, dx, dy; +{ + miDCScreenPtr pScreenPriv; + PixmapPtr pSave; + WindowPtr pWin; + GCPtr pGC; + int sourcex, sourcey, destx, desty, copyw, copyh; + + pScreenPriv = (miDCScreenPtr) pScreen->devPrivates[miDCScreenIndex].ptr; + pSave = pScreenPriv->pSave; + pWin = WindowTable[pScreen->myNum]; + /* + * restore the bits which are about to get trashed + */ + if (!pSave) + return FALSE; + if (!EnsureGC(pScreenPriv->pRestoreGC, pWin)) + return FALSE; + pGC = pScreenPriv->pRestoreGC; + if (pWin->drawable.serialNumber != pGC->serialNumber) + ValidateGC ((DrawablePtr) pWin, pGC); + /* + * copy the old bits to the screen. + */ + if (dy > 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pWin, pGC, + 0, h - dy, w, dy, x + dx, y + h); + } + else if (dy < 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pWin, pGC, + 0, 0, w, -dy, x + dx, y + dy); + } + if (dy >= 0) + { + desty = y + dy; + sourcey = 0; + copyh = h - dy; + } + else + { + desty = y; + sourcey = - dy; + copyh = h + dy; + } + if (dx > 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pWin, pGC, + w - dx, sourcey, dx, copyh, x + w, desty); + } + else if (dx < 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pWin, pGC, + 0, sourcey, -dx, copyh, x + dx, desty); + } + if (!EnsureGC(pScreenPriv->pSaveGC, pWin)) + return FALSE; + pGC = pScreenPriv->pSaveGC; + if (pSave->drawable.serialNumber != pGC->serialNumber) + ValidateGC ((DrawablePtr) pSave, pGC); + /* + * move the bits that are still valid within the pixmap + */ + if (dx >= 0) + { + sourcex = 0; + destx = dx; + copyw = w - dx; + } + else + { + destx = 0; + sourcex = - dx; + copyw = w + dx; + } + if (dy >= 0) + { + sourcey = 0; + desty = dy; + copyh = h - dy; + } + else + { + desty = 0; + sourcey = -dy; + copyh = h + dy; + } + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pSave, pGC, + sourcex, sourcey, copyw, copyh, destx, desty); + /* + * copy the new bits from the screen into the remaining areas of the + * pixmap + */ + if (dy > 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pWin, (DrawablePtr) pSave, pGC, + x, y, w, dy, 0, 0); + } + else if (dy < 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pWin, (DrawablePtr) pSave, pGC, + x, y + h + dy, w, -dy, 0, h + dy); + } + if (dy >= 0) + { + desty = dy; + sourcey = y + dy; + copyh = h - dy; + } + else + { + desty = 0; + sourcey = y; + copyh = h + dy; + } + if (dx > 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pWin, (DrawablePtr) pSave, pGC, + x, sourcey, dx, copyh, 0, desty); + } + else if (dx < 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pWin, (DrawablePtr) pSave, pGC, + x + w + dx, sourcey, -dx, copyh, w + dx, desty); + } + return TRUE; +} + +static Bool +miDCMoveCursor (pScreen, pCursor, x, y, w, h, dx, dy, source, mask) + ScreenPtr pScreen; + CursorPtr pCursor; + int x, y, w, h, dx, dy; + unsigned long source, mask; +{ + miDCCursorPtr pPriv; + miDCScreenPtr pScreenPriv; + int status; + WindowPtr pWin; + GCPtr pGC; + XID gcval = FALSE; + PixmapPtr pTemp; + + pPriv = (miDCCursorPtr) pCursor->bits->devPriv[pScreen->myNum]; + if (!pPriv) + { + pPriv = miDCRealize(pScreen, pCursor); + if (!pPriv) + return FALSE; + } + pScreenPriv = (miDCScreenPtr) pScreen->devPrivates[miDCScreenIndex].ptr; + pWin = WindowTable[pScreen->myNum]; + pTemp = pScreenPriv->pTemp; + if (!pTemp || + pTemp->drawable.width != pScreenPriv->pSave->drawable.width || + pTemp->drawable.height != pScreenPriv->pSave->drawable.height) + { + if (pTemp) + (*pScreen->DestroyPixmap) (pTemp); + pScreenPriv->pTemp = pTemp = (*pScreen->CreatePixmap) + (pScreen, w, h, pScreenPriv->pSave->drawable.depth); + if (!pTemp) + return FALSE; + } + if (!pScreenPriv->pMoveGC) + { + pScreenPriv->pMoveGC = CreateGC ((DrawablePtr)pTemp, + GCGraphicsExposures, &gcval, &status); + if (!pScreenPriv->pMoveGC) + return FALSE; + } + /* + * copy the saved area to a temporary pixmap + */ + pGC = pScreenPriv->pMoveGC; + if (pGC->serialNumber != pTemp->drawable.serialNumber) + ValidateGC ((DrawablePtr) pTemp, pGC); + (*pGC->ops->CopyArea)((DrawablePtr)pScreenPriv->pSave, + (DrawablePtr)pTemp, pGC, 0, 0, w, h, 0, 0); + + /* + * draw the cursor in the temporary pixmap + */ + if (!pScreenPriv->pPixSourceGC) + { + pScreenPriv->pPixSourceGC = CreateGC ((DrawablePtr)pTemp, + GCGraphicsExposures, &gcval, &status); + if (!pScreenPriv->pPixSourceGC) + return FALSE; + } + if (!pScreenPriv->pPixMaskGC) + { + pScreenPriv->pPixMaskGC = CreateGC ((DrawablePtr)pTemp, + GCGraphicsExposures, &gcval, &status); + if (!pScreenPriv->pPixMaskGC) + return FALSE; + } + miDCPutBits ((DrawablePtr)pTemp, pPriv, + pScreenPriv->pPixSourceGC, pScreenPriv->pPixMaskGC, + dx, dy, pCursor->bits->width, pCursor->bits->height, + source, mask); + + /* + * copy the temporary pixmap onto the screen + */ + + if (!EnsureGC(pScreenPriv->pRestoreGC, pWin)) + return FALSE; + pGC = pScreenPriv->pRestoreGC; + if (pWin->drawable.serialNumber != pGC->serialNumber) + ValidateGC ((DrawablePtr) pWin, pGC); + + (*pGC->ops->CopyArea) ((DrawablePtr) pTemp, (DrawablePtr) pWin, + pGC, + 0, 0, w, h, x, y); + return TRUE; +} diff --git a/mi/mieq.c b/mi/mieq.c new file mode 100644 index 000000000..02b35160c --- /dev/null +++ b/mi/mieq.c @@ -0,0 +1,191 @@ +/* + * $Xorg: mieq.c,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ + * +Copyright 1990, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + * + * Author: Keith Packard, MIT X Consortium + */ + +/* + * mieq.c + * + * Machine independent event queue + * + */ + +# define NEED_EVENTS +# include "X.h" +# include "Xmd.h" +# include "Xproto.h" +# include "misc.h" +# include "windowstr.h" +# include "pixmapstr.h" +# include "inputstr.h" +# include "mi.h" +# include "scrnintstr.h" + +#define QUEUE_SIZE 256 + +typedef struct _Event { + xEvent event; + ScreenPtr pScreen; +} EventRec, *EventPtr; + +typedef struct _EventQueue { + HWEventQueueType head, tail; /* long for SetInputCheck */ + CARD32 lastEventTime; /* to avoid time running backwards */ + Bool lastMotion; + EventRec events[QUEUE_SIZE]; /* static allocation for signals */ + DevicePtr pKbd, pPtr; /* device pointer, to get funcs */ + ScreenPtr pEnqueueScreen; /* screen events are being delivered to */ + ScreenPtr pDequeueScreen; /* screen events are being dispatched to */ +} EventQueueRec, *EventQueuePtr; + +static EventQueueRec miEventQueue; + +Bool +mieqInit (pKbd, pPtr) + DevicePtr pKbd, pPtr; +{ + miEventQueue.head = miEventQueue.tail = 0; + miEventQueue.lastEventTime = GetTimeInMillis (); + miEventQueue.pKbd = pKbd; + miEventQueue.pPtr = pPtr; + miEventQueue.lastMotion = FALSE; + miEventQueue.pEnqueueScreen = screenInfo.screens[0]; + miEventQueue.pDequeueScreen = miEventQueue.pEnqueueScreen; + SetInputCheck (&miEventQueue.head, &miEventQueue.tail); + return TRUE; +} + +/* + * Must be reentrant with ProcessInputEvents. Assumption: mieqEnqueue + * will never be interrupted. If this is called from both signal + * handlers and regular code, make sure the signal is suspended when + * called from regular code. + */ + +void +mieqEnqueue (e) + xEvent *e; +{ + HWEventQueueType oldtail, newtail, prevtail; + Bool isMotion; + + oldtail = miEventQueue.tail; + isMotion = e->u.u.type == MotionNotify; + if (isMotion && miEventQueue.lastMotion && oldtail != miEventQueue.head) + { + if (oldtail == 0) + oldtail = QUEUE_SIZE; + oldtail = oldtail - 1; + } + else + { + newtail = oldtail + 1; + if (newtail == QUEUE_SIZE) + newtail = 0; + /* Toss events which come in late */ + if (newtail == miEventQueue.head) + return; + miEventQueue.tail = newtail; + } + miEventQueue.lastMotion = isMotion; + miEventQueue.events[oldtail].event = *e; + /* + * Make sure that event times don't go backwards - this + * is "unnecessary", but very useful + */ + if (e->u.keyButtonPointer.time < miEventQueue.lastEventTime && + miEventQueue.lastEventTime - e->u.keyButtonPointer.time < 10000) + { + miEventQueue.events[oldtail].event.u.keyButtonPointer.time = + miEventQueue.lastEventTime; + } + miEventQueue.events[oldtail].pScreen = miEventQueue.pEnqueueScreen; +} + +void +mieqSwitchScreen (pScreen, fromDIX) + ScreenPtr pScreen; + Bool fromDIX; +{ + miEventQueue.pEnqueueScreen = pScreen; + if (fromDIX) + miEventQueue.pDequeueScreen = pScreen; +} + +/* + * Call this from ProcessInputEvents() + */ + +mieqProcessInputEvents () +{ + EventRec *e; + int x, y; + xEvent xe; + + while (miEventQueue.head != miEventQueue.tail) + { + extern int screenIsSaved; + + if (screenIsSaved == SCREEN_SAVER_ON) + SaveScreens (SCREEN_SAVER_OFF, ScreenSaverReset); + + e = &miEventQueue.events[miEventQueue.head]; + /* + * Assumption - screen switching can only occur on motion events + */ + if (e->pScreen != miEventQueue.pDequeueScreen) + { + miEventQueue.pDequeueScreen = e->pScreen; + x = e->event.u.keyButtonPointer.rootX; + y = e->event.u.keyButtonPointer.rootY; + if (miEventQueue.head == QUEUE_SIZE - 1) + miEventQueue.head = 0; + else + ++miEventQueue.head; + NewCurrentScreen (miEventQueue.pDequeueScreen, x, y); + } + else + { + xe = e->event; + if (miEventQueue.head == QUEUE_SIZE - 1) + miEventQueue.head = 0; + else + ++miEventQueue.head; + switch (xe.u.u.type) + { + case KeyPress: + case KeyRelease: + (*miEventQueue.pKbd->processInputProc) + (&xe, (DeviceIntPtr)miEventQueue.pKbd, 1); + break; + default: + (*miEventQueue.pPtr->processInputProc) + (&xe, (DeviceIntPtr)miEventQueue.pPtr, 1); + break; + } + } + } +} diff --git a/mi/miexpose.c b/mi/miexpose.c new file mode 100644 index 000000000..cacb2f983 --- /dev/null +++ b/mi/miexpose.c @@ -0,0 +1,827 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* $Xorg: miexpose.c,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ + +#include "X.h" +#define NEED_EVENTS +#include "Xproto.h" +#include "Xprotostr.h" + +#include "misc.h" +#include "regionstr.h" +#include "scrnintstr.h" +#include "gcstruct.h" +#include "windowstr.h" +#include "pixmap.h" +#include "input.h" + +#include "dixstruct.h" +#include "mi.h" +#include "Xmd.h" + +extern WindowPtr *WindowTable; + +/* + machine-independent graphics exposure code. any device that uses +the region package can call this. +*/ + +#ifndef RECTLIMIT +#define RECTLIMIT 25 /* pick a number, any number > 8 */ +#endif + +/* miHandleExposures + generate a region for exposures for areas that were copied from obscured or +non-existent areas to non-obscured areas of the destination. Paint the +background for the region, if the destination is a window. + +NOTE: + this should generally be called, even if graphicsExposures is false, +because this is where bits get recovered from backing store. + +NOTE: + added argument 'plane' is used to indicate how exposures from backing +store should be accomplished. If plane is 0 (i.e. no bit plane), CopyArea +should be used, else a CopyPlane of the indicated plane will be used. The +exposing is done by the backing store's GraphicsExpose function, of course. + +*/ + +RegionPtr +miHandleExposures(pSrcDrawable, pDstDrawable, + pGC, srcx, srcy, width, height, dstx, dsty, plane) + register DrawablePtr pSrcDrawable; + register DrawablePtr pDstDrawable; + GCPtr pGC; + int srcx, srcy; + int width, height; + int dstx, dsty; + unsigned long plane; +{ + register ScreenPtr pscr = pGC->pScreen; + RegionPtr prgnSrcClip; /* drawable-relative source clip */ + RegionRec rgnSrcRec; + RegionPtr prgnDstClip; /* drawable-relative dest clip */ + RegionRec rgnDstRec; + BoxRec srcBox; /* unclipped source */ + RegionRec rgnExposed; /* exposed region, calculated source- + relative, made dst relative to + intersect with visible parts of + dest and send events to client, + and then screen relative to paint + the window background + */ + WindowPtr pSrcWin; + BoxRec expBox; + Bool extents; + + /* avoid work if we can */ + if (!pGC->graphicsExposures && + (pDstDrawable->type == DRAWABLE_PIXMAP) && + ((pSrcDrawable->type == DRAWABLE_PIXMAP) || + (((WindowPtr)pSrcDrawable)->backStorage == NULL))) + return NULL; + + srcBox.x1 = srcx; + srcBox.y1 = srcy; + srcBox.x2 = srcx+width; + srcBox.y2 = srcy+height; + + if (pSrcDrawable->type != DRAWABLE_PIXMAP) + { + BoxRec TsrcBox; + + TsrcBox.x1 = srcx + pSrcDrawable->x; + TsrcBox.y1 = srcy + pSrcDrawable->y; + TsrcBox.x2 = TsrcBox.x1 + width; + TsrcBox.y2 = TsrcBox.y1 + height; + pSrcWin = (WindowPtr) pSrcDrawable; + if (pGC->subWindowMode == IncludeInferiors) + { + prgnSrcClip = NotClippedByChildren (pSrcWin); + if ((RECT_IN_REGION(pscr, prgnSrcClip, &TsrcBox)) == rgnIN) + { + REGION_DESTROY(pscr, prgnSrcClip); + return NULL; + } + } + else + { + if ((RECT_IN_REGION(pscr, &pSrcWin->clipList, &TsrcBox)) == rgnIN) + return NULL; + prgnSrcClip = &rgnSrcRec; + REGION_INIT(pscr, prgnSrcClip, NullBox, 0); + REGION_COPY(pscr, prgnSrcClip, &pSrcWin->clipList); + } + REGION_TRANSLATE(pscr, prgnSrcClip, + -pSrcDrawable->x, -pSrcDrawable->y); + } + else + { + BoxRec box; + + if ((srcBox.x1 >= 0) && (srcBox.y1 >= 0) && + (srcBox.x2 <= pSrcDrawable->width) && + (srcBox.y2 <= pSrcDrawable->height)) + return NULL; + + box.x1 = 0; + box.y1 = 0; + box.x2 = pSrcDrawable->width; + box.y2 = pSrcDrawable->height; + prgnSrcClip = &rgnSrcRec; + REGION_INIT(pscr, prgnSrcClip, &box, 1); + pSrcWin = (WindowPtr)NULL; + } + + if (pDstDrawable == pSrcDrawable) + { + prgnDstClip = prgnSrcClip; + } + else if (pDstDrawable->type != DRAWABLE_PIXMAP) + { + if (pGC->subWindowMode == IncludeInferiors) + { + prgnDstClip = NotClippedByChildren((WindowPtr)pDstDrawable); + } + else + { + prgnDstClip = &rgnDstRec; + REGION_INIT(pscr, prgnDstClip, NullBox, 0); + REGION_COPY(pscr, prgnDstClip, + &((WindowPtr)pDstDrawable)->clipList); + } + REGION_TRANSLATE(pscr, prgnDstClip, + -pDstDrawable->x, -pDstDrawable->y); + } + else + { + BoxRec box; + + box.x1 = 0; + box.y1 = 0; + box.x2 = pDstDrawable->width; + box.y2 = pDstDrawable->height; + prgnDstClip = &rgnDstRec; + REGION_INIT(pscr, prgnDstClip, &box, 1); + } + + /* drawable-relative source region */ + REGION_INIT(pscr, &rgnExposed, &srcBox, 1); + + /* now get the hidden parts of the source box*/ + REGION_SUBTRACT(pscr, &rgnExposed, &rgnExposed, prgnSrcClip); + + if (pSrcWin && pSrcWin->backStorage) + { + /* + * Copy any areas from the source backing store. Modifies + * rgnExposed. + */ + (* pSrcWin->drawable.pScreen->ExposeCopy) ((WindowPtr)pSrcDrawable, + pDstDrawable, + pGC, + &rgnExposed, + srcx, srcy, + dstx, dsty, + plane); + } + + /* move them over the destination */ + REGION_TRANSLATE(pscr, &rgnExposed, dstx-srcx, dsty-srcy); + + /* intersect with visible areas of dest */ + REGION_INTERSECT(pscr, &rgnExposed, &rgnExposed, prgnDstClip); + + /* + * If we have LOTS of rectangles, we decide to take the extents + * and force an exposure on that. This should require much less + * work overall, on both client and server. This is cheating, but + * isn't prohibited by the protocol ("spontaneous combustion" :-) + * for windows. + */ + extents = pGC->graphicsExposures && + (REGION_NUM_RECTS(&rgnExposed) > RECTLIMIT) && + (pDstDrawable->type != DRAWABLE_PIXMAP); +#ifdef SHAPE + if (pSrcWin) + { + RegionPtr region; + if (!(region = wClipShape (pSrcWin))) + region = wBoundingShape (pSrcWin); + /* + * If you try to CopyArea the extents of a shaped window, compacting the + * exposed region will undo all our work! + */ + if (extents && pSrcWin && region && + (RECT_IN_REGION(pscr, region, &srcBox) != rgnIN)) + extents = FALSE; + } +#endif + if (extents) + { + WindowPtr pWin = (WindowPtr)pDstDrawable; + + expBox = *REGION_EXTENTS(pscr, &rgnExposed); + REGION_RESET(pscr, &rgnExposed, &expBox); + /* need to clear out new areas of backing store */ + if (pWin->backStorage) + (void) (* pWin->drawable.pScreen->ClearBackingStore)( + pWin, + expBox.x1, + expBox.y1, + expBox.x2 - expBox.x1, + expBox.y2 - expBox.y1, + FALSE); + } + if ((pDstDrawable->type != DRAWABLE_PIXMAP) && + (((WindowPtr)pDstDrawable)->backgroundState != None)) + { + WindowPtr pWin = (WindowPtr)pDstDrawable; + + /* make the exposed area screen-relative */ + REGION_TRANSLATE(pscr, &rgnExposed, + pDstDrawable->x, pDstDrawable->y); + + if (extents) + { + /* PaintWindowBackground doesn't clip, so we have to */ + REGION_INTERSECT(pscr, &rgnExposed, &rgnExposed, &pWin->clipList); + } + (*pWin->drawable.pScreen->PaintWindowBackground)( + (WindowPtr)pDstDrawable, &rgnExposed, PW_BACKGROUND); + + if (extents) + { + REGION_RESET(pscr, &rgnExposed, &expBox); + } + else + REGION_TRANSLATE(pscr, &rgnExposed, + -pDstDrawable->x, -pDstDrawable->y); + } + if (prgnDstClip == &rgnDstRec) + { + REGION_UNINIT(pscr, prgnDstClip); + } + else if (prgnDstClip != prgnSrcClip) + { + REGION_DESTROY(pscr, prgnDstClip); + } + + if (prgnSrcClip == &rgnSrcRec) + { + REGION_UNINIT(pscr, prgnSrcClip); + } + else + { + REGION_DESTROY(pscr, prgnSrcClip); + } + + if (pGC->graphicsExposures) + { + /* don't look */ + RegionPtr exposed = REGION_CREATE(pscr, NullBox, 0); + *exposed = rgnExposed; + return exposed; + } + else + { + REGION_UNINIT(pscr, &rgnExposed); + return NULL; + } +} + +/* send GraphicsExpose events, or a NoExpose event, based on the region */ + +void +miSendGraphicsExpose (client, pRgn, drawable, major, minor) + ClientPtr client; + RegionPtr pRgn; + XID drawable; + int major; + int minor; +{ + if (pRgn && !REGION_NIL(pRgn)) + { + xEvent *pEvent; + register xEvent *pe; + register BoxPtr pBox; + register int i; + int numRects; + + numRects = REGION_NUM_RECTS(pRgn); + pBox = REGION_RECTS(pRgn); + if(!(pEvent = (xEvent *)ALLOCATE_LOCAL(numRects * sizeof(xEvent)))) + return; + pe = pEvent; + + for (i=1; i<=numRects; i++, pe++, pBox++) + { + pe->u.u.type = GraphicsExpose; + pe->u.graphicsExposure.drawable = drawable; + pe->u.graphicsExposure.x = pBox->x1; + pe->u.graphicsExposure.y = pBox->y1; + pe->u.graphicsExposure.width = pBox->x2 - pBox->x1; + pe->u.graphicsExposure.height = pBox->y2 - pBox->y1; + pe->u.graphicsExposure.count = numRects - i; + pe->u.graphicsExposure.majorEvent = major; + pe->u.graphicsExposure.minorEvent = minor; + } + TryClientEvents(client, pEvent, numRects, + (Mask)0, NoEventMask, NullGrab); + DEALLOCATE_LOCAL(pEvent); + } + else + { + xEvent event; + event.u.u.type = NoExpose; + event.u.noExposure.drawable = drawable; + event.u.noExposure.majorEvent = major; + event.u.noExposure.minorEvent = minor; + TryClientEvents(client, &event, 1, + (Mask)0, NoEventMask, NullGrab); + } +} + +void +miSendExposures(pWin, pRgn, dx, dy) + WindowPtr pWin; + RegionPtr pRgn; + register int dx, dy; +{ + register BoxPtr pBox; + int numRects; + register xEvent *pEvent, *pe; + register int i; + + pBox = REGION_RECTS(pRgn); + numRects = REGION_NUM_RECTS(pRgn); + if(!(pEvent = (xEvent *) ALLOCATE_LOCAL(numRects * sizeof(xEvent)))) + return; + + for (i=numRects, pe = pEvent; --i >= 0; pe++, pBox++) + { + pe->u.u.type = Expose; + pe->u.expose.window = pWin->drawable.id; + pe->u.expose.x = pBox->x1 - dx; + pe->u.expose.y = pBox->y1 - dy; + pe->u.expose.width = pBox->x2 - pBox->x1; + pe->u.expose.height = pBox->y2 - pBox->y1; + pe->u.expose.count = i; + } + DeliverEvents(pWin, pEvent, numRects, NullWindow); + DEALLOCATE_LOCAL(pEvent); +} + +void +miWindowExposures(pWin, prgn, other_exposed) + WindowPtr pWin; + register RegionPtr prgn, other_exposed; +{ + RegionPtr exposures = prgn; + if (pWin->backStorage && prgn) + /* + * in some cases, backing store will cause a different + * region to be exposed than needs to be repainted + * (like when a window is mapped). RestoreAreas is + * allowed to return a region other than prgn, + * in which case this routine will free the resultant + * region. If exposures is null, then no events will + * be sent to the client; if prgn is empty + * no areas will be repainted. + */ + exposures = (*pWin->drawable.pScreen->RestoreAreas)(pWin, prgn); + if ((prgn && !REGION_NIL(prgn)) || + (exposures && !REGION_NIL(exposures)) || other_exposed) + { + RegionRec expRec; + int clientInterested; + + /* + * Restore from backing-store FIRST. + */ + clientInterested = (pWin->eventMask|wOtherEventMasks(pWin)) & ExposureMask; + if (other_exposed) + { + if (exposures) + { + REGION_UNION(pWin->drawable.pScreen, other_exposed, + exposures, + other_exposed); + if (exposures != prgn) + REGION_DESTROY(pWin->drawable.pScreen, exposures); + } + exposures = other_exposed; + } + if (clientInterested && exposures && (REGION_NUM_RECTS(exposures) > RECTLIMIT)) + { + /* + * If we have LOTS of rectangles, we decide to take the extents + * and force an exposure on that. This should require much less + * work overall, on both client and server. This is cheating, but + * isn't prohibited by the protocol ("spontaneous combustion" :-). + */ + BoxRec box; + + box = *REGION_EXTENTS( pWin->drawable.pScreen, exposures); + if (exposures == prgn) { + exposures = &expRec; + REGION_INIT( pWin->drawable.pScreen, exposures, &box, 1); + REGION_RESET( pWin->drawable.pScreen, prgn, &box); + } else { + REGION_RESET( pWin->drawable.pScreen, exposures, &box); + REGION_UNION( pWin->drawable.pScreen, prgn, prgn, exposures); + } + /* PaintWindowBackground doesn't clip, so we have to */ + REGION_INTERSECT( pWin->drawable.pScreen, prgn, prgn, &pWin->clipList); + /* need to clear out new areas of backing store, too */ + if (pWin->backStorage) + (void) (* pWin->drawable.pScreen->ClearBackingStore)( + pWin, + box.x1 - pWin->drawable.x, + box.y1 - pWin->drawable.y, + box.x2 - box.x1, + box.y2 - box.y1, + FALSE); + } + if (prgn && !REGION_NIL(prgn)) + (*pWin->drawable.pScreen->PaintWindowBackground)(pWin, prgn, PW_BACKGROUND); + if (clientInterested && exposures && !REGION_NIL(exposures)) + miSendExposures(pWin, exposures, + pWin->drawable.x, pWin->drawable.y); + if (exposures == &expRec) + { + REGION_UNINIT( pWin->drawable.pScreen, exposures); + } + else if (exposures && exposures != prgn && exposures != other_exposed) + REGION_DESTROY( pWin->drawable.pScreen, exposures); + if (prgn) + REGION_EMPTY( pWin->drawable.pScreen, prgn); + } + else if (exposures && exposures != prgn) + REGION_DESTROY( pWin->drawable.pScreen, exposures); +} + + +/* + this code is highly unlikely. it is not haile selassie. + + there is some hair here. we can't just use the window's +clip region as it is, because if we are painting the border, +the border is not in the client area and so we will be excluded +when we validate the GC, and if we are painting a parent-relative +background, the area we want to paint is in some other window. +since we trust the code calling us to tell us to paint only areas +that are really ours, we will temporarily give the window a +clipList the size of the whole screen and an origin at (0,0). +this more or less assumes that ddX code will do translation +based on the window's absolute position, and that ValidateGC will +look at clipList, and that no other fields from the +window will be used. it's not possible to just draw +in the root because it may be a different depth. + +to get the tile to align correctly we set the GC's tile origin to +be the (x,y) of the window's upper left corner, after which we +get the right bits when drawing into the root. + +because the clip_mask is being set to None, we may call DoChangeGC with +fPointer set true, thus we no longer need to install the background or +border tile in the resource table. +*/ + +static RESTYPE ResType = 0; +static int numGCs = 0; +static GCPtr screenContext[MAXSCREENS]; + +/*ARGSUSED*/ +static int +tossGC (value, id) +pointer value; +XID id; +{ + GCPtr pGC = (GCPtr)value; + screenContext[pGC->pScreen->myNum] = (GCPtr)NULL; + FreeGC (pGC, id); + numGCs--; + if (!numGCs) + ResType = 0; +} + + +void +miPaintWindow(pWin, prgn, what) +register WindowPtr pWin; +RegionPtr prgn; +int what; +{ + int status; + + Bool usingScratchGC = FALSE; + WindowPtr pRoot; + +#define FUNCTION 0 +#define FOREGROUND 1 +#define TILE 2 +#define FILLSTYLE 3 +#define ABSX 4 +#define ABSY 5 +#define CLIPMASK 6 +#define SUBWINDOW 7 +#define COUNT_BITS 8 + + ChangeGCVal gcval[7]; + ChangeGCVal newValues [COUNT_BITS]; + + BITS32 gcmask, index, mask; + RegionRec prgnWin; + DDXPointRec oldCorner; + BoxRec box; + WindowPtr pBgWin; + GCPtr pGC; + register int i; + register BoxPtr pbox; + register ScreenPtr pScreen = pWin->drawable.pScreen; + register xRectangle *prect; + int numRects; + + gcmask = 0; + + if (what == PW_BACKGROUND) + { + switch (pWin->backgroundState) { + case None: + return; + case ParentRelative: + (*pWin->parent->drawable.pScreen->PaintWindowBackground)(pWin->parent, prgn, what); + return; + case BackgroundPixel: + newValues[FOREGROUND].val = pWin->background.pixel; + newValues[FILLSTYLE].val = FillSolid; + gcmask |= GCForeground | GCFillStyle; + break; + case BackgroundPixmap: + newValues[TILE].ptr = (pointer)pWin->background.pixmap; + newValues[FILLSTYLE].val = FillTiled; + gcmask |= GCTile | GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin; + break; + } + } + else + { + if (pWin->borderIsPixel) + { + newValues[FOREGROUND].val = pWin->border.pixel; + newValues[FILLSTYLE].val = FillSolid; + gcmask |= GCForeground | GCFillStyle; + } + else + { + newValues[TILE].ptr = (pointer)pWin->border.pixmap; + newValues[FILLSTYLE].val = FillTiled; + gcmask |= GCTile | GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin; + } + } + + prect = (xRectangle *)ALLOCATE_LOCAL(REGION_NUM_RECTS(prgn) * + sizeof(xRectangle)); + if (!prect) + return; + + newValues[FUNCTION].val = GXcopy; + gcmask |= GCFunction | GCClipMask; + + i = pScreen->myNum; + pRoot = WindowTable[i]; + + pBgWin = pWin; + if (what == PW_BORDER) + { + while (pBgWin->backgroundState == ParentRelative) + pBgWin = pBgWin->parent; + } + + if ((pWin->drawable.depth != pRoot->drawable.depth) || + (pWin->drawable.bitsPerPixel != pRoot->drawable.bitsPerPixel)) + { + usingScratchGC = TRUE; + pGC = GetScratchGC(pWin->drawable.depth, pWin->drawable.pScreen); + if (!pGC) + { + DEALLOCATE_LOCAL(prect); + return; + } + /* + * mash the clip list so we can paint the border by + * mangling the window in place, pretending it + * spans the entire screen + */ + if (what == PW_BORDER) + { + prgnWin = pWin->clipList; + oldCorner.x = pWin->drawable.x; + oldCorner.y = pWin->drawable.y; + pWin->drawable.x = pWin->drawable.y = 0; + box.x1 = 0; + box.y1 = 0; + box.x2 = pScreen->width; + box.y2 = pScreen->height; + REGION_INIT(pScreen, &pWin->clipList, &box, 1); + pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER; + newValues[ABSX].val = pBgWin->drawable.x; + newValues[ABSY].val = pBgWin->drawable.y; + } + else + { + newValues[ABSX].val = 0; + newValues[ABSY].val = 0; + } + } else { + /* + * draw the background to the root window + */ + if (screenContext[i] == (GCPtr)NULL) + { + if (!ResType && !(ResType = CreateNewResourceType(tossGC))) + return; + screenContext[i] = CreateGC((DrawablePtr)pWin, (BITS32) 0, + (XID *)NULL, &status); + if (!screenContext[i]) + return; + numGCs++; + if (!AddResource(FakeClientID(0), ResType, + (pointer)screenContext[i])) + return; + } + pGC = screenContext[i]; + newValues[SUBWINDOW].val = IncludeInferiors; + newValues[ABSX].val = pBgWin->drawable.x; + newValues[ABSY].val = pBgWin->drawable.y; + gcmask |= GCSubwindowMode; + pWin = pRoot; + } + + if (pWin->backStorage) + (*pWin->drawable.pScreen->DrawGuarantee) (pWin, pGC, GuaranteeVisBack); + + mask = gcmask; + gcmask = 0; + i = 0; + while (mask) { + index = lowbit (mask); + mask &= ~index; + switch (index) { + case GCFunction: + if (pGC->alu != newValues[FUNCTION].val) { + gcmask |= index; + gcval[i++].val = newValues[FUNCTION].val; + } + break; + case GCTileStipXOrigin: + if ( pGC->patOrg.x != newValues[ABSX].val) { + gcmask |= index; + gcval[i++].val = newValues[ABSX].val; + } + break; + case GCTileStipYOrigin: + if ( pGC->patOrg.y != newValues[ABSY].val) { + gcmask |= index; + gcval[i++].val = newValues[ABSY].val; + } + break; + case GCClipMask: + if ( pGC->clientClipType != CT_NONE) { + gcmask |= index; + gcval[i++].val = CT_NONE; + } + break; + case GCSubwindowMode: + if ( pGC->subWindowMode != newValues[SUBWINDOW].val) { + gcmask |= index; + gcval[i++].val = newValues[SUBWINDOW].val; + } + break; + case GCTile: + if (pGC->tileIsPixel || pGC->tile.pixmap != newValues[TILE].ptr) + { + gcmask |= index; + gcval[i++].ptr = newValues[TILE].ptr; + } + break; + case GCFillStyle: + if ( pGC->fillStyle != newValues[FILLSTYLE].val) { + gcmask |= index; + gcval[i++].val = newValues[FILLSTYLE].val; + } + break; + case GCForeground: + if ( pGC->fgPixel != newValues[FOREGROUND].val) { + gcmask |= index; + gcval[i++].val = newValues[FOREGROUND].val; + } + break; + } + } + + if (gcmask) + dixChangeGC(NullClient, pGC, gcmask, NULL, gcval); + + if (pWin->drawable.serialNumber != pGC->serialNumber) + ValidateGC((DrawablePtr)pWin, pGC); + + numRects = REGION_NUM_RECTS(prgn); + pbox = REGION_RECTS(prgn); + for (i= numRects; --i >= 0; pbox++, prect++) + { + prect->x = pbox->x1 - pWin->drawable.x; + prect->y = pbox->y1 - pWin->drawable.y; + prect->width = pbox->x2 - pbox->x1; + prect->height = pbox->y2 - pbox->y1; + } + prect -= numRects; + (*pGC->ops->PolyFillRect)((DrawablePtr)pWin, pGC, numRects, prect); + DEALLOCATE_LOCAL(prect); + + if (pWin->backStorage) + (*pWin->drawable.pScreen->DrawGuarantee) (pWin, pGC, GuaranteeNothing); + + if (usingScratchGC) + { + if (what == PW_BORDER) + { + REGION_UNINIT(pScreen, &pWin->clipList); + pWin->clipList = prgnWin; + pWin->drawable.x = oldCorner.x; + pWin->drawable.y = oldCorner.y; + pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER; + } + FreeScratchGC(pGC); + } +} + + +/* MICLEARDRAWABLE -- sets the entire drawable to the background color of + * the GC. Useful when we have a scratch drawable and need to initialize + * it. */ +miClearDrawable(pDraw, pGC) + DrawablePtr pDraw; + GCPtr pGC; +{ + XID fg = pGC->fgPixel; + XID bg = pGC->bgPixel; + xRectangle rect; + + rect.x = 0; + rect.y = 0; + rect.width = pDraw->width; + rect.height = pDraw->height; + DoChangeGC(pGC, GCForeground, &bg, 0); + ValidateGC(pDraw, pGC); + (*pGC->ops->PolyFillRect)(pDraw, pGC, 1, &rect); + DoChangeGC(pGC, GCForeground, &fg, 0); + ValidateGC(pDraw, pGC); +} diff --git a/mi/mifillarc.c b/mi/mifillarc.c new file mode 100644 index 000000000..5889f1c06 --- /dev/null +++ b/mi/mifillarc.c @@ -0,0 +1,810 @@ +/************************************************************ + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +Author: Bob Scheifler, MIT X Consortium + +********************************************************/ + +/* $Xorg: mifillarc.c,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ + +#include <math.h> +#include "X.h" +#include "Xprotostr.h" +#include "miscstruct.h" +#include "gcstruct.h" +#include "pixmapstr.h" +#include "mifpoly.h" +#include "mi.h" +#include "mifillarc.h" + +#define QUADRANT (90 * 64) +#define HALFCIRCLE (180 * 64) +#define QUADRANT3 (270 * 64) + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define Dsin(d) sin((double)d*(M_PI/11520.0)) +#define Dcos(d) cos((double)d*(M_PI/11520.0)) + +void +miFillArcSetup(arc, info) + register xArc *arc; + register miFillArcRec *info; +{ + info->y = arc->height >> 1; + info->dy = arc->height & 1; + info->yorg = arc->y + info->y; + info->dx = arc->width & 1; + info->xorg = arc->x + (arc->width >> 1) + info->dx; + info->dx = 1 - info->dx; + if (arc->width == arc->height) + { + /* (2x - 2xorg)^2 = d^2 - (2y - 2yorg)^2 */ + /* even: xorg = yorg = 0 odd: xorg = .5, yorg = -.5 */ + info->ym = 8; + info->xm = 8; + info->yk = info->y << 3; + if (!info->dx) + { + info->xk = 0; + info->e = -1; + } + else + { + info->y++; + info->yk += 4; + info->xk = -4; + info->e = - (info->y << 3); + } + } + else + { + /* h^2 * (2x - 2xorg)^2 = w^2 * h^2 - w^2 * (2y - 2yorg)^2 */ + /* even: xorg = yorg = 0 odd: xorg = .5, yorg = -.5 */ + info->ym = (arc->width * arc->width) << 3; + info->xm = (arc->height * arc->height) << 3; + info->yk = info->y * info->ym; + if (!info->dy) + info->yk -= info->ym >> 1; + if (!info->dx) + { + info->xk = 0; + info->e = - (info->xm >> 3); + } + else + { + info->y++; + info->yk += info->ym; + info->xk = -(info->xm >> 1); + info->e = info->xk - info->yk; + } + } +} + +void +miFillArcDSetup(arc, info) + register xArc *arc; + register miFillArcDRec *info; +{ + /* h^2 * (2x - 2xorg)^2 = w^2 * h^2 - w^2 * (2y - 2yorg)^2 */ + /* even: xorg = yorg = 0 odd: xorg = .5, yorg = -.5 */ + info->y = arc->height >> 1; + info->dy = arc->height & 1; + info->yorg = arc->y + info->y; + info->dx = arc->width & 1; + info->xorg = arc->x + (arc->width >> 1) + info->dx; + info->dx = 1 - info->dx; + info->ym = ((double)arc->width) * (arc->width * 8); + info->xm = ((double)arc->height) * (arc->height * 8); + info->yk = info->y * info->ym; + if (!info->dy) + info->yk -= info->ym / 2.0; + if (!info->dx) + { + info->xk = 0; + info->e = - (info->xm / 8.0); + } + else + { + info->y++; + info->yk += info->ym; + info->xk = -info->xm / 2.0; + info->e = info->xk - info->yk; + } +} + +static void +miGetArcEdge(arc, edge, k, top, left) + register xArc *arc; + register miSliceEdgePtr edge; + int k; + Bool top, left; +{ + register int xady, y; + + y = arc->height >> 1; + if (!(arc->width & 1)) + y++; + if (!top) + { + y = -y; + if (arc->height & 1) + y--; + } + xady = k + y * edge->dx; + if (xady <= 0) + edge->x = - ((-xady) / edge->dy + 1); + else + edge->x = (xady - 1) / edge->dy; + edge->e = xady - edge->x * edge->dy; + if ((top && (edge->dx < 0)) || (!top && (edge->dx > 0))) + edge->e = edge->dy - edge->e + 1; + if (left) + edge->x++; + edge->x += arc->x + (arc->width >> 1); + if (edge->dx > 0) + { + edge->deltax = 1; + edge->stepx = edge->dx / edge->dy; + edge->dx = edge->dx % edge->dy; + } + else + { + edge->deltax = -1; + edge->stepx = - ((-edge->dx) / edge->dy); + edge->dx = (-edge->dx) % edge->dy; + } + if (!top) + { + edge->deltax = -edge->deltax; + edge->stepx = -edge->stepx; + } +} + +void +miEllipseAngleToSlope (angle, width, height, dxp, dyp, d_dxp, d_dyp) + int angle; + int width; + int height; + int *dxp; + int *dyp; + double *d_dxp; + double *d_dyp; +{ + int dx, dy; + double d_dx, d_dy, scale; + Bool negative_dx, negative_dy; + + switch (angle) { + case 0: + *dxp = -1; + *dyp = 0; + if (d_dxp) { + *d_dxp = width / 2.0; + *d_dyp = 0; + } + break; + case QUADRANT: + *dxp = 0; + *dyp = 1; + if (d_dxp) { + *d_dxp = 0; + *d_dyp = - height / 2.0; + } + break; + case HALFCIRCLE: + *dxp = 1; + *dyp = 0; + if (d_dxp) { + *d_dxp = - width / 2.0; + *d_dyp = 0; + } + break; + case QUADRANT3: + *dxp = 0; + *dyp = -1; + if (d_dxp) { + *d_dxp = 0; + *d_dyp = height / 2.0; + } + break; + default: + d_dx = Dcos(angle) * width; + d_dy = Dsin(angle) * height; + if (d_dxp) { + *d_dxp = d_dx / 2.0; + *d_dyp = - d_dy / 2.0; + } + negative_dx = FALSE; + if (d_dx < 0.0) + { + d_dx = -d_dx; + negative_dx = TRUE; + } + negative_dy = FALSE; + if (d_dy < 0.0) + { + d_dy = -d_dy; + negative_dy = TRUE; + } + scale = d_dx; + if (d_dy > d_dx) + scale = d_dy; + dx = floor ((d_dx * 32768) / scale + 0.5); + if (negative_dx) + dx = -dx; + *dxp = dx; + dy = floor ((d_dy * 32768) / scale + 0.5); + if (negative_dy) + dy = -dy; + *dyp = dy; + break; + } +} + +static void +miGetPieEdge(arc, angle, edge, top, left) + register xArc *arc; + register int angle; + register miSliceEdgePtr edge; + Bool top, left; +{ + register int k, signdx, signdy; + int dx, dy; + + miEllipseAngleToSlope (angle, arc->width, arc->height, &dx, &dy, 0, 0); + + if (dy == 0) + { + edge->x = left ? -65536 : 65536; + edge->stepx = 0; + edge->e = 0; + edge->dx = -1; + return; + } + if (dx == 0) + { + edge->x = arc->x + (arc->width >> 1); + if (left && (arc->width & 1)) + edge->x++; + else if (!left && !(arc->width & 1)) + edge->x--; + edge->stepx = 0; + edge->e = 0; + edge->dx = -1; + return; + } + if (dy < 0) { + dx = -dx; + dy = -dy; + } + k = (arc->height & 1) ? dx : 0; + if (arc->width & 1) + k += dy; + edge->dx = dx << 1; + edge->dy = dy << 1; + miGetArcEdge(arc, edge, k, top, left); +} + +void +miFillArcSliceSetup(arc, slice, pGC) + register xArc *arc; + register miArcSliceRec *slice; + GCPtr pGC; +{ + register int angle1, angle2; + + angle1 = arc->angle1; + if (arc->angle2 < 0) + { + angle2 = angle1; + angle1 += arc->angle2; + } + else + angle2 = angle1 + arc->angle2; + while (angle1 < 0) + angle1 += FULLCIRCLE; + while (angle1 >= FULLCIRCLE) + angle1 -= FULLCIRCLE; + while (angle2 < 0) + angle2 += FULLCIRCLE; + while (angle2 >= FULLCIRCLE) + angle2 -= FULLCIRCLE; + slice->min_top_y = 0; + slice->max_top_y = arc->height >> 1; + slice->min_bot_y = 1 - (arc->height & 1); + slice->max_bot_y = slice->max_top_y - 1; + slice->flip_top = FALSE; + slice->flip_bot = FALSE; + if (pGC->arcMode == ArcPieSlice) + { + slice->edge1_top = (angle1 < HALFCIRCLE); + slice->edge2_top = (angle2 <= HALFCIRCLE); + if ((angle2 == 0) || (angle1 == HALFCIRCLE)) + { + if (angle2 ? slice->edge2_top : slice->edge1_top) + slice->min_top_y = slice->min_bot_y; + else + slice->min_top_y = arc->height; + slice->min_bot_y = 0; + } + else if ((angle1 == 0) || (angle2 == HALFCIRCLE)) + { + slice->min_top_y = slice->min_bot_y; + if (angle1 ? slice->edge1_top : slice->edge2_top) + slice->min_bot_y = arc->height; + else + slice->min_bot_y = 0; + } + else if (slice->edge1_top == slice->edge2_top) + { + if (angle2 < angle1) + { + slice->flip_top = slice->edge1_top; + slice->flip_bot = !slice->edge1_top; + } + else if (slice->edge1_top) + { + slice->min_top_y = 1; + slice->min_bot_y = arc->height; + } + else + { + slice->min_bot_y = 0; + slice->min_top_y = arc->height; + } + } + miGetPieEdge(arc, angle1, &slice->edge1, + slice->edge1_top, !slice->edge1_top); + miGetPieEdge(arc, angle2, &slice->edge2, + slice->edge2_top, slice->edge2_top); + } + else + { + double w2, h2, x1, y1, x2, y2, dx, dy, scale; + int signdx, signdy, y, k; + Bool isInt1 = TRUE, isInt2 = TRUE; + + w2 = (double)arc->width / 2.0; + h2 = (double)arc->height / 2.0; + if ((angle1 == 0) || (angle1 == HALFCIRCLE)) + { + x1 = angle1 ? -w2 : w2; + y1 = 0.0; + } + else if ((angle1 == QUADRANT) || (angle1 == QUADRANT3)) + { + x1 = 0.0; + y1 = (angle1 == QUADRANT) ? h2 : -h2; + } + else + { + isInt1 = FALSE; + x1 = Dcos(angle1) * w2; + y1 = Dsin(angle1) * h2; + } + if ((angle2 == 0) || (angle2 == HALFCIRCLE)) + { + x2 = angle2 ? -w2 : w2; + y2 = 0.0; + } + else if ((angle2 == QUADRANT) || (angle2 == QUADRANT3)) + { + x2 = 0.0; + y2 = (angle2 == QUADRANT) ? h2 : -h2; + } + else + { + isInt2 = FALSE; + x2 = Dcos(angle2) * w2; + y2 = Dsin(angle2) * h2; + } + dx = x2 - x1; + dy = y2 - y1; + if (arc->height & 1) + { + y1 -= 0.5; + y2 -= 0.5; + } + if (arc->width & 1) + { + x1 += 0.5; + x2 += 0.5; + } + if (dy < 0.0) + { + dy = -dy; + signdy = -1; + } + else + signdy = 1; + if (dx < 0.0) + { + dx = -dx; + signdx = -1; + } + else + signdx = 1; + if (isInt1 && isInt2) + { + slice->edge1.dx = dx * 2; + slice->edge1.dy = dy * 2; + } + else + { + scale = (dx > dy) ? dx : dy; + slice->edge1.dx = floor((dx * 32768) / scale + .5); + slice->edge1.dy = floor((dy * 32768) / scale + .5); + } + if (!slice->edge1.dy) + { + if (signdx < 0) + { + y = floor(y1 + 1.0); + if (y >= 0) + { + slice->min_top_y = y; + slice->min_bot_y = arc->height; + } + else + { + slice->max_bot_y = -y - (arc->height & 1); + } + } + else + { + y = floor(y1); + if (y >= 0) + slice->max_top_y = y; + else + { + slice->min_top_y = arc->height; + slice->min_bot_y = -y - (arc->height & 1); + } + } + slice->edge1_top = TRUE; + slice->edge1.x = 65536; + slice->edge1.stepx = 0; + slice->edge1.e = 0; + slice->edge1.dx = -1; + slice->edge2 = slice->edge1; + slice->edge2_top = FALSE; + } + else if (!slice->edge1.dx) + { + if (signdy < 0) + x1 -= 1.0; + slice->edge1.x = ceil(x1); + slice->edge1_top = signdy < 0; + slice->edge1.x += arc->x + (arc->width >> 1); + slice->edge1.stepx = 0; + slice->edge1.e = 0; + slice->edge1.dx = -1; + slice->edge2_top = !slice->edge1_top; + slice->edge2 = slice->edge1; + } + else + { + if (signdx < 0) + slice->edge1.dx = -slice->edge1.dx; + if (signdy < 0) + slice->edge1.dx = -slice->edge1.dx; + k = ceil(((x1 + x2) * slice->edge1.dy - (y1 + y2) * slice->edge1.dx) / 2.0); + slice->edge2.dx = slice->edge1.dx; + slice->edge2.dy = slice->edge1.dy; + slice->edge1_top = signdy < 0; + slice->edge2_top = !slice->edge1_top; + miGetArcEdge(arc, &slice->edge1, k, + slice->edge1_top, !slice->edge1_top); + miGetArcEdge(arc, &slice->edge2, k, + slice->edge2_top, slice->edge2_top); + } + } +} + +#define ADDSPANS() \ + pts->x = xorg - x; \ + pts->y = yorg - y; \ + *wids = slw; \ + pts++; \ + wids++; \ + if (miFillArcLower(slw)) \ + { \ + pts->x = xorg - x; \ + pts->y = yorg + y + dy; \ + pts++; \ + *wids++ = slw; \ + } + +static void +miFillEllipseI(pDraw, pGC, arc) + DrawablePtr pDraw; + GCPtr pGC; + xArc *arc; +{ + register int x, y, e; + int yk, xk, ym, xm, dx, dy, xorg, yorg; + int slw; + miFillArcRec info; + DDXPointPtr points; + register DDXPointPtr pts; + int *widths; + register int *wids; + + points = (DDXPointPtr)ALLOCATE_LOCAL(sizeof(DDXPointRec) * arc->height); + if (!points) + return; + widths = (int *)ALLOCATE_LOCAL(sizeof(int) * arc->height); + if (!widths) + { + DEALLOCATE_LOCAL(points); + return; + } + miFillArcSetup(arc, &info); + MIFILLARCSETUP(); + if (pGC->miTranslate) + { + xorg += pDraw->x; + yorg += pDraw->y; + } + pts = points; + wids = widths; + while (y > 0) + { + MIFILLARCSTEP(slw); + ADDSPANS(); + } + (*pGC->ops->FillSpans)(pDraw, pGC, pts - points, points, widths, FALSE); + DEALLOCATE_LOCAL(widths); + DEALLOCATE_LOCAL(points); +} + +static void +miFillEllipseD(pDraw, pGC, arc) + DrawablePtr pDraw; + GCPtr pGC; + xArc *arc; +{ + register int x, y; + int xorg, yorg, dx, dy, slw; + double e, yk, xk, ym, xm; + miFillArcDRec info; + DDXPointPtr points; + register DDXPointPtr pts; + int *widths; + register int *wids; + + points = (DDXPointPtr)ALLOCATE_LOCAL(sizeof(DDXPointRec) * arc->height); + if (!points) + return; + widths = (int *)ALLOCATE_LOCAL(sizeof(int) * arc->height); + if (!widths) + { + DEALLOCATE_LOCAL(points); + return; + } + miFillArcDSetup(arc, &info); + MIFILLARCSETUP(); + if (pGC->miTranslate) + { + xorg += pDraw->x; + yorg += pDraw->y; + } + pts = points; + wids = widths; + while (y > 0) + { + MIFILLARCSTEP(slw); + ADDSPANS(); + } + (*pGC->ops->FillSpans)(pDraw, pGC, pts - points, points, widths, FALSE); + DEALLOCATE_LOCAL(widths); + DEALLOCATE_LOCAL(points); +} + +#define ADDSPAN(l,r) \ + if (r >= l) \ + { \ + pts->x = l; \ + pts->y = ya; \ + pts++; \ + *wids++ = r - l + 1; \ + } + +#define ADDSLICESPANS(flip) \ + if (!flip) \ + { \ + ADDSPAN(xl, xr); \ + } \ + else \ + { \ + xc = xorg - x; \ + ADDSPAN(xc, xr); \ + xc += slw - 1; \ + ADDSPAN(xl, xc); \ + } + +static void +miFillArcSliceI(pDraw, pGC, arc) + DrawablePtr pDraw; + GCPtr pGC; + xArc *arc; +{ + int yk, xk, ym, xm, dx, dy, xorg, yorg, slw; + register int x, y, e; + miFillArcRec info; + miArcSliceRec slice; + int ya, xl, xr, xc; + DDXPointPtr points; + register DDXPointPtr pts; + int *widths; + register int *wids; + + miFillArcSetup(arc, &info); + miFillArcSliceSetup(arc, &slice, pGC); + MIFILLARCSETUP(); + slw = arc->height; + if (slice.flip_top || slice.flip_bot) + slw += (arc->height >> 1) + 1; + points = (DDXPointPtr)ALLOCATE_LOCAL(sizeof(DDXPointRec) * slw); + if (!points) + return; + widths = (int *)ALLOCATE_LOCAL(sizeof(int) * slw); + if (!widths) + { + DEALLOCATE_LOCAL(points); + return; + } + if (pGC->miTranslate) + { + xorg += pDraw->x; + yorg += pDraw->y; + slice.edge1.x += pDraw->x; + slice.edge2.x += pDraw->x; + } + pts = points; + wids = widths; + while (y > 0) + { + MIFILLARCSTEP(slw); + MIARCSLICESTEP(slice.edge1); + MIARCSLICESTEP(slice.edge2); + if (miFillSliceUpper(slice)) + { + ya = yorg - y; + MIARCSLICEUPPER(xl, xr, slice, slw); + ADDSLICESPANS(slice.flip_top); + } + if (miFillSliceLower(slice)) + { + ya = yorg + y + dy; + MIARCSLICELOWER(xl, xr, slice, slw); + ADDSLICESPANS(slice.flip_bot); + } + } + (*pGC->ops->FillSpans)(pDraw, pGC, pts - points, points, widths, FALSE); + DEALLOCATE_LOCAL(widths); + DEALLOCATE_LOCAL(points); +} + +static void +miFillArcSliceD(pDraw, pGC, arc) + DrawablePtr pDraw; + GCPtr pGC; + xArc *arc; +{ + register int x, y; + int dx, dy, xorg, yorg, slw; + double e, yk, xk, ym, xm; + miFillArcDRec info; + miArcSliceRec slice; + int ya, xl, xr, xc; + DDXPointPtr points; + register DDXPointPtr pts; + int *widths; + register int *wids; + + miFillArcDSetup(arc, &info); + miFillArcSliceSetup(arc, &slice, pGC); + MIFILLARCSETUP(); + slw = arc->height; + if (slice.flip_top || slice.flip_bot) + slw += (arc->height >> 1) + 1; + points = (DDXPointPtr)ALLOCATE_LOCAL(sizeof(DDXPointRec) * slw); + if (!points) + return; + widths = (int *)ALLOCATE_LOCAL(sizeof(int) * slw); + if (!widths) + { + DEALLOCATE_LOCAL(points); + return; + } + if (pGC->miTranslate) + { + xorg += pDraw->x; + yorg += pDraw->y; + slice.edge1.x += pDraw->x; + slice.edge2.x += pDraw->x; + } + pts = points; + wids = widths; + while (y > 0) + { + MIFILLARCSTEP(slw); + MIARCSLICESTEP(slice.edge1); + MIARCSLICESTEP(slice.edge2); + if (miFillSliceUpper(slice)) + { + ya = yorg - y; + MIARCSLICEUPPER(xl, xr, slice, slw); + ADDSLICESPANS(slice.flip_top); + } + if (miFillSliceLower(slice)) + { + ya = yorg + y + dy; + MIARCSLICELOWER(xl, xr, slice, slw); + ADDSLICESPANS(slice.flip_bot); + } + } + (*pGC->ops->FillSpans)(pDraw, pGC, pts - points, points, widths, FALSE); + DEALLOCATE_LOCAL(widths); + DEALLOCATE_LOCAL(points); +} + +/* MIPOLYFILLARC -- The public entry for the PolyFillArc request. + * Since we don't have to worry about overlapping segments, we can just + * fill each arc as it comes. + */ +void +miPolyFillArc(pDraw, pGC, narcs, parcs) + DrawablePtr pDraw; + GCPtr pGC; + int narcs; + xArc *parcs; +{ + register int i; + register xArc *arc; + + for(i = narcs, arc = parcs; --i >= 0; arc++) + { + if (miFillArcEmpty(arc)) + continue;; + if ((arc->angle2 >= FULLCIRCLE) || (arc->angle2 <= -FULLCIRCLE)) + { + if (miCanFillArc(arc)) + miFillEllipseI(pDraw, pGC, arc); + else + miFillEllipseD(pDraw, pGC, arc); + } + else + { + if (miCanFillArc(arc)) + miFillArcSliceI(pDraw, pGC, arc); + else + miFillArcSliceD(pDraw, pGC, arc); + } + } +} diff --git a/mi/mifillarc.h b/mi/mifillarc.h new file mode 100644 index 000000000..63071bbd1 --- /dev/null +++ b/mi/mifillarc.h @@ -0,0 +1,223 @@ +/************************************************************ + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +********************************************************/ + +/* $Xorg: mifillarc.h,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ + +#define FULLCIRCLE (360 * 64) + +typedef struct _miFillArc { + int xorg, yorg; + int y; + int dx, dy; + int e; + int ym, yk, xm, xk; +} miFillArcRec; + +/* could use 64-bit integers */ +typedef struct _miFillArcD { + int xorg, yorg; + int y; + int dx, dy; + double e; + double ym, yk, xm, xk; +} miFillArcDRec; + +#define miFillArcEmpty(arc) (!(arc)->angle2 || \ + !(arc)->width || !(arc)->height || \ + (((arc)->width == 1) && ((arc)->height & 1))) + +#define miCanFillArc(arc) (((arc)->width == (arc)->height) || \ + (((arc)->width <= 800) && ((arc)->height <= 800))) + +#define MIFILLARCSETUP() \ + x = 0; \ + y = info.y; \ + e = info.e; \ + xk = info.xk; \ + xm = info.xm; \ + yk = info.yk; \ + ym = info.ym; \ + dx = info.dx; \ + dy = info.dy; \ + xorg = info.xorg; \ + yorg = info.yorg + +#define MIFILLARCSTEP(slw) \ + e += yk; \ + while (e >= 0) \ + { \ + x++; \ + xk -= xm; \ + e += xk; \ + } \ + y--; \ + yk -= ym; \ + slw = (x << 1) + dx; \ + if ((e == xk) && (slw > 1)) \ + slw-- + +#define MIFILLCIRCSTEP(slw) MIFILLARCSTEP(slw) +#define MIFILLELLSTEP(slw) MIFILLARCSTEP(slw) + +#define miFillArcLower(slw) (((y + dy) != 0) && ((slw > 1) || (e != xk))) + +typedef struct _miSliceEdge { + int x; + int stepx; + int deltax; + int e; + int dy; + int dx; +} miSliceEdgeRec, *miSliceEdgePtr; + +typedef struct _miArcSlice { + miSliceEdgeRec edge1, edge2; + int min_top_y, max_top_y; + int min_bot_y, max_bot_y; + Bool edge1_top, edge2_top; + Bool flip_top, flip_bot; +} miArcSliceRec; + +#define MIARCSLICESTEP(edge) \ + edge.x -= edge.stepx; \ + edge.e -= edge.dx; \ + if (edge.e <= 0) \ + { \ + edge.x -= edge.deltax; \ + edge.e += edge.dy; \ + } + +#define miFillSliceUpper(slice) \ + ((y >= slice.min_top_y) && (y <= slice.max_top_y)) + +#define miFillSliceLower(slice) \ + ((y >= slice.min_bot_y) && (y <= slice.max_bot_y)) + +#define MIARCSLICEUPPER(xl,xr,slice,slw) \ + xl = xorg - x; \ + xr = xl + slw - 1; \ + if (slice.edge1_top && (slice.edge1.x < xr)) \ + xr = slice.edge1.x; \ + if (slice.edge2_top && (slice.edge2.x > xl)) \ + xl = slice.edge2.x; + +#define MIARCSLICELOWER(xl,xr,slice,slw) \ + xl = xorg - x; \ + xr = xl + slw - 1; \ + if (!slice.edge1_top && (slice.edge1.x > xl)) \ + xl = slice.edge1.x; \ + if (!slice.edge2_top && (slice.edge2.x < xr)) \ + xr = slice.edge2.x; + +#define MIWIDEARCSETUP(x,y,dy,slw,e,xk,xm,yk,ym) \ + x = 0; \ + y = slw >> 1; \ + yk = y << 3; \ + xm = 8; \ + ym = 8; \ + if (dy) \ + { \ + xk = 0; \ + if (slw & 1) \ + e = -1; \ + else \ + e = -(y << 2) - 2; \ + } \ + else \ + { \ + y++; \ + yk += 4; \ + xk = -4; \ + if (slw & 1) \ + e = -(y << 2) - 3; \ + else \ + e = - (y << 3); \ + } + +#define MIFILLINARCSTEP(slw) \ + ine += inyk; \ + while (ine >= 0) \ + { \ + inx++; \ + inxk -= inxm; \ + ine += inxk; \ + } \ + iny--; \ + inyk -= inym; \ + slw = (inx << 1) + dx; \ + if ((ine == inxk) && (slw > 1)) \ + slw-- + +#define miFillInArcLower(slw) (((iny + dy) != 0) && \ + ((slw > 1) || (ine != inxk))) + +extern int miFreeArcCache( +#if NeedFunctionPrototypes + pointer /*data*/, + XID /*id*/ +#endif +); + +extern struct finalSpan *realAllocSpan( +#if NeedFunctionPrototypes + void +#endif +); + +extern void miFillArcSetup( +#if NeedFunctionPrototypes + xArc * /*arc*/, + miFillArcRec * /*info*/ +#endif +); + +extern void miFillArcDSetup( +#if NeedFunctionPrototypes + xArc * /*arc*/, + miFillArcDRec * /*info*/ +#endif +); + +extern void miEllipseAngleToSlope( +#if NeedFunctionPrototypes + int /*angle*/, + int /*width*/, + int /*height*/, + int * /*dxp*/, + int * /*dyp*/, + double * /*d_dxp*/, + double * /*d_dyp*/ +#endif +); + +extern void miFillArcSliceSetup( +#if NeedFunctionPrototypes + xArc * /*arc*/, + miArcSliceRec * /*slice*/, + GCPtr /*pGC*/ +#endif +); + diff --git a/mi/mifillrct.c b/mi/mifillrct.c new file mode 100644 index 000000000..af2f9429b --- /dev/null +++ b/mi/mifillrct.c @@ -0,0 +1,139 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mifillrct.c,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ + +#include "X.h" +#include "Xprotostr.h" +#include "gcstruct.h" +#include "windowstr.h" +#include "pixmap.h" + +#include "misc.h" + +/* mi rectangles + written by newman, with debts to all and sundry +*/ + +/* MIPOLYFILLRECT -- public entry for PolyFillRect request + * very straight forward: translate rectangles if necessary + * then call FillSpans to fill each rectangle. We let FillSpans worry about + * clipping to the destination + */ +void +miPolyFillRect(pDrawable, pGC, nrectFill, prectInit) + DrawablePtr pDrawable; + GCPtr pGC; + int nrectFill; /* number of rectangles to fill */ + xRectangle *prectInit; /* Pointer to first rectangle to fill */ +{ + int i; + register int height; + register int width; + register xRectangle *prect; + int xorg; + register int yorg; + int maxheight; + DDXPointPtr pptFirst; + register DDXPointPtr ppt; + int *pwFirst; + register int *pw; + + if (pGC->miTranslate) + { + xorg = pDrawable->x; + yorg = pDrawable->y; + prect = prectInit; + maxheight = 0; + for (i = 0; i<nrectFill; i++, prect++) + { + prect->x += xorg; + prect->y += yorg; + maxheight = max(maxheight, prect->height); + } + } + else + { + prect = prectInit; + maxheight = 0; + for (i = 0; i<nrectFill; i++, prect++) + maxheight = max(maxheight, prect->height); + } + + pptFirst = (DDXPointPtr) ALLOCATE_LOCAL(maxheight * sizeof(DDXPointRec)); + pwFirst = (int *) ALLOCATE_LOCAL(maxheight * sizeof(int)); + if(!pptFirst || !pwFirst) + { + if (pwFirst) DEALLOCATE_LOCAL(pwFirst); + if (pptFirst) DEALLOCATE_LOCAL(pptFirst); + return; + } + + prect = prectInit; + while(nrectFill--) + { + ppt = pptFirst; + pw = pwFirst; + height = prect->height; + width = prect->width; + xorg = prect->x; + yorg = prect->y; + while(height--) + { + *pw++ = width; + ppt->x = xorg; + ppt->y = yorg; + ppt++; + yorg++; + } + (* pGC->ops->FillSpans)(pDrawable, pGC, + prect->height, pptFirst, pwFirst, + 1); + prect++; + } + DEALLOCATE_LOCAL(pwFirst); + DEALLOCATE_LOCAL(pptFirst); +} diff --git a/mi/mifpoly.h b/mi/mifpoly.h new file mode 100644 index 000000000..bb923e2b6 --- /dev/null +++ b/mi/mifpoly.h @@ -0,0 +1,108 @@ +/* $Xorg: mifpoly.h,v 1.4 2001/02/09 02:05:20 xorgcvs Exp $ */ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +#define EPSILON 0.000001 +#define ISEQUAL(a,b) (Fabs((a) - (b)) <= EPSILON) +#define UNEQUAL(a,b) (Fabs((a) - (b)) > EPSILON) +#define WITHINHALF(a, b) (((a) - (b) > 0.0) ? (a) - (b) < 0.5 : \ + (b) - (a) <= 0.5) +#define ROUNDTOINT(x) ((int) (((x) > 0.0) ? ((x) + 0.5) : ((x) - 0.5))) +#define ISZERO(x) (Fabs((x)) <= EPSILON) +#define PTISEQUAL(a,b) (ISEQUAL(a.x,b.x) && ISEQUAL(a.y,b.y)) +#define PTUNEQUAL(a,b) (UNEQUAL(a.x,b.x) || UNEQUAL(a.y,b.y)) +#define PtEqual(a, b) (((a).x == (b).x) && ((a).y == (b).y)) + +#define NotEnd 0 +#define FirstEnd 1 +#define SecondEnd 2 + +#define SQSECANT 108.856472512142 /* 1/sin^2(11/2) - for 11o miter cutoff */ +#define D2SECANT 5.21671526231167 /* 1/2*sin(11/2) - max extension per width */ + +#ifdef NOINLINEICEIL +#define ICEIL(x) ((int)ceil(x)) +#else +#ifdef __GNUC__ +static __inline int ICEIL(x) + double x; +{ + int _cTmp = x; + return ((x == _cTmp) || (x < 0.0)) ? _cTmp : _cTmp+1; +} +#else +#define ICEIL(x) ((((x) == (_cTmp = (x))) || ((x) < 0.0)) ? _cTmp : _cTmp+1) +#define ICEILTEMPDECL static int _cTmp; +#endif +#endif + +/* Point with sub-pixel positioning. In this case we use doubles, but + * see mifpolycon.c for other suggestions + */ +typedef struct _SppPoint { + double x, y; +} SppPointRec, *SppPointPtr; + +typedef struct _SppArc { + double x, y, width, height; + double angle1, angle2; +} SppArcRec, *SppArcPtr; + +/* mifpolycon.c */ + +extern void miFillSppPoly( +#if NeedFunctionPrototypes + DrawablePtr /*dst*/, + GCPtr /*pgc*/, + int /*count*/, + SppPointPtr /*ptsIn*/, + int /*xTrans*/, + int /*yTrans*/, + double /*xFtrans*/, + double /*yFtrans*/ +#endif +); diff --git a/mi/mifpolycon.c b/mi/mifpolycon.c new file mode 100644 index 000000000..0b6655186 --- /dev/null +++ b/mi/mifpolycon.c @@ -0,0 +1,277 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mifpolycon.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +#include <math.h> +#include "X.h" +#include "gcstruct.h" +#include "windowstr.h" +#include "pixmapstr.h" +#include "mifpoly.h" + +static int GetFPolyYBounds(); + +#ifdef ICEILTEMPDECL +ICEILTEMPDECL +#endif + +/* + * Written by Todd Newman; April. 1987. + * + * Fill a convex polygon. If the given polygon + * is not convex, then the result is undefined. + * The algorithm is to order the edges from smallest + * y to largest by partitioning the array into a left + * edge list and a right edge list. The algorithm used + * to traverse each edge is digital differencing analyzer + * line algorithm with y as the major axis. There's some funny linear + * interpolation involved because of the subpixel postioning. + */ +void +miFillSppPoly(dst, pgc, count, ptsIn, xTrans, yTrans, xFtrans, yFtrans) + DrawablePtr dst; + GCPtr pgc; + int count; /* number of points */ + SppPointPtr ptsIn; /* the points */ + int xTrans, yTrans; /* Translate each point by this */ + double xFtrans, yFtrans; /* translate before conversion + by this amount. This provides + a mechanism to match rounding + errors with any shape that must + meet the polygon exactly. + */ +{ + double xl, xr, /* x vals of left and right edges */ + ml, /* left edge slope */ + mr, /* right edge slope */ + dy, /* delta y */ + i; /* loop counter */ + int y, /* current scanline */ + j, + imin, /* index of vertex with smallest y */ + ymin, /* y-extents of polygon */ + ymax, + *width, + *FirstWidth, /* output buffer */ + *Marked; /* set if this vertex has been used */ + register int left, right, /* indices to first endpoints */ + nextleft, + nextright; /* indices to second endpoints */ + DDXPointPtr ptsOut, + FirstPoint; /* output buffer */ + + if (pgc->miTranslate) + { + xTrans += dst->x; + yTrans += dst->y; + } + + imin = GetFPolyYBounds(ptsIn, count, yFtrans, &ymin, &ymax); + + y = ymax - ymin + 1; + if ((count < 3) || (y <= 0)) + return; + ptsOut = FirstPoint = (DDXPointPtr)ALLOCATE_LOCAL(sizeof(DDXPointRec) * y); + width = FirstWidth = (int *) ALLOCATE_LOCAL(sizeof(int) * y); + Marked = (int *) ALLOCATE_LOCAL(sizeof(int) * count); + + if(!ptsOut || !width || !Marked) + { + if (Marked) DEALLOCATE_LOCAL(Marked); + if (width) DEALLOCATE_LOCAL(width); + if (ptsOut) DEALLOCATE_LOCAL(ptsOut); + return; + } + + for(j = 0; j < count; j++) + Marked[j] = 0; + nextleft = nextright = imin; + Marked[imin] = -1; + y = ICEIL(ptsIn[nextleft].y + yFtrans); + + /* + * loop through all edges of the polygon + */ + do + { + /* add a left edge if we need to */ + if ((y > (ptsIn[nextleft].y + yFtrans) || + ISEQUAL(y, ptsIn[nextleft].y + yFtrans)) && + Marked[nextleft] != 1) + { + Marked[nextleft]++; + left = nextleft++; + + /* find the next edge, considering the end conditions */ + if (nextleft >= count) + nextleft = 0; + + /* now compute the starting point and slope */ + dy = ptsIn[nextleft].y - ptsIn[left].y; + if (dy != 0.0) + { + ml = (ptsIn[nextleft].x - ptsIn[left].x) / dy; + dy = y - (ptsIn[left].y + yFtrans); + xl = (ptsIn[left].x + xFtrans) + ml * max(dy, 0); + } + } + + /* add a right edge if we need to */ + if ((y > ptsIn[nextright].y + yFtrans) || + ISEQUAL(y, ptsIn[nextright].y + yFtrans) + && Marked[nextright] != 1) + { + Marked[nextright]++; + right = nextright--; + + /* find the next edge, considering the end conditions */ + if (nextright < 0) + nextright = count - 1; + + /* now compute the starting point and slope */ + dy = ptsIn[nextright].y - ptsIn[right].y; + if (dy != 0.0) + { + mr = (ptsIn[nextright].x - ptsIn[right].x) / dy; + dy = y - (ptsIn[right].y + yFtrans); + xr = (ptsIn[right].x + xFtrans) + mr * max(dy, 0); + } + } + + + /* + * generate scans to fill while we still have + * a right edge as well as a left edge. + */ + i = (min(ptsIn[nextleft].y, ptsIn[nextright].y) + yFtrans) - y; + + if (i < EPSILON) + { + if(Marked[nextleft] && Marked[nextright]) + { + /* Arrgh, we're trapped! (no more points) + * Out, we've got to get out of here before this decadence saps + * our will completely! */ + break; + } + continue; + } + else + { + j = (int) i; + if(!j) + j++; + } + while (j > 0) + { + int cxl, cxr; + + ptsOut->y = (y) + yTrans; + + cxl = ICEIL(xl); + cxr = ICEIL(xr); + /* reverse the edges if necessary */ + if (xl < xr) + { + *(width++) = cxr - cxl; + (ptsOut++)->x = cxl + xTrans; + } + else + { + *(width++) = cxl - cxr; + (ptsOut++)->x = cxr + xTrans; + } + y++; + + /* increment down the edges */ + xl += ml; + xr += mr; + j--; + } + } while (y <= ymax); + + /* Finally, fill the spans we've collected */ + (*pgc->ops->FillSpans)(dst, pgc, + ptsOut-FirstPoint, FirstPoint, FirstWidth, 1); + DEALLOCATE_LOCAL(Marked); + DEALLOCATE_LOCAL(FirstWidth); + DEALLOCATE_LOCAL(FirstPoint); +} + + +/* Find the index of the point with the smallest y.also return the + * smallest and largest y */ +static +int +GetFPolyYBounds(pts, n, yFtrans, by, ty) + register SppPointPtr pts; + int n; + double yFtrans; + int *by, *ty; +{ + register SppPointPtr ptMin; + double ymin, ymax; + SppPointPtr ptsStart = pts; + + ptMin = pts; + ymin = ymax = (pts++)->y; + + while (--n > 0) { + if (pts->y < ymin) + { + ptMin = pts; + ymin = pts->y; + } + if(pts->y > ymax) + ymax = pts->y; + + pts++; + } + + *by = ICEIL(ymin + yFtrans); + *ty = ICEIL(ymax + yFtrans - 1); + return(ptMin-ptsStart); +} diff --git a/mi/migc.c b/mi/migc.c new file mode 100644 index 000000000..808347d22 --- /dev/null +++ b/mi/migc.c @@ -0,0 +1,295 @@ +/* $Xorg: migc.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +/* + +Copyright 1993, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + + +#include "scrnintstr.h" +#include "gcstruct.h" +#include "pixmapstr.h" +#include "windowstr.h" +#include "migc.h" + +int miGCPrivateIndex; + +void +miRegisterGCPrivateIndex(gcindex) + int gcindex; +{ + miGCPrivateIndex = gcindex; +} + +/* ARGSUSED */ +void +miChangeGC(pGC, mask) + GCPtr pGC; + unsigned long mask; +{ + return; +} + +void +miDestroyGC(pGC) + GCPtr pGC; +{ + miPrivGC *pPriv; + + pPriv = (miPrivGC *) (pGC->devPrivates[miGCPrivateIndex].ptr); + if (pPriv->pRotatedPixmap) + (*pGC->pScreen->DestroyPixmap) (pPriv->pRotatedPixmap); + if (pPriv->freeCompClip) + REGION_DESTROY(pGC->pScreen, pPriv->pCompositeClip); + miDestroyGCOps(pGC->ops); +} + +/* + * create a private op array for a gc + */ + +GCOpsPtr +miCreateGCOps(prototype) + GCOpsPtr prototype; +{ + GCOpsPtr ret; + extern Bool Must_have_memory; + + /* XXX */ Must_have_memory = TRUE; + ret = (GCOpsPtr) xalloc(sizeof(GCOps)); + /* XXX */ Must_have_memory = FALSE; + if (!ret) + return 0; + *ret = *prototype; + ret->devPrivate.val = 1; + return ret; +} + +void +miDestroyGCOps(ops) + GCOpsPtr ops; +{ + if (ops->devPrivate.val) + xfree(ops); +} + + +void +miDestroyClip(pGC) + GCPtr pGC; +{ + if (pGC->clientClipType == CT_NONE) + return; + else if (pGC->clientClipType == CT_PIXMAP) + { + (*pGC->pScreen->DestroyPixmap) ((PixmapPtr) (pGC->clientClip)); + } + else + { + /* + * we know we'll never have a list of rectangles, since ChangeClip + * immediately turns them into a region + */ + REGION_DESTROY(pGC->pScreen, pGC->clientClip); + } + pGC->clientClip = NULL; + pGC->clientClipType = CT_NONE; +} + +void +miChangeClip(pGC, type, pvalue, nrects) + GCPtr pGC; + int type; + pointer pvalue; + int nrects; +{ + (*pGC->funcs->DestroyClip) (pGC); + if (type == CT_PIXMAP) + { + /* convert the pixmap to a region */ + pGC->clientClip = (pointer) BITMAP_TO_REGION(pGC->pScreen, + (PixmapPtr) pvalue); + (*pGC->pScreen->DestroyPixmap) (pvalue); + } + else if (type == CT_REGION) + { + /* stuff the region in the GC */ + pGC->clientClip = pvalue; + } + else if (type != CT_NONE) + { + pGC->clientClip = (pointer) RECTS_TO_REGION(pGC->pScreen, nrects, + (xRectangle *) pvalue, + type); + xfree(pvalue); + } + pGC->clientClipType = (type != CT_NONE && pGC->clientClip) ? CT_REGION : CT_NONE; + pGC->stateChanges |= GCClipMask; +} + +void +miCopyClip(pgcDst, pgcSrc) + GCPtr pgcDst, pgcSrc; +{ + RegionPtr prgnNew; + + switch (pgcSrc->clientClipType) + { + case CT_PIXMAP: + ((PixmapPtr) pgcSrc->clientClip)->refcnt++; + /* Fall through !! */ + case CT_NONE: + (*pgcDst->funcs->ChangeClip) (pgcDst, (int) pgcSrc->clientClipType, + pgcSrc->clientClip, 0); + break; + case CT_REGION: + prgnNew = REGION_CREATE(pgcSrc->pScreen, NULL, 1); + REGION_COPY(pgcDst->pScreen, prgnNew, + (RegionPtr) (pgcSrc->clientClip)); + (*pgcDst->funcs->ChangeClip) (pgcDst, CT_REGION, (pointer) prgnNew, 0); + break; + } +} + +/* ARGSUSED */ +void +miCopyGC(pGCSrc, changes, pGCDst) + GCPtr pGCSrc; + unsigned long changes; + GCPtr pGCDst; +{ + return; +} + +void +miComputeCompositeClip(pGC, pDrawable) + GCPtr pGC; + DrawablePtr pDrawable; +{ + ScreenPtr pScreen = pGC->pScreen; + miPrivGC *devPriv = (miPrivGC *) (pGC->devPrivates[miGCPrivateIndex].ptr); + + if (pDrawable->type == DRAWABLE_WINDOW) + { + WindowPtr pWin = (WindowPtr) pDrawable; + RegionPtr pregWin; + Bool freeTmpClip, freeCompClip; + + if (pGC->subWindowMode == IncludeInferiors) + { + pregWin = NotClippedByChildren(pWin); + freeTmpClip = TRUE; + } + else + { + pregWin = &pWin->clipList; + freeTmpClip = FALSE; + } + freeCompClip = devPriv->freeCompClip; + + /* + * if there is no client clip, we can get by with just keeping the + * pointer we got, and remembering whether or not should destroy (or + * maybe re-use) it later. this way, we avoid unnecessary copying of + * regions. (this wins especially if many clients clip by children + * and have no client clip.) + */ + if (pGC->clientClipType == CT_NONE) + { + if (freeCompClip) + REGION_DESTROY(pScreen, devPriv->pCompositeClip); + devPriv->pCompositeClip = pregWin; + devPriv->freeCompClip = freeTmpClip; + } + else + { + /* + * we need one 'real' region to put into the composite clip. if + * pregWin the current composite clip are real, we can get rid of + * one. if pregWin is real and the current composite clip isn't, + * use pregWin for the composite clip. if the current composite + * clip is real and pregWin isn't, use the current composite + * clip. if neither is real, create a new region. + */ + + REGION_TRANSLATE(pScreen, pGC->clientClip, + pDrawable->x + pGC->clipOrg.x, + pDrawable->y + pGC->clipOrg.y); + + if (freeCompClip) + { + REGION_INTERSECT(pGC->pScreen, devPriv->pCompositeClip, + pregWin, pGC->clientClip); + if (freeTmpClip) + REGION_DESTROY(pScreen, pregWin); + } + else if (freeTmpClip) + { + REGION_INTERSECT(pScreen, pregWin, pregWin, pGC->clientClip); + devPriv->pCompositeClip = pregWin; + } + else + { + devPriv->pCompositeClip = REGION_CREATE(pScreen, NullBox, 0); + REGION_INTERSECT(pScreen, devPriv->pCompositeClip, + pregWin, pGC->clientClip); + } + devPriv->freeCompClip = TRUE; + REGION_TRANSLATE(pScreen, pGC->clientClip, + -(pDrawable->x + pGC->clipOrg.x), + -(pDrawable->y + pGC->clipOrg.y)); + } + } /* end of composite clip for a window */ + else + { + BoxRec pixbounds; + + /* XXX should we translate by drawable.x/y here ? */ + pixbounds.x1 = 0; + pixbounds.y1 = 0; + pixbounds.x2 = pDrawable->width; + pixbounds.y2 = pDrawable->height; + + if (devPriv->freeCompClip) + { + REGION_RESET(pScreen, devPriv->pCompositeClip, &pixbounds); + } + else + { + devPriv->freeCompClip = TRUE; + devPriv->pCompositeClip = REGION_CREATE(pScreen, &pixbounds, 1); + } + + if (pGC->clientClipType == CT_REGION) + { + REGION_TRANSLATE(pScreen, devPriv->pCompositeClip, + -pGC->clipOrg.x, -pGC->clipOrg.y); + REGION_INTERSECT(pScreen, devPriv->pCompositeClip, + devPriv->pCompositeClip, pGC->clientClip); + REGION_TRANSLATE(pScreen, devPriv->pCompositeClip, + pGC->clipOrg.x, pGC->clipOrg.y); + } + } /* end of composite clip for pixmap */ +} /* end miComputeCompositeClip */ diff --git a/mi/migc.h b/mi/migc.h new file mode 100644 index 000000000..9e9974c3e --- /dev/null +++ b/mi/migc.h @@ -0,0 +1,113 @@ +/* $Xorg: migc.h,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +/* + +Copyright 1993, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + +/* This structure has to line up with the mfb and cfb gc private structures so + * that when it is superimposed on them, the three fields that migc.c needs to + * see will be accessed correctly. I know this is not beautiful, but it seemed + * better than all the code duplication in cfb and mfb. + */ +typedef struct { + unsigned char pad1; + unsigned char pad2; + unsigned char pad3; + unsigned pad4:1; + unsigned freeCompClip:1; + PixmapPtr pRotatedPixmap; + RegionPtr pCompositeClip; +} miPrivGC; + +extern int miGCPrivateIndex; + +extern void miRegisterGCPrivateIndex( +#if NeedFunctionPrototypes + int /*gcindex*/ +#endif +); + +extern void miChangeGC( +#if NeedFunctionPrototypes + GCPtr /*pGC*/, + unsigned long /*mask*/ +#endif +); + +extern void miDestroyGC( +#if NeedFunctionPrototypes + GCPtr /*pGC*/ +#endif +); + +extern GCOpsPtr miCreateGCOps( +#if NeedFunctionPrototypes + GCOpsPtr /*prototype*/ +#endif +); + +extern void miDestroyGCOps( +#if NeedFunctionPrototypes + GCOpsPtr /*ops*/ +#endif +); + +extern void miDestroyClip( +#if NeedFunctionPrototypes + GCPtr /*pGC*/ +#endif +); + +extern void miChangeClip( +#if NeedFunctionPrototypes + GCPtr /*pGC*/, + int /*type*/, + pointer /*pvalue*/, + int /*nrects*/ +#endif +); + +extern void miCopyClip( +#if NeedFunctionPrototypes + GCPtr /*pgcDst*/, + GCPtr /*pgcSrc*/ +#endif +); + +extern void miCopyGC( +#if NeedFunctionPrototypes + GCPtr /*pGCSrc*/, + unsigned long /*changes*/, + GCPtr /*pGCDst*/ +#endif +); + +extern void miComputeCompositeClip( +#if NeedFunctionPrototypes + GCPtr /*pGC*/, + DrawablePtr /*pDrawable*/ +#endif +); diff --git a/mi/miglblt.c b/mi/miglblt.c new file mode 100644 index 000000000..4a7a8eaeb --- /dev/null +++ b/mi/miglblt.c @@ -0,0 +1,248 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* $Xorg: miglblt.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ + +#include "X.h" +#include "Xmd.h" +#include "Xproto.h" +#include "misc.h" +#include "fontstruct.h" +#include "dixfontstr.h" +#include "gcstruct.h" +#include "windowstr.h" +#include "scrnintstr.h" +#include "pixmap.h" +#include "servermd.h" + +/* + machine-independent glyph blt. + assumes that glyph bits in snf are written in bytes, +have same bit order as the server's bitmap format, +and are byte padded. this corresponds to the snf distributed +with the sample server. + + get a scratch GC. + in the scratch GC set alu = GXcopy, fg = 1, bg = 0 + allocate a bitmap big enough to hold the largest glyph in the font + validate the scratch gc with the bitmap + for each glyph + carefully put the bits of the glyph in a buffer, + padded to the server pixmap scanline padding rules + fake a call to PutImage from the buffer into the bitmap + use the bitmap in a call to PushPixels +*/ + +void +miPolyGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase) + DrawablePtr pDrawable; + GC *pGC; + int x, y; + unsigned int nglyph; + CharInfoPtr *ppci; /* array of character info */ + unsigned char *pglyphBase; /* start of array of glyphs */ +{ + int width, height; + PixmapPtr pPixmap; + int nbyLine; /* bytes per line of padded pixmap */ + FontPtr pfont; + GCPtr pGCtmp; + register int i; + register int j; + unsigned char *pbits; /* buffer for PutImage */ + register unsigned char *pb; /* temp pointer into buffer */ + register CharInfoPtr pci; /* currect char info */ + register unsigned char *pglyph; /* pointer bits in glyph */ + int gWidth, gHeight; /* width and height of glyph */ + register int nbyGlyphWidth; /* bytes per scanline of glyph */ + int nbyPadGlyph; /* server padded line of glyph */ + + XID gcvals[3]; + + if (pGC->miTranslate) + { + x += pDrawable->x; + y += pDrawable->y; + } + + pfont = pGC->font; + width = FONTMAXBOUNDS(pfont,rightSideBearing) - + FONTMINBOUNDS(pfont,leftSideBearing); + height = FONTMAXBOUNDS(pfont,ascent) + + FONTMAXBOUNDS(pfont,descent); + + pPixmap = (*pDrawable->pScreen->CreatePixmap)(pDrawable->pScreen, + width, height, 1); + if (!pPixmap) + return; + + pGCtmp = GetScratchGC(1, pDrawable->pScreen); + if (!pGCtmp) + { + (*pDrawable->pScreen->DestroyPixmap)(pPixmap); + return; + } + + gcvals[0] = GXcopy; + gcvals[1] = 1; + gcvals[2] = 0; + + DoChangeGC(pGCtmp, GCFunction|GCForeground|GCBackground, gcvals, 0); + + nbyLine = BitmapBytePad(width); + pbits = (unsigned char *)ALLOCATE_LOCAL(height*nbyLine); + if (!pbits) + { + (*pDrawable->pScreen->DestroyPixmap)(pPixmap); + FreeScratchGC(pGCtmp); + return; + } + while(nglyph--) + { + pci = *ppci++; + pglyph = FONTGLYPHBITS(pglyphBase, pci); + gWidth = GLYPHWIDTHPIXELS(pci); + gHeight = GLYPHHEIGHTPIXELS(pci); + if (gWidth && gHeight) + { + nbyGlyphWidth = GLYPHWIDTHBYTESPADDED(pci); + nbyPadGlyph = BitmapBytePad(gWidth); + + if (nbyGlyphWidth == nbyPadGlyph +#if GLYPHPADBYTES != 4 + && (((int) pglyph) & 3) == 0 +#endif + ) + { + pb = pglyph; + } + else + { + for (i=0, pb = pbits; i<gHeight; i++, pb = pbits+(i*nbyPadGlyph)) + for (j = 0; j < nbyGlyphWidth; j++) + *pb++ = *pglyph++; + pb = pbits; + } + + if ((pGCtmp->serialNumber) != (pPixmap->drawable.serialNumber)) + ValidateGC((DrawablePtr)pPixmap, pGCtmp); + (*pGCtmp->ops->PutImage)((DrawablePtr)pPixmap, pGCtmp, + pPixmap->drawable.depth, + 0, 0, gWidth, gHeight, + 0, XYBitmap, (char *)pb); + + if ((pGC->serialNumber) != (pDrawable->serialNumber)) + ValidateGC(pDrawable, pGC); + (*pGC->ops->PushPixels)(pGC, pPixmap, pDrawable, + gWidth, gHeight, + x + pci->metrics.leftSideBearing, + y - pci->metrics.ascent); + } + x += pci->metrics.characterWidth; + } + (*pDrawable->pScreen->DestroyPixmap)(pPixmap); + DEALLOCATE_LOCAL(pbits); + FreeScratchGC(pGCtmp); +} + + +void +miImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase) + DrawablePtr pDrawable; + GC *pGC; + int x, y; + unsigned int nglyph; + CharInfoPtr *ppci; /* array of character info */ + unsigned char *pglyphBase; /* start of array of glyphs */ +{ + ExtentInfoRec info; /* used by QueryGlyphExtents() */ + XID gcvals[3]; + int oldAlu, oldFS; + unsigned long oldFG; + xRectangle backrect; + + QueryGlyphExtents(pGC->font, ppci, (unsigned long)nglyph, &info); + + if (info.overallWidth >= 0) + { + backrect.x = x; + backrect.width = info.overallWidth; + } + else + { + backrect.x = x + info.overallWidth; + backrect.width = -info.overallWidth; + } + backrect.y = y - FONTASCENT(pGC->font); + backrect.height = FONTASCENT(pGC->font) + FONTDESCENT(pGC->font); + + oldAlu = pGC->alu; + oldFG = pGC->fgPixel; + oldFS = pGC->fillStyle; + + /* fill in the background */ + gcvals[0] = GXcopy; + gcvals[1] = pGC->bgPixel; + gcvals[2] = FillSolid; + DoChangeGC(pGC, GCFunction|GCForeground|GCFillStyle, gcvals, 0); + ValidateGC(pDrawable, pGC); + (*pGC->ops->PolyFillRect)(pDrawable, pGC, 1, &backrect); + + /* put down the glyphs */ + gcvals[0] = oldFG; + DoChangeGC(pGC, GCForeground, gcvals, 0); + ValidateGC(pDrawable, pGC); + (*pGC->ops->PolyGlyphBlt)(pDrawable, pGC, x, y, nglyph, ppci, + (char *)pglyphBase); + + /* put all the toys away when done playing */ + gcvals[0] = oldAlu; + gcvals[1] = oldFG; + gcvals[2] = oldFS; + DoChangeGC(pGC, GCFunction|GCForeground|GCFillStyle, gcvals, 0); + +} diff --git a/mi/miinitext.c b/mi/miinitext.c new file mode 100644 index 000000000..c9ea7444c --- /dev/null +++ b/mi/miinitext.c @@ -0,0 +1,246 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: miinitext.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ + +#include "misc.h" + +#ifdef NOPEXEXT /* sleaze for Solaris cpp building XsunMono */ +#undef PEXEXT +#endif +#ifdef PANORAMIX +extern Bool noPanoramiXExtension; +#endif +extern Bool noTestExtensions; +#ifdef XKB +extern Bool noXkbExtension; +#endif + +#ifdef BEZIER +extern void BezierExtensionInit(); +#endif +#ifdef XTESTEXT1 +extern void XTestExtension1Init(); +#endif +#ifdef SHAPE +extern void ShapeExtensionInit(); +#endif +#ifdef EVI +extern void EVIExtensionInit(); +#endif +#ifdef MITSHM +extern void ShmExtensionInit(); +#endif +#ifdef PEXEXT +extern void PexExtensionInit(); +#endif +#ifdef MULTIBUFFER +extern void MultibufferExtensionInit(); +#endif +#ifdef PANORAMIX +extern void PanoramiXExtensionInit(); +#endif +#ifdef XINPUT +extern void XInputExtensionInit(); +#endif +#ifdef XTEST +extern void XTestExtensionInit(); +#endif +#ifdef BIGREQS +extern void BigReqExtensionInit(); +#endif +#ifdef MITMISC +extern void MITMiscExtensionInit(); +#endif +#ifdef XIDLE +extern void XIdleExtensionInit(); +#endif +#ifdef XTRAP +extern void DEC_XTRAPInit(); +#endif +#ifdef SCREENSAVER +extern void ScreenSaverExtensionInit (); +#endif +#ifdef XV +extern void XvExtensionInit(); +#endif +#ifdef XIE +extern void XieInit(); +#endif +#ifdef XSYNC +extern void SyncExtensionInit(); +#endif +#ifdef XKB +extern void XkbExtensionInit(); +#endif +#ifdef XCMISC +extern void XCMiscExtensionInit(); +#endif +#ifdef XRECORD +extern void XRecordExtensionInit(); +#endif +#ifdef LBX +extern void LbxExtensionInit(); +#endif +#ifdef DBE +extern void DbeExtensionInit(); +#endif +#ifdef XAPPGROUP +extern void XagExtensionInit(); +#endif +#ifdef XF86VIDMODE +extern void XFree86VidModeExtensionInit(); +#endif +#ifdef XCSECURITY +extern void SecurityExtensionInit(); +#endif +#ifdef XPRINT +extern void XpExtensionInit(); +#endif +#ifdef TOGCUP +extern void XcupExtensionInit(); +#endif +#ifdef DPMSExtension +extern void DPMSExtensionInit(); +#endif + + +/*ARGSUSED*/ +void +InitExtensions(argc, argv) + int argc; + char *argv[]; +{ +#ifdef PANORAMIX +#if !defined(PRINT_ONLY_SERVER) && !defined(NO_PANORAMIX) + if (!noPanoramiXExtension) PanoramiXExtensionInit(); +#endif +#endif +#ifdef BEZIER + BezierExtensionInit(); +#endif +#ifdef XTESTEXT1 + if (!noTestExtensions) XTestExtension1Init(); +#endif +#ifdef SHAPE + ShapeExtensionInit(); +#endif +#ifdef MITSHM + ShmExtensionInit(); +#endif +#ifdef EVI + EVIExtensionInit(); +#endif +#ifdef PEXEXT + PexExtensionInit(); +#endif +#ifdef MULTIBUFFER + MultibufferExtensionInit(); +#endif +#ifdef XINPUT + XInputExtensionInit(); +#endif +#ifdef XTEST + if (!noTestExtensions) XTestExtensionInit(); +#endif +#ifdef BIGREQS + BigReqExtensionInit(); +#endif +#ifdef MITMISC + MITMiscExtensionInit(); +#endif +#ifdef XIDLE + XIdleExtensionInit(); +#endif +#ifdef XTRAP + if (!noTestExtensions) DEC_XTRAPInit(); +#endif +#if defined(SCREENSAVER) && !defined(PRINT_ONLY_SERVER) + ScreenSaverExtensionInit (); +#endif +#ifdef XV + XvExtensionInit(); +#endif +#ifdef XIE + XieInit(); +#endif +#ifdef XSYNC + SyncExtensionInit(); +#endif +#if defined(XKB) && !defined(PRINT_ONLY_SERVER) + if (!noXkbExtension) XkbExtensionInit(); +#endif +#ifdef XCMISC + XCMiscExtensionInit(); +#endif +#ifdef XRECORD + if (!noTestExtensions) RecordExtensionInit(); +#endif +#ifdef LBX + LbxExtensionInit(); +#endif +#ifdef DBE + DbeExtensionInit(); +#endif +#ifdef XAPPGROUP + XagExtensionInit(); +#endif +#if defined(XF86VIDMODE) && !defined(PRINT_ONLY_SERVER) + XFree86VidModeExtensionInit(); +#endif +#ifdef XCSECURITY + SecurityExtensionInit(); +#endif +#ifdef XPRINT + XpExtensionInit(); +#endif +#ifdef TOGCUP + XcupExtensionInit(); +#endif +#if defined(DPMSExtension) && !defined(PRINT_ONLY_SERVER) + DPMSExtensionInit(); +#endif +} diff --git a/mi/miline.h b/mi/miline.h new file mode 100644 index 000000000..329dea51e --- /dev/null +++ b/mi/miline.h @@ -0,0 +1,167 @@ +/* $Xorg: miline.h,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ + +/* + +Copyright 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ + +#ifndef MILINE_H + +/* + * Public definitions used for configuring basic pixelization aspects + * of the sample implementation line-drawing routines provided in + * {mfb,mi,cfb*} at run-time. + */ + +#define XDECREASING 4 +#define YDECREASING 2 +#define YMAJOR 1 + +#define OCTANT1 (1 << (YDECREASING)) +#define OCTANT2 (1 << (YDECREASING|YMAJOR)) +#define OCTANT3 (1 << (XDECREASING|YDECREASING|YMAJOR)) +#define OCTANT4 (1 << (XDECREASING|YDECREASING)) +#define OCTANT5 (1 << (XDECREASING)) +#define OCTANT6 (1 << (XDECREASING|YMAJOR)) +#define OCTANT7 (1 << (YMAJOR)) +#define OCTANT8 (1 << (0)) + +#define XMAJOROCTANTS (OCTANT1 | OCTANT4 | OCTANT5 | OCTANT8) + +#define DEFAULTZEROLINEBIAS (OCTANT2 | OCTANT3 | OCTANT4 | OCTANT5) + +/* + * Devices can configure the rendering of routines in mi, mfb, and cfb* + * by specifying a thin line bias to be applied to a particular screen + * using the following function. The bias parameter is an OR'ing of + * the appropriate OCTANT constants defined above to indicate which + * octants to bias a line to prefer an axial step when the Bresenham + * error term is exactly zero. The octants are mapped as follows: + * + * \ | / + * \ 3 | 2 / + * \ | / + * 4 \ | / 1 + * \|/ + * ----------- + * /|\ + * 5 / | \ 8 + * / | \ + * / 6 | 7 \ + * / | \ + * + * For more information, see "Ambiguities in Incremental Line Rastering," + * Jack E. Bresenham, IEEE CG&A, May 1987. + */ + +extern void miSetZeroLineBias( +#if NeedFunctionPrototypes + ScreenPtr /* pScreen */, + unsigned int /* bias */ +#endif +); + +/* + * Private definitions needed for drawing thin (zero width) lines + * Used by the mi, mfb, and all cfb* components. + */ + +#define X_AXIS 0 +#define Y_AXIS 1 + +#define OUT_LEFT 0x08 +#define OUT_RIGHT 0x04 +#define OUT_ABOVE 0x02 +#define OUT_BELOW 0x01 + +#define OUTCODES(_result, _x, _y, _pbox) \ + if ( (_x) < (_pbox)->x1) (_result) |= OUT_LEFT; \ + else if ( (_x) >= (_pbox)->x2) (_result) |= OUT_RIGHT; \ + if ( (_y) < (_pbox)->y1) (_result) |= OUT_ABOVE; \ + else if ( (_y) >= (_pbox)->y2) (_result) |= OUT_BELOW; + +#define SWAPINT(i, j) \ +{ register int _t = i; i = j; j = _t; } + +#define SWAPPT(i, j) \ +{ DDXPointRec _t; _t = i; i = j; j = _t; } + +#define SWAPINT_PAIR(x1, y1, x2, y2)\ +{ int t = x1; x1 = x2; x2 = t;\ + t = y1; y1 = y2; y2 = t;\ +} + +#define miGetZeroLineBias(_pScreen) \ + ((miZeroLineScreenIndex < 0) ? \ + 0 : ((_pScreen)->devPrivates[miZeroLineScreenIndex].uval)) + +#define CalcLineDeltas(_x1,_y1,_x2,_y2,_adx,_ady,_sx,_sy,_SX,_SY,_octant) \ + (_octant) = 0; \ + (_sx) = (_SX); \ + if (((_adx) = (_x2) - (_x1)) < 0) { \ + (_adx) = -(_adx); \ + (_sx = -(_sx)); \ + (_octant) |= XDECREASING; \ + } \ + (_sy) = (_SY); \ + if (((_ady) = (_y2) - (_y1)) < 0) { \ + (_ady) = -(_ady); \ + (_sy = -(_sy)); \ + (_octant) |= YDECREASING; \ + } + +#define SetYMajorOctant(_octant) ((_octant) |= YMAJOR) + +#define FIXUP_ERROR(_e, _octant, _bias) \ + (_e) -= (((_bias) >> (_octant)) & 1) + +#define IsXMajorOctant(_octant) (!((_octant) & YMAJOR)) +#define IsYMajorOctant(_octant) ((_octant) & YMAJOR) +#define IsXDecreasingOctant(_octant) ((_octant) & XDECREASING) +#define IsYDecreasingOctant(_octant) ((_octant) & YDECREASING) + +extern int miZeroLineScreenIndex; + +extern int miZeroClipLine( +#if NeedFunctionPrototypes + int /*xmin*/, + int /*ymin*/, + int /*xmax*/, + int /*ymax*/, + int * /*new_x1*/, + int * /*new_y1*/, + int * /*new_x2*/, + int * /*new_y2*/, + unsigned int /*adx*/, + unsigned int /*ady*/, + int * /*pt1_clipped*/, + int * /*pt2_clipped*/, + int /*octant*/, + unsigned int /*bias*/, + int /*oc1*/, + int /*oc2*/ +#endif +); + +#endif /* MILINE_H */ diff --git a/mi/mipointer.c b/mi/mipointer.c new file mode 100644 index 000000000..8617861cd --- /dev/null +++ b/mi/mipointer.c @@ -0,0 +1,498 @@ +/* + * mipointer.c + */ + +/* $Xorg: mipointer.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ + +/* + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. +*/ + +# define NEED_EVENTS +# include "X.h" +# include "Xmd.h" +# include "Xproto.h" +# include "misc.h" +# include "windowstr.h" +# include "pixmapstr.h" +# include "mi.h" +# include "scrnintstr.h" +# include "mipointrst.h" +# include "cursorstr.h" +# include "dixstruct.h" + +static int miPointerScreenIndex; +static unsigned long miPointerGeneration = 0; + +#define GetScreenPrivate(s) ((miPointerScreenPtr) ((s)->devPrivates[miPointerScreenIndex].ptr)) +#define SetupScreen(s) miPointerScreenPtr pScreenPriv = GetScreenPrivate(s) + +/* + * until more than one pointer device exists. + */ + +static miPointerRec miPointer; + +static Bool miPointerRealizeCursor (), miPointerUnrealizeCursor (); +static Bool miPointerDisplayCursor (); +static void miPointerConstrainCursor (), miPointerPointerNonInterestBox(); +static void miPointerCursorLimits (); +static Bool miPointerSetCursorPosition (); + +static Bool miPointerCloseScreen(); + +static void miPointerMove (); + +Bool +miPointerInitialize (pScreen, spriteFuncs, screenFuncs, waitForUpdate) + ScreenPtr pScreen; + miPointerSpriteFuncPtr spriteFuncs; + miPointerScreenFuncPtr screenFuncs; + Bool waitForUpdate; +{ + miPointerScreenPtr pScreenPriv; + + if (miPointerGeneration != serverGeneration) + { + miPointerScreenIndex = AllocateScreenPrivateIndex(); + if (miPointerScreenIndex < 0) + return FALSE; + miPointerGeneration = serverGeneration; + } + pScreenPriv = (miPointerScreenPtr) xalloc (sizeof (miPointerScreenRec)); + if (!pScreenPriv) + return FALSE; + pScreenPriv->spriteFuncs = spriteFuncs; + pScreenPriv->screenFuncs = screenFuncs; + /* + * check for uninitialized methods + */ + if (!screenFuncs->EnqueueEvent) + screenFuncs->EnqueueEvent = mieqEnqueue; + if (!screenFuncs->NewEventScreen) + screenFuncs->NewEventScreen = mieqSwitchScreen; + pScreenPriv->waitForUpdate = waitForUpdate; + pScreenPriv->CloseScreen = pScreen->CloseScreen; + pScreen->CloseScreen = miPointerCloseScreen; + pScreen->devPrivates[miPointerScreenIndex].ptr = (pointer) pScreenPriv; + /* + * set up screen cursor method table + */ + pScreen->ConstrainCursor = miPointerConstrainCursor; + pScreen->CursorLimits = miPointerCursorLimits; + pScreen->DisplayCursor = miPointerDisplayCursor; + pScreen->RealizeCursor = miPointerRealizeCursor; + pScreen->UnrealizeCursor = miPointerUnrealizeCursor; + pScreen->SetCursorPosition = miPointerSetCursorPosition; + pScreen->RecolorCursor = miRecolorCursor; + pScreen->PointerNonInterestBox = miPointerPointerNonInterestBox; + /* + * set up the pointer object + */ + miPointer.pScreen = NULL; + miPointer.pSpriteScreen = NULL; + miPointer.pCursor = NULL; + miPointer.pSpriteCursor = NULL; + miPointer.limits.x1 = 0; + miPointer.limits.x2 = 32767; + miPointer.limits.y1 = 0; + miPointer.limits.y2 = 32767; + miPointer.confined = FALSE; + miPointer.x = 0; + miPointer.y = 0; + miPointer.history_start = miPointer.history_end = 0; + return TRUE; +} + +static Bool +miPointerCloseScreen (index, pScreen) + int index; + ScreenPtr pScreen; +{ + SetupScreen(pScreen); + + if (pScreen == miPointer.pScreen) + miPointer.pScreen = 0; + if (pScreen == miPointer.pSpriteScreen) + miPointer.pSpriteScreen = 0; + pScreen->CloseScreen = pScreenPriv->CloseScreen; + xfree ((pointer) pScreenPriv); + return (*pScreen->CloseScreen) (index, pScreen); +} + +/* + * DIX/DDX interface routines + */ + +static Bool +miPointerRealizeCursor (pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + SetupScreen(pScreen); + + return (*pScreenPriv->spriteFuncs->RealizeCursor) (pScreen, pCursor); +} + +static Bool +miPointerUnrealizeCursor (pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + SetupScreen(pScreen); + + return (*pScreenPriv->spriteFuncs->UnrealizeCursor) (pScreen, pCursor); +} + +static Bool +miPointerDisplayCursor (pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + SetupScreen(pScreen); + + miPointer.pCursor = pCursor; + miPointer.pScreen = pScreen; + miPointerUpdate (); + return TRUE; +} + +static void +miPointerConstrainCursor (pScreen, pBox) + ScreenPtr pScreen; + BoxPtr pBox; +{ + miPointer.limits = *pBox; + miPointer.confined = PointerConfinedToScreen(); +} + +/*ARGSUSED*/ +static void +miPointerPointerNonInterestBox (pScreen, pBox) + ScreenPtr pScreen; + BoxPtr pBox; +{ + /* until DIX uses this, this will remain a stub */ +} + +/*ARGSUSED*/ +static void +miPointerCursorLimits(pScreen, pCursor, pHotBox, pTopLeftBox) + ScreenPtr pScreen; + CursorPtr pCursor; + BoxPtr pHotBox; + BoxPtr pTopLeftBox; +{ + *pTopLeftBox = *pHotBox; +} + +static Bool GenerateEvent; + +static Bool +miPointerSetCursorPosition(pScreen, x, y, generateEvent) + ScreenPtr pScreen; + int x, y; + Bool generateEvent; +{ + SetupScreen (pScreen); + + GenerateEvent = generateEvent; + /* device dependent - must pend signal and call miPointerWarpCursor */ + (*pScreenPriv->screenFuncs->WarpCursor) (pScreen, x, y); + if (!generateEvent) + miPointerUpdate(); + return TRUE; +} + +/* Once signals are ignored, the WarpCursor function can call this */ + +void +miPointerWarpCursor (pScreen, x, y) + ScreenPtr pScreen; + int x, y; +{ + SetupScreen (pScreen); + + if (miPointer.pScreen != pScreen) + (*pScreenPriv->screenFuncs->NewEventScreen) (pScreen, TRUE); + + if (GenerateEvent) + { + miPointerMove (pScreen, x, y, GetTimeInMillis()); + } + else + { + /* everything from miPointerMove except the event and history */ + + if (!pScreenPriv->waitForUpdate && pScreen == miPointer.pSpriteScreen) + { + miPointer.devx = x; + miPointer.devy = y; + (*pScreenPriv->spriteFuncs->MoveCursor) (pScreen, x, y); + } + miPointer.x = x; + miPointer.y = y; + miPointer.pScreen = pScreen; + } +} + +/* + * Pointer/CursorDisplay interface routines + */ + +int +miPointerGetMotionBufferSize () +{ + return MOTION_SIZE; +} + +int +miPointerGetMotionEvents (pPtr, coords, start, stop, pScreen) + DeviceIntPtr pPtr; + xTimecoord *coords; + unsigned long start, stop; + ScreenPtr pScreen; +{ + int i; + int count = 0; + miHistoryPtr h; + + for (i = miPointer.history_start; i != miPointer.history_end;) + { + h = &miPointer.history[i]; + if (h->event.time >= stop) + break; + if (h->event.time >= start) + { + *coords++ = h->event; + count++; + } + if (++i == MOTION_SIZE) i = 0; + } + return count; +} + + +/* + * miPointerUpdate + * + * Syncronize the sprite with the cursor - called from ProcessInputEvents + */ + +void +miPointerUpdate () +{ + ScreenPtr pScreen; + miPointerScreenPtr pScreenPriv; + int x, y, devx, devy; + + pScreen = miPointer.pScreen; + x = miPointer.x; + y = miPointer.y; + devx = miPointer.devx; + devy = miPointer.devy; + if (!pScreen) + return; + pScreenPriv = GetScreenPrivate (pScreen); + /* + * if the cursor has switched screens, disable the sprite + * on the old screen + */ + if (pScreen != miPointer.pSpriteScreen) + { + if (miPointer.pSpriteScreen) + { + miPointerScreenPtr pOldPriv; + + pOldPriv = GetScreenPrivate (miPointer.pSpriteScreen); + if (miPointer.pCursor) + { + (*pOldPriv->spriteFuncs->SetCursor) + (miPointer.pSpriteScreen, NullCursor, 0, 0); + } + (*pOldPriv->screenFuncs->CrossScreen) (miPointer.pSpriteScreen, FALSE); + } + (*pScreenPriv->screenFuncs->CrossScreen) (pScreen, TRUE); + (*pScreenPriv->spriteFuncs->SetCursor) + (pScreen, miPointer.pCursor, x, y); + miPointer.devx = x; + miPointer.devy = y; + miPointer.pSpriteCursor = miPointer.pCursor; + miPointer.pSpriteScreen = pScreen; + } + /* + * if the cursor has changed, display the new one + */ + else if (miPointer.pCursor != miPointer.pSpriteCursor) + { + (*pScreenPriv->spriteFuncs->SetCursor) + (pScreen, miPointer.pCursor, x, y); + miPointer.devx = x; + miPointer.devy = y; + miPointer.pSpriteCursor = miPointer.pCursor; + } + else if (x != devx || y != devy) + { + miPointer.devx = x; + miPointer.devy = y; + (*pScreenPriv->spriteFuncs->MoveCursor) (pScreen, x, y); + } +} + +/* + * miPointerDeltaCursor. The pointer has moved dx,dy from it's previous + * position. + */ + +void +miPointerDeltaCursor (dx, dy, time) + int dx, dy; + unsigned long time; +{ + miPointerAbsoluteCursor (miPointer.x + dx, miPointer.y + dy, time); +} + +/* + * miPointerAbsoluteCursor. The pointer has moved to x,y + */ + +void +miPointerAbsoluteCursor (x, y, time) + int x, y; + unsigned long time; +{ + miPointerScreenPtr pScreenPriv; + ScreenPtr pScreen; + ScreenPtr newScreen; + + pScreen = miPointer.pScreen; + if (!pScreen) + return; /* called before ready */ + if (x < 0 || x >= pScreen->width || y < 0 || y >= pScreen->height) + { + pScreenPriv = GetScreenPrivate (pScreen); + if (!miPointer.confined) + { + newScreen = pScreen; + (*pScreenPriv->screenFuncs->CursorOffScreen) (&newScreen, &x, &y); + if (newScreen != pScreen) + { + pScreen = newScreen; + (*pScreenPriv->screenFuncs->NewEventScreen) (pScreen, FALSE); + pScreenPriv = GetScreenPrivate (pScreen); + /* Smash the confine to the new screen */ + miPointer.limits.x2 = pScreen->width; + miPointer.limits.y2 = pScreen->height; + } + } + } + /* + * constrain the hot-spot to the current + * limits + */ + if (x < miPointer.limits.x1) + x = miPointer.limits.x1; + if (x >= miPointer.limits.x2) + x = miPointer.limits.x2 - 1; + if (y < miPointer.limits.y1) + y = miPointer.limits.y1; + if (y >= miPointer.limits.y2) + y = miPointer.limits.y2 - 1; + if (miPointer.x == x && miPointer.y == y && miPointer.pScreen == pScreen) + return; + miPointerMove (pScreen, x, y, time); +} + +void +miPointerPosition (x, y) + int *x, *y; +{ + *x = miPointer.x; + *y = miPointer.y; +} + +/* + * miPointerMove. The pointer has moved to x,y on current screen + */ + +static void +miPointerMove (pScreen, x, y, time) + ScreenPtr pScreen; + int x, y; + unsigned long time; +{ + SetupScreen(pScreen); + xEvent xE; + miHistoryPtr history; + int prev, end, start; + + if (!pScreenPriv->waitForUpdate && pScreen == miPointer.pSpriteScreen) + { + miPointer.devx = x; + miPointer.devy = y; + (*pScreenPriv->spriteFuncs->MoveCursor) (pScreen, x, y); + } + miPointer.x = x; + miPointer.y = y; + miPointer.pScreen = pScreen; + + xE.u.u.type = MotionNotify; + xE.u.keyButtonPointer.rootX = x; + xE.u.keyButtonPointer.rootY = y; + xE.u.keyButtonPointer.time = time; + (*pScreenPriv->screenFuncs->EnqueueEvent) (&xE); + + end = miPointer.history_end; + start = miPointer.history_start; + prev = end - 1; + if (end == 0) + prev = MOTION_SIZE - 1; + history = &miPointer.history[prev]; + if (end == start || history->event.time != time) + { + history = &miPointer.history[end]; + if (++end == MOTION_SIZE) + end = 0; + if (end == start) + { + start = end + 1; + if (start == MOTION_SIZE) + start = 0; + miPointer.history_start = start; + } + miPointer.history_end = end; + } + history->event.x = x; + history->event.y = y; + history->event.time = time; + history->pScreen = pScreen; +} + +void +miRegisterPointerDevice (pScreen, pDevice) + ScreenPtr pScreen; + DevicePtr pDevice; +{ + miPointer.pPointer = pDevice; +} diff --git a/mi/mipointer.h b/mi/mipointer.h new file mode 100644 index 000000000..a2f74b07b --- /dev/null +++ b/mi/mipointer.h @@ -0,0 +1,122 @@ +/* + * mipointer.h + * + */ + +/* $Xorg: mipointer.h,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ + +/* + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. +*/ + +typedef struct _miPointerSpriteFuncRec { + Bool (*RealizeCursor)(); /* pScreen, pCursor */ + Bool (*UnrealizeCursor)(); /* pScreen, pCursor */ + void (*SetCursor)(); /* pScreen, pCursor, x, y */ + void (*MoveCursor)(); /* pScreen, x, y */ +} miPointerSpriteFuncRec, *miPointerSpriteFuncPtr; + +typedef struct _miPointerScreenFuncRec { + Bool (*CursorOffScreen)(); /* ppScreen, px, py */ + void (*CrossScreen)(); /* pScreen, entering */ + void (*WarpCursor)(); /* pScreen, x, y */ + void (*EnqueueEvent)(); /* xEvent */ + void (*NewEventScreen)(); /* pScreen */ +} miPointerScreenFuncRec, *miPointerScreenFuncPtr; + +extern Bool miDCInitialize( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + miPointerScreenFuncPtr /*screenFuncs*/ +#endif +); + +extern Bool miPointerInitialize( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + miPointerSpriteFuncPtr /*spriteFuncs*/, + miPointerScreenFuncPtr /*screenFuncs*/, + Bool /*waitForUpdate*/ +#endif +); + +extern void miPointerWarpCursor( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + int /*x*/, + int /*y*/ +#endif +); + +extern int miPointerGetMotionBufferSize( +#if NeedFunctionPrototypes + void +#endif +); + +extern int miPointerGetMotionEvents( +#if NeedFunctionPrototypes + DeviceIntPtr /*pPtr*/, + xTimecoord * /*coords*/, + unsigned long /*start*/, + unsigned long /*stop*/, + ScreenPtr /*pScreen*/ +#endif +); + +extern void miPointerUpdate( +#if NeedFunctionPrototypes + void +#endif +); + +extern void miPointerDeltaCursor( +#if NeedFunctionPrototypes + int /*dx*/, + int /*dy*/, + unsigned long /*time*/ +#endif +); + +extern void miPointerAbsoluteCursor( +#if NeedFunctionPrototypes + int /*x*/, + int /*y*/, + unsigned long /*time*/ +#endif +); + +extern void miPointerPosition( +#if NeedFunctionPrototypes + int * /*x*/, + int * /*y*/ +#endif +); + +extern void miRegisterPointerDevice( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + DevicePtr /*pDevice*/ +#endif +); diff --git a/mi/mipointrst.h b/mi/mipointrst.h new file mode 100644 index 000000000..236079166 --- /dev/null +++ b/mi/mipointrst.h @@ -0,0 +1,62 @@ +/* + * mipointrst.h + * + */ + +/* $Xorg: mipointrst.h,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ + +/* + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. +*/ + +# include <mipointer.h> +# include <input.h> + +#define MOTION_SIZE 256 + +typedef struct { + xTimecoord event; + ScreenPtr pScreen; +} miHistoryRec, *miHistoryPtr; + +typedef struct { + ScreenPtr pScreen; /* current screen */ + ScreenPtr pSpriteScreen;/* screen containing current sprite */ + CursorPtr pCursor; /* current cursor */ + CursorPtr pSpriteCursor;/* cursor on screen */ + BoxRec limits; /* current constraints */ + Bool confined; /* pointer can't change screens */ + int x, y; /* hot spot location */ + int devx, devy; /* sprite position */ + DevicePtr pPointer; /* pointer device structure */ + miHistoryRec history[MOTION_SIZE]; + int history_start, history_end; +} miPointerRec, *miPointerPtr; + +typedef struct { + miPointerSpriteFuncPtr spriteFuncs; /* sprite-specific methods */ + miPointerScreenFuncPtr screenFuncs; /* screen-specific methods */ + CloseScreenProcPtr CloseScreen; + Bool waitForUpdate; /* don't move cursor in SIGIO */ +} miPointerScreenRec, *miPointerScreenPtr; diff --git a/mi/mipoly.c b/mi/mipoly.c new file mode 100644 index 000000000..b25ee780f --- /dev/null +++ b/mi/mipoly.c @@ -0,0 +1,124 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mipoly.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +/* + * mipoly.c + * + * Written by Brian Kelleher; June 1986 + * + * Draw polygons. This routine translates the point by the + * origin if pGC->miTranslate is non-zero, and calls + * to the appropriate routine to actually scan convert the + * polygon. + */ +#include "X.h" +#include "windowstr.h" +#include "gcstruct.h" +#include "pixmapstr.h" +#include "mi.h" +#include "miscstruct.h" + + +void +miFillPolygon(dst, pgc, shape, mode, count, pPts) + DrawablePtr dst; + register GCPtr pgc; + int shape, mode; + register int count; + DDXPointPtr pPts; +{ + int i; + register int xorg, yorg; + register DDXPointPtr ppt; + + if (count == 0) + return; + + ppt = pPts; + if (pgc->miTranslate) + { + xorg = dst->x; + yorg = dst->y; + + if (mode == CoordModeOrigin) + { + for (i = 0; i<count; i++) + { + ppt->x += xorg; + ppt++->y += yorg; + } + } + else + { + ppt->x += xorg; + ppt++->y += yorg; + for (i = 1; i<count; i++) + { + ppt->x += (ppt-1)->x; + ppt->y += (ppt-1)->y; + ppt++; + } + } + } + else + { + if (mode == CoordModePrevious) + { + ppt++; + for (i = 1; i<count; i++) + { + ppt->x += (ppt-1)->x; + ppt->y += (ppt-1)->y; + ppt++; + } + } + } + if (shape == Convex) + miFillConvexPoly(dst, pgc, count, pPts); + else + miFillGeneralPoly(dst, pgc, count, pPts); +} diff --git a/mi/mipoly.h b/mi/mipoly.h new file mode 100644 index 000000000..d946388a6 --- /dev/null +++ b/mi/mipoly.h @@ -0,0 +1,228 @@ +/* $Xorg: mipoly.h,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +/* + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + + +/* + * fill.h + * + * Created by Brian Kelleher; Oct 1985 + * + * Include file for filled polygon routines. + * + * These are the data structures needed to scan + * convert regions. Two different scan conversion + * methods are available -- the even-odd method, and + * the winding number method. + * The even-odd rule states that a point is inside + * the polygon if a ray drawn from that point in any + * direction will pass through an odd number of + * path segments. + * By the winding number rule, a point is decided + * to be inside the polygon if a ray drawn from that + * point in any direction passes through a different + * number of clockwise and counter-clockwise path + * segments. + * + * These data structures are adapted somewhat from + * the algorithm in (Foley/Van Dam) for scan converting + * polygons. + * The basic algorithm is to start at the top (smallest y) + * of the polygon, stepping down to the bottom of + * the polygon by incrementing the y coordinate. We + * keep a list of edges which the current scanline crosses, + * sorted by x. This list is called the Active Edge Table (AET) + * As we change the y-coordinate, we update each entry in + * in the active edge table to reflect the edges new xcoord. + * This list must be sorted at each scanline in case + * two edges intersect. + * We also keep a data structure known as the Edge Table (ET), + * which keeps track of all the edges which the current + * scanline has not yet reached. The ET is basically a + * list of ScanLineList structures containing a list of + * edges which are entered at a given scanline. There is one + * ScanLineList per scanline at which an edge is entered. + * When we enter a new edge, we move it from the ET to the AET. + * + * From the AET, we can implement the even-odd rule as in + * (Foley/Van Dam). + * The winding number rule is a little trickier. We also + * keep the EdgeTableEntries in the AET linked by the + * nextWETE (winding EdgeTableEntry) link. This allows + * the edges to be linked just as before for updating + * purposes, but only uses the edges linked by the nextWETE + * link as edges representing spans of the polygon to + * drawn (as with the even-odd rule). + */ + +/* + * for the winding number rule + */ +#define CLOCKWISE 1 +#define COUNTERCLOCKWISE -1 + +typedef struct _EdgeTableEntry { + int ymax; /* ycoord at which we exit this edge. */ + BRESINFO bres; /* Bresenham info to run the edge */ + struct _EdgeTableEntry *next; /* next in the list */ + struct _EdgeTableEntry *back; /* for insertion sort */ + struct _EdgeTableEntry *nextWETE; /* for winding num rule */ + int ClockWise; /* flag for winding number rule */ +} EdgeTableEntry; + + +typedef struct _ScanLineList{ + int scanline; /* the scanline represented */ + EdgeTableEntry *edgelist; /* header node */ + struct _ScanLineList *next; /* next in the list */ +} ScanLineList; + + +typedef struct { + int ymax; /* ymax for the polygon */ + int ymin; /* ymin for the polygon */ + ScanLineList scanlines; /* header node */ +} EdgeTable; + + +/* + * Here is a struct to help with storage allocation + * so we can allocate a big chunk at a time, and then take + * pieces from this heap when we need to. + */ +#define SLLSPERBLOCK 25 + +typedef struct _ScanLineListBlock { + ScanLineList SLLs[SLLSPERBLOCK]; + struct _ScanLineListBlock *next; +} ScanLineListBlock; + +/* + * number of points to buffer before sending them off + * to scanlines() : Must be an even number + */ +#define NUMPTSTOBUFFER 200 + + +/* + * + * a few macros for the inner loops of the fill code where + * performance considerations don't allow a procedure call. + * + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The winding number rule is in effect, so we must notify + * the caller when the edge has been removed so he + * can reorder the Winding Active Edge Table. + */ +#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + fixWAET = 1; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres); \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + + +/* + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The even-odd rule is in effect. + */ +#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres); \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + +/* mipolyutil.c */ + +extern Bool miInsertEdgeInET( +#if NeedFunctionPrototypes + EdgeTable * /*ET*/, + EdgeTableEntry * /*ETE*/, + int /*scanline*/, + ScanLineListBlock ** /*SLLBlock*/, + int * /*iSLLBlock*/ +#endif +); + +extern Bool miCreateETandAET( +#if NeedFunctionPrototypes + int /*count*/, + DDXPointPtr /*pts*/, + EdgeTable * /*ET*/, + EdgeTableEntry * /*AET*/, + EdgeTableEntry * /*pETEs*/, + ScanLineListBlock * /*pSLLBlock*/ +#endif +); + +extern void miloadAET( +#if NeedFunctionPrototypes + EdgeTableEntry * /*AET*/, + EdgeTableEntry * /*ETEs*/ +#endif +); + +extern void micomputeWAET( +#if NeedFunctionPrototypes + EdgeTableEntry * /*AET*/ +#endif +); + +extern int miInsertionSort( +#if NeedFunctionPrototypes + EdgeTableEntry * /*AET*/ +#endif +); + +extern void miFreeStorage( +#if NeedFunctionPrototypes + ScanLineListBlock * /*pSLLBlock*/ +#endif +); diff --git a/mi/mipolycon.c b/mi/mipolycon.c new file mode 100644 index 000000000..9daf44543 --- /dev/null +++ b/mi/mipolycon.c @@ -0,0 +1,245 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mipolycon.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +#include "gcstruct.h" +#include "pixmap.h" +#include "miscanfill.h" + +static int getPolyYBounds(); + +/* + * convexpoly.c + * + * Written by Brian Kelleher; Dec. 1985. + * + * Fill a convex polygon. If the given polygon + * is not convex, then the result is undefined. + * The algorithm is to order the edges from smallest + * y to largest by partitioning the array into a left + * edge list and a right edge list. The algorithm used + * to traverse each edge is an extension of Bresenham's + * line algorithm with y as the major axis. + * For a derivation of the algorithm, see the author of + * this code. + */ +Bool +miFillConvexPoly(dst, pgc, count, ptsIn) + DrawablePtr dst; + GCPtr pgc; + int count; /* number of points */ + DDXPointPtr ptsIn; /* the points */ +{ + register int xl, xr; /* x vals of left and right edges */ + register int dl, dr; /* decision variables */ + register int ml, m1l; /* left edge slope and slope+1 */ + int mr, m1r; /* right edge slope and slope+1 */ + int incr1l, incr2l; /* left edge error increments */ + int incr1r, incr2r; /* right edge error increments */ + int dy; /* delta y */ + int y; /* current scanline */ + int left, right; /* indices to first endpoints */ + int i; /* loop counter */ + int nextleft, nextright; /* indices to second endpoints */ + DDXPointPtr ptsOut, FirstPoint; /* output buffer */ + int *width, *FirstWidth; /* output buffer */ + int imin; /* index of smallest vertex (in y) */ + int ymin; /* y-extents of polygon */ + int ymax; + + /* + * find leftx, bottomy, rightx, topy, and the index + * of bottomy. Also translate the points. + */ + imin = getPolyYBounds(ptsIn, count, &ymin, &ymax); + + dy = ymax - ymin + 1; + if ((count < 3) || (dy < 0)) + return(TRUE); + ptsOut = FirstPoint = (DDXPointPtr )ALLOCATE_LOCAL(sizeof(DDXPointRec)*dy); + width = FirstWidth = (int *)ALLOCATE_LOCAL(sizeof(int) * dy); + if(!FirstPoint || !FirstWidth) + { + if (FirstWidth) DEALLOCATE_LOCAL(FirstWidth); + if (FirstPoint) DEALLOCATE_LOCAL(FirstPoint); + return(FALSE); + } + + nextleft = nextright = imin; + y = ptsIn[nextleft].y; + + /* + * loop through all edges of the polygon + */ + do { + /* + * add a left edge if we need to + */ + if (ptsIn[nextleft].y == y) { + left = nextleft; + + /* + * find the next edge, considering the end + * conditions of the array. + */ + nextleft++; + if (nextleft >= count) + nextleft = 0; + + /* + * now compute all of the random information + * needed to run the iterative algorithm. + */ + BRESINITPGON(ptsIn[nextleft].y-ptsIn[left].y, + ptsIn[left].x,ptsIn[nextleft].x, + xl, dl, ml, m1l, incr1l, incr2l); + } + + /* + * add a right edge if we need to + */ + if (ptsIn[nextright].y == y) { + right = nextright; + + /* + * find the next edge, considering the end + * conditions of the array. + */ + nextright--; + if (nextright < 0) + nextright = count-1; + + /* + * now compute all of the random information + * needed to run the iterative algorithm. + */ + BRESINITPGON(ptsIn[nextright].y-ptsIn[right].y, + ptsIn[right].x,ptsIn[nextright].x, + xr, dr, mr, m1r, incr1r, incr2r); + } + + /* + * generate scans to fill while we still have + * a right edge as well as a left edge. + */ + i = min(ptsIn[nextleft].y, ptsIn[nextright].y) - y; + /* in case we're called with non-convex polygon */ + if(i < 0) + { + DEALLOCATE_LOCAL(FirstWidth); + DEALLOCATE_LOCAL(FirstPoint); + return(TRUE); + } + while (i-- > 0) + { + ptsOut->y = y; + + /* + * reverse the edges if necessary + */ + if (xl < xr) + { + *(width++) = xr - xl; + (ptsOut++)->x = xl; + } + else + { + *(width++) = xl - xr; + (ptsOut++)->x = xr; + } + y++; + + /* increment down the edges */ + BRESINCRPGON(dl, xl, ml, m1l, incr1l, incr2l); + BRESINCRPGON(dr, xr, mr, m1r, incr1r, incr2r); + } + } while (y != ymax); + + /* + * Finally, fill the <remaining> spans + */ + (*pgc->ops->FillSpans)(dst, pgc, + ptsOut-FirstPoint,FirstPoint,FirstWidth, + 1); + DEALLOCATE_LOCAL(FirstWidth); + DEALLOCATE_LOCAL(FirstPoint); + return(TRUE); +} + + +/* + * Find the index of the point with the smallest y. + */ +static +int +getPolyYBounds(pts, n, by, ty) + DDXPointPtr pts; + int n; + int *by, *ty; +{ + register DDXPointPtr ptMin; + int ymin, ymax; + DDXPointPtr ptsStart = pts; + + ptMin = pts; + ymin = ymax = (pts++)->y; + + while (--n > 0) { + if (pts->y < ymin) + { + ptMin = pts; + ymin = pts->y; + } + if(pts->y > ymax) + ymax = pts->y; + + pts++; + } + + *by = ymin; + *ty = ymax; + return(ptMin-ptsStart); +} diff --git a/mi/mipolygen.c b/mi/mipolygen.c new file mode 100644 index 000000000..b9c0248c6 --- /dev/null +++ b/mi/mipolygen.c @@ -0,0 +1,226 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mipolygen.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +#include "X.h" +#include "gcstruct.h" +#include "miscanfill.h" +#include "mipoly.h" +#include "pixmap.h" + +/* + * + * Written by Brian Kelleher; Oct. 1985 + * + * Routine to fill a polygon. Two fill rules are + * supported: frWINDING and frEVENODD. + * + * See fillpoly.h for a complete description of the algorithm. + */ + +Bool +miFillGeneralPoly(dst, pgc, count, ptsIn) + DrawablePtr dst; + GCPtr pgc; + int count; /* number of points */ + DDXPointPtr ptsIn; /* the points */ +{ + register EdgeTableEntry *pAET; /* the Active Edge Table */ + register int y; /* the current scanline */ + register int nPts = 0; /* number of pts in buffer */ + register EdgeTableEntry *pWETE; /* Winding Edge Table */ + register ScanLineList *pSLL; /* Current ScanLineList */ + register DDXPointPtr ptsOut; /* ptr to output buffers */ + int *width; + DDXPointRec FirstPoint[NUMPTSTOBUFFER]; /* the output buffers */ + int FirstWidth[NUMPTSTOBUFFER]; + EdgeTableEntry *pPrevAET; /* previous AET entry */ + EdgeTable ET; /* Edge Table header node */ + EdgeTableEntry AET; /* Active ET header node */ + EdgeTableEntry *pETEs; /* Edge Table Entries buff */ + ScanLineListBlock SLLBlock; /* header for ScanLineList */ + int fixWAET = 0; + + if (count < 3) + return(TRUE); + + if(!(pETEs = (EdgeTableEntry *) + ALLOCATE_LOCAL(sizeof(EdgeTableEntry) * count))) + return(FALSE); + ptsOut = FirstPoint; + width = FirstWidth; + if (!miCreateETandAET(count, ptsIn, &ET, &AET, pETEs, &SLLBlock)) + { + DEALLOCATE_LOCAL(pETEs); + return(FALSE); + } + pSLL = ET.scanlines.next; + + if (pgc->fillRule == EvenOddRule) + { + /* + * for each scanline + */ + for (y = ET.ymin; y < ET.ymax; y++) + { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL && y == pSLL->scanline) + { + miloadAET(&AET, pSLL->edgelist); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + + /* + * for each active edge + */ + while (pAET) + { + ptsOut->x = pAET->bres.minor; + ptsOut++->y = y; + *width++ = pAET->next->bres.minor - pAET->bres.minor; + nPts++; + + /* + * send out the buffer when its full + */ + if (nPts == NUMPTSTOBUFFER) + { + (*pgc->ops->FillSpans)(dst, pgc, + nPts, FirstPoint, FirstWidth, + 1); + ptsOut = FirstPoint; + width = FirstWidth; + nPts = 0; + } + EVALUATEEDGEEVENODD(pAET, pPrevAET, y) + EVALUATEEDGEEVENODD(pAET, pPrevAET, y); + } + miInsertionSort(&AET); + } + } + else /* default to WindingNumber */ + { + /* + * for each scanline + */ + for (y = ET.ymin; y < ET.ymax; y++) + { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL && y == pSLL->scanline) + { + miloadAET(&AET, pSLL->edgelist); + micomputeWAET(&AET); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + pWETE = pAET; + + /* + * for each active edge + */ + while (pAET) + { + /* + * if the next edge in the active edge table is + * also the next edge in the winding active edge + * table. + */ + if (pWETE == pAET) + { + ptsOut->x = pAET->bres.minor; + ptsOut++->y = y; + *width++ = pAET->nextWETE->bres.minor - pAET->bres.minor; + nPts++; + + /* + * send out the buffer + */ + if (nPts == NUMPTSTOBUFFER) + { + (*pgc->ops->FillSpans)(dst, pgc, nPts, FirstPoint, + FirstWidth, 1); + ptsOut = FirstPoint; + width = FirstWidth; + nPts = 0; + } + + pWETE = pWETE->nextWETE; + while (pWETE != pAET) + EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET); + pWETE = pWETE->nextWETE; + } + EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET); + } + + /* + * reevaluate the Winding active edge table if we + * just had to resort it or if we just exited an edge. + */ + if (miInsertionSort(&AET) || fixWAET) + { + micomputeWAET(&AET); + fixWAET = 0; + } + } + } + + /* + * Get any spans that we missed by buffering + */ + (*pgc->ops->FillSpans)(dst, pgc, nPts, FirstPoint, FirstWidth, 1); + DEALLOCATE_LOCAL(pETEs); + miFreeStorage(SLLBlock.next); + return(TRUE); +} diff --git a/mi/mipolypnt.c b/mi/mipolypnt.c new file mode 100644 index 000000000..933e1cc3b --- /dev/null +++ b/mi/mipolypnt.c @@ -0,0 +1,119 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mipolypnt.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +#include "X.h" +#include "Xprotostr.h" +#include "pixmapstr.h" +#include "gcstruct.h" +#include "windowstr.h" + +void +miPolyPoint(pDrawable, pGC, mode, npt, pptInit) + DrawablePtr pDrawable; + GCPtr pGC; + int mode; /* Origin or Previous */ + int npt; + xPoint *pptInit; +{ + + int xorg; + int yorg; + int nptTmp; + XID fsOld, fsNew; + int *pwidthInit, *pwidth; + int i; + register xPoint *ppt; + + /* make pointlist origin relative */ + if (mode == CoordModePrevious) + { + ppt = pptInit; + nptTmp = npt; + nptTmp--; + while(nptTmp--) + { + ppt++; + ppt->x += (ppt-1)->x; + ppt->y += (ppt-1)->y; + } + } + + if(pGC->miTranslate) + { + ppt = pptInit; + nptTmp = npt; + xorg = pDrawable->x; + yorg = pDrawable->y; + while(nptTmp--) + { + ppt->x += xorg; + ppt++->y += yorg; + } + } + + fsOld = pGC->fillStyle; + fsNew = FillSolid; + if(pGC->fillStyle != FillSolid) + { + DoChangeGC(pGC, GCFillStyle, &fsNew, 0); + ValidateGC(pDrawable, pGC); + } + if(!(pwidthInit = (int *)ALLOCATE_LOCAL(npt * sizeof(int)))) + return; + pwidth = pwidthInit; + for(i = 0; i < npt; i++) + *pwidth++ = 1; + (*pGC->ops->FillSpans)(pDrawable, pGC, npt, pptInit, pwidthInit, FALSE); + + if(fsOld != FillSolid) + { + DoChangeGC(pGC, GCFillStyle, &fsOld, 0); + ValidateGC(pDrawable, pGC); + } + DEALLOCATE_LOCAL(pwidthInit); +} + diff --git a/mi/mipolyrect.c b/mi/mipolyrect.c new file mode 100644 index 000000000..935203d9a --- /dev/null +++ b/mi/mipolyrect.c @@ -0,0 +1,187 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mipolyrect.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +#include "X.h" +#include "Xprotostr.h" +#include "miscstruct.h" +#include "gcstruct.h" +#include "pixmap.h" + +void +miPolyRectangle(pDraw, pGC, nrects, pRects) + DrawablePtr pDraw; + GCPtr pGC; + int nrects; + xRectangle *pRects; +{ + int i; + xRectangle *pR = pRects; + DDXPointRec rect[5]; + int bound_tmp; + +#define MINBOUND(dst,eqn) bound_tmp = eqn; \ + if (bound_tmp < -32768) \ + bound_tmp = -32768; \ + dst = bound_tmp; + +#define MAXBOUND(dst,eqn) bound_tmp = eqn; \ + if (bound_tmp > 32767) \ + bound_tmp = 32767; \ + dst = bound_tmp; + +#define MAXUBOUND(dst,eqn) bound_tmp = eqn; \ + if (bound_tmp > 65535) \ + bound_tmp = 65535; \ + dst = bound_tmp; + + if (pGC->lineStyle == LineSolid && pGC->joinStyle == JoinMiter && + pGC->lineWidth != 0) + { + xRectangle *tmp, *t; + int ntmp; + int offset1, offset2, offset3; + int x, y, width, height; + + ntmp = (nrects << 2); + offset2 = pGC->lineWidth; + offset1 = offset2 >> 1; + offset3 = offset2 - offset1; + tmp = (xRectangle *) ALLOCATE_LOCAL(ntmp * sizeof (xRectangle)); + if (!tmp) + return; + t = tmp; + for (i = 0; i < nrects; i++) + { + x = pR->x; + y = pR->y; + width = pR->width; + height = pR->height; + pR++; + if (width == 0 && height == 0) + { + rect[0].x = x; + rect[0].y = y; + rect[1].x = x; + rect[1].y = y; + (*pGC->ops->Polylines)(pDraw, pGC, CoordModeOrigin, 2, rect); + } + else if (height < offset2 || width < offset1) + { + if (height == 0) + { + t->x = x; + t->width = width; + } + else + { + MINBOUND (t->x, x - offset1) + MAXUBOUND (t->width, width + offset2) + } + if (width == 0) + { + t->y = y; + t->height = height; + } + else + { + MINBOUND (t->y, y - offset1) + MAXUBOUND (t->height, height + offset2) + } + t++; + } + else + { + MINBOUND(t->x, x - offset1) + MINBOUND(t->y, y - offset1) + MAXUBOUND(t->width, width + offset2) + t->height = offset2; + t++; + MINBOUND(t->x, x - offset1) + MAXBOUND(t->y, y + offset3); + t->width = offset2; + t->height = height - offset2; + t++; + MAXBOUND(t->x, x + width - offset1); + MAXBOUND(t->y, y + offset3) + t->width = offset2; + t->height = height - offset2; + t++; + MINBOUND(t->x, x - offset1) + MAXBOUND(t->y, y + height - offset1) + MAXUBOUND(t->width, width + offset2) + t->height = offset2; + t++; + } + } + (*pGC->ops->PolyFillRect) (pDraw, pGC, t - tmp, tmp); + DEALLOCATE_LOCAL ((pointer) tmp); + } + else + { + + for (i=0; i<nrects; i++) + { + rect[0].x = pR->x; + rect[0].y = pR->y; + + MAXBOUND(rect[1].x, pR->x + (int) pR->width) + rect[1].y = rect[0].y; + + rect[2].x = rect[1].x; + MAXBOUND(rect[2].y, pR->y + (int) pR->height); + + rect[3].x = rect[0].x; + rect[3].y = rect[2].y; + + rect[4].x = rect[0].x; + rect[4].y = rect[0].y; + + (*pGC->ops->Polylines)(pDraw, pGC, CoordModeOrigin, 5, rect); + pR++; + } + } +} diff --git a/mi/mipolyseg.c b/mi/mipolyseg.c new file mode 100644 index 000000000..2500f7abb --- /dev/null +++ b/mi/mipolyseg.c @@ -0,0 +1,79 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mipolyseg.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +#include "X.h" +#include "Xprotostr.h" +#include "miscstruct.h" +#include "gcstruct.h" +#include "pixmap.h" + +/***************************************************************** + * miPolySegment + * + * For each segment, draws a line between (x1, y1) and (x2, y2). The + * lines are drawn in the order listed. + * + * Walks the segments, compressing them into format for PolyLines. + * + *****************************************************************/ + + +void +miPolySegment(pDraw, pGC, nseg, pSegs) + DrawablePtr pDraw; + GCPtr pGC; + int nseg; + xSegment *pSegs; +{ + int i; + + for (i=0; i<nseg; i++) + { + (*pGC->ops->Polylines)(pDraw, pGC, CoordModeOrigin, 2,(DDXPointPtr)pSegs); + pSegs++; + } +} diff --git a/mi/mipolytext.c b/mi/mipolytext.c new file mode 100644 index 000000000..22dc6b2a9 --- /dev/null +++ b/mi/mipolytext.c @@ -0,0 +1,196 @@ +/******************************************************************* + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ +/* $Xorg: mipolytext.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +/* + * mipolytext.c - text routines + * + * Author: haynes + * Digital Equipment Corporation + * Western Software Laboratory + * Date: Thu Feb 5 1987 + */ + +#include "X.h" +#include "Xmd.h" +#include "Xproto.h" +#include "misc.h" +#include "gcstruct.h" +#include "fontstruct.h" +#include "dixfontstr.h" + +int +miPolyText(pDraw, pGC, x, y, count, chars, fontEncoding) + DrawablePtr pDraw; + GCPtr pGC; + int x, y; + int count; + char *chars; + FontEncoding fontEncoding; +{ + unsigned long n, i; + int w; + CharInfoPtr charinfo[255]; /* encoding only has 1 byte for count */ + + GetGlyphs(pGC->font, (unsigned long)count, (unsigned char *)chars, + fontEncoding, &n, charinfo); + w = 0; + for (i=0; i < n; i++) w += charinfo[i]->metrics.characterWidth; + if (n != 0) + (*pGC->ops->PolyGlyphBlt)( + pDraw, pGC, x, y, n, charinfo, FONTGLYPHS(pGC->font)); + return x+w; +} + + +int +miPolyText8(pDraw, pGC, x, y, count, chars) + DrawablePtr pDraw; + GCPtr pGC; + int x, y; + int count; + char *chars; +{ + unsigned long n, i; + int w; + CharInfoPtr charinfo[255]; /* encoding only has 1 byte for count */ + + GetGlyphs(pGC->font, (unsigned long)count, (unsigned char *)chars, + Linear8Bit, &n, charinfo); + w = 0; + for (i=0; i < n; i++) w += charinfo[i]->metrics.characterWidth; + if (n != 0) + (*pGC->ops->PolyGlyphBlt)( + pDraw, pGC, x, y, n, charinfo, FONTGLYPHS(pGC->font)); + return x+w; +} + + +int +miPolyText16(pDraw, pGC, x, y, count, chars) + DrawablePtr pDraw; + GCPtr pGC; + int x, y; + int count; + unsigned short *chars; +{ + unsigned long n, i; + int w; + CharInfoPtr charinfo[255]; /* encoding only has 1 byte for count */ + + GetGlyphs(pGC->font, (unsigned long)count, (unsigned char *)chars, + (FONTLASTROW(pGC->font) == 0) ? Linear16Bit : TwoD16Bit, + &n, charinfo); + w = 0; + for (i=0; i < n; i++) w += charinfo[i]->metrics.characterWidth; + if (n != 0) + (*pGC->ops->PolyGlyphBlt)( + pDraw, pGC, x, y, n, charinfo, FONTGLYPHS(pGC->font)); + return x+w; +} + + +int +miImageText(pDraw, pGC, x, y, count, chars, fontEncoding) + DrawablePtr pDraw; + GCPtr pGC; + int x, y; + int count; + char *chars; + FontEncoding fontEncoding; +{ + unsigned long n, i; + FontPtr font = pGC->font; + int w; + CharInfoPtr charinfo[255]; + + GetGlyphs(font, (unsigned long)count, (unsigned char *)chars, + fontEncoding, &n, charinfo); + w = 0; + for (i=0; i < n; i++) w += charinfo[i]->metrics.characterWidth; + if (n !=0 ) + (*pGC->ops->ImageGlyphBlt)(pDraw, pGC, x, y, n, charinfo, FONTGLYPHS(font)); + return x+w; +} + + +void +miImageText8(pDraw, pGC, x, y, count, chars) + DrawablePtr pDraw; + GCPtr pGC; + int x, y; + int count; + char *chars; +{ + unsigned long n; + FontPtr font = pGC->font; + CharInfoPtr charinfo[255]; /* encoding only has 1 byte for count */ + + GetGlyphs(font, (unsigned long)count, (unsigned char *)chars, + Linear8Bit, &n, charinfo); + if (n !=0 ) + (*pGC->ops->ImageGlyphBlt)(pDraw, pGC, x, y, n, charinfo, FONTGLYPHS(font)); +} + + +void +miImageText16(pDraw, pGC, x, y, count, chars) + DrawablePtr pDraw; + GCPtr pGC; + int x, y; + int count; + unsigned short *chars; +{ + unsigned long n; + FontPtr font = pGC->font; + CharInfoPtr charinfo[255]; /* encoding only has 1 byte for count */ + + GetGlyphs(font, (unsigned long)count, (unsigned char *)chars, + (FONTLASTROW(pGC->font) == 0) ? Linear16Bit : TwoD16Bit, + &n, charinfo); + if (n !=0 ) + (*pGC->ops->ImageGlyphBlt)(pDraw, pGC, x, y, n, charinfo, FONTGLYPHS(font)); +} diff --git a/mi/mipolyutil.c b/mi/mipolyutil.c new file mode 100644 index 000000000..879bd6e1d --- /dev/null +++ b/mi/mipolyutil.c @@ -0,0 +1,398 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mipolyutil.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +#include "miscstruct.h" +#include "gc.h" +#include "miscanfill.h" +#include "mipoly.h" + +#define MAXINT 0x7fffffff +#define MININT -MAXINT + +/* + * fillUtils.c + * + * Written by Brian Kelleher; Oct. 1985 + * + * This module contains all of the utility functions + * needed to scan convert a polygon. + * + */ + +/* + * InsertEdgeInET + * + * Insert the given edge into the edge table. + * First we must find the correct bucket in the + * Edge table, then find the right slot in the + * bucket. Finally, we can insert it. + * + */ +Bool +miInsertEdgeInET(ET, ETE, scanline, SLLBlock, iSLLBlock) + EdgeTable *ET; + EdgeTableEntry *ETE; + int scanline; + ScanLineListBlock **SLLBlock; + int *iSLLBlock; +{ + register EdgeTableEntry *start, *prev; + register ScanLineList *pSLL, *pPrevSLL; + ScanLineListBlock *tmpSLLBlock; + + /* + * find the right bucket to put the edge into + */ + pPrevSLL = &ET->scanlines; + pSLL = pPrevSLL->next; + while (pSLL && (pSLL->scanline < scanline)) + { + pPrevSLL = pSLL; + pSLL = pSLL->next; + } + + /* + * reassign pSLL (pointer to ScanLineList) if necessary + */ + if ((!pSLL) || (pSLL->scanline > scanline)) + { + if (*iSLLBlock > SLLSPERBLOCK-1) + { + tmpSLLBlock = + (ScanLineListBlock *)xalloc(sizeof(ScanLineListBlock)); + if (!tmpSLLBlock) + return FALSE; + (*SLLBlock)->next = tmpSLLBlock; + tmpSLLBlock->next = (ScanLineListBlock *)NULL; + *SLLBlock = tmpSLLBlock; + *iSLLBlock = 0; + } + pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]); + + pSLL->next = pPrevSLL->next; + pSLL->edgelist = (EdgeTableEntry *)NULL; + pPrevSLL->next = pSLL; + } + pSLL->scanline = scanline; + + /* + * now insert the edge in the right bucket + */ + prev = (EdgeTableEntry *)NULL; + start = pSLL->edgelist; + while (start && (start->bres.minor < ETE->bres.minor)) + { + prev = start; + start = start->next; + } + ETE->next = start; + + if (prev) + prev->next = ETE; + else + pSLL->edgelist = ETE; + return TRUE; +} + +/* + * CreateEdgeTable + * + * This routine creates the edge table for + * scan converting polygons. + * The Edge Table (ET) looks like: + * + * EdgeTable + * -------- + * | ymax | ScanLineLists + * |scanline|-->------------>-------------->... + * -------- |scanline| |scanline| + * |edgelist| |edgelist| + * --------- --------- + * | | + * | | + * V V + * list of ETEs list of ETEs + * + * where ETE is an EdgeTableEntry data structure, + * and there is one ScanLineList per scanline at + * which an edge is initially entered. + * + */ + +Bool +miCreateETandAET(count, pts, ET, AET, pETEs, pSLLBlock) + register int count; + register DDXPointPtr pts; + EdgeTable *ET; + EdgeTableEntry *AET; + register EdgeTableEntry *pETEs; + ScanLineListBlock *pSLLBlock; +{ + register DDXPointPtr top, bottom; + register DDXPointPtr PrevPt, CurrPt; + int iSLLBlock = 0; + + int dy; + + if (count < 2) return TRUE; + + /* + * initialize the Active Edge Table + */ + AET->next = (EdgeTableEntry *)NULL; + AET->back = (EdgeTableEntry *)NULL; + AET->nextWETE = (EdgeTableEntry *)NULL; + AET->bres.minor = MININT; + + /* + * initialize the Edge Table. + */ + ET->scanlines.next = (ScanLineList *)NULL; + ET->ymax = MININT; + ET->ymin = MAXINT; + pSLLBlock->next = (ScanLineListBlock *)NULL; + + PrevPt = &pts[count-1]; + + /* + * for each vertex in the array of points. + * In this loop we are dealing with two vertices at + * a time -- these make up one edge of the polygon. + */ + while (count--) + { + CurrPt = pts++; + + /* + * find out which point is above and which is below. + */ + if (PrevPt->y > CurrPt->y) + { + bottom = PrevPt, top = CurrPt; + pETEs->ClockWise = 0; + } + else + { + bottom = CurrPt, top = PrevPt; + pETEs->ClockWise = 1; + } + + /* + * don't add horizontal edges to the Edge table. + */ + if (bottom->y != top->y) + { + pETEs->ymax = bottom->y-1; /* -1 so we don't get last scanline */ + + /* + * initialize integer edge algorithm + */ + dy = bottom->y - top->y; + BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres); + + if (!miInsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock)) + { + miFreeStorage(pSLLBlock->next); + return FALSE; + } + + ET->ymax = max(ET->ymax, PrevPt->y); + ET->ymin = min(ET->ymin, PrevPt->y); + pETEs++; + } + + PrevPt = CurrPt; + } + return TRUE; +} + +/* + * loadAET + * + * This routine moves EdgeTableEntries from the + * EdgeTable into the Active Edge Table, + * leaving them sorted by smaller x coordinate. + * + */ + +void +miloadAET(AET, ETEs) + register EdgeTableEntry *AET, *ETEs; +{ + register EdgeTableEntry *pPrevAET; + register EdgeTableEntry *tmp; + + pPrevAET = AET; + AET = AET->next; + while (ETEs) + { + while (AET && (AET->bres.minor < ETEs->bres.minor)) + { + pPrevAET = AET; + AET = AET->next; + } + tmp = ETEs->next; + ETEs->next = AET; + if (AET) + AET->back = ETEs; + ETEs->back = pPrevAET; + pPrevAET->next = ETEs; + pPrevAET = ETEs; + + ETEs = tmp; + } +} + +/* + * computeWAET + * + * This routine links the AET by the + * nextWETE (winding EdgeTableEntry) link for + * use by the winding number rule. The final + * Active Edge Table (AET) might look something + * like: + * + * AET + * ---------- --------- --------- + * |ymax | |ymax | |ymax | + * | ... | |... | |... | + * |next |->|next |->|next |->... + * |nextWETE| |nextWETE| |nextWETE| + * --------- --------- ^-------- + * | | | + * V-------------------> V---> ... + * + */ +void +micomputeWAET(AET) + register EdgeTableEntry *AET; +{ + register EdgeTableEntry *pWETE; + register int inside = 1; + register int isInside = 0; + + AET->nextWETE = (EdgeTableEntry *)NULL; + pWETE = AET; + AET = AET->next; + while (AET) + { + if (AET->ClockWise) + isInside++; + else + isInside--; + + if ((!inside && !isInside) || + ( inside && isInside)) + { + pWETE->nextWETE = AET; + pWETE = AET; + inside = !inside; + } + AET = AET->next; + } + pWETE->nextWETE = (EdgeTableEntry *)NULL; +} + +/* + * InsertionSort + * + * Just a simple insertion sort using + * pointers and back pointers to sort the Active + * Edge Table. + * + */ + +int +miInsertionSort(AET) + register EdgeTableEntry *AET; +{ + register EdgeTableEntry *pETEchase; + register EdgeTableEntry *pETEinsert; + register EdgeTableEntry *pETEchaseBackTMP; + register int changed = 0; + + AET = AET->next; + while (AET) + { + pETEinsert = AET; + pETEchase = AET; + while (pETEchase->back->bres.minor > AET->bres.minor) + pETEchase = pETEchase->back; + + AET = AET->next; + if (pETEchase != pETEinsert) + { + pETEchaseBackTMP = pETEchase->back; + pETEinsert->back->next = AET; + if (AET) + AET->back = pETEinsert->back; + pETEinsert->next = pETEchase; + pETEchase->back->next = pETEinsert; + pETEchase->back = pETEinsert; + pETEinsert->back = pETEchaseBackTMP; + changed = 1; + } + } + return(changed); +} + +/* + * Clean up our act. + */ +void +miFreeStorage(pSLLBlock) + register ScanLineListBlock *pSLLBlock; +{ + register ScanLineListBlock *tmpSLLBlock; + + while (pSLLBlock) + { + tmpSLLBlock = pSLLBlock->next; + xfree(pSLLBlock); + pSLLBlock = tmpSLLBlock; + } +} diff --git a/mi/mipushpxl.c b/mi/mipushpxl.c new file mode 100644 index 000000000..24ee8bbe3 --- /dev/null +++ b/mi/mipushpxl.c @@ -0,0 +1,197 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mipushpxl.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +#include "X.h" +#include "gcstruct.h" +#include "scrnintstr.h" +#include "pixmapstr.h" +#include "miscstruct.h" +#include "../mfb/maskbits.h" + +#define NPT 128 + +/* miPushPixels -- squeegees the fill style of pGC through pBitMap + * into pDrawable. pBitMap is a stencil (dx by dy of it is used, it may + * be bigger) which is placed on the drawable at xOrg, yOrg. Where a 1 bit + * is set in the bitmap, the fill style is put onto the drawable using + * the GC's logical function. The drawable is not changed where the bitmap + * has a zero bit or outside the area covered by the stencil. + +WARNING: + this code works if the 1-bit deep pixmap format returned by GetSpans +is the same as the format defined by the mfb code (i.e. 32-bit padding +per scanline, scanline unit = 32 bits; later, this might mean +bitsizeof(int) padding and sacnline unit == bitsizeof(int).) + + */ +void +miPushPixels(pGC, pBitMap, pDrawable, dx, dy, xOrg, yOrg) + GCPtr pGC; + PixmapPtr pBitMap; + DrawablePtr pDrawable; + int dx, dy, xOrg, yOrg; +{ + int h, dxDiv32, ibEnd; + unsigned long *pwLineStart; + register unsigned long *pw, *pwEnd; + register unsigned long msk; + register int ib, w; + register int ipt; /* index into above arrays */ + Bool fInBox; + DDXPointRec pt[NPT], ptThisLine; + int width[NPT]; + + pwLineStart = (unsigned long *)xalloc(BitmapBytePad(dx)); + if (!pwLineStart) + return; + ipt = 0; + dxDiv32 = dx/32; + + for(h = 0, ptThisLine.x = 0, ptThisLine.y = 0; + h < dy; + h++, ptThisLine.y++) + { + + (*pBitMap->drawable.pScreen->GetSpans)((DrawablePtr)pBitMap, dx, + &ptThisLine, &dx, 1, (char *)pwLineStart); + + pw = pwLineStart; + /* Process all words which are fully in the pixmap */ + + fInBox = FALSE; + pwEnd = pwLineStart + dxDiv32; + while(pw < pwEnd) + { + w = *pw; + msk = endtab[1]; + for(ib = 0; ib < 32; ib++) + { + if(w & msk) + { + if(!fInBox) + { + pt[ipt].x = ((pw - pwLineStart) << 5) + ib + xOrg; + pt[ipt].y = h + yOrg; + /* start new box */ + fInBox = TRUE; + } + } + else + { + if(fInBox) + { + width[ipt] = ((pw - pwLineStart) << 5) + + ib + xOrg - pt[ipt].x; + if (++ipt >= NPT) + { + (*pGC->ops->FillSpans)(pDrawable, pGC, + NPT, pt, width, TRUE); + ipt = 0; + } + /* end box */ + fInBox = FALSE; + } + } + msk = SCRRIGHT(msk, 1); + } + pw++; + } + ibEnd = dx & 0x1F; + if(ibEnd) + { + /* Process final partial word on line */ + w = *pw; + msk = endtab[1]; + for(ib = 0; ib < ibEnd; ib++) + { + if(w & msk) + { + if(!fInBox) + { + /* start new box */ + pt[ipt].x = ((pw - pwLineStart) << 5) + ib + xOrg; + pt[ipt].y = h + yOrg; + fInBox = TRUE; + } + } + else + { + if(fInBox) + { + /* end box */ + width[ipt] = ((pw - pwLineStart) << 5) + + ib + xOrg - pt[ipt].x; + if (++ipt >= NPT) + { + (*pGC->ops->FillSpans)(pDrawable, + pGC, NPT, pt, width, TRUE); + ipt = 0; + } + fInBox = FALSE; + } + } + msk = SCRRIGHT(msk, 1); + } + } + /* If scanline ended with last bit set, end the box */ + if(fInBox) + { + width[ipt] = dx + xOrg - pt[ipt].x; + if (++ipt >= NPT) + { + (*pGC->ops->FillSpans)(pDrawable, pGC, NPT, pt, width, TRUE); + ipt = 0; + } + } + } + xfree(pwLineStart); + /* Flush any remaining spans */ + if (ipt) + { + (*pGC->ops->FillSpans)(pDrawable, pGC, ipt, pt, width, TRUE); + } +} diff --git a/mi/miregion.c b/mi/miregion.c new file mode 100644 index 000000000..f9c25b09d --- /dev/null +++ b/mi/miregion.c @@ -0,0 +1,2419 @@ +/*********************************************************** + +Copyright 1987, 1988, 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987, 1988, 1989 by +Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: miregion.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ + +#include <stdio.h> +#include "miscstruct.h" +#include "regionstr.h" +#include "Xprotostr.h" +#include "gc.h" + +#if defined (__GNUC__) && !defined (NO_INLINES) +#define INLINE __inline +#else +#define INLINE +#endif + +/* + * hack until callers of these functions can deal with out-of-memory + */ + +extern Bool Must_have_memory; + +#ifdef DEBUG +#define assert(expr) {if (!(expr)) \ + FatalError("Assertion failed file %s, line %d: expr\n", \ + __FILE__, __LINE__); } +#else +#define assert(expr) +#endif + +#define good(reg) assert(miValidRegion(reg)) + +/* + * The functions in this file implement the Region abstraction used extensively + * throughout the X11 sample server. A Region is simply a set of disjoint + * (non-overlapping) rectangles, plus an "extent" rectangle which is the + * smallest single rectangle that contains all the non-overlapping rectangles. + * + * A Region is implemented as a "y-x-banded" array of rectangles. This array + * imposes two degrees of order. First, all rectangles are sorted by top side + * y coordinate first (y1), and then by left side x coordinate (x1). + * + * Furthermore, the rectangles are grouped into "bands". Each rectangle in a + * band has the same top y coordinate (y1), and each has the same bottom y + * coordinate (y2). Thus all rectangles in a band differ only in their left + * and right side (x1 and x2). Bands are implicit in the array of rectangles: + * there is no separate list of band start pointers. + * + * The y-x band representation does not minimize rectangles. In particular, + * if a rectangle vertically crosses a band (the rectangle has scanlines in + * the y1 to y2 area spanned by the band), then the rectangle may be broken + * down into two or more smaller rectangles stacked one atop the other. + * + * ----------- ----------- + * | | | | band 0 + * | | -------- ----------- -------- + * | | | | in y-x banded | | | | band 1 + * | | | | form is | | | | + * ----------- | | ----------- -------- + * | | | | band 2 + * -------- -------- + * + * An added constraint on the rectangles is that they must cover as much + * horizontal area as possible: no two rectangles within a band are allowed + * to touch. + * + * Whenever possible, bands will be merged together to cover a greater vertical + * distance (and thus reduce the number of rectangles). Two bands can be merged + * only if the bottom of one touches the top of the other and they have + * rectangles in the same places (of the same width, of course). + * + * Adam de Boor wrote most of the original region code. Joel McCormack + * substantially modified or rewrote most of the core arithmetic routines, + * and added miRegionValidate in order to support several speed improvements + * to miValidateTree. Bob Scheifler changed the representation to be more + * compact when empty or a single rectangle, and did a bunch of gratuitous + * reformatting. + */ + +/* true iff two Boxes overlap */ +#define EXTENTCHECK(r1,r2) \ + (!( ((r1)->x2 <= (r2)->x1) || \ + ((r1)->x1 >= (r2)->x2) || \ + ((r1)->y2 <= (r2)->y1) || \ + ((r1)->y1 >= (r2)->y2) ) ) + +/* true iff (x,y) is in Box */ +#define INBOX(r,x,y) \ + ( ((r)->x2 > x) && \ + ((r)->x1 <= x) && \ + ((r)->y2 > y) && \ + ((r)->y1 <= y) ) + +/* true iff Box r1 contains Box r2 */ +#define SUBSUMES(r1,r2) \ + ( ((r1)->x1 <= (r2)->x1) && \ + ((r1)->x2 >= (r2)->x2) && \ + ((r1)->y1 <= (r2)->y1) && \ + ((r1)->y2 >= (r2)->y2) ) + +#define xallocData(n) (RegDataPtr)xalloc(REGION_SZOF(n)) +#define xfreeData(reg) if ((reg)->data && (reg)->data->size) xfree((reg)->data) + +#define RECTALLOC(pReg,n) \ +if (!(pReg)->data || (((pReg)->data->numRects + (n)) > (pReg)->data->size)) \ + miRectAlloc(pReg, n) + +#define ADDRECT(pNextRect,nx1,ny1,nx2,ny2) \ +{ \ + pNextRect->x1 = nx1; \ + pNextRect->y1 = ny1; \ + pNextRect->x2 = nx2; \ + pNextRect->y2 = ny2; \ + pNextRect++; \ +} + +#define NEWRECT(pReg,pNextRect,nx1,ny1,nx2,ny2) \ +{ \ + if (!(pReg)->data || ((pReg)->data->numRects == (pReg)->data->size))\ + { \ + miRectAlloc(pReg, 1); \ + pNextRect = REGION_TOP(pReg); \ + } \ + ADDRECT(pNextRect,nx1,ny1,nx2,ny2); \ + pReg->data->numRects++; \ + assert(pReg->data->numRects<=pReg->data->size); \ +} + + +#define DOWNSIZE(reg,numRects) \ +if (((numRects) < ((reg)->data->size >> 1)) && ((reg)->data->size > 50)) \ +{ \ + RegDataPtr NewData; \ + NewData = (RegDataPtr)xrealloc((reg)->data, REGION_SZOF(numRects)); \ + if (NewData) \ + { \ + NewData->size = (numRects); \ + (reg)->data = NewData; \ + } \ +} + + +BoxRec miEmptyBox = {0, 0, 0, 0}; +RegDataRec miEmptyData = {0, 0}; + +#ifdef DEBUG +int +miPrintRegion(rgn) + RegionPtr rgn; +{ + int num, size; + register int i; + BoxPtr rects; + + num = REGION_NUM_RECTS(rgn); + size = REGION_SIZE(rgn); + rects = REGION_RECTS(rgn); + ErrorF("num: %d size: %d\n", num, size); + ErrorF("extents: %d %d %d %d\n", + rgn->extents.x1, rgn->extents.y1, rgn->extents.x2, rgn->extents.y2); + for (i = 0; i < num; i++) + ErrorF("%d %d %d %d \n", + rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2); + ErrorF("\n"); + return(num); +} + + +Bool +miRegionsEqual(reg1, reg2) + RegionPtr reg1; + RegionPtr reg2; +{ + int i; + BoxPtr rects1, rects2; + + if (reg1->extents.x1 != reg2->extents.x1) return FALSE; + if (reg1->extents.x2 != reg2->extents.x2) return FALSE; + if (reg1->extents.y1 != reg2->extents.y1) return FALSE; + if (reg1->extents.y2 != reg2->extents.y2) return FALSE; + if (REGION_NUM_RECTS(reg1) != REGION_NUM_RECTS(reg2)) return FALSE; + + rects1 = REGION_RECTS(reg1); + rects2 = REGION_RECTS(reg2); + for (i = 0; i != REGION_NUM_RECTS(reg1); i++) { + if (rects1[i].x1 != rects2[i].x1) return FALSE; + if (rects1[i].x2 != rects2[i].x2) return FALSE; + if (rects1[i].y1 != rects2[i].y1) return FALSE; + if (rects1[i].y2 != rects2[i].y2) return FALSE; + } + return TRUE; +} + +Bool +miValidRegion(reg) + RegionPtr reg; +{ + register int i, numRects; + + if ((reg->extents.x1 > reg->extents.x2) || + (reg->extents.y1 > reg->extents.y2)) + return FALSE; + numRects = REGION_NUM_RECTS(reg); + if (!numRects) + return ((reg->extents.x1 == reg->extents.x2) && + (reg->extents.y1 == reg->extents.y2) && + (reg->data->size || (reg->data == &miEmptyData))); + else if (numRects == 1) + return (!reg->data); + else + { + register BoxPtr pboxP, pboxN; + BoxRec box; + + pboxP = REGION_RECTS(reg); + box = *pboxP; + box.y2 = pboxP[numRects-1].y2; + pboxN = pboxP + 1; + for (i = numRects; --i > 0; pboxP++, pboxN++) + { + if ((pboxN->x1 >= pboxN->x2) || + (pboxN->y1 >= pboxN->y2)) + return FALSE; + if (pboxN->x1 < box.x1) + box.x1 = pboxN->x1; + if (pboxN->x2 > box.x2) + box.x2 = pboxN->x2; + if ((pboxN->y1 < pboxP->y1) || + ((pboxN->y1 == pboxP->y1) && + ((pboxN->x1 < pboxP->x2) || (pboxN->y2 != pboxP->y2)))) + return FALSE; + } + return ((box.x1 == reg->extents.x1) && + (box.x2 == reg->extents.x2) && + (box.y1 == reg->extents.y1) && + (box.y2 == reg->extents.y2)); + } +} + +#endif /* DEBUG */ + + +/***************************************************************** + * RegionCreate(rect, size) + * This routine does a simple malloc to make a structure of + * REGION of "size" number of rectangles. + *****************************************************************/ + +RegionPtr +miRegionCreate(rect, size) + BoxPtr rect; + int size; +{ + register RegionPtr pReg; + + Must_have_memory = TRUE; /* XXX */ + pReg = (RegionPtr)xalloc(sizeof(RegionRec)); + Must_have_memory = FALSE; /* XXX */ + if (rect) + { + pReg->extents = *rect; + pReg->data = (RegDataPtr)NULL; + } + else + { + pReg->extents = miEmptyBox; + if ((size > 1) && (pReg->data = xallocData(size))) + { + pReg->data->size = size; + pReg->data->numRects = 0; + } + else + pReg->data = &miEmptyData; + } + return(pReg); +} + +/***************************************************************** + * RegionInit(pReg, rect, size) + * Outer region rect is statically allocated. + *****************************************************************/ + +void +miRegionInit(pReg, rect, size) + RegionPtr pReg; + BoxPtr rect; + int size; +{ + if (rect) + { + pReg->extents = *rect; + pReg->data = (RegDataPtr)NULL; + } + else + { + pReg->extents = miEmptyBox; + if ((size > 1) && (pReg->data = xallocData(size))) + { + pReg->data->size = size; + pReg->data->numRects = 0; + } + else + pReg->data = &miEmptyData; + } +} + +void +miRegionDestroy(pReg) + RegionPtr pReg; +{ + good(pReg); + xfreeData(pReg); + xfree(pReg); +} + +void +miRegionUninit(pReg) + RegionPtr pReg; +{ + good(pReg); + xfreeData(pReg); +} + +Bool +miRectAlloc(pRgn, n) + register RegionPtr pRgn; + int n; +{ + Must_have_memory = TRUE; /* XXX */ + if (!pRgn->data) + { + n++; + pRgn->data = xallocData(n); + pRgn->data->numRects = 1; + *REGION_BOXPTR(pRgn) = pRgn->extents; + } + else if (!pRgn->data->size) + { + pRgn->data = xallocData(n); + pRgn->data->numRects = 0; + } + else + { + if (n == 1) + { + n = pRgn->data->numRects; + if (n > 500) /* XXX pick numbers out of a hat */ + n = 250; + } + n += pRgn->data->numRects; + pRgn->data = (RegDataPtr)xrealloc(pRgn->data, REGION_SZOF(n)); + } + Must_have_memory = FALSE; /* XXX */ + pRgn->data->size = n; + return TRUE; +} + +Bool +miRegionCopy(dst, src) + register RegionPtr dst; + register RegionPtr src; +{ + good(dst); + good(src); + if (dst == src) + return TRUE; + dst->extents = src->extents; + if (!src->data || !src->data->size) + { + xfreeData(dst); + dst->data = src->data; + return TRUE; + } + if (!dst->data || (dst->data->size < src->data->numRects)) + { + xfreeData(dst); + Must_have_memory = TRUE; /* XXX */ + dst->data = xallocData(src->data->numRects); + Must_have_memory = FALSE; /* XXX */ + dst->data->size = src->data->numRects; + } + dst->data->numRects = src->data->numRects; + memmove((char *)REGION_BOXPTR(dst),(char *)REGION_BOXPTR(src), + dst->data->numRects * sizeof(BoxRec)); + return TRUE; +} + + +/*====================================================================== + * Generic Region Operator + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miCoalesce -- + * Attempt to merge the boxes in the current band with those in the + * previous one. We are guaranteed that the current band extends to + * the end of the rects array. Used only by miRegionOp. + * + * Results: + * The new index for the previous band. + * + * Side Effects: + * If coalescing takes place: + * - rectangles in the previous band will have their y2 fields + * altered. + * - pReg->data->numRects will be decreased. + * + *----------------------------------------------------------------------- + */ +INLINE static int +miCoalesce (pReg, prevStart, curStart) + register RegionPtr pReg; /* Region to coalesce */ + int prevStart; /* Index of start of previous band */ + int curStart; /* Index of start of current band */ +{ + register BoxPtr pPrevBox; /* Current box in previous band */ + register BoxPtr pCurBox; /* Current box in current band */ + register int numRects; /* Number rectangles in both bands */ + register int y2; /* Bottom of current band */ + /* + * Figure out how many rectangles are in the band. + */ + numRects = curStart - prevStart; + assert(numRects == pReg->data->numRects - curStart); + + if (!numRects) return curStart; + + /* + * The bands may only be coalesced if the bottom of the previous + * matches the top scanline of the current. + */ + pPrevBox = REGION_BOX(pReg, prevStart); + pCurBox = REGION_BOX(pReg, curStart); + if (pPrevBox->y2 != pCurBox->y1) return curStart; + + /* + * Make sure the bands have boxes in the same places. This + * assumes that boxes have been added in such a way that they + * cover the most area possible. I.e. two boxes in a band must + * have some horizontal space between them. + */ + y2 = pCurBox->y2; + + do { + if ((pPrevBox->x1 != pCurBox->x1) || (pPrevBox->x2 != pCurBox->x2)) { + return (curStart); + } + pPrevBox++; + pCurBox++; + numRects--; + } while (numRects); + + /* + * The bands may be merged, so set the bottom y of each box + * in the previous band to the bottom y of the current band. + */ + numRects = curStart - prevStart; + pReg->data->numRects -= numRects; + do { + pPrevBox--; + pPrevBox->y2 = y2; + numRects--; + } while (numRects); + return prevStart; +} + + +/* Quicky macro to avoid trivial reject procedure calls to miCoalesce */ + +#define Coalesce(newReg, prevBand, curBand) \ + if (curBand - prevBand == newReg->data->numRects - curBand) { \ + prevBand = miCoalesce(newReg, prevBand, curBand); \ + } else { \ + prevBand = curBand; \ + } + +/*- + *----------------------------------------------------------------------- + * miAppendNonO -- + * Handle a non-overlapping band for the union and subtract operations. + * Just adds the (top/bottom-clipped) rectangles into the region. + * Doesn't have to check for subsumption or anything. + * + * Results: + * None. + * + * Side Effects: + * pReg->data->numRects is incremented and the rectangles overwritten + * with the rectangles we're passed. + * + *----------------------------------------------------------------------- + */ + +INLINE static Bool +miAppendNonO (pReg, r, rEnd, y1, y2) + register RegionPtr pReg; + register BoxPtr r; + BoxPtr rEnd; + register int y1; + register int y2; +{ + register BoxPtr pNextRect; + register int newRects; + + newRects = rEnd - r; + + assert(y1 < y2); + assert(newRects != 0); + + /* Make sure we have enough space for all rectangles to be added */ + RECTALLOC(pReg, newRects); + pNextRect = REGION_TOP(pReg); + pReg->data->numRects += newRects; + do { + assert(r->x1 < r->x2); + ADDRECT(pNextRect, r->x1, y1, r->x2, y2); + r++; + } while (r != rEnd); + + return TRUE; +} + +#define FindBand(r, rBandEnd, rEnd, ry1) \ +{ \ + ry1 = r->y1; \ + rBandEnd = r+1; \ + while ((rBandEnd != rEnd) && (rBandEnd->y1 == ry1)) { \ + rBandEnd++; \ + } \ +} + +#define AppendRegions(newReg, r, rEnd) \ +{ \ + int newRects; \ + if (newRects = rEnd - r) { \ + RECTALLOC(newReg, newRects); \ + memmove((char *)REGION_TOP(newReg),(char *)r, \ + newRects * sizeof(BoxRec)); \ + newReg->data->numRects += newRects; \ + } \ +} + +/*- + *----------------------------------------------------------------------- + * miRegionOp -- + * Apply an operation to two regions. Called by miUnion, miInverse, + * miSubtract, miIntersect.... Both regions MUST have at least one + * rectangle, and cannot be the same object. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * The new region is overwritten. + * pOverlap set to TRUE if overlapFunc ever returns TRUE. + * + * Notes: + * The idea behind this function is to view the two regions as sets. + * Together they cover a rectangle of area that this function divides + * into horizontal bands where points are covered only by one region + * or by both. For the first case, the nonOverlapFunc is called with + * each the band and the band's upper and lower extents. For the + * second, the overlapFunc is called to process the entire band. It + * is responsible for clipping the rectangles in the band, though + * this function provides the boundaries. + * At the end of each band, the new region is coalesced, if possible, + * to reduce the number of rectangles in the region. + * + *----------------------------------------------------------------------- + */ +static Bool +miRegionOp(newReg, reg1, reg2, overlapFunc, appendNon1, appendNon2, pOverlap) + RegionPtr newReg; /* Place to store result */ + RegionPtr reg1; /* First region in operation */ + RegionPtr reg2; /* 2d region in operation */ + Bool (*overlapFunc)(); /* Function to call for over- + * lapping bands */ + Bool appendNon1; /* Append non-overlapping bands */ + /* in region 1 ? */ + Bool appendNon2; /* Append non-overlapping bands */ + /* in region 2 ? */ + Bool *pOverlap; +{ + register BoxPtr r1; /* Pointer into first region */ + register BoxPtr r2; /* Pointer into 2d region */ + BoxPtr r1End; /* End of 1st region */ + BoxPtr r2End; /* End of 2d region */ + short ybot; /* Bottom of intersection */ + short ytop; /* Top of intersection */ + RegDataPtr oldData; /* Old data for newReg */ + int prevBand; /* Index of start of + * previous band in newReg */ + int curBand; /* Index of start of current + * band in newReg */ + register BoxPtr r1BandEnd; /* End of current band in r1 */ + register BoxPtr r2BandEnd; /* End of current band in r2 */ + short top; /* Top of non-overlapping band */ + short bot; /* Bottom of non-overlapping band*/ + register int r1y1; /* Temps for r1->y1 and r2->y1 */ + register int r2y1; + int newSize; + int numRects; + + /* + * Initialization: + * set r1, r2, r1End and r2End appropriately, save the rectangles + * of the destination region until the end in case it's one of + * the two source regions, then mark the "new" region empty, allocating + * another array of rectangles for it to use. + */ + + r1 = REGION_RECTS(reg1); + newSize = REGION_NUM_RECTS(reg1); + r1End = r1 + newSize; + numRects = REGION_NUM_RECTS(reg2); + r2 = REGION_RECTS(reg2); + r2End = r2 + numRects; + assert(r1 != r1End); + assert(r2 != r2End); + + oldData = (RegDataPtr)NULL; + if (((newReg == reg1) && (newSize > 1)) || + ((newReg == reg2) && (numRects > 1))) + { + oldData = newReg->data; + newReg->data = &miEmptyData; + } + /* guess at new size */ + if (numRects > newSize) + newSize = numRects; + newSize <<= 1; + if (!newReg->data) + newReg->data = &miEmptyData; + else if (newReg->data->size) + newReg->data->numRects = 0; + if (newSize > newReg->data->size) + miRectAlloc(newReg, newSize); + + /* + * Initialize ybot. + * In the upcoming loop, ybot and ytop serve different functions depending + * on whether the band being handled is an overlapping or non-overlapping + * band. + * In the case of a non-overlapping band (only one of the regions + * has points in the band), ybot is the bottom of the most recent + * intersection and thus clips the top of the rectangles in that band. + * ytop is the top of the next intersection between the two regions and + * serves to clip the bottom of the rectangles in the current band. + * For an overlapping band (where the two regions intersect), ytop clips + * the top of the rectangles of both regions and ybot clips the bottoms. + */ + + ybot = min(r1->y1, r2->y1); + + /* + * prevBand serves to mark the start of the previous band so rectangles + * can be coalesced into larger rectangles. qv. miCoalesce, above. + * In the beginning, there is no previous band, so prevBand == curBand + * (curBand is set later on, of course, but the first band will always + * start at index 0). prevBand and curBand must be indices because of + * the possible expansion, and resultant moving, of the new region's + * array of rectangles. + */ + prevBand = 0; + + do { + /* + * This algorithm proceeds one source-band (as opposed to a + * destination band, which is determined by where the two regions + * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the + * rectangle after the last one in the current band for their + * respective regions. + */ + assert(r1 != r1End); + assert(r2 != r2End); + + FindBand(r1, r1BandEnd, r1End, r1y1); + FindBand(r2, r2BandEnd, r2End, r2y1); + + /* + * First handle the band that doesn't intersect, if any. + * + * Note that attention is restricted to one band in the + * non-intersecting region at once, so if a region has n + * bands between the current position and the next place it overlaps + * the other, this entire loop will be passed through n times. + */ + if (r1y1 < r2y1) { + if (appendNon1) { + top = max(r1y1, ybot); + bot = min(r1->y2, r2y1); + if (top != bot) { + curBand = newReg->data->numRects; + miAppendNonO(newReg, r1, r1BandEnd, top, bot); + Coalesce(newReg, prevBand, curBand); + } + } + ytop = r2y1; + } else if (r2y1 < r1y1) { + if (appendNon2) { + top = max(r2y1, ybot); + bot = min(r2->y2, r1y1); + if (top != bot) { + curBand = newReg->data->numRects; + miAppendNonO(newReg, r2, r2BandEnd, top, bot); + Coalesce(newReg, prevBand, curBand); + } + } + ytop = r1y1; + } else { + ytop = r1y1; + } + + /* + * Now see if we've hit an intersecting band. The two bands only + * intersect if ybot > ytop + */ + ybot = min(r1->y2, r2->y2); + if (ybot > ytop) { + curBand = newReg->data->numRects; + (* overlapFunc)(newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot, + pOverlap); + Coalesce(newReg, prevBand, curBand); + } + + /* + * If we've finished with a band (y2 == ybot) we skip forward + * in the region to the next band. + */ + if (r1->y2 == ybot) r1 = r1BandEnd; + if (r2->y2 == ybot) r2 = r2BandEnd; + + } while (r1 != r1End && r2 != r2End); + + /* + * Deal with whichever region (if any) still has rectangles left. + * + * We only need to worry about banding and coalescing for the very first + * band left. After that, we can just group all remaining boxes, + * regardless of how many bands, into one final append to the list. + */ + + if ((r1 != r1End) && appendNon1) { + /* Do first nonOverlap1Func call, which may be able to coalesce */ + FindBand(r1, r1BandEnd, r1End, r1y1); + curBand = newReg->data->numRects; + miAppendNonO(newReg, r1, r1BandEnd, max(r1y1, ybot), r1->y2); + Coalesce(newReg, prevBand, curBand); + /* Just append the rest of the boxes */ + AppendRegions(newReg, r1BandEnd, r1End); + + } else if ((r2 != r2End) && appendNon2) { + /* Do first nonOverlap2Func call, which may be able to coalesce */ + FindBand(r2, r2BandEnd, r2End, r2y1); + curBand = newReg->data->numRects; + miAppendNonO(newReg, r2, r2BandEnd, max(r2y1, ybot), r2->y2); + Coalesce(newReg, prevBand, curBand); + /* Append rest of boxes */ + AppendRegions(newReg, r2BandEnd, r2End); + } + + if (oldData) + xfree(oldData); + + if (!(numRects = newReg->data->numRects)) + { + xfreeData(newReg); + newReg->data = &miEmptyData; + } + else if (numRects == 1) + { + newReg->extents = *REGION_BOXPTR(newReg); + xfreeData(newReg); + newReg->data = (RegDataPtr)NULL; + } + else + { + DOWNSIZE(newReg, numRects); + } + + return TRUE; +} + +/*- + *----------------------------------------------------------------------- + * miSetExtents -- + * Reset the extents of a region to what they should be. Called by + * miSubtract and miIntersect as they can't figure it out along the + * way or do so easily, as miUnion can. + * + * Results: + * None. + * + * Side Effects: + * The region's 'extents' structure is overwritten. + * + *----------------------------------------------------------------------- + */ +void +miSetExtents (pReg) + register RegionPtr pReg; +{ + register BoxPtr pBox, pBoxEnd; + + if (!pReg->data) + return; + if (!pReg->data->size) + { + pReg->extents.x2 = pReg->extents.x1; + pReg->extents.y2 = pReg->extents.y1; + return; + } + + pBox = REGION_BOXPTR(pReg); + pBoxEnd = REGION_END(pReg); + + /* + * Since pBox is the first rectangle in the region, it must have the + * smallest y1 and since pBoxEnd is the last rectangle in the region, + * it must have the largest y2, because of banding. Initialize x1 and + * x2 from pBox and pBoxEnd, resp., as good things to initialize them + * to... + */ + pReg->extents.x1 = pBox->x1; + pReg->extents.y1 = pBox->y1; + pReg->extents.x2 = pBoxEnd->x2; + pReg->extents.y2 = pBoxEnd->y2; + + assert(pReg->extents.y1 < pReg->extents.y2); + while (pBox <= pBoxEnd) { + if (pBox->x1 < pReg->extents.x1) + pReg->extents.x1 = pBox->x1; + if (pBox->x2 > pReg->extents.x2) + pReg->extents.x2 = pBox->x2; + pBox++; + }; + + assert(pReg->extents.x1 < pReg->extents.x2); +} + +/*====================================================================== + * Region Intersection + *====================================================================*/ +/*- + *----------------------------------------------------------------------- + * miIntersectO -- + * Handle an overlapping band for miIntersect. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * Rectangles may be added to the region. + * + *----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static Bool +miIntersectO (pReg, r1, r1End, r2, r2End, y1, y2, pOverlap) + register RegionPtr pReg; + register BoxPtr r1; + BoxPtr r1End; + register BoxPtr r2; + BoxPtr r2End; + short y1; + short y2; + Bool *pOverlap; +{ + register int x1; + register int x2; + register BoxPtr pNextRect; + + pNextRect = REGION_TOP(pReg); + + assert(y1 < y2); + assert(r1 != r1End && r2 != r2End); + + do { + x1 = max(r1->x1, r2->x1); + x2 = min(r1->x2, r2->x2); + + /* + * If there's any overlap between the two rectangles, add that + * overlap to the new region. + */ + if (x1 < x2) + NEWRECT(pReg, pNextRect, x1, y1, x2, y2); + + /* + * Advance the pointer(s) with the leftmost right side, since the next + * rectangle on that list may still overlap the other region's + * current rectangle. + */ + if (r1->x2 == x2) { + r1++; + } + if (r2->x2 == x2) { + r2++; + } + } while ((r1 != r1End) && (r2 != r2End)); + + return TRUE; +} + + +Bool +miIntersect(newReg, reg1, reg2) + register RegionPtr newReg; /* destination Region */ + register RegionPtr reg1; + register RegionPtr reg2; /* source regions */ +{ + good(reg1); + good(reg2); + good(newReg); + /* check for trivial reject */ + if (REGION_NIL(reg1) || REGION_NIL(reg2) || + !EXTENTCHECK(®1->extents, ®2->extents)) + { + /* Covers about 20% of all cases */ + xfreeData(newReg); + newReg->extents.x2 = newReg->extents.x1; + newReg->extents.y2 = newReg->extents.y1; + newReg->data = &miEmptyData; + } + else if (!reg1->data && !reg2->data) + { + /* Covers about 80% of cases that aren't trivially rejected */ + newReg->extents.x1 = max(reg1->extents.x1, reg2->extents.x1); + newReg->extents.y1 = max(reg1->extents.y1, reg2->extents.y1); + newReg->extents.x2 = min(reg1->extents.x2, reg2->extents.x2); + newReg->extents.y2 = min(reg1->extents.y2, reg2->extents.y2); + xfreeData(newReg); + newReg->data = (RegDataPtr)NULL; + } + else if (!reg2->data && SUBSUMES(®2->extents, ®1->extents)) + { + return miRegionCopy(newReg, reg1); + } + else if (!reg1->data && SUBSUMES(®1->extents, ®2->extents)) + { + return miRegionCopy(newReg, reg2); + } + else if (reg1 == reg2) + { + return miRegionCopy(newReg, reg1); + } + else + { + /* General purpose intersection */ + Bool overlap; /* result ignored */ + if (!miRegionOp(newReg, reg1, reg2, miIntersectO, FALSE, FALSE, + &overlap)) + return FALSE; + miSetExtents(newReg); + } + + good(newReg); + return(TRUE); +} + +#define MERGERECT(r) \ +{ \ + if (r->x1 <= x2) { \ + /* Merge with current rectangle */ \ + if (r->x1 < x2) *pOverlap = TRUE; \ + if (x2 < r->x2) x2 = r->x2; \ + } else { \ + /* Add current rectangle, start new one */ \ + NEWRECT(pReg, pNextRect, x1, y1, x2, y2); \ + x1 = r->x1; \ + x2 = r->x2; \ + } \ + r++; \ +} + +/*====================================================================== + * Region Union + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miUnionO -- + * Handle an overlapping band for the union operation. Picks the + * left-most rectangle each time and merges it into the region. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * pReg is overwritten. + * pOverlap is set to TRUE if any boxes overlap. + * + *----------------------------------------------------------------------- + */ +static Bool +miUnionO (pReg, r1, r1End, r2, r2End, y1, y2, pOverlap) + register RegionPtr pReg; + register BoxPtr r1; + BoxPtr r1End; + register BoxPtr r2; + BoxPtr r2End; + short y1; + short y2; + Bool *pOverlap; +{ + register BoxPtr pNextRect; + register int x1; /* left and right side of current union */ + register int x2; + + assert (y1 < y2); + assert(r1 != r1End && r2 != r2End); + + pNextRect = REGION_TOP(pReg); + + /* Start off current rectangle */ + if (r1->x1 < r2->x1) + { + x1 = r1->x1; + x2 = r1->x2; + r1++; + } + else + { + x1 = r2->x1; + x2 = r2->x2; + r2++; + } + while (r1 != r1End && r2 != r2End) + { + if (r1->x1 < r2->x1) MERGERECT(r1) else MERGERECT(r2); + } + + /* Finish off whoever (if any) is left */ + if (r1 != r1End) + { + do + { + MERGERECT(r1); + } while (r1 != r1End); + } + else if (r2 != r2End) + { + do + { + MERGERECT(r2); + } while (r2 != r2End); + } + + /* Add current rectangle */ + NEWRECT(pReg, pNextRect, x1, y1, x2, y2); + + return TRUE; +} + +Bool +miUnion(newReg, reg1, reg2) + RegionPtr newReg; /* destination Region */ + register RegionPtr reg1; + register RegionPtr reg2; /* source regions */ +{ + Bool overlap; /* result ignored */ + + /* Return TRUE if some overlap between reg1, reg2 */ + good(reg1); + good(reg2); + good(newReg); + /* checks all the simple cases */ + + /* + * Region 1 and 2 are the same + */ + if (reg1 == reg2) + { + return miRegionCopy(newReg, reg1); + } + + /* + * Region 1 is empty + */ + if (REGION_NIL(reg1)) + { + if (newReg != reg2) + return miRegionCopy(newReg, reg2); + return TRUE; + } + + /* + * Region 2 is empty + */ + if (REGION_NIL(reg2)) + { + if (newReg != reg1) + return miRegionCopy(newReg, reg1); + return TRUE; + } + + /* + * Region 1 completely subsumes region 2 + */ + if (!reg1->data && SUBSUMES(®1->extents, ®2->extents)) + { + if (newReg != reg1) + return miRegionCopy(newReg, reg1); + return TRUE; + } + + /* + * Region 2 completely subsumes region 1 + */ + if (!reg2->data && SUBSUMES(®2->extents, ®1->extents)) + { + if (newReg != reg2) + return miRegionCopy(newReg, reg2); + return TRUE; + } + + if (!miRegionOp(newReg, reg1, reg2, miUnionO, TRUE, TRUE, &overlap)) + return FALSE; + + newReg->extents.x1 = min(reg1->extents.x1, reg2->extents.x1); + newReg->extents.y1 = min(reg1->extents.y1, reg2->extents.y1); + newReg->extents.x2 = max(reg1->extents.x2, reg2->extents.x2); + newReg->extents.y2 = max(reg1->extents.y2, reg2->extents.y2); + good(newReg); + return TRUE; +} + + +/*====================================================================== + * Batch Rectangle Union + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miRegionAppend -- + * + * "Append" the rgn rectangles onto the end of dstrgn, maintaining + * knowledge of YX-banding when it's easy. Otherwise, dstrgn just + * becomes a non-y-x-banded random collection of rectangles, and not + * yet a true region. After a sequence of appends, the caller must + * call miRegionValidate to ensure that a valid region is constructed. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * dstrgn is modified if rgn has rectangles. + * + */ +Bool +miRegionAppend(dstrgn, rgn) + register RegionPtr dstrgn; + register RegionPtr rgn; +{ + int numRects, dnumRects, size; + BoxPtr new, old; + Bool prepend; + + if (!rgn->data && (dstrgn->data == &miEmptyData)) + { + dstrgn->extents = rgn->extents; + dstrgn->data = (RegDataPtr)NULL; + return TRUE; + } + + numRects = REGION_NUM_RECTS(rgn); + if (!numRects) + return TRUE; + prepend = FALSE; + size = numRects; + dnumRects = REGION_NUM_RECTS(dstrgn); + if (!dnumRects && (size < 200)) + size = 200; /* XXX pick numbers out of a hat */ + RECTALLOC(dstrgn, size); + old = REGION_RECTS(rgn); + if (!dnumRects) + dstrgn->extents = rgn->extents; + else if (dstrgn->extents.x2 > dstrgn->extents.x1) + { + register BoxPtr first, last; + + first = old; + last = REGION_BOXPTR(dstrgn) + (dnumRects - 1); + if ((first->y1 > last->y2) || + ((first->y1 == last->y1) && (first->y2 == last->y2) && + (first->x1 > last->x2))) + { + if (rgn->extents.x1 < dstrgn->extents.x1) + dstrgn->extents.x1 = rgn->extents.x1; + if (rgn->extents.x2 > dstrgn->extents.x2) + dstrgn->extents.x2 = rgn->extents.x2; + dstrgn->extents.y2 = rgn->extents.y2; + } + else + { + first = REGION_BOXPTR(dstrgn); + last = old + (numRects - 1); + if ((first->y1 > last->y2) || + ((first->y1 == last->y1) && (first->y2 == last->y2) && + (first->x1 > last->x2))) + { + prepend = TRUE; + if (rgn->extents.x1 < dstrgn->extents.x1) + dstrgn->extents.x1 = rgn->extents.x1; + if (rgn->extents.x2 > dstrgn->extents.x2) + dstrgn->extents.x2 = rgn->extents.x2; + dstrgn->extents.y1 = rgn->extents.y1; + } + else + dstrgn->extents.x2 = dstrgn->extents.x1; + } + } + if (prepend) + { + new = REGION_BOX(dstrgn, numRects); + if (dnumRects == 1) + *new = *REGION_BOXPTR(dstrgn); + else + memmove((char *)new,(char *)REGION_BOXPTR(dstrgn), + dnumRects * sizeof(BoxRec)); + new = REGION_BOXPTR(dstrgn); + } + else + new = REGION_BOXPTR(dstrgn) + dnumRects; + if (numRects == 1) + *new = *old; + else + memmove((char *)new, (char *)old, numRects * sizeof(BoxRec)); + dstrgn->data->numRects += numRects; + return TRUE; +} + + +#define ExchangeRects(a, b) \ +{ \ + BoxRec t; \ + t = rects[a]; \ + rects[a] = rects[b]; \ + rects[b] = t; \ +} + +static void +QuickSortRects(rects, numRects) + register BoxRec rects[]; + register int numRects; +{ + register int y1; + register int x1; + register int i, j; + register BoxPtr r; + + /* Always called with numRects > 1 */ + + do + { + if (numRects == 2) + { + if (rects[0].y1 > rects[1].y1 || + (rects[0].y1 == rects[1].y1 && rects[0].x1 > rects[1].x1)) + ExchangeRects(0, 1); + return; + } + + /* Choose partition element, stick in location 0 */ + ExchangeRects(0, numRects >> 1); + y1 = rects[0].y1; + x1 = rects[0].x1; + + /* Partition array */ + i = 0; + j = numRects; + do + { + r = &(rects[i]); + do + { + r++; + i++; + } while (i != numRects && + (r->y1 < y1 || (r->y1 == y1 && r->x1 < x1))); + r = &(rects[j]); + do + { + r--; + j--; + } while (y1 < r->y1 || (y1 == r->y1 && x1 < r->x1)); + if (i < j) + ExchangeRects(i, j); + } while (i < j); + + /* Move partition element back to middle */ + ExchangeRects(0, j); + + /* Recurse */ + if (numRects-j-1 > 1) + QuickSortRects(&rects[j+1], numRects-j-1); + numRects = j; + } while (numRects > 1); +} + +/*- + *----------------------------------------------------------------------- + * miRegionValidate -- + * + * Take a ``region'' which is a non-y-x-banded random collection of + * rectangles, and compute a nice region which is the union of all the + * rectangles. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * The passed-in ``region'' may be modified. + * pOverlap set to TRUE if any retangles overlapped, else FALSE; + * + * Strategy: + * Step 1. Sort the rectangles into ascending order with primary key y1 + * and secondary key x1. + * + * Step 2. Split the rectangles into the minimum number of proper y-x + * banded regions. This may require horizontally merging + * rectangles, and vertically coalescing bands. With any luck, + * this step in an identity tranformation (ala the Box widget), + * or a coalescing into 1 box (ala Menus). + * + * Step 3. Merge the separate regions down to a single region by calling + * miUnion. Maximize the work each miUnion call does by using + * a binary merge. + * + *----------------------------------------------------------------------- + */ + +Bool +miRegionValidate(badreg, pOverlap) + RegionPtr badreg; + Bool *pOverlap; +{ + /* Descriptor for regions under construction in Step 2. */ + typedef struct { + RegionRec reg; + int prevBand; + int curBand; + } RegionInfo; + + int numRects; /* Original numRects for badreg */ + RegionInfo *ri; /* Array of current regions */ + int numRI; /* Number of entries used in ri */ + int sizeRI; /* Number of entries available in ri */ + int i; /* Index into rects */ + register int j; /* Index into ri */ + register RegionInfo *rit; /* &ri[j] */ + register RegionPtr reg; /* ri[j].reg */ + register BoxPtr box; /* Current box in rects */ + register BoxPtr riBox; /* Last box in ri[j].reg */ + register RegionPtr hreg; /* ri[j_half].reg */ + + *pOverlap = FALSE; + if (!badreg->data) + { + good(badreg); + return TRUE; + } + numRects = badreg->data->numRects; + if (!numRects) + { + good(badreg); + return TRUE; + } + if (badreg->extents.x1 < badreg->extents.x2) + { + if ((numRects) == 1) + { + xfreeData(badreg); + badreg->data = (RegDataPtr) NULL; + } + else + { + DOWNSIZE(badreg, numRects); + } + good(badreg); + return TRUE; + } + + /* Step 1: Sort the rects array into ascending (y1, x1) order */ + QuickSortRects(REGION_BOXPTR(badreg), numRects); + + /* Step 2: Scatter the sorted array into the minimum number of regions */ + + /* Set up the first region to be the first rectangle in badreg */ + /* Note that step 2 code will never overflow the ri[0].reg rects array */ + Must_have_memory = TRUE; /* XXX */ + ri = (RegionInfo *) xalloc(4 * sizeof(RegionInfo)); + Must_have_memory = FALSE; /* XXX */ + sizeRI = 4; + numRI = 1; + ri[0].prevBand = 0; + ri[0].curBand = 0; + ri[0].reg = *badreg; + box = REGION_BOXPTR(&ri[0].reg); + ri[0].reg.extents = *box; + ri[0].reg.data->numRects = 1; + + /* Now scatter rectangles into the minimum set of valid regions. If the + next rectangle to be added to a region would force an existing rectangle + in the region to be split up in order to maintain y-x banding, just + forget it. Try the next region. If it doesn't fit cleanly into any + region, make a new one. */ + + for (i = numRects; --i > 0;) + { + box++; + /* Look for a region to append box to */ + for (j = numRI, rit = ri; --j >= 0; rit++) + { + reg = &rit->reg; + riBox = REGION_END(reg); + + if (box->y1 == riBox->y1 && box->y2 == riBox->y2) + { + /* box is in same band as riBox. Merge or append it */ + if (box->x1 <= riBox->x2) + { + /* Merge it with riBox */ + if (box->x1 < riBox->x2) *pOverlap = TRUE; + if (box->x2 > riBox->x2) riBox->x2 = box->x2; + } + else + { + RECTALLOC(reg, 1); + *REGION_TOP(reg) = *box; + reg->data->numRects++; + } + goto NextRect; /* So sue me */ + } + else if (box->y1 >= riBox->y2) + { + /* Put box into new band */ + if (reg->extents.x2 < riBox->x2) reg->extents.x2 = riBox->x2; + if (reg->extents.x1 > box->x1) reg->extents.x1 = box->x1; + Coalesce(reg, rit->prevBand, rit->curBand); + rit->curBand = reg->data->numRects; + RECTALLOC(reg, 1); + *REGION_TOP(reg) = *box; + reg->data->numRects++; + goto NextRect; + } + /* Well, this region was inappropriate. Try the next one. */ + } /* for j */ + + /* Uh-oh. No regions were appropriate. Create a new one. */ + if (sizeRI == numRI) + { + /* Oops, allocate space for new region information */ + sizeRI <<= 1; + Must_have_memory = TRUE; /* XXX */ + ri = (RegionInfo *) xrealloc(ri, sizeRI * sizeof(RegionInfo)); + Must_have_memory = FALSE; /* XXX */ + rit = &ri[numRI]; + } + numRI++; + rit->prevBand = 0; + rit->curBand = 0; + rit->reg.extents = *box; + rit->reg.data = (RegDataPtr)NULL; + miRectAlloc(&rit->reg, (i+numRI) / numRI); /* MUST force allocation */ +NextRect: ; + } /* for i */ + + /* Make a final pass over each region in order to Coalesce and set + extents.x2 and extents.y2 */ + + for (j = numRI, rit = ri; --j >= 0; rit++) + { + reg = &rit->reg; + riBox = REGION_END(reg); + reg->extents.y2 = riBox->y2; + if (reg->extents.x2 < riBox->x2) reg->extents.x2 = riBox->x2; + Coalesce(reg, rit->prevBand, rit->curBand); + if (reg->data->numRects == 1) /* keep unions happy below */ + { + xfreeData(reg); + reg->data = (RegDataPtr)NULL; + } + } + + /* Step 3: Union all regions into a single region */ + while (numRI > 1) + { + int half = numRI/2; + for (j = numRI & 1; j < (half + (numRI & 1)); j++) + { + reg = &ri[j].reg; + hreg = &ri[j+half].reg; + miRegionOp(reg, reg, hreg, miUnionO, TRUE, TRUE, pOverlap); + if (hreg->extents.x1 < reg->extents.x1) + reg->extents.x1 = hreg->extents.x1; + if (hreg->extents.y1 < reg->extents.y1) + reg->extents.y1 = hreg->extents.y1; + if (hreg->extents.x2 > reg->extents.x2) + reg->extents.x2 = hreg->extents.x2; + if (hreg->extents.y2 > reg->extents.y2) + reg->extents.y2 = hreg->extents.y2; + xfreeData(hreg); + } + numRI -= half; + } + *badreg = ri[0].reg; + xfree(ri); + good(badreg); + return TRUE; +} + +RegionPtr +miRectsToRegion(nrects, prect, ctype) + int nrects; + register xRectangle *prect; + int ctype; +{ + register RegionPtr pRgn; + register RegDataPtr pData; + register BoxPtr pBox; + register int i; + int x1, y1, x2, y2; + + pRgn = miRegionCreate(NullBox, 0); + if (!nrects) + return pRgn; + if (nrects == 1) + { + x1 = prect->x; + y1 = prect->y; + if ((x2 = x1 + (int) prect->width) > MAXSHORT) + x2 = MAXSHORT; + if ((y2 = y1 + (int) prect->height) > MAXSHORT) + y2 = MAXSHORT; + if (x1 != x2 && y1 != y2) + { + pRgn->extents.x1 = x1; + pRgn->extents.y1 = y1; + pRgn->extents.x2 = x2; + pRgn->extents.y2 = y2; + pRgn->data = (RegDataPtr)NULL; + } + return pRgn; + } + Must_have_memory = TRUE; /* XXX */ + pData = xallocData(nrects); + pBox = (BoxPtr) (pData + 1); + Must_have_memory = FALSE; /* XXX */ + for (i = nrects; --i >= 0; prect++) + { + x1 = prect->x; + y1 = prect->y; + if ((x2 = x1 + (int) prect->width) > MAXSHORT) + x2 = MAXSHORT; + if ((y2 = y1 + (int) prect->height) > MAXSHORT) + y2 = MAXSHORT; + if (x1 != x2 && y1 != y2) + { + pBox->x1 = x1; + pBox->y1 = y1; + pBox->x2 = x2; + pBox->y2 = y2; + pBox++; + } + } + if (pBox != (BoxPtr) (pData + 1)) + { + pData->size = nrects; + pData->numRects = pBox - (BoxPtr) (pData + 1); + pRgn->data = pData; + if (ctype != CT_YXBANDED) + { + Bool overlap; /* result ignored */ + pRgn->extents.x1 = pRgn->extents.x2 = 0; + miRegionValidate(pRgn, &overlap); + } + else + miSetExtents(pRgn); + good(pRgn); + } + else + { + xfree (pData); + } + return pRgn; +} + +/*====================================================================== + * Region Subtraction + *====================================================================*/ + + +/*- + *----------------------------------------------------------------------- + * miSubtractO -- + * Overlapping band subtraction. x1 is the left-most point not yet + * checked. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * pReg may have rectangles added to it. + * + *----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static Bool +miSubtractO (pReg, r1, r1End, r2, r2End, y1, y2, pOverlap) + register RegionPtr pReg; + register BoxPtr r1; + BoxPtr r1End; + register BoxPtr r2; + BoxPtr r2End; + register int y1; + int y2; + Bool *pOverlap; +{ + register BoxPtr pNextRect; + register int x1; + + x1 = r1->x1; + + assert(y1<y2); + assert(r1 != r1End && r2 != r2End); + + pNextRect = REGION_TOP(pReg); + + do + { + if (r2->x2 <= x1) + { + /* + * Subtrahend entirely to left of minuend: go to next subtrahend. + */ + r2++; + } + else if (r2->x1 <= x1) + { + /* + * Subtrahend preceeds minuend: nuke left edge of minuend. + */ + x1 = r2->x2; + if (x1 >= r1->x2) + { + /* + * Minuend completely covered: advance to next minuend and + * reset left fence to edge of new minuend. + */ + r1++; + if (r1 != r1End) + x1 = r1->x1; + } + else + { + /* + * Subtrahend now used up since it doesn't extend beyond + * minuend + */ + r2++; + } + } + else if (r2->x1 < r1->x2) + { + /* + * Left part of subtrahend covers part of minuend: add uncovered + * part of minuend to region and skip to next subtrahend. + */ + assert(x1<r2->x1); + NEWRECT(pReg, pNextRect, x1, y1, r2->x1, y2); + + x1 = r2->x2; + if (x1 >= r1->x2) + { + /* + * Minuend used up: advance to new... + */ + r1++; + if (r1 != r1End) + x1 = r1->x1; + } + else + { + /* + * Subtrahend used up + */ + r2++; + } + } + else + { + /* + * Minuend used up: add any remaining piece before advancing. + */ + if (r1->x2 > x1) + NEWRECT(pReg, pNextRect, x1, y1, r1->x2, y2); + r1++; + if (r1 != r1End) + x1 = r1->x1; + } + } while ((r1 != r1End) && (r2 != r2End)); + + + /* + * Add remaining minuend rectangles to region. + */ + while (r1 != r1End) + { + assert(x1<r1->x2); + NEWRECT(pReg, pNextRect, x1, y1, r1->x2, y2); + r1++; + if (r1 != r1End) + x1 = r1->x1; + } + return TRUE; +} + +/*- + *----------------------------------------------------------------------- + * miSubtract -- + * Subtract regS from regM and leave the result in regD. + * S stands for subtrahend, M for minuend and D for difference. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * regD is overwritten. + * + *----------------------------------------------------------------------- + */ +Bool +miSubtract(regD, regM, regS) + register RegionPtr regD; + register RegionPtr regM; + register RegionPtr regS; +{ + Bool overlap; /* result ignored */ + + good(regM); + good(regS); + good(regD); + /* check for trivial rejects */ + if (REGION_NIL(regM) || REGION_NIL(regS) || + !EXTENTCHECK(®M->extents, ®S->extents)) + { + return miRegionCopy(regD, regM); + } + else if (regM == regS) + { + xfreeData(regD); + regD->extents.x2 = regD->extents.x1; + regD->extents.y2 = regD->extents.y1; + regD->data = &miEmptyData; + return TRUE; + } + + /* Add those rectangles in region 1 that aren't in region 2, + do yucky substraction for overlaps, and + just throw away rectangles in region 2 that aren't in region 1 */ + if (!miRegionOp(regD, regM, regS, miSubtractO, TRUE, FALSE, &overlap)) + return FALSE; + + /* + * Can't alter RegD's extents before we call miRegionOp because + * it might be one of the source regions and miRegionOp depends + * on the extents of those regions being unaltered. Besides, this + * way there's no checking against rectangles that will be nuked + * due to coalescing, so we have to examine fewer rectangles. + */ + miSetExtents(regD); + good(regD); + return TRUE; +} + +/*====================================================================== + * Region Inversion + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miInverse -- + * Take a region and a box and return a region that is everything + * in the box but not in the region. The careful reader will note + * that this is the same as subtracting the region from the box... + * + * Results: + * TRUE. + * + * Side Effects: + * newReg is overwritten. + * + *----------------------------------------------------------------------- + */ +Bool +miInverse(newReg, reg1, invRect) + RegionPtr newReg; /* Destination region */ + RegionPtr reg1; /* Region to invert */ + BoxPtr invRect; /* Bounding box for inversion */ +{ + RegionRec invReg; /* Quick and dirty region made from the + * bounding box */ + Bool overlap; /* result ignored */ + + good(reg1); + good(newReg); + /* check for trivial rejects */ + if (REGION_NIL(reg1) || !EXTENTCHECK(invRect, ®1->extents)) + { + newReg->extents = *invRect; + xfreeData(newReg); + newReg->data = (RegDataPtr)NULL; + return TRUE; + } + + /* Add those rectangles in region 1 that aren't in region 2, + do yucky substraction for overlaps, and + just throw away rectangles in region 2 that aren't in region 1 */ + invReg.extents = *invRect; + invReg.data = (RegDataPtr)NULL; + if (!miRegionOp(newReg, &invReg, reg1, miSubtractO, TRUE, FALSE, &overlap)) + return FALSE; + + /* + * Can't alter newReg's extents before we call miRegionOp because + * it might be one of the source regions and miRegionOp depends + * on the extents of those regions being unaltered. Besides, this + * way there's no checking against rectangles that will be nuked + * due to coalescing, so we have to examine fewer rectangles. + */ + miSetExtents(newReg); + good(newReg); + return TRUE; +} + +/* + * RectIn(region, rect) + * This routine takes a pointer to a region and a pointer to a box + * and determines if the box is outside/inside/partly inside the region. + * + * The idea is to travel through the list of rectangles trying to cover the + * passed box with them. Anytime a piece of the rectangle isn't covered + * by a band of rectangles, partOut is set TRUE. Any time a rectangle in + * the region covers part of the box, partIn is set TRUE. The process ends + * when either the box has been completely covered (we reached a band that + * doesn't overlap the box, partIn is TRUE and partOut is false), the + * box has been partially covered (partIn == partOut == TRUE -- because of + * the banding, the first time this is true we know the box is only + * partially in the region) or is outside the region (we reached a band + * that doesn't overlap the box at all and partIn is false) + */ + +int +miRectIn(region, prect) + register RegionPtr region; + register BoxPtr prect; +{ + register int x; + register int y; + register BoxPtr pbox; + register BoxPtr pboxEnd; + int partIn, partOut; + int numRects; + + good(region); + numRects = REGION_NUM_RECTS(region); + /* useful optimization */ + if (!numRects || !EXTENTCHECK(®ion->extents, prect)) + return(rgnOUT); + + if (numRects == 1) + { + /* We know that it must be rgnIN or rgnPART */ + if (SUBSUMES(®ion->extents, prect)) + return(rgnIN); + else + return(rgnPART); + } + + partOut = FALSE; + partIn = FALSE; + + /* (x,y) starts at upper left of rect, moving to the right and down */ + x = prect->x1; + y = prect->y1; + + /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */ + for (pbox = REGION_BOXPTR(region), pboxEnd = pbox + numRects; + pbox != pboxEnd; + pbox++) + { + + if (pbox->y2 <= y) + continue; /* getting up to speed or skipping remainder of band */ + + if (pbox->y1 > y) + { + partOut = TRUE; /* missed part of rectangle above */ + if (partIn || (pbox->y1 >= prect->y2)) + break; + y = pbox->y1; /* x guaranteed to be == prect->x1 */ + } + + if (pbox->x2 <= x) + continue; /* not far enough over yet */ + + if (pbox->x1 > x) + { + partOut = TRUE; /* missed part of rectangle to left */ + if (partIn) + break; + } + + if (pbox->x1 < prect->x2) + { + partIn = TRUE; /* definitely overlap */ + if (partOut) + break; + } + + if (pbox->x2 >= prect->x2) + { + y = pbox->y2; /* finished with this band */ + if (y >= prect->y2) + break; + x = prect->x1; /* reset x out to left again */ + } + else + { + /* + * Because boxes in a band are maximal width, if the first box + * to overlap the rectangle doesn't completely cover it in that + * band, the rectangle must be partially out, since some of it + * will be uncovered in that band. partIn will have been set true + * by now... + */ + partOut = TRUE; + break; + } + } + + return(partIn ? ((y < prect->y2) ? rgnPART : rgnIN) : rgnOUT); +} + +/* TranslateRegion(pReg, x, y) + translates in place +*/ + +void +miTranslateRegion(pReg, x, y) + register RegionPtr pReg; + register int x; + register int y; +{ + int x1, x2, y1, y2; + register int nbox; + register BoxPtr pbox; + + good(pReg); + pReg->extents.x1 = x1 = pReg->extents.x1 + x; + pReg->extents.y1 = y1 = pReg->extents.y1 + y; + pReg->extents.x2 = x2 = pReg->extents.x2 + x; + pReg->extents.y2 = y2 = pReg->extents.y2 + y; + if (((x1 - MINSHORT)|(y1 - MINSHORT)|(MAXSHORT - x2)|(MAXSHORT - y2)) >= 0) + { + if (pReg->data && (nbox = pReg->data->numRects)) + { + for (pbox = REGION_BOXPTR(pReg); nbox--; pbox++) + { + pbox->x1 += x; + pbox->y1 += y; + pbox->x2 += x; + pbox->y2 += y; + } + } + return; + } + if (((x2 - MINSHORT)|(y2 - MINSHORT)|(MAXSHORT - x1)|(MAXSHORT - y1)) <= 0) + { + pReg->extents.x2 = pReg->extents.x1; + pReg->extents.y2 = pReg->extents.y1; + xfreeData(pReg); + pReg->data = &miEmptyData; + return; + } + if (x1 < MINSHORT) + pReg->extents.x1 = MINSHORT; + else if (x2 > MAXSHORT) + pReg->extents.x2 = MAXSHORT; + if (y1 < MINSHORT) + pReg->extents.y1 = MINSHORT; + else if (y2 > MAXSHORT) + pReg->extents.y2 = MAXSHORT; + if (pReg->data && (nbox = pReg->data->numRects)) + { + register BoxPtr pboxout; + + for (pboxout = pbox = REGION_BOXPTR(pReg); nbox--; pbox++) + { + pboxout->x1 = x1 = pbox->x1 + x; + pboxout->y1 = y1 = pbox->y1 + y; + pboxout->x2 = x2 = pbox->x2 + x; + pboxout->y2 = y2 = pbox->y2 + y; + if (((x2 - MINSHORT)|(y2 - MINSHORT)| + (MAXSHORT - x1)|(MAXSHORT - y1)) <= 0) + { + pReg->data->numRects--; + continue; + } + if (x1 < MINSHORT) + pboxout->x1 = MINSHORT; + else if (x2 > MAXSHORT) + pboxout->x2 = MAXSHORT; + if (y1 < MINSHORT) + pboxout->y1 = MINSHORT; + else if (y2 > MAXSHORT) + pboxout->y2 = MAXSHORT; + pboxout++; + } + if (pboxout != pbox) + { + if (pReg->data->numRects == 1) + { + pReg->extents = *REGION_BOXPTR(pReg); + xfreeData(pReg); + pReg->data = (RegDataPtr)NULL; + } + else + miSetExtents(pReg); + } + } +} + +void +miRegionReset(pReg, pBox) + RegionPtr pReg; + BoxPtr pBox; +{ + good(pReg); + assert(pBox->x1<=pBox->x2); + assert(pBox->y1<=pBox->y2); + pReg->extents = *pBox; + xfreeData(pReg); + pReg->data = (RegDataPtr)NULL; +} + +Bool +miPointInRegion(pReg, x, y, box) + register RegionPtr pReg; + register int x, y; + BoxPtr box; /* "return" value */ +{ + register BoxPtr pbox, pboxEnd; + int numRects; + + good(pReg); + numRects = REGION_NUM_RECTS(pReg); + if (!numRects || !INBOX(&pReg->extents, x, y)) + return(FALSE); + if (numRects == 1) + { + *box = pReg->extents; + return(TRUE); + } + for (pbox = REGION_BOXPTR(pReg), pboxEnd = pbox + numRects; + pbox != pboxEnd; + pbox++) + { + if (y >= pbox->y2) + continue; /* not there yet */ + if ((y < pbox->y1) || (x < pbox->x1)) + break; /* missed it */ + if (x >= pbox->x2) + continue; /* not there yet */ + *box = *pbox; + return(TRUE); + } + return(FALSE); +} + +Bool +miRegionNotEmpty(pReg) + RegionPtr pReg; +{ + good(pReg); + return(!REGION_NIL(pReg)); +} + + +void +miRegionEmpty(pReg) + RegionPtr pReg; +{ + good(pReg); + xfreeData(pReg); + pReg->extents.x2 = pReg->extents.x1; + pReg->extents.y2 = pReg->extents.y1; + pReg->data = &miEmptyData; +} + +BoxPtr +miRegionExtents(pReg) + RegionPtr pReg; +{ + good(pReg); + return(&pReg->extents); +} + +#define ExchangeSpans(a, b) \ +{ \ + DDXPointRec tpt; \ + register int tw; \ + \ + tpt = spans[a]; spans[a] = spans[b]; spans[b] = tpt; \ + tw = widths[a]; widths[a] = widths[b]; widths[b] = tw; \ +} + +/* ||| I should apply the merge sort code to rectangle sorting above, and see + if mapping time can be improved. But right now I've been at work 12 hours, + so forget it. +*/ + +static void QuickSortSpans(spans, widths, numSpans) + register DDXPointRec spans[]; + register int widths[]; + register int numSpans; +{ + register int y; + register int i, j, m; + register DDXPointPtr r; + + /* Always called with numSpans > 1 */ + /* Sorts only by y, doesn't bother to sort by x */ + + do + { + if (numSpans < 9) + { + /* Do insertion sort */ + register int yprev; + + yprev = spans[0].y; + i = 1; + do + { /* while i != numSpans */ + y = spans[i].y; + if (yprev > y) + { + /* spans[i] is out of order. Move into proper location. */ + DDXPointRec tpt; + int tw, k; + + for (j = 0; y >= spans[j].y; j++) {} + tpt = spans[i]; + tw = widths[i]; + for (k = i; k != j; k--) + { + spans[k] = spans[k-1]; + widths[k] = widths[k-1]; + } + spans[j] = tpt; + widths[j] = tw; + y = spans[i].y; + } /* if out of order */ + yprev = y; + i++; + } while (i != numSpans); + return; + } + + /* Choose partition element, stick in location 0 */ + m = numSpans / 2; + if (spans[m].y > spans[0].y) ExchangeSpans(m, 0); + if (spans[m].y > spans[numSpans-1].y) ExchangeSpans(m, numSpans-1); + if (spans[m].y > spans[0].y) ExchangeSpans(m, 0); + y = spans[0].y; + + /* Partition array */ + i = 0; + j = numSpans; + do + { + r = &(spans[i]); + do + { + r++; + i++; + } while (i != numSpans && r->y < y); + r = &(spans[j]); + do + { + r--; + j--; + } while (y < r->y); + if (i < j) + ExchangeSpans(i, j); + } while (i < j); + + /* Move partition element back to middle */ + ExchangeSpans(0, j); + + /* Recurse */ + if (numSpans-j-1 > 1) + QuickSortSpans(&spans[j+1], &widths[j+1], numSpans-j-1); + numSpans = j; + } while (numSpans > 1); +} + +#define NextBand() \ +{ \ + clipy1 = pboxBandStart->y1; \ + clipy2 = pboxBandStart->y2; \ + pboxBandEnd = pboxBandStart + 1; \ + while (pboxBandEnd != pboxLast && pboxBandEnd->y1 == clipy1) { \ + pboxBandEnd++; \ + } \ + for (; ppt != pptLast && ppt->y < clipy1; ppt++, pwidth++) {} \ +} + +/* + Clip a list of scanlines to a region. The caller has allocated the + space. FSorted is non-zero if the scanline origins are in ascending + order. + returns the number of new, clipped scanlines. +*/ + +int +miClipSpans(prgnDst, ppt, pwidth, nspans, pptNew, pwidthNew, fSorted) + RegionPtr prgnDst; + register DDXPointPtr ppt; + register int *pwidth; + int nspans; + register DDXPointPtr pptNew; + int *pwidthNew; + int fSorted; +{ + register DDXPointPtr pptLast; + int *pwidthNewStart; /* the vengeance of Xerox! */ + register int y, x1, x2; + register int numRects; + + good(prgnDst); + pptLast = ppt + nspans; + pwidthNewStart = pwidthNew; + + if (!prgnDst->data) + { + /* Do special fast code with clip boundaries in registers(?) */ + /* It doesn't pay much to make use of fSorted in this case, + so we lump everything together. */ + + register int clipx1, clipx2, clipy1, clipy2; + + clipx1 = prgnDst->extents.x1; + clipy1 = prgnDst->extents.y1; + clipx2 = prgnDst->extents.x2; + clipy2 = prgnDst->extents.y2; + + for (; ppt != pptLast; ppt++, pwidth++) + { + y = ppt->y; + x1 = ppt->x; + if (clipy1 <= y && y < clipy2) + { + x2 = x1 + *pwidth; + if (x1 < clipx1) x1 = clipx1; + if (x2 > clipx2) x2 = clipx2; + if (x1 < x2) + { + /* part of span in clip rectangle */ + pptNew->x = x1; + pptNew->y = y; + *pwidthNew = x2 - x1; + pptNew++; + pwidthNew++; + } + } + } /* end for */ + + } + else if (numRects = prgnDst->data->numRects) + { + /* Have to clip against many boxes */ + BoxPtr pboxBandStart, pboxBandEnd; + register BoxPtr pbox; + register BoxPtr pboxLast; + register int clipy1, clipy2; + + /* In this case, taking advantage of sorted spans gains more than + the sorting costs. */ + if ((! fSorted) && (nspans > 1)) + QuickSortSpans(ppt, pwidth, nspans); + + pboxBandStart = REGION_BOXPTR(prgnDst); + pboxLast = pboxBandStart + numRects; + + NextBand(); + + for (; ppt != pptLast; ) + { + y = ppt->y; + if (y < clipy2) + { + /* span is in the current band */ + pbox = pboxBandStart; + x1 = ppt->x; + x2 = x1 + *pwidth; + do + { /* For each box in band */ + register int newx1, newx2; + + newx1 = x1; + newx2 = x2; + if (newx1 < pbox->x1) newx1 = pbox->x1; + if (newx2 > pbox->x2) newx2 = pbox->x2; + if (newx1 < newx2) + { + /* Part of span in clip rectangle */ + pptNew->x = newx1; + pptNew->y = y; + *pwidthNew = newx2 - newx1; + pptNew++; + pwidthNew++; + } + pbox++; + } while (pbox != pboxBandEnd); + ppt++; + pwidth++; + } + else + { + /* Move to next band, adjust ppt as needed */ + pboxBandStart = pboxBandEnd; + if (pboxBandStart == pboxLast) + break; /* We're completely done */ + NextBand(); + } + } + } + return (pwidthNew - pwidthNewStart); +} + +/* find the band in a region with the most rectangles */ +int +miFindMaxBand(prgn) + RegionPtr prgn; +{ + register int nbox; + register BoxPtr pbox; + register int nThisBand; + register int nMaxBand = 0; + short yThisBand; + + good(prgn); + nbox = REGION_NUM_RECTS(prgn); + pbox = REGION_RECTS(prgn); + + while(nbox > 0) + { + yThisBand = pbox->y1; + nThisBand = 0; + while((nbox > 0) && (pbox->y1 == yThisBand)) + { + nbox--; + pbox++; + nThisBand++; + } + if (nThisBand > nMaxBand) + nMaxBand = nThisBand; + } + return (nMaxBand); +} diff --git a/mi/miscanfill.h b/mi/miscanfill.h new file mode 100644 index 000000000..06baee399 --- /dev/null +++ b/mi/miscanfill.h @@ -0,0 +1,144 @@ +/* $Xorg: miscanfill.h,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +/* + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + + +#ifndef SCANFILLINCLUDED +#define SCANFILLINCLUDED +/* + * scanfill.h + * + * Written by Brian Kelleher; Jan 1985 + * + * This file contains a few macros to help track + * the edge of a filled object. The object is assumed + * to be filled in scanline order, and thus the + * algorithm used is an extension of Bresenham's line + * drawing algorithm which assumes that y is always the + * major axis. + * Since these pieces of code are the same for any filled shape, + * it is more convenient to gather the library in one + * place, but since these pieces of code are also in + * the inner loops of output primitives, procedure call + * overhead is out of the question. + * See the author for a derivation if needed. + */ + + +/* + * In scan converting polygons, we want to choose those pixels + * which are inside the polygon. Thus, we add .5 to the starting + * x coordinate for both left and right edges. Now we choose the + * first pixel which is inside the pgon for the left edge and the + * first pixel which is outside the pgon for the right edge. + * Draw the left pixel, but not the right. + * + * How to add .5 to the starting x coordinate: + * If the edge is moving to the right, then subtract dy from the + * error term from the general form of the algorithm. + * If the edge is moving to the left, then add dy to the error term. + * + * The reason for the difference between edges moving to the left + * and edges moving to the right is simple: If an edge is moving + * to the right, then we want the algorithm to flip immediately. + * If it is moving to the left, then we don't want it to flip until + * we traverse an entire pixel. + */ +#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \ + int dx; /* local storage */ \ +\ + /* \ + * if the edge is horizontal, then it is ignored \ + * and assumed not to be processed. Otherwise, do this stuff. \ + */ \ + if ((dy) != 0) { \ + xStart = (x1); \ + dx = (x2) - xStart; \ + if (dx < 0) { \ + m = dx / (dy); \ + m1 = m - 1; \ + incr1 = -2 * dx + 2 * (dy) * m1; \ + incr2 = -2 * dx + 2 * (dy) * m; \ + d = 2 * m * (dy) - 2 * dx - 2 * (dy); \ + } else { \ + m = dx / (dy); \ + m1 = m + 1; \ + incr1 = 2 * dx - 2 * (dy) * m1; \ + incr2 = 2 * dx - 2 * (dy) * m; \ + d = -2 * m * (dy) + 2 * dx; \ + } \ + } \ +} + +#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \ + if (m1 > 0) { \ + if (d > 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } else {\ + if (d >= 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } \ +} + + +/* + * This structure contains all of the information needed + * to run the bresenham algorithm. + * The variables may be hardcoded into the declarations + * instead of using this structure to make use of + * register declarations. + */ +typedef struct { + int minor; /* minor axis */ + int d; /* decision variable */ + int m, m1; /* slope and slope+1 */ + int incr1, incr2; /* error increments */ +} BRESINFO; + + +#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \ + BRESINITPGON(dmaj, min1, min2, bres.minor, bres.d, \ + bres.m, bres.m1, bres.incr1, bres.incr2) + +#define BRESINCRPGONSTRUCT(bres) \ + BRESINCRPGON(bres.d, bres.minor, bres.m, bres.m1, bres.incr1, bres.incr2) + + +#endif diff --git a/mi/miscrinit.c b/mi/miscrinit.c new file mode 100644 index 000000000..1561c1844 --- /dev/null +++ b/mi/miscrinit.c @@ -0,0 +1,288 @@ +/* $Xorg: miscrinit.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ +/* + +Copyright 1990, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + +#include "X.h" +#include "servermd.h" +#include "misc.h" +#include "mi.h" +#include "scrnintstr.h" +#include "pixmapstr.h" +#include "mibstore.h" +#include "dix.h" +#include "miline.h" + +/* We use this structure to propogate some information from miScreenInit to + * miCreateScreenResources. miScreenInit allocates the structure, fills it + * in, and puts it into pScreen->devPrivate. miCreateScreenResources + * extracts the info and frees the structure. We could've accomplished the + * same thing by adding fields to the screen structure, but they would have + * ended up being redundant, and would have exposed this mi implementation + * detail to the whole server. + */ + +typedef struct +{ + pointer pbits; /* pointer to framebuffer */ + int width; /* delta to add to a framebuffer addr to move one row down */ +} miScreenInitParmsRec, *miScreenInitParmsPtr; + + +/* this plugs into pScreen->ModifyPixmapHeader */ +Bool +miModifyPixmapHeader(pPixmap, width, height, depth, bitsPerPixel, devKind, + pPixData) + PixmapPtr pPixmap; + int width; + int height; + int depth; + int bitsPerPixel; + int devKind; + pointer pPixData; +{ + if (!pPixmap) + return FALSE; + pPixmap->drawable.depth = depth; + pPixmap->drawable.bitsPerPixel = bitsPerPixel; + pPixmap->drawable.id = 0; + pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; + pPixmap->drawable.x = 0; + pPixmap->drawable.y = 0; + pPixmap->drawable.width = width; + pPixmap->drawable.height = height; + pPixmap->devKind = devKind; + pPixmap->refcnt = 1; + pPixmap->devPrivate.ptr = pPixData; + return TRUE; +} + + +/*ARGSUSED*/ +Bool +miCloseScreen (index, pScreen) + int index; + ScreenPtr pScreen; +{ + return ((*pScreen->DestroyPixmap)((PixmapPtr)pScreen->devPrivate)); +} + +/* With the introduction of pixmap privates, the "screen pixmap" can no + * longer be created in miScreenInit, since all the modules that could + * possibly ask for pixmap private space have not been initialized at + * that time. pScreen->CreateScreenResources is called after all + * possible private-requesting modules have been inited; we create the + * screen pixmap here. + */ +Bool +miCreateScreenResources(pScreen) + ScreenPtr pScreen; +{ + miScreenInitParmsPtr pScrInitParms; + pointer value; + + pScrInitParms = (miScreenInitParmsPtr)pScreen->devPrivate; + + /* if width is non-zero, pScreen->devPrivate will be a pixmap + * else it will just take the value pbits + */ + if (pScrInitParms->width) + { + PixmapPtr pPixmap; + + /* create a pixmap with no data, then redirect it to point to + * the screen + */ + pPixmap = (*pScreen->CreatePixmap)(pScreen, 0, 0, pScreen->rootDepth); + if (!pPixmap) + return FALSE; + + if (!(*pScreen->ModifyPixmapHeader)(pPixmap, pScreen->width, + pScreen->height, pScreen->rootDepth, pScreen->rootDepth, + PixmapBytePad(pScrInitParms->width, pScreen->rootDepth), + pScrInitParms->pbits)) + return FALSE; + value = (pointer)pPixmap; + } + else + { + value = pScrInitParms->pbits; + } + xfree(pScreen->devPrivate); /* freeing miScreenInitParmsRec */ + pScreen->devPrivate = value; /* pPixmap or pbits */ + return TRUE; +} + +Bool +miScreenDevPrivateInit(pScreen, width, pbits) + register ScreenPtr pScreen; + int width; + pointer pbits; +{ + miScreenInitParmsPtr pScrInitParms; + + /* Stash pbits and width in a short-lived miScreenInitParmsRec attached + * to the screen, until CreateScreenResources can put them in the + * screen pixmap. + */ + pScrInitParms = (miScreenInitParmsPtr)xalloc(sizeof(miScreenInitParmsRec)); + if (!pScrInitParms) + return FALSE; + pScrInitParms->pbits = pbits; + pScrInitParms->width = width; + pScreen->devPrivate = (pointer)pScrInitParms; + return TRUE; +} + +/* + * If you pass in bsfuncs, then you must preinitialize the missing + * screen procs before calling miScreenInit, so that the backing store + * code can correctly wrap them. + */ + +Bool +miScreenInit(pScreen, pbits, xsize, ysize, dpix, dpiy, width, + rootDepth, numDepths, depths, rootVisual, numVisuals, visuals, + bsfuncs) + register ScreenPtr pScreen; + pointer pbits; /* pointer to screen bits */ + int xsize, ysize; /* in pixels */ + int dpix, dpiy; /* dots per inch */ + int width; /* pixel width of frame buffer */ + int rootDepth; /* depth of root window */ + int numDepths; /* number of depths supported */ + DepthRec *depths; /* supported depths */ + VisualID rootVisual; /* root visual */ + int numVisuals; /* number of visuals supported */ + VisualRec *visuals; /* supported visuals */ + miBSFuncPtr bsfuncs; /* backing store functions */ +{ + pScreen->width = xsize; + pScreen->height = ysize; + pScreen->mmWidth = (xsize * 254 + dpix * 5) / (dpix * 10); + pScreen->mmHeight = (ysize * 254 + dpiy * 5) / (dpiy * 10); + pScreen->numDepths = numDepths; + pScreen->rootDepth = rootDepth; + pScreen->allowedDepths = depths; + pScreen->rootVisual = rootVisual; + /* defColormap */ + pScreen->minInstalledCmaps = 1; + pScreen->maxInstalledCmaps = 1; + pScreen->backingStoreSupport = Always; + pScreen->saveUnderSupport = NotUseful; + /* whitePixel, blackPixel */ + pScreen->ModifyPixmapHeader = miModifyPixmapHeader; + pScreen->CreateScreenResources = miCreateScreenResources; + pScreen->numVisuals = numVisuals; + pScreen->visuals = visuals; + if (width) + { +#ifdef MITSHM + ShmRegisterFbFuncs(pScreen); +#endif + pScreen->CloseScreen = miCloseScreen; + } + /* else CloseScreen */ + /* QueryBestSize, SaveScreen, GetImage, GetSpans */ + pScreen->PointerNonInterestBox = (void (*)()) 0; + pScreen->SourceValidate = (void (*)()) 0; + /* CreateWindow, DestroyWindow, PositionWindow, ChangeWindowAttributes */ + /* RealizeWindow, UnrealizeWindow */ + pScreen->ValidateTree = miValidateTree; + pScreen->PostValidateTree = (void (*)()) 0; + pScreen->WindowExposures = miWindowExposures; + /* PaintWindowBackground, PaintWindowBorder, CopyWindow */ + pScreen->ClearToBackground = miClearToBackground; + pScreen->ClipNotify = (void (*)()) 0; + /* CreatePixmap, DestroyPixmap */ + /* RealizeFont, UnrealizeFont */ + /* CreateGC */ + /* CreateColormap, DestroyColormap, InstallColormap, UninstallColormap */ + /* ListInstalledColormaps, StoreColors, ResolveColor */ + pScreen->RegionCreate = miRegionCreate; + pScreen->RegionInit = miRegionInit; + pScreen->RegionCopy = miRegionCopy; + pScreen->RegionDestroy = miRegionDestroy; + pScreen->RegionUninit = miRegionUninit; + pScreen->Intersect = miIntersect; + pScreen->Union = miUnion; + pScreen->Subtract = miSubtract; + pScreen->Inverse = miInverse; + pScreen->RegionReset = miRegionReset; + pScreen->TranslateRegion = miTranslateRegion; + pScreen->RectIn = miRectIn; + pScreen->PointInRegion = miPointInRegion; + pScreen->RegionNotEmpty = miRegionNotEmpty; + pScreen->RegionEmpty = miRegionEmpty; + pScreen->RegionExtents = miRegionExtents; + pScreen->RegionAppend = miRegionAppend; + pScreen->RegionValidate = miRegionValidate; + /* BitmapToRegion */ + pScreen->RectsToRegion = miRectsToRegion; + pScreen->SendGraphicsExpose = miSendGraphicsExpose; + pScreen->BlockHandler = (void (*)())NoopDDA; + pScreen->WakeupHandler = (void (*)())NoopDDA; + pScreen->blockData = (pointer)0; + pScreen->wakeupData = (pointer)0; + if (bsfuncs) + miInitializeBackingStore (pScreen, bsfuncs); + pScreen->MarkWindow = miMarkWindow; + pScreen->MarkOverlappedWindows = miMarkOverlappedWindows; + pScreen->ChangeSaveUnder = miChangeSaveUnder; + pScreen->PostChangeSaveUnder = miPostChangeSaveUnder; + pScreen->MoveWindow = miMoveWindow; + pScreen->ResizeWindow = miSlideAndSizeWindow; + pScreen->GetLayerWindow = miGetLayerWindow; + pScreen->HandleExposures = miHandleValidateExposures; + pScreen->ReparentWindow = (void (*)())0; + pScreen->ChangeBorderWidth = miChangeBorderWidth; +#ifdef SHAPE + pScreen->SetShape = miSetShape; +#endif + pScreen->MarkUnrealizedWindow = miMarkUnrealizedWindow; + + miSetZeroLineBias(pScreen, DEFAULTZEROLINEBIAS); + + return miScreenDevPrivateInit(pScreen, width, pbits); +} + +int miZeroLineScreenIndex; +int miZeroLineGeneration; + +void +miSetZeroLineBias(pScreen, bias) + ScreenPtr pScreen; + unsigned int bias; +{ + if (miZeroLineGeneration != serverGeneration) + { + miZeroLineScreenIndex = AllocateScreenPrivateIndex(); + miZeroLineGeneration = serverGeneration; + } + if (miZeroLineScreenIndex >= 0) + pScreen->devPrivates[miZeroLineScreenIndex].uval = bias; +} diff --git a/mi/mispans.c b/mi/mispans.c new file mode 100644 index 000000000..562b843ce --- /dev/null +++ b/mi/mispans.c @@ -0,0 +1,554 @@ +/*********************************************************** + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1989 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* $Xorg: mispans.c,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ + +#include "misc.h" +#include "pixmapstr.h" +#include "gcstruct.h" +#include "mispans.h" + +/* + +These routines maintain lists of Spans, in order to implement the +``touch-each-pixel-once'' rules of wide lines and arcs. + +Written by Joel McCormack, Summer 1989. + +*/ + + +void miInitSpanGroup(spanGroup) + SpanGroup *spanGroup; +{ + spanGroup->size = 0; + spanGroup->count = 0; + spanGroup->group = NULL; + spanGroup->ymin = MAXSHORT; + spanGroup->ymax = MINSHORT; +} /* InitSpanGroup */ + +#define YMIN(spans) (spans->points[0].y) +#define YMAX(spans) (spans->points[spans->count-1].y) + +void miSubtractSpans (spanGroup, sub) + SpanGroup *spanGroup; + Spans *sub; +{ + int i, subCount, spansCount; + int ymin, ymax, xmin, xmax; + Spans *spans; + DDXPointPtr subPt, spansPt; + int *subWid, *spansWid; + int extra; + + ymin = YMIN(sub); + ymax = YMAX(sub); + spans = spanGroup->group; + for (i = spanGroup->count; i; i--, spans++) { + if (YMIN(spans) <= ymax && ymin <= YMAX(spans)) { + subCount = sub->count; + subPt = sub->points; + subWid = sub->widths; + spansCount = spans->count; + spansPt = spans->points; + spansWid = spans->widths; + extra = 0; + for (;;) + { + while (spansCount && spansPt->y < subPt->y) + { + spansPt++; spansWid++; spansCount--; + } + if (!spansCount) + break; + while (subCount && subPt->y < spansPt->y) + { + subPt++; subWid++; subCount--; + } + if (!subCount) + break; + if (subPt->y == spansPt->y) + { + xmin = subPt->x; + xmax = xmin + *subWid; + if (xmin >= spansPt->x + *spansWid || spansPt->x >= xmax) + { + ; + } + else if (xmin <= spansPt->x) + { + if (xmax >= spansPt->x + *spansWid) + { + memmove (spansPt, spansPt + 1, sizeof *spansPt * (spansCount - 1)); + memmove (spansWid, spansWid + 1, sizeof *spansWid * (spansCount - 1)); + spansPt--; + spansWid--; + spans->count--; + extra++; + } + else + { + *spansWid = *spansWid - (xmax - spansPt->x); + spansPt->x = xmax; + } + } + else + { + if (xmax >= spansPt->x + *spansWid) + { + *spansWid = xmin - spansPt->x; + } + else + { + if (!extra) { + DDXPointPtr newPt; + int *newwid; + +#define EXTRA 8 + newPt = (DDXPointPtr) xrealloc (spans->points, (spans->count + EXTRA) * sizeof (DDXPointRec)); + if (!newPt) + break; + spansPt = newPt + (spansPt - spans->points); + spans->points = newPt; + newwid = (int *) xrealloc (spans->widths, (spans->count + EXTRA) * sizeof (int)); + if (!newwid) + break; + spansWid = newwid + (spansWid - spans->widths); + spans->widths = newwid; + extra = EXTRA; + } + memmove (spansPt + 1, spansPt, sizeof *spansPt * (spansCount)); + memmove (spansWid + 1, spansWid, sizeof *spansWid * (spansCount)); + spans->count++; + extra--; + *spansWid = xmin - spansPt->x; + spansWid++; + spansPt++; + *spansWid = *spansWid - (xmax - spansPt->x); + spansPt->x = xmax; + } + } + } + spansPt++; spansWid++; spansCount--; + } + } + } +} + +void miAppendSpans(spanGroup, otherGroup, spans) + SpanGroup *spanGroup; + SpanGroup *otherGroup; + Spans *spans; +{ + register int ymin, ymax; + register int spansCount; + + spansCount = spans->count; + if (spansCount > 0) { + if (spanGroup->size == spanGroup->count) { + spanGroup->size = (spanGroup->size + 8) * 2; + spanGroup->group = (Spans *) + xrealloc(spanGroup->group, sizeof(Spans) * spanGroup->size); + } + + spanGroup->group[spanGroup->count] = *spans; + (spanGroup->count)++; + ymin = spans->points[0].y; + if (ymin < spanGroup->ymin) spanGroup->ymin = ymin; + ymax = spans->points[spansCount - 1].y; + if (ymax > spanGroup->ymax) spanGroup->ymax = ymax; + if (otherGroup && + otherGroup->ymin < ymax && + ymin < otherGroup->ymax) + { + miSubtractSpans (otherGroup, spans); + } + } + else + { + xfree (spans->points); + xfree (spans->widths); + } +} /* AppendSpans */ + +void miFreeSpanGroup(spanGroup) + SpanGroup *spanGroup; +{ + if (spanGroup->group != NULL) xfree(spanGroup->group); +} + +static void QuickSortSpansX(points, widths, numSpans) + register DDXPointRec points[]; + register int widths[]; + register int numSpans; +{ + register int x; + register int i, j, m; + register DDXPointPtr r; + +/* Always called with numSpans > 1 */ +/* Sorts only by x, as all y should be the same */ + +#define ExchangeSpans(a, b) \ +{ \ + DDXPointRec tpt; \ + register int tw; \ + \ + tpt = points[a]; points[a] = points[b]; points[b] = tpt; \ + tw = widths[a]; widths[a] = widths[b]; widths[b] = tw; \ +} + + do { + if (numSpans < 9) { + /* Do insertion sort */ + register int xprev; + + xprev = points[0].x; + i = 1; + do { /* while i != numSpans */ + x = points[i].x; + if (xprev > x) { + /* points[i] is out of order. Move into proper location. */ + DDXPointRec tpt; + int tw, k; + + for (j = 0; x >= points[j].x; j++) {} + tpt = points[i]; + tw = widths[i]; + for (k = i; k != j; k--) { + points[k] = points[k-1]; + widths[k] = widths[k-1]; + } + points[j] = tpt; + widths[j] = tw; + x = points[i].x; + } /* if out of order */ + xprev = x; + i++; + } while (i != numSpans); + return; + } + + /* Choose partition element, stick in location 0 */ + m = numSpans / 2; + if (points[m].x > points[0].x) ExchangeSpans(m, 0); + if (points[m].x > points[numSpans-1].x) ExchangeSpans(m, numSpans-1); + if (points[m].x > points[0].x) ExchangeSpans(m, 0); + x = points[0].x; + + /* Partition array */ + i = 0; + j = numSpans; + do { + r = &(points[i]); + do { + r++; + i++; + } while (i != numSpans && r->x < x); + r = &(points[j]); + do { + r--; + j--; + } while (x < r->x); + if (i < j) ExchangeSpans(i, j); + } while (i < j); + + /* Move partition element back to middle */ + ExchangeSpans(0, j); + + /* Recurse */ + if (numSpans-j-1 > 1) + QuickSortSpansX(&points[j+1], &widths[j+1], numSpans-j-1); + numSpans = j; + } while (numSpans > 1); +} /* QuickSortSpans */ + + +static int UniquifySpansX(spans, newPoints, newWidths) + Spans *spans; + register DDXPointRec *newPoints; + register int *newWidths; +{ + register int newx1, newx2, oldpt, i, y; + register DDXPointRec *oldPoints; + register int *oldWidths; + int *startNewWidths; + +/* Always called with numSpans > 1 */ +/* Uniquify the spans, and stash them into newPoints and newWidths. Return the + number of unique spans. */ + + + startNewWidths = newWidths; + + oldPoints = spans->points; + oldWidths = spans->widths; + + y = oldPoints->y; + newx1 = oldPoints->x; + newx2 = newx1 + *oldWidths; + + for (i = spans->count-1; i != 0; i--) { + oldPoints++; + oldWidths++; + oldpt = oldPoints->x; + if (oldpt > newx2) { + /* Write current span, start a new one */ + newPoints->x = newx1; + newPoints->y = y; + *newWidths = newx2 - newx1; + newPoints++; + newWidths++; + newx1 = oldpt; + newx2 = oldpt + *oldWidths; + } else { + /* extend current span, if old extends beyond new */ + oldpt = oldpt + *oldWidths; + if (oldpt > newx2) newx2 = oldpt; + } + } /* for */ + + /* Write final span */ + newPoints->x = newx1; + *newWidths = newx2 - newx1; + newPoints->y = y; + + return (newWidths - startNewWidths) + 1; +} /* UniquifySpansX */ + +void +miDisposeSpanGroup (spanGroup) + SpanGroup *spanGroup; +{ + int i; + Spans *spans; + + for (i = 0; i < spanGroup->count; i++) + { + spans = spanGroup->group + i; + xfree (spans->points); + xfree (spans->widths); + } +} + +void miFillUniqueSpanGroup(pDraw, pGC, spanGroup) + DrawablePtr pDraw; + GCPtr pGC; + SpanGroup *spanGroup; +{ + register int i; + register Spans *spans; + register Spans *yspans; + register int *ysizes; + register int ymin, ylength; + + /* Outgoing spans for one big call to FillSpans */ + register DDXPointPtr points; + register int *widths; + register int count; + + if (spanGroup->count == 0) return; + + if (spanGroup->count == 1) { + /* Already should be sorted, unique */ + spans = spanGroup->group; + (*pGC->ops->FillSpans) + (pDraw, pGC, spans->count, spans->points, spans->widths, TRUE); + xfree(spans->points); + xfree(spans->widths); + } + else + { + /* Yuck. Gross. Radix sort into y buckets, then sort x and uniquify */ + /* This seems to be the fastest thing to do. I've tried sorting on + both x and y at the same time rather than creating into all those + y buckets, but it was somewhat slower. */ + + ymin = spanGroup->ymin; + ylength = spanGroup->ymax - ymin + 1; + + /* Allocate Spans for y buckets */ + yspans = (Spans *) xalloc(ylength * sizeof(Spans)); + ysizes = (int *) xalloc(ylength * sizeof (int)); + + if (!yspans || !ysizes) + { + xfree (yspans); + xfree (ysizes); + miDisposeSpanGroup (spanGroup); + return; + } + + for (i = 0; i != ylength; i++) { + ysizes[i] = 0; + yspans[i].count = 0; + yspans[i].points = NULL; + yspans[i].widths = NULL; + } + + /* Go through every single span and put it into the correct bucket */ + count = 0; + for (i = 0, spans = spanGroup->group; + i != spanGroup->count; + i++, spans++) { + int index; + int j; + + for (j = 0, points = spans->points, widths = spans->widths; + j != spans->count; + j++, points++, widths++) { + index = points->y - ymin; + if (index >= 0 && index < ylength) { + Spans *newspans = &(yspans[index]); + if (newspans->count == ysizes[index]) { + DDXPointPtr newpoints; + int *newwidths; + ysizes[index] = (ysizes[index] + 8) * 2; + newpoints = (DDXPointPtr) xrealloc( + newspans->points, + ysizes[index] * sizeof(DDXPointRec)); + newwidths = (int *) xrealloc( + newspans->widths, + ysizes[index] * sizeof(int)); + if (!newpoints || !newwidths) + { + int i; + + for (i = 0; i < ylength; i++) + { + xfree (yspans[i].points); + xfree (yspans[i].widths); + } + xfree (yspans); + xfree (ysizes); + miDisposeSpanGroup (spanGroup); + return; + } + newspans->points = newpoints; + newspans->widths = newwidths; + } + newspans->points[newspans->count] = *points; + newspans->widths[newspans->count] = *widths; + (newspans->count)++; + } /* if y value of span in range */ + } /* for j through spans */ + count += spans->count; + xfree(spans->points); + spans->points = NULL; + xfree(spans->widths); + spans->widths = NULL; + } /* for i thorough Spans */ + + /* Now sort by x and uniquify each bucket into the final array */ + points = (DDXPointPtr) xalloc(count * sizeof(DDXPointRec)); + widths = (int *) xalloc(count * sizeof(int)); + if (!points || !widths) + { + int i; + + for (i = 0; i < ylength; i++) + { + xfree (yspans[i].points); + xfree (yspans[i].widths); + } + xfree (points); + xfree (widths); + xfree (yspans); + xfree (ysizes); + return; + } + count = 0; + for (i = 0; i != ylength; i++) { + int ycount = yspans[i].count; + if (ycount > 0) { + if (ycount > 1) { + QuickSortSpansX(yspans[i].points, yspans[i].widths, ycount); + count += UniquifySpansX + (&(yspans[i]), &(points[count]), &(widths[count])); + } else { + points[count] = yspans[i].points[0]; + widths[count] = yspans[i].widths[0]; + count++; + } + xfree(yspans[i].points); + xfree(yspans[i].widths); + } + } + + (*pGC->ops->FillSpans) (pDraw, pGC, count, points, widths, TRUE); + xfree(points); + xfree(widths); + xfree(yspans); + xfree(ysizes); + } + + spanGroup->count = 0; + spanGroup->ymin = MAXSHORT; + spanGroup->ymax = MINSHORT; +} + + +void miFillSpanGroup(pDraw, pGC, spanGroup) + DrawablePtr pDraw; + GCPtr pGC; + SpanGroup *spanGroup; +{ + register int i; + register Spans *spans; + + for (i = 0, spans = spanGroup->group; i != spanGroup->count; i++, spans++) { + (*pGC->ops->FillSpans) + (pDraw, pGC, spans->count, spans->points, spans->widths, TRUE); + xfree(spans->points); + xfree(spans->widths); + } + + spanGroup->count = 0; + spanGroup->ymin = MAXSHORT; + spanGroup->ymax = MINSHORT; +} /* FillSpanGroup */ diff --git a/mi/mispans.h b/mi/mispans.h new file mode 100644 index 000000000..3750d2add --- /dev/null +++ b/mi/mispans.h @@ -0,0 +1,132 @@ +/*********************************************************** + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1989 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* $Xorg: mispans.h,v 1.4 2001/02/09 02:05:21 xorgcvs Exp $ */ + +typedef struct { + int count; /* number of spans */ + DDXPointPtr points; /* pointer to list of start points */ + int *widths; /* pointer to list of widths */ +} Spans; + +typedef struct { + int size; /* Total number of *Spans allocated */ + int count; /* Number of *Spans actually in group */ + Spans *group; /* List of Spans */ + int ymin, ymax; /* Min, max y values encountered */ +} SpanGroup; + +/* Initialize SpanGroup. MUST BE DONE before use. */ +extern void miInitSpanGroup( +#if NeedFunctionPrototypes + SpanGroup * /*spanGroup*/ +#endif +); + +/* Add a Spans to a SpanGroup. The spans MUST BE in y-sorted order */ +extern void miAppendSpans( +#if NeedFunctionPrototypes + SpanGroup * /*spanGroup*/, + SpanGroup * /*otherGroup*/, + Spans * /*spans*/ +#endif +); + +/* Paint a span group, possibly with some overlap */ +extern void miFillSpanGroup( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + SpanGroup * /*spanGroup*/ +#endif +); + +/* Paint a span group, insuring that each pixel is painted at most once */ +extern void miFillUniqueSpanGroup( +#if NeedFunctionPrototypes + DrawablePtr /*pDraw*/, + GCPtr /*pGC*/, + SpanGroup * /*spanGroup*/ +#endif +); + +/* Free up data in a span group. MUST BE DONE or you'll suffer memory leaks */ +extern void miFreeSpanGroup( +#if NeedFunctionPrototypes + SpanGroup * /*spanGroup*/ +#endif +); + +extern void miSubtractSpans( +#if NeedFunctionPrototypes + SpanGroup * /*spanGroup*/, + Spans * /*sub*/ +#endif +); + +extern void miDisposeSpanGroup( +#if NeedFunctionPrototypes + SpanGroup * /*spanGroup*/ +#endif +); + +extern int miClipSpans( +#if NeedFunctionPrototypes + RegionPtr /*prgnDst*/, + DDXPointPtr /*ppt*/, + int * /*pwidth*/, + int /*nspans*/, + DDXPointPtr /*pptNew*/, + int * /*pwidthNew*/, + int /*fSorted*/ +#endif +); + +/* Rops which must use span groups */ +#define miSpansCarefulRop(rop) (((rop) & 0xc) == 0x8 || ((rop) & 0x3) == 0x2) +#define miSpansEasyRop(rop) (!miSpansCarefulRop(rop)) + diff --git a/mi/misprite.c b/mi/misprite.c new file mode 100644 index 000000000..2fc6da8dd --- /dev/null +++ b/mi/misprite.c @@ -0,0 +1,2067 @@ +/* + * misprite.c + * + * machine independent software sprite routines + */ + +/* $Xorg: misprite.c,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ + +/* + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. +*/ + +# include "X.h" +# include "Xproto.h" +# include "misc.h" +# include "pixmapstr.h" +# include "input.h" +# include "mi.h" +# include "cursorstr.h" +# include "font.h" +# include "scrnintstr.h" +# include "colormapst.h" +# include "windowstr.h" +# include "gcstruct.h" +# include "mipointer.h" +# include "mispritest.h" +# include "dixfontstr.h" +# include "fontstruct.h" + +/* + * screen wrappers + */ + +static int miSpriteScreenIndex; +static unsigned long miSpriteGeneration = 0; + +static Bool miSpriteCloseScreen(); +static void miSpriteGetImage(); +static void miSpriteGetSpans(); +static void miSpriteSourceValidate(); +static Bool miSpriteCreateGC(); +static void miSpriteBlockHandler(); +static void miSpriteInstallColormap(); +static void miSpriteStoreColors(); + +static void miSpritePaintWindowBackground(); +static void miSpritePaintWindowBorder(); +static void miSpriteCopyWindow(); +static void miSpriteClearToBackground(); + +static void miSpriteSaveDoomedAreas(); +static RegionPtr miSpriteRestoreAreas(); +static void miSpriteComputeSaved(); + +#define SCREEN_PROLOGUE(pScreen, field)\ + ((pScreen)->field = \ + ((miSpriteScreenPtr) (pScreen)->devPrivates[miSpriteScreenIndex].ptr)->field) + +#define SCREEN_EPILOGUE(pScreen, field, wrapper)\ + ((pScreen)->field = wrapper) + +/* + * GC func wrappers + */ + +static int miSpriteGCIndex; + +static void miSpriteValidateGC (), miSpriteCopyGC (); +static void miSpriteDestroyGC(), miSpriteChangeGC(); +static void miSpriteChangeClip(), miSpriteDestroyClip(); +static void miSpriteCopyClip(); + +static GCFuncs miSpriteGCFuncs = { + miSpriteValidateGC, + miSpriteChangeGC, + miSpriteCopyGC, + miSpriteDestroyGC, + miSpriteChangeClip, + miSpriteDestroyClip, + miSpriteCopyClip, +}; + +#define GC_FUNC_PROLOGUE(pGC) \ + miSpriteGCPtr pGCPriv = \ + (miSpriteGCPtr) (pGC)->devPrivates[miSpriteGCIndex].ptr;\ + (pGC)->funcs = pGCPriv->wrapFuncs; \ + if (pGCPriv->wrapOps) \ + (pGC)->ops = pGCPriv->wrapOps; + +#define GC_FUNC_EPILOGUE(pGC) \ + pGCPriv->wrapFuncs = (pGC)->funcs; \ + (pGC)->funcs = &miSpriteGCFuncs; \ + if (pGCPriv->wrapOps) \ + { \ + pGCPriv->wrapOps = (pGC)->ops; \ + (pGC)->ops = &miSpriteGCOps; \ + } + +/* + * GC op wrappers + */ + +static void miSpriteFillSpans(), miSpriteSetSpans(); +static void miSpritePutImage(); +static RegionPtr miSpriteCopyArea(), miSpriteCopyPlane(); +static void miSpritePolyPoint(), miSpritePolylines(); +static void miSpritePolySegment(), miSpritePolyRectangle(); +static void miSpritePolyArc(), miSpriteFillPolygon(); +static void miSpritePolyFillRect(), miSpritePolyFillArc(); +static int miSpritePolyText8(), miSpritePolyText16(); +static void miSpriteImageText8(), miSpriteImageText16(); +static void miSpriteImageGlyphBlt(), miSpritePolyGlyphBlt(); +static void miSpritePushPixels(); +#ifdef NEED_LINEHELPER +static void miSpriteLineHelper(); +#endif + +static GCOps miSpriteGCOps = { + miSpriteFillSpans, miSpriteSetSpans, miSpritePutImage, + miSpriteCopyArea, miSpriteCopyPlane, miSpritePolyPoint, + miSpritePolylines, miSpritePolySegment, miSpritePolyRectangle, + miSpritePolyArc, miSpriteFillPolygon, miSpritePolyFillRect, + miSpritePolyFillArc, miSpritePolyText8, miSpritePolyText16, + miSpriteImageText8, miSpriteImageText16, miSpriteImageGlyphBlt, + miSpritePolyGlyphBlt, miSpritePushPixels +#ifdef NEED_LINEHELPER + , miSpriteLineHelper +#endif +}; + +/* + * testing only -- remove cursor for every draw. Eventually, + * each draw operation will perform a bounding box check against + * the saved cursor area + */ + +#define GC_SETUP_CHEAP(pDrawable) \ + miSpriteScreenPtr pScreenPriv = (miSpriteScreenPtr) \ + (pDrawable)->pScreen->devPrivates[miSpriteScreenIndex].ptr; \ + +#define GC_SETUP(pDrawable, pGC) \ + GC_SETUP_CHEAP(pDrawable) \ + miSpriteGCPtr pGCPrivate = (miSpriteGCPtr) \ + (pGC)->devPrivates[miSpriteGCIndex].ptr; \ + GCFuncs *oldFuncs = pGC->funcs; + +#define GC_SETUP_AND_CHECK(pDrawable, pGC) \ + GC_SETUP(pDrawable, pGC); \ + if (GC_CHECK((WindowPtr)pDrawable)) \ + miSpriteRemoveCursor (pDrawable->pScreen); + +#define GC_CHECK(pWin) \ + (pScreenPriv->isUp && \ + (pScreenPriv->pCacheWin == pWin ? \ + pScreenPriv->isInCacheWin : ( \ + ((int) (pScreenPriv->pCacheWin = (pWin))) , \ + (pScreenPriv->isInCacheWin = \ + (pWin)->drawable.x < pScreenPriv->saved.x2 && \ + pScreenPriv->saved.x1 < (pWin)->drawable.x + \ + (int) (pWin)->drawable.width && \ + (pWin)->drawable.y < pScreenPriv->saved.y2 && \ + pScreenPriv->saved.y1 < (pWin)->drawable.y + \ + (int) (pWin)->drawable.height &&\ + RECT_IN_REGION((pWin)->drawable.pScreen, &(pWin)->borderClip, \ + &pScreenPriv->saved) != rgnOUT)))) + +#define GC_OP_PROLOGUE(pGC) { \ + (pGC)->funcs = pGCPrivate->wrapFuncs; \ + (pGC)->ops = pGCPrivate->wrapOps; \ + } + +#define GC_OP_EPILOGUE(pGC) { \ + pGCPrivate->wrapOps = (pGC)->ops; \ + (pGC)->funcs = oldFuncs; \ + (pGC)->ops = &miSpriteGCOps; \ + } + +/* + * pointer-sprite method table + */ + +static Bool miSpriteRealizeCursor (), miSpriteUnrealizeCursor (); +static void miSpriteSetCursor (), miSpriteMoveCursor (); + +miPointerSpriteFuncRec miSpritePointerFuncs = { + miSpriteRealizeCursor, + miSpriteUnrealizeCursor, + miSpriteSetCursor, + miSpriteMoveCursor, +}; + +/* + * other misc functions + */ + +static void miSpriteRemoveCursor (), miSpriteRestoreCursor(); + +/* + * miSpriteInitialize -- called from device-dependent screen + * initialization proc after all of the function pointers have + * been stored in the screen structure. + */ + +Bool +miSpriteInitialize (pScreen, cursorFuncs, screenFuncs) + ScreenPtr pScreen; + miSpriteCursorFuncPtr cursorFuncs; + miPointerScreenFuncPtr screenFuncs; +{ + miSpriteScreenPtr pPriv; + VisualPtr pVisual; + + if (miSpriteGeneration != serverGeneration) + { + miSpriteScreenIndex = AllocateScreenPrivateIndex (); + if (miSpriteScreenIndex < 0) + return FALSE; + miSpriteGeneration = serverGeneration; + miSpriteGCIndex = AllocateGCPrivateIndex (); + } + if (!AllocateGCPrivate(pScreen, miSpriteGCIndex, sizeof(miSpriteGCRec))) + return FALSE; + pPriv = (miSpriteScreenPtr) xalloc (sizeof (miSpriteScreenRec)); + if (!pPriv) + return FALSE; + if (!miPointerInitialize (pScreen, &miSpritePointerFuncs, screenFuncs,TRUE)) + { + xfree ((pointer) pPriv); + return FALSE; + } + for (pVisual = pScreen->visuals; + pVisual->vid != pScreen->rootVisual; + pVisual++) + ; + pPriv->pVisual = pVisual; + pPriv->CloseScreen = pScreen->CloseScreen; + pPriv->GetImage = pScreen->GetImage; + pPriv->GetSpans = pScreen->GetSpans; + pPriv->SourceValidate = pScreen->SourceValidate; + pPriv->CreateGC = pScreen->CreateGC; + pPriv->BlockHandler = pScreen->BlockHandler; + pPriv->InstallColormap = pScreen->InstallColormap; + pPriv->StoreColors = pScreen->StoreColors; + + pPriv->PaintWindowBackground = pScreen->PaintWindowBackground; + pPriv->PaintWindowBorder = pScreen->PaintWindowBorder; + pPriv->CopyWindow = pScreen->CopyWindow; + pPriv->ClearToBackground = pScreen->ClearToBackground; + + pPriv->SaveDoomedAreas = pScreen->SaveDoomedAreas; + pPriv->RestoreAreas = pScreen->RestoreAreas; + + pPriv->pCursor = NULL; + pPriv->x = 0; + pPriv->y = 0; + pPriv->isUp = FALSE; + pPriv->shouldBeUp = FALSE; + pPriv->pCacheWin = NullWindow; + pPriv->isInCacheWin = FALSE; + pPriv->checkPixels = TRUE; + pPriv->pInstalledMap = NULL; + pPriv->pColormap = NULL; + pPriv->funcs = cursorFuncs; + pPriv->colors[SOURCE_COLOR].red = 0; + pPriv->colors[SOURCE_COLOR].green = 0; + pPriv->colors[SOURCE_COLOR].blue = 0; + pPriv->colors[MASK_COLOR].red = 0; + pPriv->colors[MASK_COLOR].green = 0; + pPriv->colors[MASK_COLOR].blue = 0; + pScreen->devPrivates[miSpriteScreenIndex].ptr = (pointer) pPriv; + pScreen->CloseScreen = miSpriteCloseScreen; + pScreen->GetImage = miSpriteGetImage; + pScreen->GetSpans = miSpriteGetSpans; + pScreen->SourceValidate = miSpriteSourceValidate; + pScreen->CreateGC = miSpriteCreateGC; + pScreen->BlockHandler = miSpriteBlockHandler; + pScreen->InstallColormap = miSpriteInstallColormap; + pScreen->StoreColors = miSpriteStoreColors; + + pScreen->PaintWindowBackground = miSpritePaintWindowBackground; + pScreen->PaintWindowBorder = miSpritePaintWindowBorder; + pScreen->CopyWindow = miSpriteCopyWindow; + pScreen->ClearToBackground = miSpriteClearToBackground; + + pScreen->SaveDoomedAreas = miSpriteSaveDoomedAreas; + pScreen->RestoreAreas = miSpriteRestoreAreas; + + return TRUE; +} + +/* + * Screen wrappers + */ + +/* + * CloseScreen wrapper -- unwrap everything, free the private data + * and call the wrapped function + */ + +static Bool +miSpriteCloseScreen (i, pScreen) + ScreenPtr pScreen; +{ + miSpriteScreenPtr pScreenPriv; + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + + pScreen->CloseScreen = pScreenPriv->CloseScreen; + pScreen->GetImage = pScreenPriv->GetImage; + pScreen->GetSpans = pScreenPriv->GetSpans; + pScreen->SourceValidate = pScreenPriv->SourceValidate; + pScreen->CreateGC = pScreenPriv->CreateGC; + pScreen->BlockHandler = pScreenPriv->BlockHandler; + pScreen->InstallColormap = pScreenPriv->InstallColormap; + pScreen->StoreColors = pScreenPriv->StoreColors; + + pScreen->PaintWindowBackground = pScreenPriv->PaintWindowBackground; + pScreen->PaintWindowBorder = pScreenPriv->PaintWindowBorder; + pScreen->CopyWindow = pScreenPriv->CopyWindow; + pScreen->ClearToBackground = pScreenPriv->ClearToBackground; + + pScreen->SaveDoomedAreas = pScreenPriv->SaveDoomedAreas; + pScreen->RestoreAreas = pScreenPriv->RestoreAreas; + + xfree ((pointer) pScreenPriv); + + return (*pScreen->CloseScreen) (i, pScreen); +} + +static void +miSpriteGetImage (pDrawable, sx, sy, w, h, format, planemask, pdstLine) + DrawablePtr pDrawable; + int sx, sy, w, h; + unsigned int format; + unsigned long planemask; + char *pdstLine; +{ + ScreenPtr pScreen = pDrawable->pScreen; + miSpriteScreenPtr pScreenPriv; + + SCREEN_PROLOGUE (pScreen, GetImage); + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + + if (pDrawable->type == DRAWABLE_WINDOW && + pScreenPriv->isUp && + ORG_OVERLAP(&pScreenPriv->saved,pDrawable->x,pDrawable->y, sx, sy, w, h)) + { + miSpriteRemoveCursor (pScreen); + } + + (*pScreen->GetImage) (pDrawable, sx, sy, w, h, + format, planemask, pdstLine); + + SCREEN_EPILOGUE (pScreen, GetImage, miSpriteGetImage); +} + +static void +miSpriteGetSpans (pDrawable, wMax, ppt, pwidth, nspans, pdstStart) + DrawablePtr pDrawable; + int wMax; + DDXPointPtr ppt; + int *pwidth; + int nspans; + char *pdstStart; +{ + ScreenPtr pScreen = pDrawable->pScreen; + miSpriteScreenPtr pScreenPriv; + + SCREEN_PROLOGUE (pScreen, GetSpans); + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + + if (pDrawable->type == DRAWABLE_WINDOW && pScreenPriv->isUp) + { + register DDXPointPtr pts; + register int *widths; + register int nPts; + register int xorg, + yorg; + + xorg = pDrawable->x; + yorg = pDrawable->y; + + for (pts = ppt, widths = pwidth, nPts = nspans; + nPts--; + pts++, widths++) + { + if (SPN_OVERLAP(&pScreenPriv->saved,pts->y+yorg, + pts->x+xorg,*widths)) + { + miSpriteRemoveCursor (pScreen); + break; + } + } + } + + (*pScreen->GetSpans) (pDrawable, wMax, ppt, pwidth, nspans, pdstStart); + + SCREEN_EPILOGUE (pScreen, GetSpans, miSpriteGetSpans); +} + +static void +miSpriteSourceValidate (pDrawable, x, y, width, height) + DrawablePtr pDrawable; + int x, y, width, height; +{ + ScreenPtr pScreen = pDrawable->pScreen; + miSpriteScreenPtr pScreenPriv; + + SCREEN_PROLOGUE (pScreen, SourceValidate); + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + + if (pDrawable->type == DRAWABLE_WINDOW && pScreenPriv->isUp && + ORG_OVERLAP(&pScreenPriv->saved, pDrawable->x, pDrawable->y, + x, y, width, height)) + { + miSpriteRemoveCursor (pScreen); + } + + if (pScreen->SourceValidate) + (*pScreen->SourceValidate) (pDrawable, x, y, width, height); + + SCREEN_EPILOGUE (pScreen, SourceValidate, miSpriteSourceValidate); +} + +static Bool +miSpriteCreateGC (pGC) + GCPtr pGC; +{ + ScreenPtr pScreen = pGC->pScreen; + Bool ret; + miSpriteGCPtr pPriv; + + SCREEN_PROLOGUE (pScreen, CreateGC); + + pPriv = (miSpriteGCPtr)pGC->devPrivates[miSpriteGCIndex].ptr; + + ret = (*pScreen->CreateGC) (pGC); + + pPriv->wrapOps = NULL; + pPriv->wrapFuncs = pGC->funcs; + pGC->funcs = &miSpriteGCFuncs; + + SCREEN_EPILOGUE (pScreen, CreateGC, miSpriteCreateGC); + + return ret; +} + +static void +miSpriteBlockHandler (i, blockData, pTimeout, pReadmask) + int i; + pointer blockData; + OSTimePtr pTimeout; + pointer pReadmask; +{ + ScreenPtr pScreen = screenInfo.screens[i]; + miSpriteScreenPtr pPriv; + + pPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + + SCREEN_PROLOGUE(pScreen, BlockHandler); + + (*pScreen->BlockHandler) (i, blockData, pTimeout, pReadmask); + + SCREEN_EPILOGUE(pScreen, BlockHandler, miSpriteBlockHandler); + + if (!pPriv->isUp && pPriv->shouldBeUp) + miSpriteRestoreCursor (pScreen); +} + +static void +miSpriteInstallColormap (pMap) + ColormapPtr pMap; +{ + ScreenPtr pScreen = pMap->pScreen; + miSpriteScreenPtr pPriv; + + pPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + + SCREEN_PROLOGUE(pScreen, InstallColormap); + + (*pScreen->InstallColormap) (pMap); + + SCREEN_EPILOGUE(pScreen, InstallColormap, miSpriteInstallColormap); + + pPriv->pInstalledMap = pMap; + if (pPriv->pColormap != pMap) + { + pPriv->checkPixels = TRUE; + if (pPriv->isUp) + miSpriteRemoveCursor (pScreen); + } +} + +static void +miSpriteStoreColors (pMap, ndef, pdef) + ColormapPtr pMap; + int ndef; + xColorItem *pdef; +{ + ScreenPtr pScreen = pMap->pScreen; + miSpriteScreenPtr pPriv; + int i; + int updated; + VisualPtr pVisual; + + pPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + + SCREEN_PROLOGUE(pScreen, StoreColors); + + (*pScreen->StoreColors) (pMap, ndef, pdef); + + SCREEN_EPILOGUE(pScreen, StoreColors, miSpriteStoreColors); + + if (pPriv->pColormap == pMap) + { + updated = 0; + pVisual = pMap->pVisual; + if (pVisual->class == DirectColor) + { + /* Direct color - match on any of the subfields */ + +#define MaskMatch(a,b,mask) ((a) & (pVisual->mask) == (b) & (pVisual->mask)) + +#define UpdateDAC(plane,dac,mask) {\ + if (MaskMatch (pPriv->colors[plane].pixel,pdef[i].pixel,mask)) {\ + pPriv->colors[plane].dac = pdef[i].dac; \ + updated = 1; \ + } \ +} + +#define CheckDirect(plane) \ + UpdateDAC(plane,red,redMask) \ + UpdateDAC(plane,green,greenMask) \ + UpdateDAC(plane,blue,blueMask) + + for (i = 0; i < ndef; i++) + { + CheckDirect (SOURCE_COLOR) + CheckDirect (MASK_COLOR) + } + } + else + { + /* PseudoColor/GrayScale - match on exact pixel */ + for (i = 0; i < ndef; i++) + { + if (pdef[i].pixel == pPriv->colors[SOURCE_COLOR].pixel) + { + pPriv->colors[SOURCE_COLOR] = pdef[i]; + if (++updated == 2) + break; + } + if (pdef[i].pixel == pPriv->colors[MASK_COLOR].pixel) + { + pPriv->colors[MASK_COLOR] = pdef[i]; + if (++updated == 2) + break; + } + } + } + if (updated) + { + pPriv->checkPixels = TRUE; + if (pPriv->isUp) + miSpriteRemoveCursor (pScreen); + } + } +} + +static void +miSpriteFindColors (pScreen) + ScreenPtr pScreen; +{ + miSpriteScreenPtr pScreenPriv = (miSpriteScreenPtr) + pScreen->devPrivates[miSpriteScreenIndex].ptr; + CursorPtr pCursor; + xColorItem *sourceColor, *maskColor; + + pCursor = pScreenPriv->pCursor; + sourceColor = &pScreenPriv->colors[SOURCE_COLOR]; + maskColor = &pScreenPriv->colors[MASK_COLOR]; + if (pScreenPriv->pColormap != pScreenPriv->pInstalledMap || + !(pCursor->foreRed == sourceColor->red && + pCursor->foreGreen == sourceColor->green && + pCursor->foreBlue == sourceColor->blue && + pCursor->backRed == maskColor->red && + pCursor->backGreen == maskColor->green && + pCursor->backBlue == maskColor->blue)) + { + pScreenPriv->pColormap = pScreenPriv->pInstalledMap; + sourceColor->red = pCursor->foreRed; + sourceColor->green = pCursor->foreGreen; + sourceColor->blue = pCursor->foreBlue; + FakeAllocColor (pScreenPriv->pColormap, sourceColor); + maskColor->red = pCursor->backRed; + maskColor->green = pCursor->backGreen; + maskColor->blue = pCursor->backBlue; + FakeAllocColor (pScreenPriv->pColormap, maskColor); + /* "free" the pixels right away, don't let this confuse you */ + FakeFreeColor(pScreenPriv->pColormap, sourceColor->pixel); + FakeFreeColor(pScreenPriv->pColormap, maskColor->pixel); + } + pScreenPriv->checkPixels = FALSE; +} + +/* + * BackingStore wrappers + */ + +static void +miSpriteSaveDoomedAreas (pWin, pObscured, dx, dy) + WindowPtr pWin; + RegionPtr pObscured; + int dx, dy; +{ + ScreenPtr pScreen; + miSpriteScreenPtr pScreenPriv; + BoxRec cursorBox; + + pScreen = pWin->drawable.pScreen; + + SCREEN_PROLOGUE (pScreen, SaveDoomedAreas); + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + if (pScreenPriv->isUp) + { + cursorBox = pScreenPriv->saved; + + if (dx || dy) + { + cursorBox.x1 += dx; + cursorBox.y1 += dy; + cursorBox.x2 += dx; + cursorBox.y2 += dy; + } + if (RECT_IN_REGION( pScreen, pObscured, &cursorBox) != rgnOUT) + miSpriteRemoveCursor (pScreen); + } + + (*pScreen->SaveDoomedAreas) (pWin, pObscured, dx, dy); + + SCREEN_EPILOGUE (pScreen, SaveDoomedAreas, miSpriteSaveDoomedAreas); +} + +static RegionPtr +miSpriteRestoreAreas (pWin, prgnExposed) + WindowPtr pWin; + RegionPtr prgnExposed; +{ + ScreenPtr pScreen; + miSpriteScreenPtr pScreenPriv; + RegionPtr result; + + pScreen = pWin->drawable.pScreen; + + SCREEN_PROLOGUE (pScreen, RestoreAreas); + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + if (pScreenPriv->isUp) + { + if (RECT_IN_REGION( pScreen, prgnExposed, &pScreenPriv->saved) != rgnOUT) + miSpriteRemoveCursor (pScreen); + } + + result = (*pScreen->RestoreAreas) (pWin, prgnExposed); + + SCREEN_EPILOGUE (pScreen, RestoreAreas, miSpriteRestoreAreas); + + return result; +} + +/* + * Window wrappers + */ + +static void +miSpritePaintWindowBackground (pWin, pRegion, what) + WindowPtr pWin; + RegionPtr pRegion; + int what; +{ + ScreenPtr pScreen; + miSpriteScreenPtr pScreenPriv; + + pScreen = pWin->drawable.pScreen; + + SCREEN_PROLOGUE (pScreen, PaintWindowBackground); + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + if (pScreenPriv->isUp) + { + /* + * If the cursor is on the same screen as the window, check the + * region to paint for the cursor and remove it as necessary + */ + if (RECT_IN_REGION( pScreen, pRegion, &pScreenPriv->saved) != rgnOUT) + miSpriteRemoveCursor (pScreen); + } + + (*pScreen->PaintWindowBackground) (pWin, pRegion, what); + + SCREEN_EPILOGUE (pScreen, PaintWindowBackground, miSpritePaintWindowBackground); +} + +static void +miSpritePaintWindowBorder (pWin, pRegion, what) + WindowPtr pWin; + RegionPtr pRegion; + int what; +{ + ScreenPtr pScreen; + miSpriteScreenPtr pScreenPriv; + + pScreen = pWin->drawable.pScreen; + + SCREEN_PROLOGUE (pScreen, PaintWindowBorder); + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + if (pScreenPriv->isUp) + { + /* + * If the cursor is on the same screen as the window, check the + * region to paint for the cursor and remove it as necessary + */ + if (RECT_IN_REGION( pScreen, pRegion, &pScreenPriv->saved) != rgnOUT) + miSpriteRemoveCursor (pScreen); + } + + (*pScreen->PaintWindowBorder) (pWin, pRegion, what); + + SCREEN_EPILOGUE (pScreen, PaintWindowBorder, miSpritePaintWindowBorder); +} + +static void +miSpriteCopyWindow (pWin, ptOldOrg, pRegion) + WindowPtr pWin; + DDXPointRec ptOldOrg; + RegionPtr pRegion; +{ + ScreenPtr pScreen; + miSpriteScreenPtr pScreenPriv; + BoxRec cursorBox; + int dx, dy; + + pScreen = pWin->drawable.pScreen; + + SCREEN_PROLOGUE (pScreen, CopyWindow); + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + if (pScreenPriv->isUp) + { + /* + * check both the source and the destination areas. The given + * region is source relative, so offset the cursor box by + * the delta position + */ + cursorBox = pScreenPriv->saved; + dx = pWin->drawable.x - ptOldOrg.x; + dy = pWin->drawable.y - ptOldOrg.y; + cursorBox.x1 -= dx; + cursorBox.x2 -= dx; + cursorBox.y1 -= dy; + cursorBox.y2 -= dy; + if (RECT_IN_REGION( pScreen, pRegion, &pScreenPriv->saved) != rgnOUT || + RECT_IN_REGION( pScreen, pRegion, &cursorBox) != rgnOUT) + miSpriteRemoveCursor (pScreen); + } + + (*pScreen->CopyWindow) (pWin, ptOldOrg, pRegion); + + SCREEN_EPILOGUE (pScreen, CopyWindow, miSpriteCopyWindow); +} + +static void +miSpriteClearToBackground (pWin, x, y, w, h, generateExposures) + WindowPtr pWin; + short x,y; + unsigned short w,h; + Bool generateExposures; +{ + ScreenPtr pScreen; + miSpriteScreenPtr pScreenPriv; + int realw, realh; + + pScreen = pWin->drawable.pScreen; + + SCREEN_PROLOGUE (pScreen, ClearToBackground); + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + if (GC_CHECK(pWin)) + { + if (!(realw = w)) + realw = (int) pWin->drawable.width - x; + if (!(realh = h)) + realh = (int) pWin->drawable.height - y; + if (ORG_OVERLAP(&pScreenPriv->saved, pWin->drawable.x, pWin->drawable.y, + x, y, realw, realh)) + { + miSpriteRemoveCursor (pScreen); + } + } + + (*pScreen->ClearToBackground) (pWin, x, y, w, h, generateExposures); + + SCREEN_EPILOGUE (pScreen, ClearToBackground, miSpriteClearToBackground); +} + +/* + * GC Func wrappers + */ + +static void +miSpriteValidateGC (pGC, changes, pDrawable) + GCPtr pGC; + Mask changes; + DrawablePtr pDrawable; +{ + GC_FUNC_PROLOGUE (pGC); + + (*pGC->funcs->ValidateGC) (pGC, changes, pDrawable); + + pGCPriv->wrapOps = NULL; + if (pDrawable->type == DRAWABLE_WINDOW && ((WindowPtr) pDrawable)->viewable) + { + WindowPtr pWin; + RegionPtr pRegion; + + pWin = (WindowPtr) pDrawable; + pRegion = &pWin->clipList; + if (pGC->subWindowMode == IncludeInferiors) + pRegion = &pWin->borderClip; + if (REGION_NOTEMPTY(pDrawable->pScreen, pRegion)) + pGCPriv->wrapOps = pGC->ops; + } + + GC_FUNC_EPILOGUE (pGC); +} + +static void +miSpriteChangeGC (pGC, mask) + GCPtr pGC; + unsigned long mask; +{ + GC_FUNC_PROLOGUE (pGC); + + (*pGC->funcs->ChangeGC) (pGC, mask); + + GC_FUNC_EPILOGUE (pGC); +} + +static void +miSpriteCopyGC (pGCSrc, mask, pGCDst) + GCPtr pGCSrc, pGCDst; + unsigned long mask; +{ + GC_FUNC_PROLOGUE (pGCDst); + + (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst); + + GC_FUNC_EPILOGUE (pGCDst); +} + +static void +miSpriteDestroyGC (pGC) + GCPtr pGC; +{ + GC_FUNC_PROLOGUE (pGC); + + (*pGC->funcs->DestroyGC) (pGC); + + GC_FUNC_EPILOGUE (pGC); +} + +static void +miSpriteChangeClip (pGC, type, pvalue, nrects) + GCPtr pGC; + int type; + pointer pvalue; + int nrects; +{ + GC_FUNC_PROLOGUE (pGC); + + (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects); + + GC_FUNC_EPILOGUE (pGC); +} + +static void +miSpriteCopyClip(pgcDst, pgcSrc) + GCPtr pgcDst, pgcSrc; +{ + GC_FUNC_PROLOGUE (pgcDst); + + (* pgcDst->funcs->CopyClip)(pgcDst, pgcSrc); + + GC_FUNC_EPILOGUE (pgcDst); +} + +static void +miSpriteDestroyClip(pGC) + GCPtr pGC; +{ + GC_FUNC_PROLOGUE (pGC); + + (* pGC->funcs->DestroyClip)(pGC); + + GC_FUNC_EPILOGUE (pGC); +} + +/* + * GC Op wrappers + */ + +static void +miSpriteFillSpans(pDrawable, pGC, nInit, pptInit, pwidthInit, fSorted) + DrawablePtr pDrawable; + GCPtr pGC; + int nInit; /* number of spans to fill */ + DDXPointPtr pptInit; /* pointer to list of start points */ + int *pwidthInit; /* pointer to list of n widths */ + int fSorted; +{ + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + register DDXPointPtr pts; + register int *widths; + register int nPts; + + for (pts = pptInit, widths = pwidthInit, nPts = nInit; + nPts--; + pts++, widths++) + { + if (SPN_OVERLAP(&pScreenPriv->saved,pts->y,pts->x,*widths)) + { + miSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->FillSpans) (pDrawable, pGC, nInit, pptInit, pwidthInit, fSorted); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpriteSetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted) + DrawablePtr pDrawable; + GCPtr pGC; + char *psrc; + register DDXPointPtr ppt; + int *pwidth; + int nspans; + int fSorted; +{ + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + register DDXPointPtr pts; + register int *widths; + register int nPts; + + for (pts = ppt, widths = pwidth, nPts = nspans; + nPts--; + pts++, widths++) + { + if (SPN_OVERLAP(&pScreenPriv->saved,pts->y,pts->x,*widths)) + { + miSpriteRemoveCursor(pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->SetSpans) (pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpritePutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format, pBits) + DrawablePtr pDrawable; + GCPtr pGC; + int depth; + int x; + int y; + int w; + int h; + int format; + char *pBits; +{ + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + if (ORG_OVERLAP(&pScreenPriv->saved,pDrawable->x,pDrawable->y, + x,y,w,h)) + { + miSpriteRemoveCursor (pDrawable->pScreen); + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PutImage) (pDrawable, pGC, depth, x, y, w, h, leftPad, format, pBits); + + GC_OP_EPILOGUE (pGC); +} + +static RegionPtr +miSpriteCopyArea (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty) + DrawablePtr pSrc; + DrawablePtr pDst; + GCPtr pGC; + int srcx; + int srcy; + int w; + int h; + int dstx; + int dsty; +{ + RegionPtr rgn; + + GC_SETUP(pDst, pGC); + + /* check destination/source overlap. */ + if (GC_CHECK((WindowPtr) pDst) && + (ORG_OVERLAP(&pScreenPriv->saved,pDst->x,pDst->y,dstx,dsty,w,h) || + ((pDst == pSrc) && + ORG_OVERLAP(&pScreenPriv->saved,pSrc->x,pSrc->y,srcx,srcy,w,h)))) + { + miSpriteRemoveCursor (pDst->pScreen); + } + + GC_OP_PROLOGUE (pGC); + + rgn = (*pGC->ops->CopyArea) (pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty); + + GC_OP_EPILOGUE (pGC); + + return rgn; +} + +static RegionPtr +miSpriteCopyPlane (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, plane) + DrawablePtr pSrc; + DrawablePtr pDst; + register GCPtr pGC; + int srcx, + srcy; + int w, + h; + int dstx, + dsty; + unsigned long plane; +{ + RegionPtr rgn; + + GC_SETUP(pDst, pGC); + + /* + * check destination/source for overlap. + */ + if (GC_CHECK((WindowPtr) pDst) && + (ORG_OVERLAP(&pScreenPriv->saved,pDst->x,pDst->y,dstx,dsty,w,h) || + ((pDst == pSrc) && + ORG_OVERLAP(&pScreenPriv->saved,pSrc->x,pSrc->y,srcx,srcy,w,h)))) + { + miSpriteRemoveCursor (pDst->pScreen); + } + + GC_OP_PROLOGUE (pGC); + + rgn = (*pGC->ops->CopyPlane) (pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty, plane); + + GC_OP_EPILOGUE (pGC); + + return rgn; +} + +static void +miSpritePolyPoint (pDrawable, pGC, mode, npt, pptInit) + DrawablePtr pDrawable; + GCPtr pGC; + int mode; /* Origin or Previous */ + int npt; + xPoint *pptInit; +{ + xPoint t; + int n; + BoxRec cursor; + register xPoint *pts; + + GC_SETUP (pDrawable, pGC); + + if (npt && GC_CHECK((WindowPtr) pDrawable)) + { + cursor.x1 = pScreenPriv->saved.x1 - pDrawable->x; + cursor.y1 = pScreenPriv->saved.y1 - pDrawable->y; + cursor.x2 = pScreenPriv->saved.x2 - pDrawable->x; + cursor.y2 = pScreenPriv->saved.y2 - pDrawable->y; + + if (mode == CoordModePrevious) + { + t.x = 0; + t.y = 0; + for (pts = pptInit, n = npt; n--; pts++) + { + t.x += pts->x; + t.y += pts->y; + if (cursor.x1 <= t.x && t.x <= cursor.x2 && + cursor.y1 <= t.y && t.y <= cursor.y2) + { + miSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + else + { + for (pts = pptInit, n = npt; n--; pts++) + { + if (cursor.x1 <= pts->x && pts->x <= cursor.x2 && + cursor.y1 <= pts->y && pts->y <= cursor.y2) + { + miSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pptInit); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpritePolylines (pDrawable, pGC, mode, npt, pptInit) + DrawablePtr pDrawable; + GCPtr pGC; + int mode; + int npt; + DDXPointPtr pptInit; +{ + BoxPtr cursor; + register DDXPointPtr pts; + int n; + int x, y, x1, y1, x2, y2; + int lw; + int extra; + + GC_SETUP (pDrawable, pGC); + + if (npt && GC_CHECK((WindowPtr) pDrawable)) + { + cursor = &pScreenPriv->saved; + lw = pGC->lineWidth; + x = pptInit->x + pDrawable->x; + y = pptInit->y + pDrawable->y; + + if (npt == 1) + { + extra = lw >> 1; + if (LINE_OVERLAP(cursor, x, y, x, y, extra)) + miSpriteRemoveCursor (pDrawable->pScreen); + } + else + { + extra = lw >> 1; + /* + * mitered joins can project quite a way from + * the line end; the 11 degree miter limit limits + * this extension to 10.43 * lw / 2, rounded up + * and converted to int yields 6 * lw + */ + if (pGC->joinStyle == JoinMiter) + extra = 6 * lw; + else if (pGC->capStyle == CapProjecting) + extra = lw; + for (pts = pptInit + 1, n = npt - 1; n--; pts++) + { + x1 = x; + y1 = y; + if (mode == CoordModeOrigin) + { + x2 = pDrawable->x + pts->x; + y2 = pDrawable->y + pts->y; + } + else + { + x2 = x + pts->x; + y2 = y + pts->y; + } + x = x2; + y = y2; + LINE_SORT(x1, y1, x2, y2); + if (LINE_OVERLAP(cursor, x1, y1, x2, y2, extra)) + { + miSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + } + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->Polylines) (pDrawable, pGC, mode, npt, pptInit); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpritePolySegment(pDrawable, pGC, nseg, pSegs) + DrawablePtr pDrawable; + GCPtr pGC; + int nseg; + xSegment *pSegs; +{ + int n; + register xSegment *segs; + BoxPtr cursor; + int x1, y1, x2, y2; + int extra; + + GC_SETUP(pDrawable, pGC); + + if (nseg && GC_CHECK((WindowPtr) pDrawable)) + { + cursor = &pScreenPriv->saved; + extra = pGC->lineWidth >> 1; + if (pGC->capStyle == CapProjecting) + extra = pGC->lineWidth; + for (segs = pSegs, n = nseg; n--; segs++) + { + x1 = segs->x1 + pDrawable->x; + y1 = segs->y1 + pDrawable->y; + x2 = segs->x2 + pDrawable->x; + y2 = segs->y2 + pDrawable->y; + LINE_SORT(x1, y1, x2, y2); + if (LINE_OVERLAP(cursor, x1, y1, x2, y2, extra)) + { + miSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolySegment) (pDrawable, pGC, nseg, pSegs); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpritePolyRectangle(pDrawable, pGC, nrects, pRects) + DrawablePtr pDrawable; + GCPtr pGC; + int nrects; + xRectangle *pRects; +{ + register xRectangle *rects; + BoxPtr cursor; + int lw; + int n; + int x1, y1, x2, y2; + + GC_SETUP (pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + lw = pGC->lineWidth >> 1; + cursor = &pScreenPriv->saved; + for (rects = pRects, n = nrects; n--; rects++) + { + x1 = rects->x + pDrawable->x; + y1 = rects->y + pDrawable->y; + x2 = x1 + (int)rects->width; + y2 = y1 + (int)rects->height; + if (LINE_OVERLAP(cursor, x1, y1, x2, y1, lw) || + LINE_OVERLAP(cursor, x2, y1, x2, y2, lw) || + LINE_OVERLAP(cursor, x1, y2, x2, y2, lw) || + LINE_OVERLAP(cursor, x1, y1, x1, y2, lw)) + { + miSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolyRectangle) (pDrawable, pGC, nrects, pRects); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpritePolyArc(pDrawable, pGC, narcs, parcs) + DrawablePtr pDrawable; + register GCPtr pGC; + int narcs; + xArc *parcs; +{ + BoxPtr cursor; + int lw; + int n; + register xArc *arcs; + + GC_SETUP (pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + lw = pGC->lineWidth >> 1; + cursor = &pScreenPriv->saved; + for (arcs = parcs, n = narcs; n--; arcs++) + { + if (ORG_OVERLAP (cursor, pDrawable->x, pDrawable->y, + arcs->x - lw, arcs->y - lw, + (int) arcs->width + pGC->lineWidth, + (int) arcs->height + pGC->lineWidth)) + { + miSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolyArc) (pDrawable, pGC, narcs, parcs); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpriteFillPolygon(pDrawable, pGC, shape, mode, count, pPts) + register DrawablePtr pDrawable; + register GCPtr pGC; + int shape, mode; + int count; + DDXPointPtr pPts; +{ + int x, y, minx, miny, maxx, maxy; + register DDXPointPtr pts; + int n; + + GC_SETUP (pDrawable, pGC); + + if (count && GC_CHECK((WindowPtr) pDrawable)) + { + x = pDrawable->x; + y = pDrawable->y; + pts = pPts; + minx = maxx = pts->x; + miny = maxy = pts->y; + pts++; + n = count - 1; + + if (mode == CoordModeOrigin) + { + for (; n--; pts++) + { + if (pts->x < minx) + minx = pts->x; + else if (pts->x > maxx) + maxx = pts->x; + if (pts->y < miny) + miny = pts->y; + else if (pts->y > maxy) + maxy = pts->y; + } + minx += x; + miny += y; + maxx += x; + maxy += y; + } + else + { + x += minx; + y += miny; + minx = maxx = x; + miny = maxy = y; + for (; n--; pts++) + { + x += pts->x; + y += pts->y; + if (x < minx) + minx = x; + else if (x > maxx) + maxx = x; + if (y < miny) + miny = y; + else if (y > maxy) + maxy = y; + } + } + if (BOX_OVERLAP(&pScreenPriv->saved,minx,miny,maxx,maxy)) + miSpriteRemoveCursor (pDrawable->pScreen); + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->FillPolygon) (pDrawable, pGC, shape, mode, count, pPts); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpritePolyFillRect(pDrawable, pGC, nrectFill, prectInit) + DrawablePtr pDrawable; + GCPtr pGC; + int nrectFill; /* number of rectangles to fill */ + xRectangle *prectInit; /* Pointer to first rectangle to fill */ +{ + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + register int nRect; + register xRectangle *pRect; + register int xorg, yorg; + + xorg = pDrawable->x; + yorg = pDrawable->y; + + for (nRect = nrectFill, pRect = prectInit; nRect--; pRect++) { + if (ORGRECT_OVERLAP(&pScreenPriv->saved,xorg,yorg,pRect)){ + miSpriteRemoveCursor(pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrectFill, prectInit); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpritePolyFillArc(pDrawable, pGC, narcs, parcs) + DrawablePtr pDrawable; + GCPtr pGC; + int narcs; + xArc *parcs; +{ + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + register int n; + BoxPtr cursor; + register xArc *arcs; + + cursor = &pScreenPriv->saved; + + for (arcs = parcs, n = narcs; n--; arcs++) + { + if (ORG_OVERLAP(cursor, pDrawable->x, pDrawable->y, + arcs->x, arcs->y, + (int) arcs->width, (int) arcs->height)) + { + miSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolyFillArc) (pDrawable, pGC, narcs, parcs); + + GC_OP_EPILOGUE (pGC); +} + +/* + * general Poly/Image text function. Extract glyph information, + * compute bounding box and remove cursor if it is overlapped. + */ + +static Bool +miSpriteTextOverlap (pDraw, font, x, y, n, charinfo, imageblt, w, cursorBox) + DrawablePtr pDraw; + FontPtr font; + int x, y; + unsigned int n; + CharInfoPtr *charinfo; + Bool imageblt; + unsigned int w; + BoxPtr cursorBox; +{ + ExtentInfoRec extents; + + x += pDraw->x; + y += pDraw->y; + + if (FONTMINBOUNDS(font,characterWidth) >= 0) + { + /* compute an approximate (but covering) bounding box */ + if (!imageblt || (charinfo[0]->metrics.leftSideBearing < 0)) + extents.overallLeft = charinfo[0]->metrics.leftSideBearing; + else + extents.overallLeft = 0; + if (w) + extents.overallRight = w - charinfo[n-1]->metrics.characterWidth; + else + extents.overallRight = FONTMAXBOUNDS(font,characterWidth) + * (n - 1); + if (imageblt && (charinfo[n-1]->metrics.characterWidth > + charinfo[n-1]->metrics.rightSideBearing)) + extents.overallRight += charinfo[n-1]->metrics.characterWidth; + else + extents.overallRight += charinfo[n-1]->metrics.rightSideBearing; + if (imageblt && FONTASCENT(font) > FONTMAXBOUNDS(font,ascent)) + extents.overallAscent = FONTASCENT(font); + else + extents.overallAscent = FONTMAXBOUNDS(font, ascent); + if (imageblt && FONTDESCENT(font) > FONTMAXBOUNDS(font,descent)) + extents.overallDescent = FONTDESCENT(font); + else + extents.overallDescent = FONTMAXBOUNDS(font,descent); + if (!BOX_OVERLAP(cursorBox, + x + extents.overallLeft, + y - extents.overallAscent, + x + extents.overallRight, + y + extents.overallDescent)) + return FALSE; + else if (imageblt && w) + return TRUE; + /* if it does overlap, fall through and compute exactly, because + * taking down the cursor is expensive enough to make this worth it + */ + } + QueryGlyphExtents(font, charinfo, n, &extents); + if (imageblt) + { + if (extents.overallWidth > extents.overallRight) + extents.overallRight = extents.overallWidth; + if (extents.overallWidth < extents.overallLeft) + extents.overallLeft = extents.overallWidth; + if (extents.overallLeft > 0) + extents.overallLeft = 0; + if (extents.fontAscent > extents.overallAscent) + extents.overallAscent = extents.fontAscent; + if (extents.fontDescent > extents.overallDescent) + extents.overallDescent = extents.fontDescent; + } + return (BOX_OVERLAP(cursorBox, + x + extents.overallLeft, + y - extents.overallAscent, + x + extents.overallRight, + y + extents.overallDescent)); +} + +/* + * values for textType: + */ +#define TT_POLY8 0 +#define TT_IMAGE8 1 +#define TT_POLY16 2 +#define TT_IMAGE16 3 + +static int +miSpriteText (pDraw, pGC, x, y, count, chars, fontEncoding, textType, cursorBox) + DrawablePtr pDraw; + GCPtr pGC; + int x, + y; + unsigned long count; + char *chars; + FontEncoding fontEncoding; + Bool textType; + BoxPtr cursorBox; +{ + CharInfoPtr *charinfo; + register CharInfoPtr *info; + unsigned long i; + unsigned int n; + int w; + void (*drawFunc)(); + + Bool imageblt; + + imageblt = (textType == TT_IMAGE8) || (textType == TT_IMAGE16); + + charinfo = (CharInfoPtr *) ALLOCATE_LOCAL(count * sizeof(CharInfoPtr)); + if (!charinfo) + return x; + + GetGlyphs(pGC->font, count, (unsigned char *)chars, + fontEncoding, &i, charinfo); + n = (unsigned int)i; + w = 0; + if (!imageblt) + for (info = charinfo; i--; info++) + w += (*info)->metrics.characterWidth; + + if (n != 0) { + if (miSpriteTextOverlap(pDraw, pGC->font, x, y, n, charinfo, imageblt, w, cursorBox)) + miSpriteRemoveCursor(pDraw->pScreen); + +#ifdef AVOID_GLYPHBLT + /* + * On displays like Apollos, which do not optimize the GlyphBlt functions because they + * convert fonts to their internal form in RealizeFont and optimize text directly, we + * want to invoke the text functions here, not the GlyphBlt functions. + */ + switch (textType) + { + case TT_POLY8: + drawFunc = (void (*)())pGC->ops->PolyText8; + break; + case TT_IMAGE8: + drawFunc = pGC->ops->ImageText8; + break; + case TT_POLY16: + drawFunc = (void (*)())pGC->ops->PolyText16; + break; + case TT_IMAGE16: + drawFunc = pGC->ops->ImageText16; + break; + } + (*drawFunc) (pDraw, pGC, x, y, (int) count, chars); +#else /* don't AVOID_GLYPHBLT */ + /* + * On the other hand, if the device does use GlyphBlt ultimately to do text, we + * don't want to slow it down by invoking the text functions and having them call + * GetGlyphs all over again, so we go directly to the GlyphBlt functions here. + */ + drawFunc = imageblt ? pGC->ops->ImageGlyphBlt : pGC->ops->PolyGlyphBlt; + (*drawFunc) (pDraw, pGC, x, y, n, charinfo, FONTGLYPHS(pGC->font)); +#endif /* AVOID_GLYPHBLT */ + } + DEALLOCATE_LOCAL(charinfo); + return x + w; +} + +static int +miSpritePolyText8(pDrawable, pGC, x, y, count, chars) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + int count; + char *chars; +{ + int ret; + + GC_SETUP (pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + ret = miSpriteText (pDrawable, pGC, x, y, (unsigned long)count, chars, + Linear8Bit, TT_POLY8, &pScreenPriv->saved); + else + ret = (*pGC->ops->PolyText8) (pDrawable, pGC, x, y, count, chars); + + GC_OP_EPILOGUE (pGC); + return ret; +} + +static int +miSpritePolyText16(pDrawable, pGC, x, y, count, chars) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + int count; + unsigned short *chars; +{ + int ret; + + GC_SETUP(pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + ret = miSpriteText (pDrawable, pGC, x, y, (unsigned long)count, + (char *)chars, + FONTLASTROW(pGC->font) == 0 ? + Linear16Bit : TwoD16Bit, TT_POLY16, &pScreenPriv->saved); + else + ret = (*pGC->ops->PolyText16) (pDrawable, pGC, x, y, count, chars); + + GC_OP_EPILOGUE (pGC); + return ret; +} + +static void +miSpriteImageText8(pDrawable, pGC, x, y, count, chars) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + int count; + char *chars; +{ + GC_SETUP(pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + (void) miSpriteText (pDrawable, pGC, x, y, (unsigned long)count, + chars, Linear8Bit, TT_IMAGE8, &pScreenPriv->saved); + else + (*pGC->ops->ImageText8) (pDrawable, pGC, x, y, count, chars); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpriteImageText16(pDrawable, pGC, x, y, count, chars) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + int count; + unsigned short *chars; +{ + GC_SETUP(pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + (void) miSpriteText (pDrawable, pGC, x, y, (unsigned long)count, + (char *)chars, + FONTLASTROW(pGC->font) == 0 ? + Linear16Bit : TwoD16Bit, TT_IMAGE16, &pScreenPriv->saved); + else + (*pGC->ops->ImageText16) (pDrawable, pGC, x, y, count, chars); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpriteImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + unsigned int nglyph; + CharInfoPtr *ppci; /* array of character info */ + pointer pglyphBase; /* start of array of glyphs */ +{ + GC_SETUP(pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable) && + miSpriteTextOverlap (pDrawable, pGC->font, x, y, nglyph, ppci, TRUE, 0, &pScreenPriv->saved)) + { + miSpriteRemoveCursor(pDrawable->pScreen); + } + (*pGC->ops->ImageGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpritePolyGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + unsigned int nglyph; + CharInfoPtr *ppci; /* array of character info */ + pointer pglyphBase; /* start of array of glyphs */ +{ + GC_SETUP (pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable) && + miSpriteTextOverlap (pDrawable, pGC->font, x, y, nglyph, ppci, FALSE, 0, &pScreenPriv->saved)) + { + miSpriteRemoveCursor(pDrawable->pScreen); + } + (*pGC->ops->PolyGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); + + GC_OP_EPILOGUE (pGC); +} + +static void +miSpritePushPixels(pGC, pBitMap, pDrawable, w, h, x, y) + GCPtr pGC; + PixmapPtr pBitMap; + DrawablePtr pDrawable; + int w, h, x, y; +{ + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable) && + ORG_OVERLAP(&pScreenPriv->saved,pDrawable->x,pDrawable->y,x,y,w,h)) + { + miSpriteRemoveCursor (pDrawable->pScreen); + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PushPixels) (pGC, pBitMap, pDrawable, w, h, x, y); + + GC_OP_EPILOGUE (pGC); +} + +#ifdef NEED_LINEHELPER +/* + * I don't expect this routine will ever be called, as the GC + * will have been unwrapped for the line drawing + */ + +static void +miSpriteLineHelper() +{ + FatalError("miSpriteLineHelper called\n"); +} +#endif + +/* + * miPointer interface routines + */ + +#define SPRITE_PAD 8 + +static Bool +miSpriteRealizeCursor (pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + miSpriteScreenPtr pScreenPriv; + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + if (pCursor == pScreenPriv->pCursor) + pScreenPriv->checkPixels = TRUE; + return (*pScreenPriv->funcs->RealizeCursor) (pScreen, pCursor); +} + +static Bool +miSpriteUnrealizeCursor (pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + miSpriteScreenPtr pScreenPriv; + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + return (*pScreenPriv->funcs->UnrealizeCursor) (pScreen, pCursor); +} + +static void +miSpriteSetCursor (pScreen, pCursor, x, y) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + miSpriteScreenPtr pScreenPriv; + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + pScreenPriv->shouldBeUp = TRUE; + if (pScreenPriv->x == x && + pScreenPriv->y == y && + pScreenPriv->pCursor == pCursor && + !pScreenPriv->checkPixels) + { + return; + } + if (!pCursor) + { + pScreenPriv->shouldBeUp = FALSE; + if (pScreenPriv->isUp) + miSpriteRemoveCursor (pScreen); + pScreenPriv->pCursor = 0; + return; + } + pScreenPriv->x = x; + pScreenPriv->y = y; + pScreenPriv->pCacheWin = NullWindow; + if (pScreenPriv->checkPixels || pScreenPriv->pCursor != pCursor) + { + pScreenPriv->pCursor = pCursor; + miSpriteFindColors (pScreen); + } + if (pScreenPriv->isUp) { + int sx, sy; + /* + * check to see if the old saved region + * encloses the new sprite, in which case we use + * the flicker-free MoveCursor primitive. + */ + sx = pScreenPriv->x - (int)pCursor->bits->xhot; + sy = pScreenPriv->y - (int)pCursor->bits->yhot; + if (sx + (int) pCursor->bits->width >= pScreenPriv->saved.x1 && + sx < pScreenPriv->saved.x2 && + sy + (int) pCursor->bits->height >= pScreenPriv->saved.y1 && + sy < pScreenPriv->saved.y2 && + (int) pCursor->bits->width + (2 * SPRITE_PAD) == + pScreenPriv->saved.x2 - pScreenPriv->saved.x1 && + (int) pCursor->bits->height + (2 * SPRITE_PAD) == + pScreenPriv->saved.y2 - pScreenPriv->saved.y1 + ) + { + pScreenPriv->isUp = FALSE; + if (!(sx >= pScreenPriv->saved.x1 && + sx + (int)pCursor->bits->width < pScreenPriv->saved.x2 && + sy >= pScreenPriv->saved.y1 && + sy + (int)pCursor->bits->height < pScreenPriv->saved.y2)) + { + int oldx1, oldy1, dx, dy; + + oldx1 = pScreenPriv->saved.x1; + oldy1 = pScreenPriv->saved.y1; + dx = oldx1 - (sx - SPRITE_PAD); + dy = oldy1 - (sy - SPRITE_PAD); + pScreenPriv->saved.x1 -= dx; + pScreenPriv->saved.y1 -= dy; + pScreenPriv->saved.x2 -= dx; + pScreenPriv->saved.y2 -= dy; + (void) (*pScreenPriv->funcs->ChangeSave) (pScreen, + pScreenPriv->saved.x1, + pScreenPriv->saved.y1, + pScreenPriv->saved.x2 - pScreenPriv->saved.x1, + pScreenPriv->saved.y2 - pScreenPriv->saved.y1, + dx, dy); + } + (void) (*pScreenPriv->funcs->MoveCursor) (pScreen, pCursor, + pScreenPriv->saved.x1, + pScreenPriv->saved.y1, + pScreenPriv->saved.x2 - pScreenPriv->saved.x1, + pScreenPriv->saved.y2 - pScreenPriv->saved.y1, + sx - pScreenPriv->saved.x1, + sy - pScreenPriv->saved.y1, + pScreenPriv->colors[SOURCE_COLOR].pixel, + pScreenPriv->colors[MASK_COLOR].pixel); + pScreenPriv->isUp = TRUE; + } + else + { + miSpriteRemoveCursor (pScreen); + } + } + if (!pScreenPriv->isUp && pScreenPriv->pCursor) + miSpriteRestoreCursor (pScreen); +} + +static void +miSpriteMoveCursor (pScreen, x, y) + ScreenPtr pScreen; + int x, y; +{ + miSpriteScreenPtr pScreenPriv; + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + miSpriteSetCursor (pScreen, pScreenPriv->pCursor, x, y); +} + +/* + * undraw/draw cursor + */ + +static void +miSpriteRemoveCursor (pScreen) + ScreenPtr pScreen; +{ + miSpriteScreenPtr pScreenPriv; + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + pScreenPriv->isUp = FALSE; + pScreenPriv->pCacheWin = NullWindow; + if (!(*pScreenPriv->funcs->RestoreUnderCursor) (pScreen, + pScreenPriv->saved.x1, + pScreenPriv->saved.y1, + pScreenPriv->saved.x2 - pScreenPriv->saved.x1, + pScreenPriv->saved.y2 - pScreenPriv->saved.y1)) + { + pScreenPriv->isUp = TRUE; + } +} + +/* + * Called from the block handler, restores the cursor + * before waiting for something to do. + */ + +static void +miSpriteRestoreCursor (pScreen) + ScreenPtr pScreen; +{ + miSpriteScreenPtr pScreenPriv; + int x, y; + CursorPtr pCursor; + + miSpriteComputeSaved (pScreen); + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + pCursor = pScreenPriv->pCursor; + x = pScreenPriv->x - (int)pCursor->bits->xhot; + y = pScreenPriv->y - (int)pCursor->bits->yhot; + if ((*pScreenPriv->funcs->SaveUnderCursor) (pScreen, + pScreenPriv->saved.x1, + pScreenPriv->saved.y1, + pScreenPriv->saved.x2 - pScreenPriv->saved.x1, + pScreenPriv->saved.y2 - pScreenPriv->saved.y1)) + { + if (pScreenPriv->checkPixels) + miSpriteFindColors (pScreen); + if ((*pScreenPriv->funcs->PutUpCursor) (pScreen, pCursor, x, y, + pScreenPriv->colors[SOURCE_COLOR].pixel, + pScreenPriv->colors[MASK_COLOR].pixel)) + pScreenPriv->isUp = TRUE; + } +} + +/* + * compute the desired area of the screen to save + */ + +static void +miSpriteComputeSaved (pScreen) + ScreenPtr pScreen; +{ + miSpriteScreenPtr pScreenPriv; + int x, y, w, h; + int wpad, hpad; + CursorPtr pCursor; + + pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; + pCursor = pScreenPriv->pCursor; + x = pScreenPriv->x - (int)pCursor->bits->xhot; + y = pScreenPriv->y - (int)pCursor->bits->yhot; + w = pCursor->bits->width; + h = pCursor->bits->height; + wpad = SPRITE_PAD; + hpad = SPRITE_PAD; + pScreenPriv->saved.x1 = x - wpad; + pScreenPriv->saved.y1 = y - hpad; + pScreenPriv->saved.x2 = pScreenPriv->saved.x1 + w + wpad * 2; + pScreenPriv->saved.y2 = pScreenPriv->saved.y1 + h + hpad * 2; +} diff --git a/mi/misprite.h b/mi/misprite.h new file mode 100644 index 000000000..be2f03e04 --- /dev/null +++ b/mi/misprite.h @@ -0,0 +1,111 @@ +/* + * misprite.h + * + * software-sprite/sprite drawing interface spec + * + * mi versions of these routines exist. + */ + +/* $Xorg: misprite.h,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ + +/* + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. +*/ + +typedef struct { + Bool (*RealizeCursor)( +#if NeedNestedPrototypes + ScreenPtr /*pScreen*/, + CursorPtr /*pCursor*/ +#endif +); + Bool (*UnrealizeCursor)( +#if NeedNestedPrototypes + ScreenPtr /*pScreen*/, + CursorPtr /*pCursor*/ +#endif +); + Bool (*PutUpCursor)( +#if NeedNestedPrototypes + ScreenPtr /*pScreen*/, + CursorPtr /*pCursor*/, + int /*x*/, + int /*y*/, + unsigned long /*source*/, + unsigned long /*mask*/ +#endif +); + Bool (*SaveUnderCursor)( +#if NeedNestedPrototypes + ScreenPtr /*pScreen*/, + int /*x*/, + int /*y*/, + int /*w*/, + int /*h*/ +#endif +); + Bool (*RestoreUnderCursor)( +#if NeedNestedPrototypes + ScreenPtr /*pScreen*/, + int /*x*/, + int /*y*/, + int /*w*/, + int /*h*/ +#endif +); + Bool (*MoveCursor)( +#if NeedNestedPrototypes + ScreenPtr /*pScreen*/, + CursorPtr /*pCursor*/, + int /*x*/, + int /*y*/, + int /*w*/, + int /*h*/, + int /*dx*/, + int /*dy*/, + unsigned long /*source*/, + unsigned long /*mask*/ +#endif +); + Bool (*ChangeSave)( +#if NeedNestedPrototypes + ScreenPtr /*pScreen*/, + int /*x*/, + int /*y*/, + int /*w*/, + int /*h*/, + int /*dx*/, + int /*dy*/ +#endif +); + +} miSpriteCursorFuncRec, *miSpriteCursorFuncPtr; + +extern Bool miSpriteInitialize( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + miSpriteCursorFuncPtr /*cursorFuncs*/, + miPointerScreenFuncPtr /*screenFuncs*/ +#endif +); diff --git a/mi/mispritest.h b/mi/mispritest.h new file mode 100644 index 000000000..1b873ecbd --- /dev/null +++ b/mi/mispritest.h @@ -0,0 +1,110 @@ +/* + * mispritest.h + * + * mi sprite structures + */ + +/* $Xorg: mispritest.h,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ + +/* + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. +*/ + +# include "misprite.h" + +/* + * per screen information + */ + +typedef struct { + CloseScreenProcPtr CloseScreen; + GetImageProcPtr GetImage; + GetSpansProcPtr GetSpans; + SourceValidateProcPtr SourceValidate; + CreateGCProcPtr CreateGC; + ScreenBlockHandlerProcPtr BlockHandler; + InstallColormapProcPtr InstallColormap; + StoreColorsProcPtr StoreColors; + PaintWindowBackgroundProcPtr PaintWindowBackground; + PaintWindowBorderProcPtr PaintWindowBorder; + CopyWindowProcPtr CopyWindow; + ClearToBackgroundProcPtr ClearToBackground; + SaveDoomedAreasProcPtr SaveDoomedAreas; + RestoreAreasProcPtr RestoreAreas; + + CursorPtr pCursor; + int x; + int y; + BoxRec saved; + Bool isUp; + Bool shouldBeUp; + WindowPtr pCacheWin; + Bool isInCacheWin; + Bool checkPixels; + xColorItem colors[2]; + ColormapPtr pInstalledMap; + ColormapPtr pColormap; + VisualPtr pVisual; + miSpriteCursorFuncPtr funcs; +} miSpriteScreenRec, *miSpriteScreenPtr; + +#define SOURCE_COLOR 0 +#define MASK_COLOR 1 + +typedef struct { + GCFuncs *wrapFuncs; + GCOps *wrapOps; +} miSpriteGCRec, *miSpriteGCPtr; + +/* + * Overlap BoxPtr and Box elements + */ +#define BOX_OVERLAP(pCbox,X1,Y1,X2,Y2) \ + (((pCbox)->x1 <= (X2)) && ((X1) <= (pCbox)->x2) && \ + ((pCbox)->y1 <= (Y2)) && ((Y1) <= (pCbox)->y2)) + +/* + * Overlap BoxPtr, origins, and rectangle + */ +#define ORG_OVERLAP(pCbox,xorg,yorg,x,y,w,h) \ + BOX_OVERLAP((pCbox),(x)+(xorg),(y)+(yorg),(x)+(xorg)+(w),(y)+(yorg)+(h)) + +/* + * Overlap BoxPtr, origins and RectPtr + */ +#define ORGRECT_OVERLAP(pCbox,xorg,yorg,pRect) \ + ORG_OVERLAP((pCbox),(xorg),(yorg),(pRect)->x,(pRect)->y, \ + (int)((pRect)->width), (int)((pRect)->height)) +/* + * Overlap BoxPtr and horizontal span + */ +#define SPN_OVERLAP(pCbox,y,x,w) BOX_OVERLAP((pCbox),(x),(y),(x)+(w),(y)) + +#define LINE_SORT(x1,y1,x2,y2) \ +{ int _t; \ + if (x1 > x2) { _t = x1; x1 = x2; x2 = _t; } \ + if (y1 > y2) { _t = y1; y1 = y2; y2 = _t; } } + +#define LINE_OVERLAP(pCbox,x1,y1,x2,y2,lw2) \ + BOX_OVERLAP((pCbox), (x1)-(lw2), (y1)-(lw2), (x2)+(lw2), (y2)+(lw2)) diff --git a/mi/mistruct.h b/mi/mistruct.h new file mode 100644 index 000000000..28994fec5 --- /dev/null +++ b/mi/mistruct.h @@ -0,0 +1,63 @@ +/* $Xorg: mistruct.h,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +#ifndef MISTRUCT_H +#define MISTRUCT_H + +#include "mi.h" +#include "miscstruct.h" + +/* information about dashes */ +typedef struct _miDash { + DDXPointRec pt; + int e1, e2; /* keep these, so we don't have to do it again */ + int e; /* bresenham error term for this point on line */ + int which; + int newLine;/* 0 if part of same original line as previous dash */ + } miDashRec; + +#endif /* MISTRUCT_H */ diff --git a/mi/mivalidate.h b/mi/mivalidate.h new file mode 100644 index 000000000..cca4a1b7c --- /dev/null +++ b/mi/mivalidate.h @@ -0,0 +1,51 @@ +/* $Xorg: mivalidate.h,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ +/* + +Copyright 1993, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + + +#ifndef MIVALIDATE_H +#define MIVALIDATE_H + +#include "miscstruct.h" +#include "regionstr.h" + +typedef union _Validate { + struct BeforeValidate { + DDXPointRec oldAbsCorner; /* old window position */ + RegionPtr borderVisible; /* visible region of border, */ + /* non-null when size changes */ + Bool resized; /* unclipped winSize has changed - */ + /* don't call SaveDoomedAreas */ + } before; + struct AfterValidate { + RegionRec exposed; /* exposed regions, absolute pos */ + RegionRec borderExposed; + } after; +} ValidateRec; + +#endif /* MIVALIDATE_H */ diff --git a/mi/mivaltree.c b/mi/mivaltree.c new file mode 100644 index 000000000..26819ee45 --- /dev/null +++ b/mi/mivaltree.c @@ -0,0 +1,729 @@ +/* $Xorg: mivaltree.c,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ +/* + * mivaltree.c -- + * Functions for recalculating window clip lists. Main function + * is miValidateTree. + * + +Copyright 1987, 1988, 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + * + * Copyright 1987, 1988, 1989 by + * Digital Equipment Corporation, Maynard, Massachusetts, + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Digital not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL + * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR + * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * + ******************************************************************/ + + /* + * Aug '86: Susan Angebranndt -- original code + * July '87: Adam de Boor -- substantially modified and commented + * Summer '89: Joel McCormack -- so fast you wouldn't believe it possible. + * In particular, much improved code for window mapping and + * circulating. + * Bob Scheifler -- avoid miComputeClips for unmapped windows, + * valdata changes + */ + +#include "X.h" +#include "scrnintstr.h" +#include "validate.h" +#include "windowstr.h" +#include "mi.h" +#include "regionstr.h" +#include "mivalidate.h" + +#ifdef SHAPE +/* + * Compute the visibility of a shaped window + */ +miShapedWindowIn (pScreen, universe, bounding, rect, x, y) + ScreenPtr pScreen; + RegionPtr universe, bounding; + BoxPtr rect; + register int x, y; +{ + BoxRec box; + register BoxPtr boundBox; + int nbox; + Bool someIn, someOut; + register int t, x1, y1, x2, y2; + + nbox = REGION_NUM_RECTS (bounding); + boundBox = REGION_RECTS (bounding); + someIn = someOut = FALSE; + x1 = rect->x1; + y1 = rect->y1; + x2 = rect->x2; + y2 = rect->y2; + while (nbox--) + { + if ((t = boundBox->x1 + x) < x1) + t = x1; + box.x1 = t; + if ((t = boundBox->y1 + y) < y1) + t = y1; + box.y1 = t; + if ((t = boundBox->x2 + x) > x2) + t = x2; + box.x2 = t; + if ((t = boundBox->y2 + y) > y2) + t = y2; + box.y2 = t; + if (box.x1 > box.x2) + box.x2 = box.x1; + if (box.y1 > box.y2) + box.y2 = box.y1; + switch (RECT_IN_REGION(pScreen, universe, &box)) + { + case rgnIN: + if (someOut) + return rgnPART; + someIn = TRUE; + break; + case rgnOUT: + if (someIn) + return rgnPART; + someOut = TRUE; + break; + default: + return rgnPART; + } + boundBox++; + } + if (someIn) + return rgnIN; + return rgnOUT; +} +#endif + +#define HasParentRelativeBorder(w) (!(w)->borderIsPixel && \ + HasBorder(w) && \ + (w)->backgroundState == ParentRelative) + +/*- + *----------------------------------------------------------------------- + * miComputeClips -- + * Recompute the clipList, borderClip, exposed and borderExposed + * regions for pParent and its children. Only viewable windows are + * taken into account. + * + * Results: + * None. + * + * Side Effects: + * clipList, borderClip, exposed and borderExposed are altered. + * A VisibilityNotify event may be generated on the parent window. + * + *----------------------------------------------------------------------- + */ + +static void +miComputeClips (pParent, pScreen, universe, kind, exposed) + register WindowPtr pParent; + register ScreenPtr pScreen; + register RegionPtr universe; + VTKind kind; + RegionPtr exposed; /* for intermediate calculations */ +{ + int dx, + dy; + RegionRec childUniverse; + register WindowPtr pChild; + int oldVis, newVis; + BoxRec borderSize; + RegionRec childUnion; + Bool overlap; + RegionPtr borderVisible; + Bool resized; + + /* + * Figure out the new visibility of this window. + * The extent of the universe should be the same as the extent of + * the borderSize region. If the window is unobscured, this rectangle + * will be completely inside the universe (the universe will cover it + * completely). If the window is completely obscured, none of the + * universe will cover the rectangle. + */ + + borderSize.x1 = pParent->drawable.x - wBorderWidth(pParent); + borderSize.y1 = pParent->drawable.y - wBorderWidth(pParent); + dx = (int) pParent->drawable.x + (int) pParent->drawable.width + wBorderWidth(pParent); + if (dx > 32767) + dx = 32767; + borderSize.x2 = dx; + dy = (int) pParent->drawable.y + (int) pParent->drawable.height + wBorderWidth(pParent); + if (dy > 32767) + dy = 32767; + borderSize.y2 = dy; + + oldVis = pParent->visibility; + switch (RECT_IN_REGION( pScreen, universe, &borderSize)) + { + case rgnIN: + newVis = VisibilityUnobscured; + break; + case rgnPART: + newVis = VisibilityPartiallyObscured; +#ifdef SHAPE + { + RegionPtr pBounding; + + if ((pBounding = wBoundingShape (pParent))) + { + switch (miShapedWindowIn (pScreen, universe, pBounding, + &borderSize, + pParent->drawable.x, + pParent->drawable.y)) + { + case rgnIN: + newVis = VisibilityUnobscured; + break; + case rgnOUT: + newVis = VisibilityFullyObscured; + break; + } + } + } +#endif + break; + default: + newVis = VisibilityFullyObscured; + break; + } + pParent->visibility = newVis; + if (oldVis != newVis && + ((pParent->eventMask | wOtherEventMasks(pParent)) & VisibilityChangeMask)) + SendVisibilityNotify(pParent); + + dx = pParent->drawable.x - pParent->valdata->before.oldAbsCorner.x; + dy = pParent->drawable.y - pParent->valdata->before.oldAbsCorner.y; + + /* + * avoid computations when dealing with simple operations + */ + + switch (kind) { + case VTMap: + case VTStack: + case VTUnmap: + break; + case VTMove: + if ((oldVis == newVis) && + ((oldVis == VisibilityFullyObscured) || + (oldVis == VisibilityUnobscured))) + { + pChild = pParent; + while (1) + { + if (pChild->viewable) + { + if (pChild->visibility != VisibilityFullyObscured) + { + REGION_TRANSLATE( pScreen, &pChild->borderClip, + dx, dy); + REGION_TRANSLATE( pScreen, &pChild->clipList, + dx, dy); + pChild->drawable.serialNumber = NEXT_SERIAL_NUMBER; + if (pScreen->ClipNotify) + (* pScreen->ClipNotify) (pChild, dx, dy); + + } + if (pChild->valdata) + { + REGION_INIT(pScreen, + &pChild->valdata->after.borderExposed, + NullBox, 0); + if (HasParentRelativeBorder(pChild)) + { + REGION_SUBTRACT(pScreen, + &pChild->valdata->after.borderExposed, + &pChild->borderClip, + &pChild->winSize); + } + REGION_INIT( pScreen, &pChild->valdata->after.exposed, + NullBox, 0); + } + if (pChild->firstChild) + { + pChild = pChild->firstChild; + continue; + } + } + while (!pChild->nextSib && (pChild != pParent)) + pChild = pChild->parent; + if (pChild == pParent) + break; + pChild = pChild->nextSib; + } + return; + } + /* fall through */ + default: + /* + * To calculate exposures correctly, we have to translate the old + * borderClip and clipList regions to the window's new location so there + * is a correspondence between pieces of the new and old clipping regions. + */ + if (dx || dy) + { + /* + * We translate the old clipList because that will be exposed or copied + * if gravity is right. + */ + REGION_TRANSLATE( pScreen, &pParent->borderClip, dx, dy); + REGION_TRANSLATE( pScreen, &pParent->clipList, dx, dy); + } + break; + } + + borderVisible = pParent->valdata->before.borderVisible; + resized = pParent->valdata->before.resized; + REGION_INIT( pScreen, &pParent->valdata->after.borderExposed, NullBox, 0); + REGION_INIT( pScreen, &pParent->valdata->after.exposed, NullBox, 0); + + /* + * Since the borderClip must not be clipped by the children, we do + * the border exposure first... + * + * 'universe' is the window's borderClip. To figure the exposures, remove + * the area that used to be exposed from the new. + * This leaves a region of pieces that weren't exposed before. + */ + + if (HasBorder (pParent)) + { + if (borderVisible) + { + /* + * when the border changes shape, the old visible portions + * of the border will be saved by DIX in borderVisible -- + * use that region and destroy it + */ + REGION_SUBTRACT( pScreen, exposed, universe, borderVisible); + REGION_DESTROY( pScreen, borderVisible); + } + else + { + REGION_SUBTRACT( pScreen, exposed, universe, &pParent->borderClip); + } + if (HasParentRelativeBorder(pParent) && (dx || dy)) + REGION_SUBTRACT( pScreen, &pParent->valdata->after.borderExposed, + universe, + &pParent->winSize); + else + REGION_SUBTRACT( pScreen, &pParent->valdata->after.borderExposed, + exposed, &pParent->winSize); + + REGION_COPY( pScreen, &pParent->borderClip, universe); + + /* + * To get the right clipList for the parent, and to make doubly sure + * that no child overlaps the parent's border, we remove the parent's + * border from the universe before proceeding. + */ + + REGION_INTERSECT( pScreen, universe, universe, &pParent->winSize); + } + else + REGION_COPY( pScreen, &pParent->borderClip, universe); + + if ((pChild = pParent->firstChild) && pParent->mapped) + { + REGION_INIT(pScreen, &childUniverse, NullBox, 0); + REGION_INIT(pScreen, &childUnion, NullBox, 0); + if ((pChild->drawable.y < pParent->lastChild->drawable.y) || + ((pChild->drawable.y == pParent->lastChild->drawable.y) && + (pChild->drawable.x < pParent->lastChild->drawable.x))) + { + for (; pChild; pChild = pChild->nextSib) + { + if (pChild->viewable) + REGION_APPEND( pScreen, &childUnion, &pChild->borderSize); + } + } + else + { + for (pChild = pParent->lastChild; pChild; pChild = pChild->prevSib) + { + if (pChild->viewable) + REGION_APPEND( pScreen, &childUnion, &pChild->borderSize); + } + } + REGION_VALIDATE( pScreen, &childUnion, &overlap); + + for (pChild = pParent->firstChild; + pChild; + pChild = pChild->nextSib) + { + if (pChild->viewable) { + /* + * If the child is viewable, we want to remove its extents + * from the current universe, but we only re-clip it if + * it's been marked. + */ + if (pChild->valdata) { + /* + * Figure out the new universe from the child's + * perspective and recurse. + */ + REGION_INTERSECT( pScreen, &childUniverse, + universe, + &pChild->borderSize); + miComputeClips (pChild, pScreen, &childUniverse, kind, + exposed); + } + /* + * Once the child has been processed, we remove its extents + * from the current universe, thus denying its space to any + * other sibling. + */ + if (overlap) + REGION_SUBTRACT( pScreen, universe, universe, + &pChild->borderSize); + } + } + if (!overlap) + REGION_SUBTRACT( pScreen, universe, universe, &childUnion); + REGION_UNINIT( pScreen, &childUnion); + REGION_UNINIT( pScreen, &childUniverse); + } /* if any children */ + + /* + * 'universe' now contains the new clipList for the parent window. + * + * To figure the exposure of the window we subtract the old clip from the + * new, just as for the border. + */ + + if (oldVis == VisibilityFullyObscured || + oldVis == VisibilityNotViewable) + { + REGION_COPY( pScreen, &pParent->valdata->after.exposed, universe); + } + else if (newVis != VisibilityFullyObscured && + newVis != VisibilityNotViewable) + { + REGION_SUBTRACT( pScreen, &pParent->valdata->after.exposed, + universe, &pParent->clipList); + } + + /* + * One last thing: backing storage. We have to try to save what parts of + * the window are about to be obscured. We can just subtract the universe + * from the old clipList and get the areas that were in the old but aren't + * in the new and, hence, are about to be obscured. + */ + if (pParent->backStorage && !resized) + { + REGION_SUBTRACT( pScreen, exposed, &pParent->clipList, universe); + (* pScreen->SaveDoomedAreas)(pParent, exposed, dx, dy); + } + + /* HACK ALERT - copying contents of regions, instead of regions */ + { + RegionRec tmp; + + tmp = pParent->clipList; + pParent->clipList = *universe; + *universe = tmp; + } + +#ifdef NOTDEF + REGION_COPY( pScreen, &pParent->clipList, universe); +#endif + + pParent->drawable.serialNumber = NEXT_SERIAL_NUMBER; + + if (pScreen->ClipNotify) + (* pScreen->ClipNotify) (pParent, dx, dy); +} + +static void +miTreeObscured(pParent) + register WindowPtr pParent; +{ + register WindowPtr pChild; + register int oldVis; + + pChild = pParent; + while (1) + { + if (pChild->viewable) + { + oldVis = pChild->visibility; + if (oldVis != (pChild->visibility = VisibilityFullyObscured) && + ((pChild->eventMask | wOtherEventMasks(pChild)) & VisibilityChangeMask)) + SendVisibilityNotify(pChild); + if (pChild->firstChild) + { + pChild = pChild->firstChild; + continue; + } + } + while (!pChild->nextSib && (pChild != pParent)) + pChild = pChild->parent; + if (pChild == pParent) + break; + pChild = pChild->nextSib; + } +} + +/*- + *----------------------------------------------------------------------- + * miValidateTree -- + * Recomputes the clip list for pParent and all its inferiors. + * + * Results: + * Always returns 1. + * + * Side Effects: + * The clipList, borderClip, exposed, and borderExposed regions for + * each marked window are altered. + * + * Notes: + * This routine assumes that all affected windows have been marked + * (valdata created) and their winSize and borderSize regions + * adjusted to correspond to their new positions. The borderClip and + * clipList regions should not have been touched. + * + * The top-most level is treated differently from all lower levels + * because pParent is unchanged. For the top level, we merge the + * regions taken up by the marked children back into the clipList + * for pParent, thus forming a region from which the marked children + * can claim their areas. For lower levels, where the old clipList + * and borderClip are invalid, we can't do this and have to do the + * extra operations done in miComputeClips, but this is much faster + * e.g. when only one child has moved... + * + *----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +int +miValidateTree (pParent, pChild, kind) + WindowPtr pParent; /* Parent to validate */ + WindowPtr pChild; /* First child of pParent that was + * affected */ + VTKind kind; /* What kind of configuration caused call */ +{ + RegionRec totalClip; /* Total clipping region available to + * the marked children. pParent's clipList + * merged with the borderClips of all + * the marked children. */ + RegionRec childClip; /* The new borderClip for the current + * child */ + RegionRec childUnion; /* the space covered by borderSize for + * all marked children */ + RegionRec exposed; /* For intermediate calculations */ + register ScreenPtr pScreen; + register WindowPtr pWin; + Bool overlap; + int viewvals; + Bool forward; + + pScreen = pParent->drawable.pScreen; + if (pChild == NullWindow) + pChild = pParent->firstChild; + + REGION_INIT(pScreen, &childClip, NullBox, 0); + REGION_INIT(pScreen, &exposed, NullBox, 0); + + /* + * compute the area of the parent window occupied + * by the marked children + the parent itself. This + * is the area which can be divied up among the marked + * children in their new configuration. + */ + REGION_INIT(pScreen, &totalClip, NullBox, 0); + viewvals = 0; + if ((pChild->drawable.y < pParent->lastChild->drawable.y) || + ((pChild->drawable.y == pParent->lastChild->drawable.y) && + (pChild->drawable.x < pParent->lastChild->drawable.x))) + { + forward = TRUE; + for (pWin = pChild; pWin; pWin = pWin->nextSib) + { + if (pWin->valdata) + { + REGION_APPEND( pScreen, &totalClip, &pWin->borderClip); + if (pWin->viewable) + viewvals++; + } + } + } + else + { + forward = FALSE; + pWin = pParent->lastChild; + while (1) + { + if (pWin->valdata) + { + REGION_APPEND( pScreen, &totalClip, &pWin->borderClip); + if (pWin->viewable) + viewvals++; + } + if (pWin == pChild) + break; + pWin = pWin->prevSib; + } + } + REGION_VALIDATE( pScreen, &totalClip, &overlap); + + /* + * Now go through the children of the root and figure their new + * borderClips from the totalClip, passing that off to miComputeClips + * to handle recursively. Once that's done, we remove the child + * from the totalClip to clip any siblings below it. + */ + + overlap = TRUE; + if (kind != VTStack) + { + REGION_UNION( pScreen, &totalClip, &totalClip, &pParent->clipList); + if (viewvals > 1) + { + /* + * precompute childUnion to discover whether any of them + * overlap. This seems redundant, but performance studies + * have demonstrated that the cost of this loop is + * lower than the cost of multiple Subtracts in the + * loop below. + */ + REGION_INIT(pScreen, &childUnion, NullBox, 0); + if (forward) + { + for (pWin = pChild; pWin; pWin = pWin->nextSib) + if (pWin->valdata && pWin->viewable) + REGION_APPEND( pScreen, &childUnion, + &pWin->borderSize); + } + else + { + pWin = pParent->lastChild; + while (1) + { + if (pWin->valdata && pWin->viewable) + REGION_APPEND( pScreen, &childUnion, + &pWin->borderSize); + if (pWin == pChild) + break; + pWin = pWin->prevSib; + } + } + REGION_VALIDATE(pScreen, &childUnion, &overlap); + if (overlap) + REGION_UNINIT(pScreen, &childUnion); + } + } + + for (pWin = pChild; + pWin != NullWindow; + pWin = pWin->nextSib) + { + if (pWin->viewable) { + if (pWin->valdata) { + REGION_INTERSECT( pScreen, &childClip, + &totalClip, + &pWin->borderSize); + miComputeClips (pWin, pScreen, &childClip, kind, &exposed); + if (overlap) + { + REGION_SUBTRACT( pScreen, &totalClip, + &totalClip, + &pWin->borderSize); + } + } else if (pWin->visibility == VisibilityNotViewable) { + miTreeObscured(pWin); + } + } else { + if (pWin->valdata) { + REGION_EMPTY( pScreen, &pWin->clipList); + if (pScreen->ClipNotify) + (* pScreen->ClipNotify) (pWin, 0, 0); + REGION_EMPTY( pScreen, &pWin->borderClip); + pWin->valdata = (ValidatePtr)NULL; + } + } + } + + REGION_UNINIT( pScreen, &childClip); + if (!overlap) + { + REGION_SUBTRACT(pScreen, &totalClip, &totalClip, &childUnion); + REGION_UNINIT(pScreen, &childUnion); + } + + REGION_INIT( pScreen, &pParent->valdata->after.exposed, NullBox, 0); + REGION_INIT( pScreen, &pParent->valdata->after.borderExposed, NullBox, 0); + + /* + * each case below is responsible for updating the + * clipList and serial number for the parent window + */ + + switch (kind) { + case VTStack: + break; + default: + /* + * totalClip contains the new clipList for the parent. Figure out + * exposures and obscures as per miComputeClips and reset the parent's + * clipList. + */ + REGION_SUBTRACT( pScreen, &pParent->valdata->after.exposed, + &totalClip, &pParent->clipList); + /* fall through */ + case VTMap: + if (pParent->backStorage) { + REGION_SUBTRACT( pScreen, &exposed, &pParent->clipList, &totalClip); + (* pScreen->SaveDoomedAreas)(pParent, &exposed, 0, 0); + } + + REGION_COPY( pScreen, &pParent->clipList, &totalClip); + pParent->drawable.serialNumber = NEXT_SERIAL_NUMBER; + break; + } + + REGION_UNINIT( pScreen, &totalClip); + REGION_UNINIT( pScreen, &exposed); + if (pScreen->ClipNotify) + (*pScreen->ClipNotify) (pParent, 0, 0); + return (1); +} diff --git a/mi/miwideline.c b/mi/miwideline.c new file mode 100644 index 000000000..b78435705 --- /dev/null +++ b/mi/miwideline.c @@ -0,0 +1,2203 @@ +/* $Xorg: miwideline.c,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ +/* + +Copyright 1988, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + +/* Author: Keith Packard, MIT X Consortium */ + +/* + * Mostly integer wideline code. Uses a technique similar to + * bresenham zero-width lines, except walks an X edge + */ + +#include <stdio.h> +#ifdef _XOPEN_SOURCE +#include <math.h> +#else +#define _XOPEN_SOURCE /* to get prototype for hypot on some systems */ +#include <math.h> +#undef _XOPEN_SOURCE +#endif +#include "X.h" +#include "windowstr.h" +#include "gcstruct.h" +#include "miscstruct.h" +#include "miwideline.h" +#include "mi.h" + +#ifdef ICEILTEMPDECL +ICEILTEMPDECL +#endif + +static void miLineArc(); + +/* + * spans-based polygon filler + */ + +void +miFillPolyHelper (pDrawable, pGC, pixel, spanData, y, overall_height, + left, right, left_count, right_count) + DrawablePtr pDrawable; + GCPtr pGC; + unsigned long pixel; + SpanDataPtr spanData; + int y; /* start y coordinate */ + int overall_height; /* height of entire segment */ + PolyEdgePtr left, right; + int left_count, right_count; +{ + register int left_x, left_e; + int left_stepx; + int left_signdx; + int left_dy, left_dx; + + register int right_x, right_e; + int right_stepx; + int right_signdx; + int right_dy, right_dx; + + int height; + int left_height, right_height; + + register DDXPointPtr ppt; + DDXPointPtr pptInit; + register int *pwidth; + int *pwidthInit; + XID oldPixel; + int xorg; + Spans spanRec; + + left_height = 0; + right_height = 0; + + if (!spanData) + { + pptInit = (DDXPointPtr) ALLOCATE_LOCAL (overall_height * sizeof(*ppt)); + if (!pptInit) + return; + pwidthInit = (int *) ALLOCATE_LOCAL (overall_height * sizeof(*pwidth)); + if (!pwidthInit) + { + DEALLOCATE_LOCAL (pptInit); + return; + } + ppt = pptInit; + pwidth = pwidthInit; + oldPixel = pGC->fgPixel; + if (pixel != oldPixel) + { + DoChangeGC (pGC, GCForeground, (XID *)&pixel, FALSE); + ValidateGC (pDrawable, pGC); + } + } + else + { + spanRec.points = (DDXPointPtr) xalloc (overall_height * sizeof (*ppt)); + if (!spanRec.points) + return; + spanRec.widths = (int *) xalloc (overall_height * sizeof (int)); + if (!spanRec.widths) + { + xfree (spanRec.points); + return; + } + ppt = spanRec.points; + pwidth = spanRec.widths; + } + + xorg = 0; + if (pGC->miTranslate) + { + y += pDrawable->y; + xorg = pDrawable->x; + } + while ((left_count || left_height) && + (right_count || right_height)) + { + MIPOLYRELOADLEFT + MIPOLYRELOADRIGHT + + height = left_height; + if (height > right_height) + height = right_height; + + left_height -= height; + right_height -= height; + + while (--height >= 0) + { + if (right_x >= left_x) + { + ppt->y = y; + ppt->x = left_x + xorg; + ppt++; + *pwidth++ = right_x - left_x + 1; + } + y++; + + MIPOLYSTEPLEFT + + MIPOLYSTEPRIGHT + } + } + if (!spanData) + { + (*pGC->ops->FillSpans) (pDrawable, pGC, ppt - pptInit, pptInit, pwidthInit, TRUE); + DEALLOCATE_LOCAL (pwidthInit); + DEALLOCATE_LOCAL (pptInit); + if (pixel != oldPixel) + { + DoChangeGC (pGC, GCForeground, &oldPixel, FALSE); + ValidateGC (pDrawable, pGC); + } + } + else + { + spanRec.count = ppt - spanRec.points; + AppendSpanGroup (pGC, pixel, &spanRec, spanData) + } +} + +static void +miFillRectPolyHelper (pDrawable, pGC, pixel, spanData, x, y, w, h) + DrawablePtr pDrawable; + GCPtr pGC; + unsigned long pixel; + SpanDataPtr spanData; + int x, y, w, h; +{ + register DDXPointPtr ppt; + register int *pwidth; + XID oldPixel; + Spans spanRec; + xRectangle rect; + + if (!spanData) + { + rect.x = x; + rect.y = y; + rect.width = w; + rect.height = h; + oldPixel = pGC->fgPixel; + if (pixel != oldPixel) + { + DoChangeGC (pGC, GCForeground, (XID *)&pixel, FALSE); + ValidateGC (pDrawable, pGC); + } + (*pGC->ops->PolyFillRect) (pDrawable, pGC, 1, &rect); + if (pixel != oldPixel) + { + DoChangeGC (pGC, GCForeground, &oldPixel, FALSE); + ValidateGC (pDrawable, pGC); + } + } + else + { + spanRec.points = (DDXPointPtr) xalloc (h * sizeof (*ppt)); + if (!spanRec.points) + return; + spanRec.widths = (int *) xalloc (h * sizeof (int)); + if (!spanRec.widths) + { + xfree (spanRec.points); + return; + } + ppt = spanRec.points; + pwidth = spanRec.widths; + + if (pGC->miTranslate) + { + y += pDrawable->y; + x += pDrawable->x; + } + while (h--) + { + ppt->x = x; + ppt->y = y; + ppt++; + *pwidth++ = w; + y++; + } + spanRec.count = ppt - spanRec.points; + AppendSpanGroup (pGC, pixel, &spanRec, spanData) + } +} + +/* static */ int +miPolyBuildEdge (x0, y0, k, dx, dy, xi, yi, left, edge) + double x0, y0; + double k; /* x0 * dy - y0 * dx */ + register int dx, dy; + int xi, yi; + int left; + register PolyEdgePtr edge; +{ + int x, y, e; + int xady; + + if (dy < 0) + { + dy = -dy; + dx = -dx; + k = -k; + } + +#ifdef NOTDEF + { + double realk, kerror; + realk = x0 * dy - y0 * dx; + kerror = Fabs (realk - k); + if (kerror > .1) + printf ("realk: %g k: %g\n", realk, k); + } +#endif + y = ICEIL (y0); + xady = ICEIL (k) + y * dx; + + if (xady <= 0) + x = - (-xady / dy) - 1; + else + x = (xady - 1) / dy; + + e = xady - x * dy; + + if (dx >= 0) + { + edge->signdx = 1; + edge->stepx = dx / dy; + edge->dx = dx % dy; + } + else + { + edge->signdx = -1; + edge->stepx = - (-dx / dy); + edge->dx = -dx % dy; + e = dy - e + 1; + } + edge->dy = dy; + edge->x = x + left + xi; + edge->e = e - dy; /* bias to compare against 0 instead of dy */ + return y + yi; +} + +#define StepAround(v, incr, max) (((v) + (incr) < 0) ? (max - 1) : ((v) + (incr) == max) ? 0 : ((v) + (incr))) + +/* static */ int +miPolyBuildPoly (vertices, slopes, count, xi, yi, left, right, pnleft, pnright, h) + register PolyVertexPtr vertices; + register PolySlopePtr slopes; + int count; + int xi, yi; + PolyEdgePtr left, right; + int *pnleft, *pnright; + int *h; +{ + int top, bottom; + double miny, maxy; + register int i; + int j; + int clockwise; + int slopeoff; + register int s; + register int nright, nleft; + int y, lasty, bottomy, topy; + + /* find the top of the polygon */ + maxy = miny = vertices[0].y; + bottom = top = 0; + for (i = 1; i < count; i++) + { + if (vertices[i].y < miny) + { + top = i; + miny = vertices[i].y; + } + if (vertices[i].y >= maxy) + { + bottom = i; + maxy = vertices[i].y; + } + } + clockwise = 1; + slopeoff = 0; + + i = top; + j = StepAround (top, -1, count); + + if (slopes[j].dy * slopes[i].dx > slopes[i].dy * slopes[j].dx) + { + clockwise = -1; + slopeoff = -1; + } + + bottomy = ICEIL (maxy) + yi; + + nright = 0; + + s = StepAround (top, slopeoff, count); + i = top; + while (i != bottom) + { + if (slopes[s].dy != 0) + { + y = miPolyBuildEdge (vertices[i].x, vertices[i].y, + slopes[s].k, + slopes[s].dx, slopes[s].dy, + xi, yi, 0, + &right[nright]); + if (nright != 0) + right[nright-1].height = y - lasty; + else + topy = y; + nright++; + lasty = y; + } + + i = StepAround (i, clockwise, count); + s = StepAround (s, clockwise, count); + } + if (nright != 0) + right[nright-1].height = bottomy - lasty; + + if (slopeoff == 0) + slopeoff = -1; + else + slopeoff = 0; + + nleft = 0; + s = StepAround (top, slopeoff, count); + i = top; + while (i != bottom) + { + if (slopes[s].dy != 0) + { + y = miPolyBuildEdge (vertices[i].x, vertices[i].y, + slopes[s].k, + slopes[s].dx, slopes[s].dy, xi, yi, 1, + &left[nleft]); + + if (nleft != 0) + left[nleft-1].height = y - lasty; + nleft++; + lasty = y; + } + i = StepAround (i, -clockwise, count); + s = StepAround (s, -clockwise, count); + } + if (nleft != 0) + left[nleft-1].height = bottomy - lasty; + *pnleft = nleft; + *pnright = nright; + *h = bottomy - topy; + return topy; +} + +static void +miLineOnePoint (pDrawable, pGC, pixel, spanData, x, y) + DrawablePtr pDrawable; + GCPtr pGC; + unsigned long pixel; + SpanDataPtr spanData; + int x, y; +{ + DDXPointRec pt; + int wid; + unsigned long oldPixel; + + MILINESETPIXEL (pDrawable, pGC, pixel, oldPixel); + if (pGC->fillStyle == FillSolid) + { + pt.x = x; + pt.y = y; + (*pGC->ops->PolyPoint) (pDrawable, pGC, CoordModeOrigin, 1, &pt); + } + else + { + wid = 1; + if (pGC->miTranslate) + { + x += pDrawable->x; + y += pDrawable->y; + } + pt.x = x; + pt.y = y; + (*pGC->ops->FillSpans) (pDrawable, pGC, 1, &pt, &wid, TRUE); + } + MILINERESETPIXEL (pDrawable, pGC, pixel, oldPixel); +} + +static void +miLineJoin (pDrawable, pGC, pixel, spanData, pLeft, pRight) + DrawablePtr pDrawable; + GCPtr pGC; + unsigned long pixel; + SpanDataPtr spanData; + register LineFacePtr pLeft, pRight; +{ + double mx, my; + double denom; + PolyVertexRec vertices[4]; + PolySlopeRec slopes[4]; + int edgecount; + PolyEdgeRec left[4], right[4]; + int nleft, nright; + int y, height; + int swapslopes; + int joinStyle = pGC->joinStyle; + int lw = pGC->lineWidth; + + if (lw == 1 && !spanData) { + /* Lines going in the same direction have no join */ + if (pLeft->dx >= 0 == pRight->dx <= 0) + return; + if (joinStyle != JoinRound) { + denom = - pLeft->dx * (double)pRight->dy + pRight->dx * (double)pLeft->dy; + if (denom == 0) + return; /* no join to draw */ + } + if (joinStyle != JoinMiter) { + miLineOnePoint (pDrawable, pGC, pixel, spanData, pLeft->x, pLeft->y); + return; + } + } else { + if (joinStyle == JoinRound) + { + miLineArc(pDrawable, pGC, pixel, spanData, + pLeft, pRight, + (double)0.0, (double)0.0, TRUE); + return; + } + denom = - pLeft->dx * (double)pRight->dy + pRight->dx * (double)pLeft->dy; + if (denom == 0.0) + return; /* no join to draw */ + } + + swapslopes = 0; + if (denom > 0) + { + pLeft->xa = -pLeft->xa; + pLeft->ya = -pLeft->ya; + pLeft->dx = -pLeft->dx; + pLeft->dy = -pLeft->dy; + } + else + { + swapslopes = 1; + pRight->xa = -pRight->xa; + pRight->ya = -pRight->ya; + pRight->dx = -pRight->dx; + pRight->dy = -pRight->dy; + } + + vertices[0].x = pRight->xa; + vertices[0].y = pRight->ya; + slopes[0].dx = -pRight->dy; + slopes[0].dy = pRight->dx; + slopes[0].k = 0; + + vertices[1].x = 0; + vertices[1].y = 0; + slopes[1].dx = pLeft->dy; + slopes[1].dy = -pLeft->dx; + slopes[1].k = 0; + + vertices[2].x = pLeft->xa; + vertices[2].y = pLeft->ya; + + if (joinStyle == JoinMiter) + { + my = (pLeft->dy * (pRight->xa * pRight->dy - pRight->ya * pRight->dx) - + pRight->dy * (pLeft->xa * pLeft->dy - pLeft->ya * pLeft->dx )) / + denom; + if (pLeft->dy != 0) + { + mx = pLeft->xa + (my - pLeft->ya) * + (double) pLeft->dx / (double) pLeft->dy; + } + else + { + mx = pRight->xa + (my - pRight->ya) * + (double) pRight->dx / (double) pRight->dy; + } + /* check miter limit */ + if ((mx * mx + my * my) * 4 > SQSECANT * lw * lw) + joinStyle = JoinBevel; + } + + if (joinStyle == JoinMiter) + { + slopes[2].dx = pLeft->dx; + slopes[2].dy = pLeft->dy; + slopes[2].k = pLeft->k; + if (swapslopes) + { + slopes[2].dx = -slopes[2].dx; + slopes[2].dy = -slopes[2].dy; + slopes[2].k = -slopes[2].k; + } + vertices[3].x = mx; + vertices[3].y = my; + slopes[3].dx = pRight->dx; + slopes[3].dy = pRight->dy; + slopes[3].k = pRight->k; + if (swapslopes) + { + slopes[3].dx = -slopes[3].dx; + slopes[3].dy = -slopes[3].dy; + slopes[3].k = -slopes[3].k; + } + edgecount = 4; + } + else + { + double scale, dx, dy, adx, ady; + + adx = dx = pRight->xa - pLeft->xa; + ady = dy = pRight->ya - pLeft->ya; + if (adx < 0) + adx = -adx; + if (ady < 0) + ady = -ady; + scale = ady; + if (adx > ady) + scale = adx; + slopes[2].dx = (dx * 65536) / scale; + slopes[2].dy = (dy * 65536) / scale; + slopes[2].k = ((pLeft->xa + pRight->xa) * slopes[2].dy - + (pLeft->ya + pRight->ya) * slopes[2].dx) / 2.0; + edgecount = 3; + } + + y = miPolyBuildPoly (vertices, slopes, edgecount, pLeft->x, pLeft->y, + left, right, &nleft, &nright, &height); + miFillPolyHelper (pDrawable, pGC, pixel, spanData, y, height, left, right, nleft, nright); +} + +static int +miLineArcI (pDraw, pGC, xorg, yorg, points, widths) + DrawablePtr pDraw; + GCPtr pGC; + int xorg, yorg; + DDXPointPtr points; + int *widths; +{ + register DDXPointPtr tpts, bpts; + register int *twids, *bwids; + register int x, y, e, ex, slw; + + tpts = points; + twids = widths; + if (pGC->miTranslate) + { + xorg += pDraw->x; + yorg += pDraw->y; + } + slw = pGC->lineWidth; + if (slw == 1) + { + tpts->x = xorg; + tpts->y = yorg; + *twids = 1; + return 1; + } + bpts = tpts + slw; + bwids = twids + slw; + y = (slw >> 1) + 1; + if (slw & 1) + e = - ((y << 2) + 3); + else + e = - (y << 3); + ex = -4; + x = 0; + while (y) + { + e += (y << 3) - 4; + while (e >= 0) + { + x++; + e += (ex = -((x << 3) + 4)); + } + y--; + slw = (x << 1) + 1; + if ((e == ex) && (slw > 1)) + slw--; + tpts->x = xorg - x; + tpts->y = yorg - y; + tpts++; + *twids++ = slw; + if ((y != 0) && ((slw > 1) || (e != ex))) + { + bpts--; + bpts->x = xorg - x; + bpts->y = yorg + y; + *--bwids = slw; + } + } + return (pGC->lineWidth); +} + +#define CLIPSTEPEDGE(edgey,edge,edgeleft) \ + if (ybase == edgey) \ + { \ + if (edgeleft) \ + { \ + if (edge->x > xcl) \ + xcl = edge->x; \ + } \ + else \ + { \ + if (edge->x < xcr) \ + xcr = edge->x; \ + } \ + edgey++; \ + edge->x += edge->stepx; \ + edge->e += edge->dx; \ + if (edge->e > 0) \ + { \ + edge->x += edge->signdx; \ + edge->e -= edge->dy; \ + } \ + } + +static int +miLineArcD (pDraw, pGC, xorg, yorg, points, widths, + edge1, edgey1, edgeleft1, edge2, edgey2, edgeleft2) + DrawablePtr pDraw; + GCPtr pGC; + double xorg, yorg; + DDXPointPtr points; + int *widths; + PolyEdgePtr edge1, edge2; + int edgey1, edgey2; + Bool edgeleft1, edgeleft2; +{ + register DDXPointPtr pts; + register int *wids; + double radius, x0, y0, el, er, yk, xlk, xrk, k; + int xbase, ybase, y, boty, xl, xr, xcl, xcr; + int ymin, ymax; + Bool edge1IsMin, edge2IsMin; + int ymin1, ymin2; + + pts = points; + wids = widths; + xbase = floor(xorg); + x0 = xorg - xbase; + ybase = ICEIL (yorg); + y0 = yorg - ybase; + if (pGC->miTranslate) + { + xbase += pDraw->x; + ybase += pDraw->y; + edge1->x += pDraw->x; + edge2->x += pDraw->x; + edgey1 += pDraw->y; + edgey2 += pDraw->y; + } + xlk = x0 + x0 + 1.0; + xrk = x0 + x0 - 1.0; + yk = y0 + y0 - 1.0; + radius = ((double)pGC->lineWidth) / 2.0; + y = floor(radius - y0 + 1.0); + ybase -= y; + ymin = ybase; + ymax = 65536; + edge1IsMin = FALSE; + ymin1 = edgey1; + if (edge1->dy >= 0) + { + if (!edge1->dy) + { + if (edgeleft1) + edge1IsMin = TRUE; + else + ymax = edgey1; + edgey1 = 65536; + } + else + { + if ((edge1->signdx < 0) == edgeleft1) + edge1IsMin = TRUE; + } + } + edge2IsMin = FALSE; + ymin2 = edgey2; + if (edge2->dy >= 0) + { + if (!edge2->dy) + { + if (edgeleft2) + edge2IsMin = TRUE; + else + ymax = edgey2; + edgey2 = 65536; + } + else + { + if ((edge2->signdx < 0) == edgeleft2) + edge2IsMin = TRUE; + } + } + if (edge1IsMin) + { + ymin = ymin1; + if (edge2IsMin && ymin1 > ymin2) + ymin = ymin2; + } else if (edge2IsMin) + ymin = ymin2; + el = radius * radius - ((y + y0) * (y + y0)) - (x0 * x0); + er = el + xrk; + xl = 1; + xr = 0; + if (x0 < 0.5) + { + xl = 0; + el -= xlk; + } + boty = (y0 < -0.5) ? 1 : 0; + if (ybase + y - boty > ymax) + boty = ymax - ybase - y; + while (y > boty) + { + k = (y << 1) + yk; + er += k; + while (er > 0.0) + { + xr++; + er += xrk - (xr << 1); + } + el += k; + while (el >= 0.0) + { + xl--; + el += (xl << 1) - xlk; + } + y--; + ybase++; + if (ybase < ymin) + continue; + xcl = xl + xbase; + xcr = xr + xbase; + CLIPSTEPEDGE(edgey1, edge1, edgeleft1); + CLIPSTEPEDGE(edgey2, edge2, edgeleft2); + if (xcr >= xcl) + { + pts->x = xcl; + pts->y = ybase; + pts++; + *wids++ = xcr - xcl + 1; + } + } + er = xrk - (xr << 1) - er; + el = (xl << 1) - xlk - el; + boty = floor(-y0 - radius + 1.0); + if (ybase + y - boty > ymax) + boty = ymax - ybase - y; + while (y > boty) + { + k = (y << 1) + yk; + er -= k; + while ((er >= 0.0) && (xr >= 0)) + { + xr--; + er += xrk - (xr << 1); + } + el -= k; + while ((el > 0.0) && (xl <= 0)) + { + xl++; + el += (xl << 1) - xlk; + } + y--; + ybase++; + if (ybase < ymin) + continue; + xcl = xl + xbase; + xcr = xr + xbase; + CLIPSTEPEDGE(edgey1, edge1, edgeleft1); + CLIPSTEPEDGE(edgey2, edge2, edgeleft2); + if (xcr >= xcl) + { + pts->x = xcl; + pts->y = ybase; + pts++; + *wids++ = xcr - xcl + 1; + } + } + return (pts - points); +} + +int +miRoundJoinFace (face, edge, leftEdge) + register LineFacePtr face; + register PolyEdgePtr edge; + Bool *leftEdge; +{ + int y; + int dx, dy; + double xa, ya; + Bool left; + + dx = -face->dy; + dy = face->dx; + xa = face->xa; + ya = face->ya; + left = 1; + if (ya > 0) + { + ya = 0.0; + xa = 0.0; + } + if (dy < 0 || dy == 0 && dx > 0) + { + dx = -dx; + dy = -dy; + left = !left; + } + if (dx == 0 && dy == 0) + dy = 1; + if (dy == 0) + { + y = ICEIL (face->ya) + face->y; + edge->x = -32767; + edge->stepx = 0; + edge->signdx = 0; + edge->e = -1; + edge->dy = 0; + edge->dx = 0; + edge->height = 0; + } + else + { + y = miPolyBuildEdge (xa, ya, 0.0, dx, dy, face->x, face->y, !left, edge); + edge->height = 32767; + } + *leftEdge = !left; + return y; +} + +void +miRoundJoinClip (pLeft, pRight, edge1, edge2, y1, y2, left1, left2) + register LineFacePtr pLeft, pRight; + PolyEdgePtr edge1, edge2; + int *y1, *y2; + Bool *left1, *left2; +{ + double denom; + + denom = - pLeft->dx * (double)pRight->dy + pRight->dx * (double)pLeft->dy; + + if (denom >= 0) + { + pLeft->xa = -pLeft->xa; + pLeft->ya = -pLeft->ya; + } + else + { + pRight->xa = -pRight->xa; + pRight->ya = -pRight->ya; + } + *y1 = miRoundJoinFace (pLeft, edge1, left1); + *y2 = miRoundJoinFace (pRight, edge2, left2); +} + +int +miRoundCapClip (face, isInt, edge, leftEdge) + register LineFacePtr face; + Bool isInt; + register PolyEdgePtr edge; + Bool *leftEdge; +{ + int y; + register int dx, dy; + double xa, ya, k; + Bool left; + + dx = -face->dy; + dy = face->dx; + xa = face->xa; + ya = face->ya; + k = 0.0; + if (!isInt) + k = face->k; + left = 1; + if (dy < 0 || dy == 0 && dx > 0) + { + dx = -dx; + dy = -dy; + xa = -xa; + ya = -ya; + left = !left; + } + if (dx == 0 && dy == 0) + dy = 1; + if (dy == 0) + { + y = ICEIL (face->ya) + face->y; + edge->x = -32767; + edge->stepx = 0; + edge->signdx = 0; + edge->e = -1; + edge->dy = 0; + edge->dx = 0; + edge->height = 0; + } + else + { + y = miPolyBuildEdge (xa, ya, k, dx, dy, face->x, face->y, !left, edge); + edge->height = 32767; + } + *leftEdge = !left; + return y; +} + +static void +miLineArc (pDraw, pGC, pixel, spanData, leftFace, rightFace, xorg, yorg, isInt) + DrawablePtr pDraw; + register GCPtr pGC; + unsigned long pixel; + SpanDataPtr spanData; + register LineFacePtr leftFace, rightFace; + double xorg, yorg; + Bool isInt; +{ + DDXPointPtr points; + int *widths; + int xorgi, yorgi; + XID oldPixel; + Spans spanRec; + int n; + PolyEdgeRec edge1, edge2; + int edgey1, edgey2; + Bool edgeleft1, edgeleft2; + + if (isInt) + { + xorgi = leftFace ? leftFace->x : rightFace->x; + yorgi = leftFace ? leftFace->y : rightFace->y; + } + edgey1 = 65536; + edgey2 = 65536; + edge1.x = 0; /* not used, keep memory checkers happy */ + edge1.dy = -1; + edge2.x = 0; /* not used, keep memory checkers happy */ + edge2.dy = -1; + edgeleft1 = FALSE; + edgeleft2 = FALSE; + if ((pGC->lineStyle != LineSolid || pGC->lineWidth > 2) && + (pGC->capStyle == CapRound && pGC->joinStyle != JoinRound || + pGC->joinStyle == JoinRound && pGC->capStyle == CapButt)) + { + if (isInt) + { + xorg = (double) xorgi; + yorg = (double) yorgi; + } + if (leftFace && rightFace) + { + miRoundJoinClip (leftFace, rightFace, &edge1, &edge2, + &edgey1, &edgey2, &edgeleft1, &edgeleft2); + } + else if (leftFace) + { + edgey1 = miRoundCapClip (leftFace, isInt, &edge1, &edgeleft1); + } + else if (rightFace) + { + edgey2 = miRoundCapClip (rightFace, isInt, &edge2, &edgeleft2); + } + isInt = FALSE; + } + if (!spanData) + { + points = (DDXPointPtr)ALLOCATE_LOCAL(sizeof(DDXPointRec) * pGC->lineWidth); + if (!points) + return; + widths = (int *)ALLOCATE_LOCAL(sizeof(int) * pGC->lineWidth); + if (!widths) + { + DEALLOCATE_LOCAL(points); + return; + } + oldPixel = pGC->fgPixel; + if (pixel != oldPixel) + { + DoChangeGC(pGC, GCForeground, (XID *)&pixel, FALSE); + ValidateGC (pDraw, pGC); + } + } + else + { + points = (DDXPointPtr) xalloc (pGC->lineWidth * sizeof (DDXPointRec)); + if (!points) + return; + widths = (int *) xalloc (pGC->lineWidth * sizeof (int)); + if (!widths) + { + xfree (points); + return; + } + spanRec.points = points; + spanRec.widths = widths; + } + if (isInt) + n = miLineArcI(pDraw, pGC, xorgi, yorgi, points, widths); + else + n = miLineArcD(pDraw, pGC, xorg, yorg, points, widths, + &edge1, edgey1, edgeleft1, + &edge2, edgey2, edgeleft2); + + if (!spanData) + { + (*pGC->ops->FillSpans)(pDraw, pGC, n, points, widths, TRUE); + DEALLOCATE_LOCAL(widths); + DEALLOCATE_LOCAL(points); + if (pixel != oldPixel) + { + DoChangeGC(pGC, GCForeground, &oldPixel, FALSE); + ValidateGC (pDraw, pGC); + } + } + else + { + spanRec.count = n; + AppendSpanGroup (pGC, pixel, &spanRec, spanData) + } +} + +void +miLineProjectingCap (pDrawable, pGC, pixel, spanData, face, isLeft, xorg, yorg, isInt) + DrawablePtr pDrawable; + register GCPtr pGC; + unsigned long pixel; + SpanDataPtr spanData; + register LineFacePtr face; + Bool isLeft; + double xorg, yorg; + Bool isInt; +{ + int xorgi, yorgi; + int lw; + PolyEdgeRec lefts[2], rights[2]; + int lefty, righty, topy, bottomy; + PolyEdgePtr left, right; + PolyEdgePtr top, bottom; + double xa,ya; + double k; + double xap, yap; + int dx, dy; + double projectXoff, projectYoff; + double maxy; + int finaly; + + if (isInt) + { + xorgi = face->x; + yorgi = face->y; + } + lw = pGC->lineWidth; + dx = face->dx; + dy = face->dy; + k = face->k; + if (dy == 0) + { + lefts[0].height = lw; + lefts[0].x = xorgi; + if (isLeft) + lefts[0].x -= (lw >> 1); + lefts[0].stepx = 0; + lefts[0].signdx = 1; + lefts[0].e = -lw; + lefts[0].dx = 0; + lefts[0].dy = lw; + rights[0].height = lw; + rights[0].x = xorgi; + if (!isLeft) + rights[0].x += (lw + 1 >> 1); + rights[0].stepx = 0; + rights[0].signdx = 1; + rights[0].e = -lw; + rights[0].dx = 0; + rights[0].dy = lw; + miFillPolyHelper (pDrawable, pGC, pixel, spanData, yorgi - (lw >> 1), lw, + lefts, rights, 1, 1); + } + else if (dx == 0) + { + topy = yorgi; + bottomy = yorgi + dy; + if (isLeft) + topy -= (lw >> 1); + else + bottomy += (lw >> 1); + lefts[0].height = bottomy - topy; + lefts[0].x = xorgi - (lw >> 1); + lefts[0].stepx = 0; + lefts[0].signdx = 1; + lefts[0].e = -dy; + lefts[0].dx = dx; + lefts[0].dy = dy; + + rights[0].height = bottomy - topy; + rights[0].x = lefts[0].x + (lw-1); + rights[0].stepx = 0; + rights[0].signdx = 1; + rights[0].e = -dy; + rights[0].dx = dx; + rights[0].dy = dy; + miFillPolyHelper (pDrawable, pGC, pixel, spanData, topy, bottomy - topy, lefts, rights, 1, 1); + } + else + { + xa = face->xa; + ya = face->ya; + projectXoff = -ya; + projectYoff = xa; + if (dx < 0) + { + right = &rights[1]; + left = &lefts[0]; + top = &rights[0]; + bottom = &lefts[1]; + } + else + { + right = &rights[0]; + left = &lefts[1]; + top = &lefts[0]; + bottom = &rights[1]; + } + if (isLeft) + { + righty = miPolyBuildEdge (xa, ya, + k, dx, dy, xorgi, yorgi, 0, right); + + xa = -xa; + ya = -ya; + k = -k; + lefty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff, + k, dx, dy, xorgi, yorgi, 1, left); + if (dx > 0) + { + ya = -ya; + xa = -xa; + } + xap = xa - projectXoff; + yap = ya - projectYoff; + topy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy, + -dy, dx, xorgi, yorgi, dx > 0, top); + bottomy = miPolyBuildEdge (xa, ya, + 0.0, -dy, dx, xorgi, yorgi, dx < 0, bottom); + maxy = -ya; + } + else + { + righty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff, + k, dx, dy, xorgi, yorgi, 0, right); + + xa = -xa; + ya = -ya; + k = -k; + lefty = miPolyBuildEdge (xa, ya, + k, dx, dy, xorgi, yorgi, 1, left); + if (dx > 0) + { + ya = -ya; + xa = -xa; + } + xap = xa - projectXoff; + yap = ya - projectYoff; + topy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, xorgi, xorgi, dx > 0, top); + bottomy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy, + -dy, dx, xorgi, xorgi, dx < 0, bottom); + maxy = -ya + projectYoff; + } + finaly = ICEIL(maxy) + yorgi; + if (dx < 0) + { + left->height = bottomy - lefty; + right->height = finaly - righty; + top->height = righty - topy; + } + else + { + right->height = bottomy - righty; + left->height = finaly - lefty; + top->height = lefty - topy; + } + bottom->height = finaly - bottomy; + miFillPolyHelper (pDrawable, pGC, pixel, spanData, topy, + bottom->height + bottomy - topy, lefts, rights, 2, 2); + } +} + +static void +miWideSegment (pDrawable, pGC, pixel, spanData, + x1, y1, x2, y2, projectLeft, projectRight, leftFace, rightFace) + DrawablePtr pDrawable; + GCPtr pGC; + unsigned long pixel; + SpanDataPtr spanData; + register int x1, y1, x2, y2; + Bool projectLeft, projectRight; + register LineFacePtr leftFace, rightFace; +{ + double l, L, r; + double xa, ya; + double projectXoff, projectYoff; + double k; + double maxy; + int x, y; + int dx, dy; + int finaly; + PolyEdgePtr left, right; + PolyEdgePtr top, bottom; + int lefty, righty, topy, bottomy; + int signdx; + PolyEdgeRec lefts[2], rights[2]; + LineFacePtr tface; + int lw = pGC->lineWidth; + + /* draw top-to-bottom always */ + if (y2 < y1 || y2 == y1 && x2 < x1) + { + x = x1; + x1 = x2; + x2 = x; + + y = y1; + y1 = y2; + y2 = y; + + x = projectLeft; + projectLeft = projectRight; + projectRight = x; + + tface = leftFace; + leftFace = rightFace; + rightFace = tface; + } + + dy = y2 - y1; + signdx = 1; + dx = x2 - x1; + if (dx < 0) + signdx = -1; + + leftFace->x = x1; + leftFace->y = y1; + leftFace->dx = dx; + leftFace->dy = dy; + + rightFace->x = x2; + rightFace->y = y2; + rightFace->dx = -dx; + rightFace->dy = -dy; + + if (dy == 0) + { + rightFace->xa = 0; + rightFace->ya = (double) lw / 2.0; + rightFace->k = -(double) (lw * dx) / 2.0; + leftFace->xa = 0; + leftFace->ya = -rightFace->ya; + leftFace->k = rightFace->k; + x = x1; + if (projectLeft) + x -= (lw >> 1); + y = y1 - (lw >> 1); + dx = x2 - x; + if (projectRight) + dx += (lw + 1 >> 1); + dy = lw; + miFillRectPolyHelper (pDrawable, pGC, pixel, spanData, + x, y, dx, dy); + } + else if (dx == 0) + { + leftFace->xa = (double) lw / 2.0; + leftFace->ya = 0; + leftFace->k = (double) (lw * dy) / 2.0; + rightFace->xa = -leftFace->xa; + rightFace->ya = 0; + rightFace->k = leftFace->k; + y = y1; + if (projectLeft) + y -= lw >> 1; + x = x1 - (lw >> 1); + dy = y2 - y; + if (projectRight) + dy += (lw + 1 >> 1); + dx = lw; + miFillRectPolyHelper (pDrawable, pGC, pixel, spanData, + x, y, dx, dy); + } + else + { + l = ((double) lw) / 2.0; + L = hypot ((double) dx, (double) dy); + + if (dx < 0) + { + right = &rights[1]; + left = &lefts[0]; + top = &rights[0]; + bottom = &lefts[1]; + } + else + { + right = &rights[0]; + left = &lefts[1]; + top = &lefts[0]; + bottom = &rights[1]; + } + r = l / L; + + /* coord of upper bound at integral y */ + ya = -r * dx; + xa = r * dy; + + if (projectLeft | projectRight) + { + projectXoff = -ya; + projectYoff = xa; + } + + /* xa * dy - ya * dx */ + k = l * L; + + leftFace->xa = xa; + leftFace->ya = ya; + leftFace->k = k; + rightFace->xa = -xa; + rightFace->ya = -ya; + rightFace->k = k; + + if (projectLeft) + righty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff, + k, dx, dy, x1, y1, 0, right); + else + righty = miPolyBuildEdge (xa, ya, + k, dx, dy, x1, y1, 0, right); + + /* coord of lower bound at integral y */ + ya = -ya; + xa = -xa; + + /* xa * dy - ya * dx */ + k = - k; + + if (projectLeft) + lefty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff, + k, dx, dy, x1, y1, 1, left); + else + lefty = miPolyBuildEdge (xa, ya, + k, dx, dy, x1, y1, 1, left); + + /* coord of top face at integral y */ + + if (signdx > 0) + { + ya = -ya; + xa = -xa; + } + + if (projectLeft) + { + double xap = xa - projectXoff; + double yap = ya - projectYoff; + topy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy, + -dy, dx, x1, y1, dx > 0, top); + } + else + topy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, x1, y1, dx > 0, top); + + /* coord of bottom face at integral y */ + + if (projectRight) + { + double xap = xa + projectXoff; + double yap = ya + projectYoff; + bottomy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy, + -dy, dx, x2, y2, dx < 0, bottom); + maxy = -ya + projectYoff; + } + else + { + bottomy = miPolyBuildEdge (xa, ya, + 0.0, -dy, dx, x2, y2, dx < 0, bottom); + maxy = -ya; + } + + finaly = ICEIL (maxy) + y2; + + if (dx < 0) + { + left->height = bottomy - lefty; + right->height = finaly - righty; + top->height = righty - topy; + } + else + { + right->height = bottomy - righty; + left->height = finaly - lefty; + top->height = lefty - topy; + } + bottom->height = finaly - bottomy; + miFillPolyHelper (pDrawable, pGC, pixel, spanData, topy, + bottom->height + bottomy - topy, lefts, rights, 2, 2); + } +} + +SpanDataPtr +miSetupSpanData (pGC, spanData, npt) + register GCPtr pGC; + SpanDataPtr spanData; + int npt; +{ + if (npt < 3 && pGC->capStyle != CapRound || miSpansEasyRop(pGC->alu)) + return (SpanDataPtr) NULL; + if (pGC->lineStyle == LineDoubleDash) + miInitSpanGroup (&spanData->bgGroup); + miInitSpanGroup (&spanData->fgGroup); + return spanData; +} + +void +miCleanupSpanData (pDrawable, pGC, spanData) + DrawablePtr pDrawable; + GCPtr pGC; + SpanDataPtr spanData; +{ + if (pGC->lineStyle == LineDoubleDash) + { + XID oldPixel, pixel; + + pixel = pGC->bgPixel; + oldPixel = pGC->fgPixel; + if (pixel != oldPixel) + { + DoChangeGC (pGC, GCForeground, &pixel, FALSE); + ValidateGC (pDrawable, pGC); + } + miFillUniqueSpanGroup (pDrawable, pGC, &spanData->bgGroup); + miFreeSpanGroup (&spanData->bgGroup); + if (pixel != oldPixel) + { + DoChangeGC (pGC, GCForeground, &oldPixel, FALSE); + ValidateGC (pDrawable, pGC); + } + } + miFillUniqueSpanGroup (pDrawable, pGC, &spanData->fgGroup); + miFreeSpanGroup (&spanData->fgGroup); +} + +void +miWideLine (pDrawable, pGC, mode, npt, pPts) + DrawablePtr pDrawable; + register GCPtr pGC; + int mode; + register int npt; + register DDXPointPtr pPts; +{ + int x1, y1, x2, y2; + SpanDataRec spanDataRec; + SpanDataPtr spanData; + unsigned long pixel; + Bool projectLeft, projectRight; + LineFaceRec leftFace, rightFace, prevRightFace; + LineFaceRec firstFace; + register int first; + Bool somethingDrawn = FALSE; + Bool selfJoin; + + spanData = miSetupSpanData (pGC, &spanDataRec, npt); + pixel = pGC->fgPixel; + x2 = pPts->x; + y2 = pPts->y; + first = TRUE; + selfJoin = FALSE; + if (npt > 1) + { + if (mode == CoordModePrevious) + { + int nptTmp; + DDXPointPtr pPtsTmp; + + x1 = x2; + y1 = y2; + nptTmp = npt; + pPtsTmp = pPts + 1; + while (--nptTmp) + { + x1 += pPtsTmp->x; + y1 += pPtsTmp->y; + ++pPtsTmp; + } + if (x2 == x1 && y2 == y1) + selfJoin = TRUE; + } + else if (x2 == pPts[npt-1].x && y2 == pPts[npt-1].y) + { + selfJoin = TRUE; + } + } + projectLeft = pGC->capStyle == CapProjecting && !selfJoin; + projectRight = FALSE; + while (--npt) + { + x1 = x2; + y1 = y2; + ++pPts; + x2 = pPts->x; + y2 = pPts->y; + if (mode == CoordModePrevious) + { + x2 += x1; + y2 += y1; + } + if (x1 != x2 || y1 != y2) + { + somethingDrawn = TRUE; + if (npt == 1 && pGC->capStyle == CapProjecting && !selfJoin) + projectRight = TRUE; + miWideSegment (pDrawable, pGC, pixel, spanData, x1, y1, x2, y2, + projectLeft, projectRight, &leftFace, &rightFace); + if (first) + { + if (selfJoin) + firstFace = leftFace; + else if (pGC->capStyle == CapRound) + { + if (pGC->lineWidth == 1 && !spanData) + miLineOnePoint (pDrawable, pGC, pixel, spanData, x1, y1); + else + miLineArc (pDrawable, pGC, pixel, spanData, + &leftFace, (LineFacePtr) NULL, + (double)0.0, (double)0.0, + TRUE); + } + } + else + { + miLineJoin (pDrawable, pGC, pixel, spanData, &leftFace, + &prevRightFace); + } + prevRightFace = rightFace; + first = FALSE; + projectLeft = FALSE; + } + if (npt == 1 && somethingDrawn) + { + if (selfJoin) + miLineJoin (pDrawable, pGC, pixel, spanData, &firstFace, + &rightFace); + else if (pGC->capStyle == CapRound) + { + if (pGC->lineWidth == 1 && !spanData) + miLineOnePoint (pDrawable, pGC, pixel, spanData, x2, y2); + else + miLineArc (pDrawable, pGC, pixel, spanData, + (LineFacePtr) NULL, &rightFace, + (double)0.0, (double)0.0, + TRUE); + } + } + } + /* handle crock where all points are coincedent */ + if (!somethingDrawn) + { + projectLeft = pGC->capStyle == CapProjecting; + miWideSegment (pDrawable, pGC, pixel, spanData, + x2, y2, x2, y2, projectLeft, projectLeft, + &leftFace, &rightFace); + if (pGC->capStyle == CapRound) + { + miLineArc (pDrawable, pGC, pixel, spanData, + &leftFace, (LineFacePtr) NULL, + (double)0.0, (double)0.0, + TRUE); + rightFace.dx = -1; /* sleezy hack to make it work */ + miLineArc (pDrawable, pGC, pixel, spanData, + (LineFacePtr) NULL, &rightFace, + (double)0.0, (double)0.0, + TRUE); + } + } + if (spanData) + miCleanupSpanData (pDrawable, pGC, spanData); +} + +#define V_TOP 0 +#define V_RIGHT 1 +#define V_BOTTOM 2 +#define V_LEFT 3 + +static void +miWideDashSegment (pDrawable, pGC, spanData, pDashOffset, pDashIndex, + x1, y1, x2, y2, projectLeft, projectRight, leftFace, rightFace) + DrawablePtr pDrawable; + register GCPtr pGC; + int *pDashOffset, *pDashIndex; + SpanDataPtr spanData; + int x1, y1, x2, y2; + Bool projectLeft, projectRight; + LineFacePtr leftFace, rightFace; +{ + int dashIndex, dashRemain; + unsigned char *pDash; + double L, l; + double k; + PolyVertexRec vertices[4]; + PolyVertexRec saveRight, saveBottom; + PolySlopeRec slopes[4]; + PolyEdgeRec left[2], right[2]; + LineFaceRec lcapFace, rcapFace; + int nleft, nright; + int h; + int y; + int dy, dx; + unsigned long pixel; + double LRemain; + double r; + double rdx, rdy; + double dashDx, dashDy; + double saveK; + Bool first = TRUE; + double lcenterx, lcentery, rcenterx, rcentery; + unsigned long fgPixel, bgPixel; + + dx = x2 - x1; + dy = y2 - y1; + dashIndex = *pDashIndex; + pDash = pGC->dash; + dashRemain = pDash[dashIndex] - *pDashOffset; + fgPixel = pGC->fgPixel; + bgPixel = pGC->bgPixel; + if (pGC->fillStyle == FillOpaqueStippled || + pGC->fillStyle == FillTiled) + { + bgPixel = fgPixel; + } + + l = ((double) pGC->lineWidth) / 2.0; + if (dx == 0) + { + L = dy; + rdx = 0; + rdy = l; + if (dy < 0) + { + L = -dy; + rdy = -l; + } + } + else if (dy == 0) + { + L = dx; + rdx = l; + rdy = 0; + if (dx < 0) + { + L = -dx; + rdx = -l; + } + } + else + { + L = hypot ((double) dx, (double) dy); + r = l / L; + + rdx = r * dx; + rdy = r * dy; + } + k = l * L; + LRemain = L; + /* All position comments are relative to a line with dx and dy > 0, + * but the code does not depend on this */ + /* top */ + slopes[V_TOP].dx = dx; + slopes[V_TOP].dy = dy; + slopes[V_TOP].k = k; + /* right */ + slopes[V_RIGHT].dx = -dy; + slopes[V_RIGHT].dy = dx; + slopes[V_RIGHT].k = 0; + /* bottom */ + slopes[V_BOTTOM].dx = -dx; + slopes[V_BOTTOM].dy = -dy; + slopes[V_BOTTOM].k = k; + /* left */ + slopes[V_LEFT].dx = dy; + slopes[V_LEFT].dy = -dx; + slopes[V_LEFT].k = 0; + + /* preload the start coordinates */ + vertices[V_RIGHT].x = vertices[V_TOP].x = rdy; + vertices[V_RIGHT].y = vertices[V_TOP].y = -rdx; + + vertices[V_BOTTOM].x = vertices[V_LEFT].x = -rdy; + vertices[V_BOTTOM].y = vertices[V_LEFT].y = rdx; + + if (projectLeft) + { + vertices[V_TOP].x -= rdx; + vertices[V_TOP].y -= rdy; + + vertices[V_LEFT].x -= rdx; + vertices[V_LEFT].y -= rdy; + + slopes[V_LEFT].k = rdx * dx + rdy * dy; + } + + lcenterx = x1; + lcentery = y1; + + if (pGC->capStyle == CapRound) + { + lcapFace.dx = dx; + lcapFace.dy = dy; + lcapFace.x = x1; + lcapFace.y = y1; + + rcapFace.dx = -dx; + rcapFace.dy = -dy; + rcapFace.x = x1; + rcapFace.y = y1; + } + while (LRemain > dashRemain) + { + dashDx = (dashRemain * dx) / L; + dashDy = (dashRemain * dy) / L; + + rcenterx = lcenterx + dashDx; + rcentery = lcentery + dashDy; + + vertices[V_RIGHT].x += dashDx; + vertices[V_RIGHT].y += dashDy; + + vertices[V_BOTTOM].x += dashDx; + vertices[V_BOTTOM].y += dashDy; + + slopes[V_RIGHT].k = vertices[V_RIGHT].x * dx + vertices[V_RIGHT].y * dy; + + if (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1)) + { + if (pGC->lineStyle == LineOnOffDash && + pGC->capStyle == CapProjecting) + { + saveRight = vertices[V_RIGHT]; + saveBottom = vertices[V_BOTTOM]; + saveK = slopes[V_RIGHT].k; + + if (!first) + { + vertices[V_TOP].x -= rdx; + vertices[V_TOP].y -= rdy; + + vertices[V_LEFT].x -= rdx; + vertices[V_LEFT].y -= rdy; + + slopes[V_LEFT].k = vertices[V_LEFT].x * + slopes[V_LEFT].dy - + vertices[V_LEFT].y * + slopes[V_LEFT].dx; + } + + vertices[V_RIGHT].x += rdx; + vertices[V_RIGHT].y += rdy; + + vertices[V_BOTTOM].x += rdx; + vertices[V_BOTTOM].y += rdy; + + slopes[V_RIGHT].k = vertices[V_RIGHT].x * + slopes[V_RIGHT].dy - + vertices[V_RIGHT].y * + slopes[V_RIGHT].dx; + } + y = miPolyBuildPoly (vertices, slopes, 4, x1, y1, + left, right, &nleft, &nright, &h); + pixel = (dashIndex & 1) ? bgPixel : fgPixel; + miFillPolyHelper (pDrawable, pGC, pixel, spanData, y, h, left, right, nleft, nright); + + if (pGC->lineStyle == LineOnOffDash) + { + switch (pGC->capStyle) + { + case CapProjecting: + vertices[V_BOTTOM] = saveBottom; + vertices[V_RIGHT] = saveRight; + slopes[V_RIGHT].k = saveK; + break; + case CapRound: + if (!first) + { + if (dx < 0) + { + lcapFace.xa = -vertices[V_LEFT].x; + lcapFace.ya = -vertices[V_LEFT].y; + lcapFace.k = slopes[V_LEFT].k; + } + else + { + lcapFace.xa = vertices[V_TOP].x; + lcapFace.ya = vertices[V_TOP].y; + lcapFace.k = -slopes[V_LEFT].k; + } + miLineArc (pDrawable, pGC, pixel, spanData, + &lcapFace, (LineFacePtr) NULL, + lcenterx, lcentery, FALSE); + } + if (dx < 0) + { + rcapFace.xa = vertices[V_BOTTOM].x; + rcapFace.ya = vertices[V_BOTTOM].y; + rcapFace.k = slopes[V_RIGHT].k; + } + else + { + rcapFace.xa = -vertices[V_RIGHT].x; + rcapFace.ya = -vertices[V_RIGHT].y; + rcapFace.k = -slopes[V_RIGHT].k; + } + miLineArc (pDrawable, pGC, pixel, spanData, + (LineFacePtr) NULL, &rcapFace, + rcenterx, rcentery, FALSE); + break; + } + } + } + LRemain -= dashRemain; + ++dashIndex; + if (dashIndex == pGC->numInDashList) + dashIndex = 0; + dashRemain = pDash[dashIndex]; + + lcenterx = rcenterx; + lcentery = rcentery; + + vertices[V_TOP] = vertices[V_RIGHT]; + vertices[V_LEFT] = vertices[V_BOTTOM]; + slopes[V_LEFT].k = -slopes[V_RIGHT].k; + first = FALSE; + } + + if (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1)) + { + vertices[V_TOP].x -= dx; + vertices[V_TOP].y -= dy; + + vertices[V_LEFT].x -= dx; + vertices[V_LEFT].y -= dy; + + vertices[V_RIGHT].x = rdy; + vertices[V_RIGHT].y = -rdx; + + vertices[V_BOTTOM].x = -rdy; + vertices[V_BOTTOM].y = rdx; + + + if (projectRight) + { + vertices[V_RIGHT].x += rdx; + vertices[V_RIGHT].y += rdy; + + vertices[V_BOTTOM].x += rdx; + vertices[V_BOTTOM].y += rdy; + slopes[V_RIGHT].k = vertices[V_RIGHT].x * + slopes[V_RIGHT].dy - + vertices[V_RIGHT].y * + slopes[V_RIGHT].dx; + } + else + slopes[V_RIGHT].k = 0; + + if (!first && pGC->lineStyle == LineOnOffDash && + pGC->capStyle == CapProjecting) + { + vertices[V_TOP].x -= rdx; + vertices[V_TOP].y -= rdy; + + vertices[V_LEFT].x -= rdx; + vertices[V_LEFT].y -= rdy; + slopes[V_LEFT].k = vertices[V_LEFT].x * + slopes[V_LEFT].dy - + vertices[V_LEFT].y * + slopes[V_LEFT].dx; + } + else + slopes[V_LEFT].k += dx * dx + dy * dy; + + + y = miPolyBuildPoly (vertices, slopes, 4, x2, y2, + left, right, &nleft, &nright, &h); + + pixel = (dashIndex & 1) ? pGC->bgPixel : pGC->fgPixel; + miFillPolyHelper (pDrawable, pGC, pixel, spanData, y, h, left, right, nleft, nright); + if (!first && pGC->lineStyle == LineOnOffDash && + pGC->capStyle == CapRound) + { + lcapFace.x = x2; + lcapFace.y = y2; + if (dx < 0) + { + lcapFace.xa = -vertices[V_LEFT].x; + lcapFace.ya = -vertices[V_LEFT].y; + lcapFace.k = slopes[V_LEFT].k; + } + else + { + lcapFace.xa = vertices[V_TOP].x; + lcapFace.ya = vertices[V_TOP].y; + lcapFace.k = -slopes[V_LEFT].k; + } + miLineArc (pDrawable, pGC, pixel, spanData, + &lcapFace, (LineFacePtr) NULL, + rcenterx, rcentery, FALSE); + } + } + dashRemain = ((double) dashRemain) - LRemain; + if (dashRemain == 0) + { + dashIndex++; + if (dashIndex == pGC->numInDashList) + dashIndex = 0; + dashRemain = pDash[dashIndex]; + } + + leftFace->x = x1; + leftFace->y = y1; + leftFace->dx = dx; + leftFace->dy = dy; + leftFace->xa = rdy; + leftFace->ya = -rdx; + leftFace->k = k; + + rightFace->x = x2; + rightFace->y = y2; + rightFace->dx = -dx; + rightFace->dy = -dy; + rightFace->xa = -rdy; + rightFace->ya = rdx; + rightFace->k = k; + + *pDashIndex = dashIndex; + *pDashOffset = pDash[dashIndex] - dashRemain; +} + +void +miWideDash (pDrawable, pGC, mode, npt, pPts) + DrawablePtr pDrawable; + register GCPtr pGC; + int mode; + register int npt; + register DDXPointPtr pPts; +{ + int x1, y1, x2, y2; + unsigned long pixel; + Bool projectLeft, projectRight; + LineFaceRec leftFace, rightFace, prevRightFace; + LineFaceRec firstFace; + int first; + int dashIndex, dashOffset; + register int prevDashIndex; + SpanDataRec spanDataRec; + SpanDataPtr spanData; + Bool somethingDrawn = FALSE; + Bool selfJoin; + Bool endIsFg, startIsFg, firstIsFg = FALSE, prevIsFg; + + /* XXX backward compatibility */ + if (pGC->lineWidth == 0) + { + miZeroDashLine (pDrawable, pGC, mode, npt, pPts); + return; + } + if (pGC->lineStyle == LineDoubleDash && + (pGC->fillStyle == FillOpaqueStippled || pGC->fillStyle == FillTiled)) + { + miWideLine (pDrawable, pGC, mode, npt, pPts); + return; + } + if (npt == 0) + return; + spanData = miSetupSpanData (pGC, &spanDataRec, npt); + x2 = pPts->x; + y2 = pPts->y; + first = TRUE; + selfJoin = FALSE; + if (mode == CoordModePrevious) + { + int nptTmp; + DDXPointPtr pPtsTmp; + + x1 = x2; + y1 = y2; + nptTmp = npt; + pPtsTmp = pPts + 1; + while (--nptTmp) + { + x1 += pPtsTmp->x; + y1 += pPtsTmp->y; + ++pPtsTmp; + } + if (x2 == x1 && y2 == y1) + selfJoin = TRUE; + } + else if (x2 == pPts[npt-1].x && y2 == pPts[npt-1].y) + { + selfJoin = TRUE; + } + projectLeft = pGC->capStyle == CapProjecting && !selfJoin; + projectRight = FALSE; + dashIndex = 0; + dashOffset = 0; + miStepDash ((int)pGC->dashOffset, &dashIndex, + pGC->dash, (int)pGC->numInDashList, &dashOffset); + while (--npt) + { + x1 = x2; + y1 = y2; + ++pPts; + x2 = pPts->x; + y2 = pPts->y; + if (mode == CoordModePrevious) + { + x2 += x1; + y2 += y1; + } + if (x1 != x2 || y1 != y2) + { + somethingDrawn = TRUE; + if (npt == 1 && pGC->capStyle == CapProjecting && + (!selfJoin || !firstIsFg)) + projectRight = TRUE; + prevDashIndex = dashIndex; + miWideDashSegment (pDrawable, pGC, spanData, &dashOffset, &dashIndex, + x1, y1, x2, y2, + projectLeft, projectRight, &leftFace, &rightFace); + startIsFg = !(prevDashIndex & 1); + endIsFg = (dashIndex & 1) ^ (dashOffset != 0); + if (pGC->lineStyle == LineDoubleDash || startIsFg) + { + pixel = startIsFg ? pGC->fgPixel : pGC->bgPixel; + if (first || (pGC->lineStyle == LineOnOffDash && !prevIsFg)) + { + if (first && selfJoin) + { + firstFace = leftFace; + firstIsFg = startIsFg; + } + else if (pGC->capStyle == CapRound) + miLineArc (pDrawable, pGC, pixel, spanData, + &leftFace, (LineFacePtr) NULL, + (double)0.0, (double)0.0, TRUE); + } + else + { + miLineJoin (pDrawable, pGC, pixel, spanData, &leftFace, + &prevRightFace); + } + } + prevRightFace = rightFace; + prevIsFg = endIsFg; + first = FALSE; + projectLeft = FALSE; + } + if (npt == 1 && somethingDrawn) + { + if (pGC->lineStyle == LineDoubleDash || endIsFg) + { + pixel = endIsFg ? pGC->fgPixel : pGC->bgPixel; + if (selfJoin && (pGC->lineStyle == LineDoubleDash || firstIsFg)) + { + miLineJoin (pDrawable, pGC, pixel, spanData, &firstFace, + &rightFace); + } + else + { + if (pGC->capStyle == CapRound) + miLineArc (pDrawable, pGC, pixel, spanData, + (LineFacePtr) NULL, &rightFace, + (double)0.0, (double)0.0, TRUE); + } + } + else + { + /* glue a cap to the start of the line if + * we're OnOffDash and ended on odd dash + */ + if (selfJoin && firstIsFg) + { + pixel = pGC->fgPixel; + if (pGC->capStyle == CapProjecting) + miLineProjectingCap (pDrawable, pGC, pixel, spanData, + &firstFace, TRUE, + (double)0.0, (double)0.0, TRUE); + else if (pGC->capStyle == CapRound) + miLineArc (pDrawable, pGC, pixel, spanData, + &firstFace, (LineFacePtr) NULL, + (double)0.0, (double)0.0, TRUE); + } + } + } + } + /* handle crock where all points are coincident */ + if (!somethingDrawn && (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1))) + { + /* not the same as endIsFg computation above */ + pixel = (dashIndex & 1) ? pGC->bgPixel : pGC->fgPixel; + switch (pGC->capStyle) { + case CapRound: + miLineArc (pDrawable, pGC, pixel, spanData, + (LineFacePtr) NULL, (LineFacePtr) NULL, + (double)x2, (double)y2, + FALSE); + break; + case CapProjecting: + x1 = pGC->lineWidth; + miFillRectPolyHelper (pDrawable, pGC, pixel, spanData, + x2 - (x1 >> 1), y2 - (x1 >> 1), x1, x1); + break; + } + } + if (spanData) + miCleanupSpanData (pDrawable, pGC, spanData); +} + +/* these are stubs to allow old ddx ValidateGCs to work without change */ + +void +miMiter() +{ +} + +void +miNotMiter() +{ +} diff --git a/mi/miwideline.h b/mi/miwideline.h new file mode 100644 index 000000000..71d2c369c --- /dev/null +++ b/mi/miwideline.h @@ -0,0 +1,244 @@ +/* $Xorg: miwideline.h,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ +/* + +Copyright 1988, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + +/* Author: Keith Packard, MIT X Consortium */ + +#include "mispans.h" + +/* + * interface data to span-merging polygon filler + */ + +typedef struct _SpanData { + SpanGroup fgGroup, bgGroup; +} SpanDataRec, *SpanDataPtr; + +#define AppendSpanGroup(pGC, pixel, spanPtr, spanData) { \ + SpanGroup *group, *othergroup = NULL; \ + if (pixel == pGC->fgPixel) \ + { \ + group = &spanData->fgGroup; \ + if (pGC->lineStyle == LineDoubleDash) \ + othergroup = &spanData->bgGroup; \ + } \ + else \ + { \ + group = &spanData->bgGroup; \ + othergroup = &spanData->fgGroup; \ + } \ + miAppendSpans (group, othergroup, spanPtr); \ +} + +/* + * Polygon edge description for integer wide-line routines + */ + +typedef struct _PolyEdge { + int height; /* number of scanlines to process */ + int x; /* starting x coordinate */ + int stepx; /* fixed integral dx */ + int signdx; /* variable dx sign */ + int e; /* initial error term */ + int dy; + int dx; +} PolyEdgeRec, *PolyEdgePtr; + +#define SQSECANT 108.856472512142 /* 1/sin^2(11/2) - miter limit constant */ + +/* + * types for general polygon routines + */ + +typedef struct _PolyVertex { + double x, y; +} PolyVertexRec, *PolyVertexPtr; + +typedef struct _PolySlope { + int dx, dy; + double k; /* x0 * dy - y0 * dx */ +} PolySlopeRec, *PolySlopePtr; + +/* + * Line face description for caps/joins + */ + +typedef struct _LineFace { + double xa, ya; + int dx, dy; + int x, y; + double k; +} LineFaceRec, *LineFacePtr; + +/* + * macros for polygon fillers + */ + +#define MIPOLYRELOADLEFT if (!left_height && left_count) { \ + left_height = left->height; \ + left_x = left->x; \ + left_stepx = left->stepx; \ + left_signdx = left->signdx; \ + left_e = left->e; \ + left_dy = left->dy; \ + left_dx = left->dx; \ + --left_count; \ + ++left; \ + } + +#define MIPOLYRELOADRIGHT if (!right_height && right_count) { \ + right_height = right->height; \ + right_x = right->x; \ + right_stepx = right->stepx; \ + right_signdx = right->signdx; \ + right_e = right->e; \ + right_dy = right->dy; \ + right_dx = right->dx; \ + --right_count; \ + ++right; \ + } + +#define MIPOLYSTEPLEFT left_x += left_stepx; \ + left_e += left_dx; \ + if (left_e > 0) \ + { \ + left_x += left_signdx; \ + left_e -= left_dy; \ + } + +#define MIPOLYSTEPRIGHT right_x += right_stepx; \ + right_e += right_dx; \ + if (right_e > 0) \ + { \ + right_x += right_signdx; \ + right_e -= right_dy; \ + } + +#define MILINESETPIXEL(pDrawable, pGC, pixel, oldPixel) { \ + oldPixel = pGC->fgPixel; \ + if (pixel != oldPixel) { \ + DoChangeGC (pGC, GCForeground, (XID *) &pixel, FALSE); \ + ValidateGC (pDrawable, pGC); \ + } \ +} +#define MILINERESETPIXEL(pDrawable, pGC, pixel, oldPixel) { \ + if (pixel != oldPixel) { \ + DoChangeGC (pGC, GCForeground, (XID *) &oldPixel, FALSE); \ + ValidateGC (pDrawable, pGC); \ + } \ +} + +#ifdef NOINLINEICEIL +#define ICEIL(x) ((int)ceil(x)) +#else +#ifdef __GNUC__ +static __inline int ICEIL(x) + double x; +{ + int _cTmp = x; + return ((x == _cTmp) || (x < 0.0)) ? _cTmp : _cTmp+1; +} +#else +#define ICEIL(x) ((((x) == (_cTmp = (x))) || ((x) < 0.0)) ? _cTmp : _cTmp+1) +#define ICEILTEMPDECL static int _cTmp; +#endif +#endif + +extern void miFillPolyHelper( +#if NeedFunctionPrototypes + DrawablePtr /*pDrawable*/, + GCPtr /*pGC*/, + unsigned long /*pixel*/, + SpanDataPtr /*spanData*/, + int /*y*/, + int /*overall_height*/, + PolyEdgePtr /*left*/, + PolyEdgePtr /*right*/, + int /*left_count*/, + int /*right_count*/ +#endif +); +extern int miRoundJoinFace( +#if NeedFunctionPrototypes + LineFacePtr /*face*/, + PolyEdgePtr /*edge*/, + Bool * /*leftEdge*/ +#endif +); + +extern void miRoundJoinClip( +#if NeedFunctionPrototypes + LineFacePtr /*pLeft*/, + LineFacePtr /*pRight*/, + PolyEdgePtr /*edge1*/, + PolyEdgePtr /*edge2*/, + int * /*y1*/, + int * /*y2*/, + Bool * /*left1*/, + Bool * /*left2*/ +#endif +); + +extern int miRoundCapClip( +#if NeedFunctionPrototypes + LineFacePtr /*face*/, + Bool /*isInt*/, + PolyEdgePtr /*edge*/, + Bool * /*leftEdge*/ +#endif +); + +extern void miLineProjectingCap( +#if NeedFunctionPrototypes + DrawablePtr /*pDrawable*/, + GCPtr /*pGC*/, + unsigned long /*pixel*/, + SpanDataPtr /*spanData*/, + LineFacePtr /*face*/, + Bool /*isLeft*/, + double /*xorg*/, + double /*yorg*/, + Bool /*isInt*/ +#endif +); + +extern SpanDataPtr miSetupSpanData( +#if NeedFunctionPrototypes + GCPtr /*pGC*/, + SpanDataPtr /*spanData*/, + int /*npt*/ +#endif +); + +extern void miCleanupSpanData( +#if NeedFunctionPrototypes + DrawablePtr /*pDrawable*/, + GCPtr /*pGC*/, + SpanDataPtr /*spanData*/ +#endif +); diff --git a/mi/miwindow.c b/mi/miwindow.c new file mode 100644 index 000000000..f9428074c --- /dev/null +++ b/mi/miwindow.c @@ -0,0 +1,1144 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: miwindow.c,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ +#include "X.h" +#include "miscstruct.h" +#include "region.h" +#include "mi.h" +#include "windowstr.h" +#include "scrnintstr.h" +#include "pixmapstr.h" +#include "mivalidate.h" + +void +miClearToBackground(pWin, x, y, w, h, generateExposures) + WindowPtr pWin; + int x,y; + int w,h; + Bool generateExposures; +{ + BoxRec box; + RegionRec reg; + RegionPtr pBSReg = NullRegion; + ScreenPtr pScreen; + BoxPtr extents; + int x1, y1, x2, y2; + + /* compute everything using ints to avoid overflow */ + + x1 = pWin->drawable.x + x; + y1 = pWin->drawable.y + y; + if (w) + x2 = x1 + (int) w; + else + x2 = x1 + (int) pWin->drawable.width - (int) x; + if (h) + y2 = y1 + h; + else + y2 = y1 + (int) pWin->drawable.height - (int) y; + + extents = &pWin->clipList.extents; + + /* clip the resulting rectangle to the window clipList extents. This + * makes sure that the result will fit in a box, given that the + * screen is < 32768 on a side. + */ + + if (x1 < extents->x1) + x1 = extents->x1; + if (x2 > extents->x2) + x2 = extents->x2; + if (y1 < extents->y1) + y1 = extents->y1; + if (y2 > extents->y2) + y2 = extents->y2; + + if (x2 <= x1 || y2 <= y1) + { + x2 = x1 = 0; + y2 = y1 = 0; + } + + box.x1 = x1; + box.x2 = x2; + box.y1 = y1; + box.y2 = y2; + + pScreen = pWin->drawable.pScreen; + REGION_INIT(pScreen, ®, &box, 1); + if (pWin->backStorage) + { + /* + * If the window has backing-store on, call through the + * ClearToBackground vector to handle the special semantics + * (i.e. things backing store is to be cleared out and + * an Expose event is to be generated for those areas in backing + * store if generateExposures is TRUE). + */ + pBSReg = (* pScreen->ClearBackingStore)(pWin, x, y, w, h, + generateExposures); + } + + REGION_INTERSECT(pScreen, ®, ®, &pWin->clipList); + if (generateExposures) + (*pScreen->WindowExposures)(pWin, ®, pBSReg); + else if (pWin->backgroundState != None) + (*pScreen->PaintWindowBackground)(pWin, ®, PW_BACKGROUND); + REGION_UNINIT(pScreen, ®); + if (pBSReg) + REGION_DESTROY(pScreen, pBSReg); +} + +/* + * For SaveUnders using backing-store. The idea is that when a window is mapped + * with saveUnder set TRUE, any windows it obscures will have its backing + * store turned on setting the DIXsaveUnder bit, + * The backing-store code must be written to allow for this + */ + +/*- + *----------------------------------------------------------------------- + * miCheckSubSaveUnder -- + * Check all the inferiors of a window for coverage by saveUnder + * windows. Called from ChangeSaveUnder and CheckSaveUnder. + * This code is very inefficient. + * + * Results: + * TRUE if any windows need to have backing-store removed. + * + * Side Effects: + * Windows may have backing-store turned on or off. + * + *----------------------------------------------------------------------- + */ +static Bool +miCheckSubSaveUnder(pParent, pFirst, pRegion) + register WindowPtr pParent; /* Parent to check */ + WindowPtr pFirst; /* first reconfigured window */ + RegionPtr pRegion; /* Initial area obscured by saveUnder */ +{ + register WindowPtr pChild; /* Current child */ + register ScreenPtr pScreen; /* Screen to use */ + RegionRec SubRegion; /* Area of children obscured */ + Bool res = FALSE; /* result */ + Bool subInited=FALSE;/* SubRegion initialized */ + + pScreen = pParent->drawable.pScreen; + if ( (pChild = pParent->firstChild) ) + { + /* + * build region above first changed window + */ + + for (; pChild != pFirst; pChild = pChild->nextSib) + if (pChild->viewable && pChild->saveUnder) + REGION_UNION(pScreen, pRegion, pRegion, &pChild->borderSize); + + /* + * check region below and including first changed window + */ + + for (; pChild; pChild = pChild->nextSib) + { + if (pChild->viewable) + { + /* + * don't save under nephew/niece windows; + * use a separate region + */ + + if (pChild->firstChild) + { + if (!subInited) + { + REGION_INIT(pScreen, &SubRegion, NullBox, 0); + subInited = TRUE; + } + REGION_COPY(pScreen, &SubRegion, pRegion); + res |= miCheckSubSaveUnder(pChild, pChild->firstChild, + &SubRegion); + } + else + { + res |= miCheckSubSaveUnder(pChild, pChild->firstChild, + pRegion); + } + + if (pChild->saveUnder) + REGION_UNION(pScreen, pRegion, pRegion, &pChild->borderSize); + } + } + + if (subInited) + REGION_UNINIT(pScreen, &SubRegion); + } + + /* + * Check the state of this window. DIX save unders are + * enabled for viewable windows with some client expressing + * exposure interest and which intersect the save under region + */ + + if (pParent->viewable && + ((pParent->eventMask | wOtherEventMasks(pParent)) & ExposureMask) && + REGION_NOTEMPTY(pScreen, &pParent->borderSize) && + RECT_IN_REGION(pScreen, pRegion, REGION_EXTENTS(pScreen, + &pParent->borderSize)) != rgnOUT) + { + if (!pParent->DIXsaveUnder) + { + pParent->DIXsaveUnder = TRUE; + (*pScreen->ChangeWindowAttributes) (pParent, CWBackingStore); + } + } + else + { + if (pParent->DIXsaveUnder) + { + res = TRUE; + pParent->DIXsaveUnder = FALSE; + } + } + return res; +} + + +/*- + *----------------------------------------------------------------------- + * miChangeSaveUnder -- + * Change the save-under state of a tree of windows. Called when + * a window with saveUnder TRUE is mapped/unmapped/reconfigured. + * + * Results: + * TRUE if any windows need to have backing-store removed (which + * means that PostChangeSaveUnder needs to be called later to + * finish the job). + * + * Side Effects: + * Windows may have backing-store turned on or off. + * + *----------------------------------------------------------------------- + */ +Bool +miChangeSaveUnder(pWin, first) + register WindowPtr pWin; + WindowPtr first; /* First window to check. + * Used when pWin was restacked */ +{ + RegionRec rgn; /* Area obscured by saveUnder windows */ + register ScreenPtr pScreen; + Bool res; + + if (!deltaSaveUndersViewable && !numSaveUndersViewable) + return FALSE; + numSaveUndersViewable += deltaSaveUndersViewable; + deltaSaveUndersViewable = 0; + pScreen = pWin->drawable.pScreen; + REGION_INIT(pScreen, &rgn, NullBox, 1); + res = miCheckSubSaveUnder (pWin->parent, + pWin->saveUnder ? first : pWin->nextSib, + &rgn); + REGION_UNINIT(pScreen, &rgn); + return res; +} + +/*- + *----------------------------------------------------------------------- + * miPostChangeSaveUnder -- + * Actually turn backing-store off for those windows that no longer + * need to have it on. + * + * Results: + * None. + * + * Side Effects: + * Backing-store and SAVE_UNDER_CHANGE_BIT are turned off for those + * windows affected. + * + *----------------------------------------------------------------------- + */ +void +miPostChangeSaveUnder(pWin, pFirst) + WindowPtr pWin; + WindowPtr pFirst; +{ + register WindowPtr pParent, pChild; + ChangeWindowAttributesProcPtr ChangeWindowAttributes; + + if (!(pParent = pWin->parent)) + return; + ChangeWindowAttributes = pParent->drawable.pScreen->ChangeWindowAttributes; + if (!pParent->DIXsaveUnder && + (pParent->backingStore == NotUseful) && pParent->backStorage) + (*ChangeWindowAttributes)(pParent, CWBackingStore); + if (!(pChild = pFirst)) + return; + while (1) + { + if (!pChild->DIXsaveUnder && + (pChild->backingStore == NotUseful) && pChild->backStorage) + (*ChangeWindowAttributes)(pChild, CWBackingStore); + if (pChild->firstChild) + { + pChild = pChild->firstChild; + continue; + } + while (!pChild->nextSib) + { + pChild = pChild->parent; + if (pChild == pParent) + return; + } + pChild = pChild->nextSib; + } +} + +void +miMarkWindow(pWin) + register WindowPtr pWin; +{ + register ValidatePtr val; + + if (pWin->valdata) + return; + val = (ValidatePtr)xnfalloc(sizeof(ValidateRec)); + val->before.oldAbsCorner.x = pWin->drawable.x; + val->before.oldAbsCorner.y = pWin->drawable.y; + val->before.borderVisible = NullRegion; + val->before.resized = FALSE; + pWin->valdata = val; +} + +Bool +miMarkOverlappedWindows(pWin, pFirst, ppLayerWin) + WindowPtr pWin; + WindowPtr pFirst; + WindowPtr *ppLayerWin; +{ + register BoxPtr box; + register WindowPtr pChild, pLast; + Bool anyMarked = FALSE; + void (* MarkWindow)() = pWin->drawable.pScreen->MarkWindow; + ScreenPtr pScreen = pWin->drawable.pScreen; + + /* single layered systems are easy */ + if (ppLayerWin) *ppLayerWin = pWin; + + if (pWin == pFirst) + { + /* Blindly mark pWin and all of its inferiors. This is a slight + * overkill if there are mapped windows that outside pWin's border, + * but it's better than wasting time on RectIn checks. + */ + pChild = pWin; + while (1) + { + if (pChild->viewable) + { + (* MarkWindow)(pChild); + if (pChild->firstChild) + { + pChild = pChild->firstChild; + continue; + } + } + while (!pChild->nextSib && (pChild != pWin)) + pChild = pChild->parent; + if (pChild == pWin) + break; + pChild = pChild->nextSib; + } + anyMarked = TRUE; + pFirst = pFirst->nextSib; + } + if ( (pChild = pFirst) ) + { + box = REGION_EXTENTS(pChild->drawable.pScreen, &pWin->borderSize); + pLast = pChild->parent->lastChild; + while (1) + { + if (pChild->viewable && RECT_IN_REGION(pScreen, &pChild->borderSize, + box)) + { + (* MarkWindow)(pChild); + anyMarked = TRUE; + if (pChild->firstChild) + { + pChild = pChild->firstChild; + continue; + } + } + while (!pChild->nextSib && (pChild != pLast)) + pChild = pChild->parent; + if (pChild == pLast) + break; + pChild = pChild->nextSib; + } + } + if (anyMarked) + (* MarkWindow)(pWin->parent); + return anyMarked; +} + +/***** + * miHandleValidateExposures(pWin) + * starting at pWin, draw background in any windows that have exposure + * regions, translate the regions, restore any backing store, + * and then send any regions still exposed to the client + *****/ +void +miHandleValidateExposures(pWin) + WindowPtr pWin; +{ + register WindowPtr pChild; + register ValidatePtr val; + ScreenPtr pScreen = pWin->drawable.pScreen; + void (* WindowExposures)(); + + pChild = pWin; + WindowExposures = pChild->drawable.pScreen->WindowExposures; + while (1) + { + if ( (val = pChild->valdata) ) + { + if (REGION_NOTEMPTY(pScreen, &val->after.borderExposed)) + (*pChild->drawable.pScreen->PaintWindowBorder)(pChild, + &val->after.borderExposed, + PW_BORDER); + REGION_UNINIT(pScreen, &val->after.borderExposed); + (*WindowExposures)(pChild, &val->after.exposed, NullRegion); + REGION_UNINIT(pScreen, &val->after.exposed); + xfree(val); + pChild->valdata = (ValidatePtr)NULL; + if (pChild->firstChild) + { + pChild = pChild->firstChild; + continue; + } + } + while (!pChild->nextSib && (pChild != pWin)) + pChild = pChild->parent; + if (pChild == pWin) + break; + pChild = pChild->nextSib; + } +} + +void +miMoveWindow(pWin, x, y, pNextSib, kind) + register WindowPtr pWin; + int x,y; + WindowPtr pNextSib; + VTKind kind; +{ + WindowPtr pParent; + Bool WasViewable = (Bool)(pWin->viewable); + short bw; + RegionPtr oldRegion; + DDXPointRec oldpt; + Bool anyMarked; + register ScreenPtr pScreen; + WindowPtr windowToValidate; +#ifdef DO_SAVE_UNDERS + Bool dosave = FALSE; +#endif + WindowPtr pLayerWin; + + /* if this is a root window, can't be moved */ + if (!(pParent = pWin->parent)) + return ; + pScreen = pWin->drawable.pScreen; + bw = wBorderWidth (pWin); + + oldpt.x = pWin->drawable.x; + oldpt.y = pWin->drawable.y; + if (WasViewable) + { + oldRegion = REGION_CREATE(pScreen, NullBox, 1); + REGION_COPY(pScreen, oldRegion, &pWin->borderClip); + anyMarked = (*pScreen->MarkOverlappedWindows)(pWin, pWin, &pLayerWin); + } + pWin->origin.x = x + (int)bw; + pWin->origin.y = y + (int)bw; + x = pWin->drawable.x = pParent->drawable.x + x + (int)bw; + y = pWin->drawable.y = pParent->drawable.y + y + (int)bw; + + SetWinSize (pWin); + SetBorderSize (pWin); + + (*pScreen->PositionWindow)(pWin, x, y); + + windowToValidate = MoveWindowInStack(pWin, pNextSib); + + ResizeChildrenWinSize(pWin, x - oldpt.x, y - oldpt.y, 0, 0); + + if (WasViewable) + { + if (pLayerWin == pWin) + anyMarked |= (*pScreen->MarkOverlappedWindows) + (pWin, windowToValidate, (WindowPtr *)NULL); + else + anyMarked |= (*pScreen->MarkOverlappedWindows) + (pWin, pLayerWin, (WindowPtr *)NULL); + +#ifdef DO_SAVE_UNDERS + if (DO_SAVE_UNDERS(pWin)) + { + dosave = (*pScreen->ChangeSaveUnder)(pLayerWin, windowToValidate); + } +#endif /* DO_SAVE_UNDERS */ + + if (anyMarked) + { + (*pScreen->ValidateTree)(pLayerWin->parent, NullWindow, kind); + (* pWin->drawable.pScreen->CopyWindow)(pWin, oldpt, oldRegion); + REGION_DESTROY(pScreen, oldRegion); + /* XXX need to retile border if ParentRelative origin */ + (*pScreen->HandleExposures)(pLayerWin->parent); + } +#ifdef DO_SAVE_UNDERS + if (dosave) + (*pScreen->PostChangeSaveUnder)(pLayerWin, windowToValidate); +#endif /* DO_SAVE_UNDERS */ + if (anyMarked && pScreen->PostValidateTree) + (*pScreen->PostValidateTree)(pLayerWin->parent, NullWindow, kind); + } + if (pWin->realized) + WindowsRestructured (); +} + + +/* + * pValid is a region of the screen which has been + * successfully copied -- recomputed exposed regions for affected windows + */ + +static int +miRecomputeExposures (pWin, value) + register WindowPtr pWin; + pointer value; /* must conform to VisitWindowProcPtr */ +{ + register ScreenPtr pScreen; + RegionPtr pValid = (RegionPtr)value; + + if (pWin->valdata) + { + pScreen = pWin->drawable.pScreen; + /* + * compute exposed regions of this window + */ + REGION_SUBTRACT(pScreen, &pWin->valdata->after.exposed, + &pWin->clipList, pValid); + /* + * compute exposed regions of the border + */ + REGION_SUBTRACT(pScreen, &pWin->valdata->after.borderExposed, + &pWin->borderClip, &pWin->winSize); + REGION_SUBTRACT(pScreen, &pWin->valdata->after.borderExposed, + &pWin->valdata->after.borderExposed, pValid); + return WT_WALKCHILDREN; + } + return WT_NOMATCH; +} + +void +miSlideAndSizeWindow(pWin, x, y, w, h, pSib) + register WindowPtr pWin; + int x,y; + unsigned int w, h; + WindowPtr pSib; +{ + WindowPtr pParent; + Bool WasViewable = (Bool)(pWin->viewable); + unsigned short width = pWin->drawable.width, + height = pWin->drawable.height; + short oldx = pWin->drawable.x, + oldy = pWin->drawable.y; + int bw = wBorderWidth (pWin); + short dw, dh; + DDXPointRec oldpt; + RegionPtr oldRegion; + Bool anyMarked; + register ScreenPtr pScreen; + WindowPtr pFirstChange; + register WindowPtr pChild; + RegionPtr gravitate[StaticGravity + 1]; + register unsigned g; + int nx, ny; /* destination x,y */ + int newx, newy; /* new inner window position */ + RegionPtr pRegion; + RegionPtr destClip; /* portions of destination already written */ + RegionPtr oldWinClip; /* old clip list for window */ + RegionPtr borderVisible = NullRegion; /* visible area of the border */ + RegionPtr bsExposed = NullRegion; /* backing store exposures */ + Bool shrunk = FALSE; /* shrunk in an inner dimension */ + Bool moved = FALSE; /* window position changed */ +#ifdef DO_SAVE_UNDERS + Bool dosave = FALSE; +#endif + WindowPtr pLayerWin; + + /* if this is a root window, can't be resized */ + if (!(pParent = pWin->parent)) + return ; + + pScreen = pWin->drawable.pScreen; + newx = pParent->drawable.x + x + bw; + newy = pParent->drawable.y + y + bw; + if (WasViewable) + { + anyMarked = FALSE; + /* + * save the visible region of the window + */ + oldRegion = REGION_CREATE(pScreen, NullBox, 1); + REGION_COPY(pScreen, oldRegion, &pWin->winSize); + + /* + * categorize child windows into regions to be moved + */ + for (g = 0; g <= StaticGravity; g++) + gravitate[g] = (RegionPtr) NULL; + for (pChild = pWin->firstChild; pChild; pChild = pChild->nextSib) + { + g = pChild->winGravity; + if (g != UnmapGravity) + { + if (!gravitate[g]) + gravitate[g] = REGION_CREATE(pScreen, NullBox, 1); + REGION_UNION(pScreen, gravitate[g], + gravitate[g], &pChild->borderClip); + } + else + { + UnmapWindow(pChild, TRUE); + anyMarked = TRUE; + } + } + anyMarked |= (*pScreen->MarkOverlappedWindows)(pWin, pWin, + &pLayerWin); + + oldWinClip = NULL; + if (pWin->bitGravity != ForgetGravity) + { + oldWinClip = REGION_CREATE(pScreen, NullBox, 1); + REGION_COPY(pScreen, oldWinClip, &pWin->clipList); + } + /* + * if the window is changing size, borderExposed + * can't be computed correctly without some help. + */ + if (pWin->drawable.height > h || pWin->drawable.width > w) + shrunk = TRUE; + + if (newx != oldx || newy != oldy) + moved = TRUE; + + if ((pWin->drawable.height != h || pWin->drawable.width != w) && + HasBorder (pWin)) + { + borderVisible = REGION_CREATE(pScreen, NullBox, 1); + /* for tiled borders, we punt and draw the whole thing */ + if (pWin->borderIsPixel || !moved) + { + if (shrunk || moved) + REGION_SUBTRACT(pScreen, borderVisible, + &pWin->borderClip, + &pWin->winSize); + else + REGION_COPY(pScreen, borderVisible, + &pWin->borderClip); + } + } + } + pWin->origin.x = x + bw; + pWin->origin.y = y + bw; + pWin->drawable.height = h; + pWin->drawable.width = w; + + x = pWin->drawable.x = newx; + y = pWin->drawable.y = newy; + + SetWinSize (pWin); + SetBorderSize (pWin); + + dw = (int)w - (int)width; + dh = (int)h - (int)height; + ResizeChildrenWinSize(pWin, x - oldx, y - oldy, dw, dh); + + /* let the hardware adjust background and border pixmaps, if any */ + (*pScreen->PositionWindow)(pWin, x, y); + + pFirstChange = MoveWindowInStack(pWin, pSib); + + if (WasViewable) + { + pRegion = REGION_CREATE(pScreen, NullBox, 1); + if (pWin->backStorage) + REGION_COPY(pScreen, pRegion, &pWin->clipList); + + if (pLayerWin == pWin) + anyMarked |= (*pScreen->MarkOverlappedWindows)(pWin, pFirstChange, + (WindowPtr *)NULL); + else + anyMarked |= (*pScreen->MarkOverlappedWindows)(pWin, pLayerWin, + (WindowPtr *)NULL); + + if (pWin->valdata) + { + pWin->valdata->before.resized = TRUE; + pWin->valdata->before.borderVisible = borderVisible; + } + +#ifdef DO_SAVE_UNDERS + if (DO_SAVE_UNDERS(pWin)) + { + dosave = (*pScreen->ChangeSaveUnder)(pLayerWin, pFirstChange); + } +#endif /* DO_SAVE_UNDERS */ + + if (anyMarked) + (*pScreen->ValidateTree)(pLayerWin->parent, pFirstChange, VTOther); + /* + * the entire window is trashed unless bitGravity + * recovers portions of it + */ + REGION_COPY(pScreen, &pWin->valdata->after.exposed, &pWin->clipList); + } + + GravityTranslate (x, y, oldx, oldy, dw, dh, pWin->bitGravity, &nx, &ny); + + if (pWin->backStorage && + ((pWin->backingStore == Always) || WasViewable)) + { + if (!WasViewable) + pRegion = &pWin->clipList; /* a convenient empty region */ + if (pWin->bitGravity == ForgetGravity) + bsExposed = (*pScreen->TranslateBackingStore) + (pWin, 0, 0, NullRegion, oldx, oldy); + else + { + bsExposed = (*pScreen->TranslateBackingStore) + (pWin, nx - x, ny - y, pRegion, oldx, oldy); + } + } + + if (WasViewable) + { + /* avoid the border */ + if (HasBorder (pWin)) + { + int offx, offy, dx, dy; + + /* kruft to avoid double translates for each gravity */ + offx = 0; + offy = 0; + for (g = 0; g <= StaticGravity; g++) + { + if (!gravitate[g]) + continue; + + /* align winSize to gravitate[g]. + * winSize is in new coordinates, + * gravitate[g] is still in old coordinates */ + GravityTranslate (x, y, oldx, oldy, dw, dh, g, &nx, &ny); + + dx = (oldx - nx) - offx; + dy = (oldy - ny) - offy; + if (dx || dy) + { + REGION_TRANSLATE(pScreen, &pWin->winSize, dx, dy); + offx += dx; + offy += dy; + } + REGION_INTERSECT(pScreen, gravitate[g], gravitate[g], + &pWin->winSize); + } + /* get winSize back where it belongs */ + if (offx || offy) + REGION_TRANSLATE(pScreen, &pWin->winSize, -offx, -offy); + } + /* + * add screen bits to the appropriate bucket + */ + + if (oldWinClip) + { + /* + * clip to new clipList + */ + REGION_COPY(pScreen, pRegion, oldWinClip); + REGION_TRANSLATE(pScreen, pRegion, nx - oldx, ny - oldy); + REGION_INTERSECT(pScreen, oldWinClip, pRegion, &pWin->clipList); + /* + * don't step on any gravity bits which will be copied after this + * region. Note -- this assumes that the regions will be copied + * in gravity order. + */ + for (g = pWin->bitGravity + 1; g <= StaticGravity; g++) + { + if (gravitate[g]) + REGION_SUBTRACT(pScreen, oldWinClip, oldWinClip, + gravitate[g]); + } + REGION_TRANSLATE(pScreen, oldWinClip, oldx - nx, oldy - ny); + g = pWin->bitGravity; + if (!gravitate[g]) + gravitate[g] = oldWinClip; + else + { + REGION_UNION(pScreen, gravitate[g], gravitate[g], oldWinClip); + REGION_DESTROY(pScreen, oldWinClip); + } + } + + /* + * move the bits on the screen + */ + + destClip = NULL; + + for (g = 0; g <= StaticGravity; g++) + { + if (!gravitate[g]) + continue; + + GravityTranslate (x, y, oldx, oldy, dw, dh, g, &nx, &ny); + + oldpt.x = oldx + (x - nx); + oldpt.y = oldy + (y - ny); + + /* Note that gravitate[g] is *translated* by CopyWindow */ + + /* only copy the remaining useful bits */ + + REGION_INTERSECT(pScreen, gravitate[g], gravitate[g], oldRegion); + + /* clip to not overwrite already copied areas */ + + if (destClip) { + REGION_TRANSLATE(pScreen, destClip, oldpt.x - x, oldpt.y - y); + REGION_SUBTRACT(pScreen, gravitate[g], gravitate[g], destClip); + REGION_TRANSLATE(pScreen, destClip, x - oldpt.x, y - oldpt.y); + } + + /* and move those bits */ + + if (oldpt.x != x || oldpt.y != y) + (*pWin->drawable.pScreen->CopyWindow)(pWin, oldpt, gravitate[g]); + + /* remove any overwritten bits from the remaining useful bits */ + + REGION_SUBTRACT(pScreen, oldRegion, oldRegion, gravitate[g]); + + /* + * recompute exposed regions of child windows + */ + + for (pChild = pWin->firstChild; pChild; pChild = pChild->nextSib) + { + if (pChild->winGravity != g) + continue; + REGION_INTERSECT(pScreen, pRegion, + &pChild->borderClip, gravitate[g]); + TraverseTree (pChild, miRecomputeExposures, (pointer)pRegion); + } + + /* + * remove the successfully copied regions of the + * window from its exposed region + */ + + if (g == pWin->bitGravity) + REGION_SUBTRACT(pScreen, &pWin->valdata->after.exposed, + &pWin->valdata->after.exposed, gravitate[g]); + if (!destClip) + destClip = gravitate[g]; + else + { + REGION_UNION(pScreen, destClip, destClip, gravitate[g]); + REGION_DESTROY(pScreen, gravitate[g]); + } + } + + REGION_DESTROY(pScreen, oldRegion); + REGION_DESTROY(pScreen, pRegion); + if (destClip) + REGION_DESTROY(pScreen, destClip); + if (bsExposed) + { + RegionPtr valExposed = NullRegion; + + if (pWin->valdata) + valExposed = &pWin->valdata->after.exposed; + (*pScreen->WindowExposures) (pWin, valExposed, bsExposed); + if (valExposed) + REGION_EMPTY(pScreen, valExposed); + REGION_DESTROY(pScreen, bsExposed); + } + if (anyMarked) + (*pScreen->HandleExposures)(pLayerWin->parent); +#ifdef DO_SAVE_UNDERS + if (dosave) + { + (*pScreen->PostChangeSaveUnder)(pLayerWin, pFirstChange); + } +#endif /* DO_SAVE_UNDERS */ + if (anyMarked && pScreen->PostValidateTree) + (*pScreen->PostValidateTree)(pLayerWin->parent, pFirstChange, + VTOther); + } + else if (bsExposed) + { + (*pScreen->WindowExposures) (pWin, NullRegion, bsExposed); + REGION_DESTROY(pScreen, bsExposed); + } + if (pWin->realized) + WindowsRestructured (); +} + +WindowPtr +miGetLayerWindow(pWin) + WindowPtr pWin; +{ + return pWin->firstChild; +} + +#ifdef SHAPE +/****** + * + * miSetShape + * The border/window shape has changed. Recompute winSize/borderSize + * and send appropriate exposure events + */ + +void +miSetShape(pWin) + register WindowPtr pWin; +{ + Bool WasViewable = (Bool)(pWin->viewable); + register ScreenPtr pScreen = pWin->drawable.pScreen; + Bool anyMarked; + WindowPtr pParent = pWin->parent; + RegionPtr pOldClip, bsExposed; +#ifdef DO_SAVE_UNDERS + Bool dosave = FALSE; +#endif + WindowPtr pLayerWin; + + if (WasViewable) + { + anyMarked = (*pScreen->MarkOverlappedWindows)(pWin, pWin, + &pLayerWin); + if (pWin->valdata) + { + if (HasBorder (pWin)) + { + RegionPtr borderVisible; + + borderVisible = REGION_CREATE(pScreen, NullBox, 1); + REGION_SUBTRACT(pScreen, borderVisible, + &pWin->borderClip, &pWin->winSize); + pWin->valdata->before.borderVisible = borderVisible; + } + pWin->valdata->before.resized = TRUE; + } + } + + SetWinSize (pWin); + SetBorderSize (pWin); + + ResizeChildrenWinSize(pWin, 0, 0, 0, 0); + + if (WasViewable) + { + if (pWin->backStorage) + { + pOldClip = REGION_CREATE(pScreen, NullBox, 1); + REGION_COPY(pScreen, pOldClip, &pWin->clipList); + } + + anyMarked |= (*pScreen->MarkOverlappedWindows)(pWin, pWin, + (WindowPtr *)NULL); + +#ifdef DO_SAVE_UNDERS + if (DO_SAVE_UNDERS(pWin)) + { + dosave = (*pScreen->ChangeSaveUnder)(pLayerWin, pLayerWin); + } +#endif /* DO_SAVE_UNDERS */ + + if (anyMarked) + (*pScreen->ValidateTree)(pLayerWin->parent, NullWindow, VTOther); + } + + if (pWin->backStorage && + ((pWin->backingStore == Always) || WasViewable)) + { + if (!WasViewable) + pOldClip = &pWin->clipList; /* a convenient empty region */ + bsExposed = (*pScreen->TranslateBackingStore) + (pWin, 0, 0, pOldClip, + pWin->drawable.x, pWin->drawable.y); + if (WasViewable) + REGION_DESTROY(pScreen, pOldClip); + if (bsExposed) + { + RegionPtr valExposed = NullRegion; + + if (pWin->valdata) + valExposed = &pWin->valdata->after.exposed; + (*pScreen->WindowExposures) (pWin, valExposed, bsExposed); + if (valExposed) + REGION_EMPTY(pScreen, valExposed); + REGION_DESTROY(pScreen, bsExposed); + } + } + if (WasViewable) + { + if (anyMarked) + (*pScreen->HandleExposures)(pLayerWin->parent); +#ifdef DO_SAVE_UNDERS + if (dosave) + (*pScreen->PostChangeSaveUnder)(pLayerWin, pLayerWin); +#endif /* DO_SAVE_UNDERS */ + if (anyMarked && pScreen->PostValidateTree) + (*pScreen->PostValidateTree)(pLayerWin->parent, NullWindow, VTOther); + } + if (pWin->realized) + WindowsRestructured (); + CheckCursorConfinement(pWin); +} +#endif + +/* Keeps the same inside(!) origin */ + +void +miChangeBorderWidth(pWin, width) + register WindowPtr pWin; + unsigned int width; +{ + WindowPtr pParent; + int oldwidth; + Bool anyMarked; + register ScreenPtr pScreen; + Bool WasViewable = (Bool)(pWin->viewable); + Bool HadBorder; +#ifdef DO_SAVE_UNDERS + Bool dosave = FALSE; +#endif + WindowPtr pLayerWin; + + oldwidth = wBorderWidth (pWin); + if (oldwidth == width) + return; + HadBorder = HasBorder(pWin); + pScreen = pWin->drawable.pScreen; + pParent = pWin->parent; + if (WasViewable && width < oldwidth) + anyMarked = (*pScreen->MarkOverlappedWindows)(pWin, pWin, &pLayerWin); + + pWin->borderWidth = width; + SetBorderSize (pWin); + + if (WasViewable) + { + if (width > oldwidth) + { + anyMarked = (*pScreen->MarkOverlappedWindows)(pWin, pWin, + &pLayerWin); + /* + * save the old border visible region to correctly compute + * borderExposed. + */ + if (pWin->valdata && HadBorder) + { + RegionPtr borderVisible; + borderVisible = REGION_CREATE(pScreen, NULL, 1); + REGION_SUBTRACT(pScreen, borderVisible, + &pWin->borderClip, &pWin->winSize); + pWin->valdata->before.borderVisible = borderVisible; + } + } +#ifdef DO_SAVE_UNDERS + if (DO_SAVE_UNDERS(pWin)) + { + dosave = (*pScreen->ChangeSaveUnder)(pLayerWin, pWin->nextSib); + } +#endif /* DO_SAVE_UNDERS */ + + if (anyMarked) + { + (*pScreen->ValidateTree)(pLayerWin->parent, pLayerWin, VTOther); + (*pScreen->HandleExposures)(pLayerWin->parent); + } +#ifdef DO_SAVE_UNDERS + if (dosave) + (*pScreen->PostChangeSaveUnder)(pLayerWin, pWin->nextSib); +#endif /* DO_SAVE_UNDERS */ + if (anyMarked && pScreen->PostValidateTree) + (*pScreen->PostValidateTree)(pLayerWin->parent, pLayerWin, + VTOther); + } + if (pWin->realized) + WindowsRestructured (); +} + +void +miMarkUnrealizedWindow(pChild, pWin, fromConfigure) + WindowPtr pChild; + WindowPtr pWin; + Bool fromConfigure; +{ + if ((pChild != pWin) || fromConfigure) + { + REGION_EMPTY(pChild->drawable.pScreen, &pChild->clipList); + if (pChild->drawable.pScreen->ClipNotify) + (* pChild->drawable.pScreen->ClipNotify)(pChild, 0, 0); + REGION_EMPTY(pChild->drawable.pScreen, &pChild->borderClip); + } +} diff --git a/mi/mizerarc.c b/mi/mizerarc.c new file mode 100644 index 000000000..def8d8c51 --- /dev/null +++ b/mi/mizerarc.c @@ -0,0 +1,848 @@ +/************************************************************ + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +Author: Bob Scheifler, MIT X Consortium + +********************************************************/ + +/* $Xorg: mizerarc.c,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ + +/* Derived from: + * "Algorithm for drawing ellipses or hyperbolae with a digital plotter" + * by M. L. V. Pitteway + * The Computer Journal, November 1967, Volume 10, Number 3, pp. 282-289 + */ + +#include <math.h> +#include "X.h" +#include "Xprotostr.h" +#include "miscstruct.h" +#include "gcstruct.h" +#include "pixmapstr.h" +#include "mi.h" +#include "mizerarc.h" + +#define FULLCIRCLE (360 * 64) +#define OCTANT (45 * 64) +#define QUADRANT (90 * 64) +#define HALFCIRCLE (180 * 64) +#define QUADRANT3 (270 * 64) + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define Dsin(d) ((d) == 0 ? 0.0 : ((d) == QUADRANT ? 1.0 : \ + ((d) == HALFCIRCLE ? 0.0 : \ + ((d) == QUADRANT3 ? -1.0 : sin((double)d*(M_PI/11520.0)))))) + +#define Dcos(d) ((d) == 0 ? 1.0 : ((d) == QUADRANT ? 0.0 : \ + ((d) == HALFCIRCLE ? -1.0 : \ + ((d) == QUADRANT3 ? 0.0 : cos((double)d*(M_PI/11520.0)))))) + +#define EPSILON45 64 + +typedef struct { + int skipStart; + int haveStart; + DDXPointRec startPt; + int haveLast; + int skipLast; + DDXPointRec endPt; + int dashIndex; + int dashOffset; + int dashIndexInit; + int dashOffsetInit; +} DashInfo; + +static miZeroArcPtRec oob = {65536, 65536, 0}; + +/* + * (x - l)^2 / (W/2)^2 + (y + H/2)^2 / (H/2)^2 = 1 + * + * where l is either 0 or .5 + * + * alpha = 4(W^2) + * beta = 4(H^2) + * gamma = 0 + * u = 2(W^2)H + * v = 4(H^2)l + * k = -4(H^2)(l^2) + * + */ + +Bool +miZeroArcSetup(arc, info, ok360) + register xArc *arc; + register miZeroArcRec *info; + Bool ok360; +{ + int l; + int angle1, angle2; + int startseg, endseg; + int startAngle, endAngle; + int i, overlap; + miZeroArcPtRec start, end; + + l = arc->width & 1; + if (arc->width == arc->height) + { + info->alpha = 4; + info->beta = 4; + info->k1 = -8; + info->k3 = -16; + info->b = 12; + info->a = (arc->width << 2) - 12; + info->d = 17 - (arc->width << 1); + if (l) + { + info->b -= 4; + info->a += 4; + info->d -= 7; + } + } + else if (!arc->width || !arc->height) + { + info->alpha = 0; + info->beta = 0; + info->k1 = 0; + info->k3 = 0; + info->a = -(int)arc->height; + info->b = 0; + info->d = -1; + } + else + { + /* initial conditions */ + info->alpha = (arc->width * arc->width) << 2; + info->beta = (arc->height * arc->height) << 2; + info->k1 = info->beta << 1; + info->k3 = info->k1 + (info->alpha << 1); + info->b = l ? 0 : -info->beta; + info->a = info->alpha * arc->height; + info->d = info->b - (info->a >> 1) - (info->alpha >> 2); + if (l) + info->d -= info->beta >> 2; + info->a -= info->b; + /* take first step, d < 0 always */ + info->b -= info->k1; + info->a += info->k1; + info->d += info->b; + /* octant change, b < 0 always */ + info->k1 = -info->k1; + info->k3 = -info->k3; + info->b = -info->b; + info->d = info->b - info->a - info->d; + info->a = info->a - (info->b << 1); + } + info->dx = 1; + info->dy = 0; + info->w = (arc->width + 1) >> 1; + info->h = arc->height >> 1; + info->xorg = arc->x + (arc->width >> 1); + info->yorg = arc->y; + info->xorgo = info->xorg + l; + info->yorgo = info->yorg + arc->height; + if (!arc->width) + { + if (!arc->height) + { + info->x = 0; + info->y = 0; + info->initialMask = 0; + info->startAngle = 0; + info->endAngle = 0; + info->start = oob; + info->end = oob; + return FALSE; + } + info->x = 0; + info->y = 1; + } + else + { + info->x = 1; + info->y = 0; + } + angle1 = arc->angle1; + angle2 = arc->angle2; + if ((angle1 == 0) && (angle2 >= FULLCIRCLE)) + { + startAngle = 0; + endAngle = 0; + } + else + { + if (angle2 > FULLCIRCLE) + angle2 = FULLCIRCLE; + else if (angle2 < -FULLCIRCLE) + angle2 = -FULLCIRCLE; + if (angle2 < 0) + { + startAngle = angle1 + angle2; + endAngle = angle1; + } + else + { + startAngle = angle1; + endAngle = angle1 + angle2; + } + if (startAngle < 0) + startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE; + if (startAngle >= FULLCIRCLE) + startAngle = startAngle % FULLCIRCLE; + if (endAngle < 0) + endAngle = FULLCIRCLE - (-endAngle) % FULLCIRCLE; + if (endAngle >= FULLCIRCLE) + endAngle = endAngle % FULLCIRCLE; + } + info->startAngle = startAngle; + info->endAngle = endAngle; + if (ok360 && (startAngle == endAngle) && arc->angle2 && + arc->width && arc->height) + { + info->initialMask = 0xf; + info->start = oob; + info->end = oob; + return TRUE; + } + startseg = startAngle / OCTANT; + if (!arc->height || (((startseg + 1) & 2) && arc->width)) + { + start.x = Dcos(startAngle) * ((arc->width + 1) / 2.0); + if (start.x < 0) + start.x = -start.x; + start.y = -1; + } + else + { + start.y = Dsin(startAngle) * (arc->height / 2.0); + if (start.y < 0) + start.y = -start.y; + start.y = info->h - start.y; + start.x = 65536; + } + endseg = endAngle / OCTANT; + if (!arc->height || (((endseg + 1) & 2) && arc->width)) + { + end.x = Dcos(endAngle) * ((arc->width + 1) / 2.0); + if (end.x < 0) + end.x = -end.x; + end.y = -1; + } + else + { + end.y = Dsin(endAngle) * (arc->height / 2.0); + if (end.y < 0) + end.y = -end.y; + end.y = info->h - end.y; + end.x = 65536; + } + info->firstx = start.x; + info->firsty = start.y; + info->initialMask = 0; + overlap = arc->angle2 && (endAngle <= startAngle); + for (i = 0; i < 4; i++) + { + if (overlap ? + ((i * QUADRANT <= endAngle) || ((i + 1) * QUADRANT > startAngle)) : + ((i * QUADRANT <= endAngle) && ((i + 1) * QUADRANT > startAngle))) + info->initialMask |= (1 << i); + } + start.mask = info->initialMask; + end.mask = info->initialMask; + startseg >>= 1; + endseg >>= 1; + overlap = overlap && (endseg == startseg); + if (start.x != end.x || start.y != end.y || !overlap) + { + if (startseg & 1) + { + if (!overlap) + info->initialMask &= ~(1 << startseg); + if (start.x > end.x || start.y > end.y) + end.mask &= ~(1 << startseg); + } + else + { + start.mask &= ~(1 << startseg); + if (((start.x < end.x || start.y < end.y) || + (start.x == end.x && start.y == end.y && (endseg & 1))) && + !overlap) + end.mask &= ~(1 << startseg); + } + if (endseg & 1) + { + end.mask &= ~(1 << endseg); + if (((start.x > end.x || start.y > end.y) || + (start.x == end.x && start.y == end.y && !(startseg & 1))) && + !overlap) + start.mask &= ~(1 << endseg); + } + else + { + if (!overlap) + info->initialMask &= ~(1 << endseg); + if (start.x < end.x || start.y < end.y) + start.mask &= ~(1 << endseg); + } + } + /* take care of case when start and stop are both near 45 */ + /* handle here rather than adding extra code to pixelization loops */ + if (startAngle && + ((start.y < 0 && end.y >= 0) || (start.y >= 0 && end.y < 0))) + { + i = (startAngle + OCTANT) % OCTANT; + if (i < EPSILON45 || i > OCTANT - EPSILON45) + { + i = (endAngle + OCTANT) % OCTANT; + if (i < EPSILON45 || i > OCTANT - EPSILON45) + { + if (start.y < 0) + { + i = Dsin(startAngle) * (arc->height / 2.0); + if (i < 0) + i = -i; + if (info->h - i == end.y) + start.mask = end.mask; + } + else + { + i = Dsin(endAngle) * (arc->height / 2.0); + if (i < 0) + i = -i; + if (info->h - i == start.y) + end.mask = start.mask; + } + } + } + } + if (startseg & 1) + { + info->start = start; + info->end = oob; + } + else + { + info->end = start; + info->start = oob; + } + if (endseg & 1) + { + info->altend = end; + if (info->altend.x < info->end.x || info->altend.y < info->end.y) + { + miZeroArcPtRec tmp; + tmp = info->altend; + info->altend = info->end; + info->end = tmp; + } + info->altstart = oob; + } + else + { + info->altstart = end; + if (info->altstart.x < info->start.x || + info->altstart.y < info->start.y) + { + miZeroArcPtRec tmp; + tmp = info->altstart; + info->altstart = info->start; + info->start = tmp; + } + info->altend = oob; + } + if (!info->start.x || !info->start.y) + { + info->initialMask = info->start.mask; + info->start = info->altstart; + } + if (!arc->width && (arc->height == 1)) + { + /* kludge! */ + info->initialMask |= info->end.mask; + info->initialMask |= info->initialMask << 1; + info->end.x = 0; + info->end.mask = 0; + } + return FALSE; +} + +#define Pixelate(xval,yval) \ + { \ + pts->x = xval; \ + pts->y = yval; \ + pts++; \ + } + +#define DoPix(idx,xval,yval) if (mask & (1 << idx)) Pixelate(xval, yval); + +DDXPointPtr +miZeroArcPts(arc, pts) + xArc *arc; + register DDXPointPtr pts; +{ + miZeroArcRec info; + register int x, y, a, b, d, mask; + register int k1, k3, dx, dy; + Bool do360; + + do360 = miZeroArcSetup(arc, &info, TRUE); + MIARCSETUP(); + mask = info.initialMask; + if (!(arc->width & 1)) + { + DoPix(1, info.xorgo, info.yorg); + DoPix(3, info.xorgo, info.yorgo); + } + if (!info.end.x || !info.end.y) + { + mask = info.end.mask; + info.end = info.altend; + } + if (do360 && (arc->width == arc->height) && !(arc->width & 1)) + { + int yorgh = info.yorg + info.h; + int xorghp = info.xorg + info.h; + int xorghn = info.xorg - info.h; + + while (1) + { + Pixelate(info.xorg + x, info.yorg + y); + Pixelate(info.xorg - x, info.yorg + y); + Pixelate(info.xorg - x, info.yorgo - y); + Pixelate(info.xorg + x, info.yorgo - y); + if (a < 0) + break; + Pixelate(xorghp - y, yorgh - x); + Pixelate(xorghn + y, yorgh - x); + Pixelate(xorghn + y, yorgh + x); + Pixelate(xorghp - y, yorgh + x); + MIARCCIRCLESTEP(;); + } + if (x > 1 && pts[-1].x == pts[-5].x && pts[-1].y == pts[-5].y) + pts -= 4; + x = info.w; + y = info.h; + } + else if (do360) + { + while (y < info.h || x < info.w) + { + MIARCOCTANTSHIFT(;); + Pixelate(info.xorg + x, info.yorg + y); + Pixelate(info.xorgo - x, info.yorg + y); + Pixelate(info.xorgo - x, info.yorgo - y); + Pixelate(info.xorg + x, info.yorgo - y); + MIARCSTEP(;,;); + } + } + else + { + while (y < info.h || x < info.w) + { + MIARCOCTANTSHIFT(;); + if ((x == info.start.x) || (y == info.start.y)) + { + mask = info.start.mask; + info.start = info.altstart; + } + DoPix(0, info.xorg + x, info.yorg + y); + DoPix(1, info.xorgo - x, info.yorg + y); + DoPix(2, info.xorgo - x, info.yorgo - y); + DoPix(3, info.xorg + x, info.yorgo - y); + if ((x == info.end.x) || (y == info.end.y)) + { + mask = info.end.mask; + info.end = info.altend; + } + MIARCSTEP(;,;); + } + } + if ((x == info.start.x) || (y == info.start.y)) + mask = info.start.mask; + DoPix(0, info.xorg + x, info.yorg + y); + DoPix(2, info.xorgo - x, info.yorgo - y); + if (arc->height & 1) + { + DoPix(1, info.xorgo - x, info.yorg + y); + DoPix(3, info.xorg + x, info.yorgo - y); + } + return pts; +} + +#undef DoPix +#define DoPix(idx,xval,yval) \ + if (mask & (1 << idx)) \ + { \ + arcPts[idx]->x = xval; \ + arcPts[idx]->y = yval; \ + arcPts[idx]++; \ + } + +static void +miZeroArcDashPts(pGC, arc, dinfo, points, maxPts, evenPts, oddPts) + GCPtr pGC; + xArc *arc; + DashInfo *dinfo; + int maxPts; + register DDXPointPtr points, *evenPts, *oddPts; +{ + miZeroArcRec info; + register int x, y, a, b, d, mask; + register int k1, k3, dx, dy; + int dashRemaining; + DDXPointPtr arcPts[4]; + DDXPointPtr startPts[5], endPts[5]; + int deltas[5]; + DDXPointPtr startPt, pt, lastPt, pts; + int i, j, delta, ptsdelta, seg, startseg; + + for (i = 0; i < 4; i++) + arcPts[i] = points + (i * maxPts); + (void)miZeroArcSetup(arc, &info, FALSE); + MIARCSETUP(); + mask = info.initialMask; + startseg = info.startAngle / QUADRANT; + startPt = arcPts[startseg]; + if (!(arc->width & 1)) + { + DoPix(1, info.xorgo, info.yorg); + DoPix(3, info.xorgo, info.yorgo); + } + if (!info.end.x || !info.end.y) + { + mask = info.end.mask; + info.end = info.altend; + } + while (y < info.h || x < info.w) + { + MIARCOCTANTSHIFT(;); + if ((x == info.firstx) || (y == info.firsty)) + startPt = arcPts[startseg]; + if ((x == info.start.x) || (y == info.start.y)) + { + mask = info.start.mask; + info.start = info.altstart; + } + DoPix(0, info.xorg + x, info.yorg + y); + DoPix(1, info.xorgo - x, info.yorg + y); + DoPix(2, info.xorgo - x, info.yorgo - y); + DoPix(3, info.xorg + x, info.yorgo - y); + if ((x == info.end.x) || (y == info.end.y)) + { + mask = info.end.mask; + info.end = info.altend; + } + MIARCSTEP(;,;); + } + if ((x == info.firstx) || (y == info.firsty)) + startPt = arcPts[startseg]; + if ((x == info.start.x) || (y == info.start.y)) + mask = info.start.mask; + DoPix(0, info.xorg + x, info.yorg + y); + DoPix(2, info.xorgo - x, info.yorgo - y); + if (arc->height & 1) + { + DoPix(1, info.xorgo - x, info.yorg + y); + DoPix(3, info.xorg + x, info.yorgo - y); + } + for (i = 0; i < 4; i++) + { + seg = (startseg + i) & 3; + pt = points + (seg * maxPts); + if (seg & 1) + { + startPts[i] = pt; + endPts[i] = arcPts[seg]; + deltas[i] = 1; + } + else + { + startPts[i] = arcPts[seg] - 1; + endPts[i] = pt - 1; + deltas[i] = -1; + } + } + startPts[4] = startPts[0]; + endPts[4] = startPt; + startPts[0] = startPt; + if (startseg & 1) + { + if (startPts[4] != endPts[4]) + endPts[4]--; + deltas[4] = 1; + } + else + { + if (startPts[0] > startPts[4]) + startPts[0]--; + if (startPts[4] < endPts[4]) + endPts[4]--; + deltas[4] = -1; + } + if (arc->angle2 < 0) + { + DDXPointPtr tmps, tmpe; + int tmpd; + + tmpd = deltas[0]; + tmps = startPts[0] - tmpd; + tmpe = endPts[0] - tmpd; + startPts[0] = endPts[4] - deltas[4]; + endPts[0] = startPts[4] - deltas[4]; + deltas[0] = -deltas[4]; + startPts[4] = tmpe; + endPts[4] = tmps; + deltas[4] = -tmpd; + tmpd = deltas[1]; + tmps = startPts[1] - tmpd; + tmpe = endPts[1] - tmpd; + startPts[1] = endPts[3] - deltas[3]; + endPts[1] = startPts[3] - deltas[3]; + deltas[1] = -deltas[3]; + startPts[3] = tmpe; + endPts[3] = tmps; + deltas[3] = -tmpd; + tmps = startPts[2] - deltas[2]; + startPts[2] = endPts[2] - deltas[2]; + endPts[2] = tmps; + deltas[2] = -deltas[2]; + } + for (i = 0; i < 5 && startPts[i] == endPts[i]; i++) + ; + if (i == 5) + return; + pt = startPts[i]; + for (j = 4; startPts[j] == endPts[j]; j--) + ; + lastPt = endPts[j] - deltas[j]; + if (dinfo->haveLast && + (pt->x == dinfo->endPt.x) && (pt->y == dinfo->endPt.y)) + { + startPts[i] += deltas[i]; + } + else + { + dinfo->dashIndex = dinfo->dashIndexInit; + dinfo->dashOffset = dinfo->dashOffsetInit; + } + if (!dinfo->skipStart && (info.startAngle != info.endAngle)) + { + dinfo->startPt = *pt; + dinfo->haveStart = TRUE; + } + else if (!dinfo->skipLast && dinfo->haveStart && + (lastPt->x == dinfo->startPt.x) && + (lastPt->y == dinfo->startPt.y) && + (lastPt != startPts[i])) + endPts[j] = lastPt; + if (info.startAngle != info.endAngle) + { + dinfo->haveLast = TRUE; + dinfo->endPt = *lastPt; + } + dashRemaining = pGC->dash[dinfo->dashIndex] - dinfo->dashOffset; + for (i = 0; i < 5; i++) + { + pt = startPts[i]; + lastPt = endPts[i]; + delta = deltas[i]; + while (pt != lastPt) + { + if (dinfo->dashIndex & 1) + { + pts = *oddPts; + ptsdelta = -1; + } + else + { + pts = *evenPts; + ptsdelta = 1; + } + while ((pt != lastPt) && --dashRemaining >= 0) + { + *pts = *pt; + pts += ptsdelta; + pt += delta; + } + if (dinfo->dashIndex & 1) + *oddPts = pts; + else + *evenPts = pts; + if (dashRemaining <= 0) + { + if (++(dinfo->dashIndex) == pGC->numInDashList) + dinfo->dashIndex = 0; + dashRemaining = pGC->dash[dinfo->dashIndex]; + } + } + } + dinfo->dashOffset = pGC->dash[dinfo->dashIndex] - dashRemaining; +} + +void +miZeroPolyArc(pDraw, pGC, narcs, parcs) + DrawablePtr pDraw; + GCPtr pGC; + int narcs; + xArc *parcs; +{ + int maxPts = 0; + register int n, maxw; + register xArc *arc; + register int i; + DDXPointPtr points, pts, oddPts; + register DDXPointPtr pt; + int numPts; + Bool dospans; + int *widths; + XID fgPixel = pGC->fgPixel; + DashInfo dinfo; + + for (arc = parcs, i = narcs; --i >= 0; arc++) + { + if (!miCanZeroArc(arc)) + miPolyArc(pDraw, pGC, 1, arc); + else + { + if (arc->width > arc->height) + n = arc->width + (arc->height >> 1); + else + n = arc->height + (arc->width >> 1); + if (n > maxPts) + maxPts = n; + } + } + if (!maxPts) + return; + numPts = maxPts << 2; + dospans = (pGC->lineStyle != LineSolid) || (pGC->fillStyle != FillSolid); + if (dospans) + { + widths = (int *)ALLOCATE_LOCAL(sizeof(int) * numPts); + if (!widths) + return; + maxw = 0; + } + if (pGC->lineStyle != LineSolid) + { + numPts <<= 1; + dinfo.haveStart = FALSE; + dinfo.skipStart = FALSE; + dinfo.haveLast = FALSE; + dinfo.dashIndexInit = 0; + dinfo.dashOffsetInit = 0; + miStepDash((int)pGC->dashOffset, &dinfo.dashIndexInit, + (unsigned char *) pGC->dash, (int)pGC->numInDashList, + &dinfo.dashOffsetInit); + } + points = (DDXPointPtr)ALLOCATE_LOCAL(sizeof(DDXPointRec) * numPts); + if (!points) + { + if (dospans) + { + DEALLOCATE_LOCAL(widths); + } + return; + } + for (arc = parcs, i = narcs; --i >= 0; arc++) + { + if (miCanZeroArc(arc)) + { + if (pGC->lineStyle == LineSolid) + pts = miZeroArcPts(arc, points); + else + { + pts = points; + oddPts = &points[(numPts >> 1) - 1]; + dinfo.skipLast = i; + miZeroArcDashPts(pGC, arc, &dinfo, + oddPts + 1, maxPts, &pts, &oddPts); + dinfo.skipStart = TRUE; + } + n = pts - points; + if (!dospans) + (*pGC->ops->PolyPoint)(pDraw, pGC, CoordModeOrigin, n, points); + else + { + if (n > maxw) + { + while (maxw < n) + widths[maxw++] = 1; + } + if (pGC->miTranslate) + { + for (pt = points; pt != pts; pt++) + { + pt->x += pDraw->x; + pt->y += pDraw->y; + } + } + (*pGC->ops->FillSpans)(pDraw, pGC, n, points, widths, FALSE); + } + if (pGC->lineStyle != LineDoubleDash) + continue; + if ((pGC->fillStyle == FillSolid) || + (pGC->fillStyle == FillStippled)) + { + DoChangeGC(pGC, GCForeground, (XID *)&pGC->bgPixel, 0); + ValidateGC(pDraw, pGC); + } + pts = &points[numPts >> 1]; + oddPts++; + n = pts - oddPts; + if (!dospans) + (*pGC->ops->PolyPoint)(pDraw, pGC, CoordModeOrigin, n, oddPts); + else + { + if (n > maxw) + { + while (maxw < n) + widths[maxw++] = 1; + } + if (pGC->miTranslate) + { + for (pt = oddPts; pt != pts; pt++) + { + pt->x += pDraw->x; + pt->y += pDraw->y; + } + } + (*pGC->ops->FillSpans)(pDraw, pGC, n, oddPts, widths, FALSE); + } + if ((pGC->fillStyle == FillSolid) || + (pGC->fillStyle == FillStippled)) + { + DoChangeGC(pGC, GCForeground, &fgPixel, 0); + ValidateGC(pDraw, pGC); + } + } + } + DEALLOCATE_LOCAL(points); + if (dospans) + { + DEALLOCATE_LOCAL(widths); + } +} diff --git a/mi/mizerarc.h b/mi/mizerarc.h new file mode 100644 index 000000000..68c8ce3de --- /dev/null +++ b/mi/mizerarc.h @@ -0,0 +1,137 @@ +/************************************************************ + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +********************************************************/ + +/* $Xorg: mizerarc.h,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ + +typedef struct { + int x; + int y; + int mask; +} miZeroArcPtRec; + +typedef struct { + int x, y, k1, k3, a, b, d, dx, dy; + int alpha, beta; + int xorg, yorg; + int xorgo, yorgo; + int w, h; + int initialMask; + miZeroArcPtRec start, altstart, end, altend; + int firstx, firsty; + int startAngle, endAngle; +} miZeroArcRec; + +#define miCanZeroArc(arc) (((arc)->width == (arc)->height) || \ + (((arc)->width <= 800) && ((arc)->height <= 800))) + +#define MIARCSETUP() \ + x = info.x; \ + y = info.y; \ + k1 = info.k1; \ + k3 = info.k3; \ + a = info.a; \ + b = info.b; \ + d = info.d; \ + dx = info.dx; \ + dy = info.dy + +#define MIARCOCTANTSHIFT(clause) \ + if (a < 0) \ + { \ + if (y == info.h) \ + { \ + d = -1; \ + a = b = k1 = 0; \ + } \ + else \ + { \ + dx = (k1 << 1) - k3; \ + k1 = dx - k1; \ + k3 = -k3; \ + b = b + a - (k1 >> 1); \ + d = b + ((-a) >> 1) - d + (k3 >> 3); \ + if (dx < 0) \ + a = -((-dx) >> 1) - a; \ + else \ + a = (dx >> 1) - a; \ + dx = 0; \ + dy = 1; \ + clause \ + } \ + } + +#define MIARCSTEP(move1,move2) \ + b -= k1; \ + if (d < 0) \ + { \ + x += dx; \ + y += dy; \ + a += k1; \ + d += b; \ + move1 \ + } \ + else \ + { \ + x++; \ + y++; \ + a += k3; \ + d -= a; \ + move2 \ + } + +#define MIARCCIRCLESTEP(clause) \ + b -= k1; \ + x++; \ + if (d < 0) \ + { \ + a += k1; \ + d += b; \ + } \ + else \ + { \ + y++; \ + a += k3; \ + d -= a; \ + clause \ + } + +/* mizerarc.c */ + +extern Bool miZeroArcSetup( +#if NeedFunctionPrototypes + xArc * /*arc*/, + miZeroArcRec * /*info*/, + Bool /*ok360*/ +#endif +); + +extern DDXPointPtr miZeroArcPts( +#if NeedFunctionPrototypes + xArc * /*arc*/, + DDXPointPtr /*pts*/ +#endif +); + diff --git a/mi/mizerline.c b/mi/mizerline.c new file mode 100644 index 000000000..2279cb822 --- /dev/null +++ b/mi/mizerline.c @@ -0,0 +1,960 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* $Xorg: mizerline.c,v 1.4 2001/02/09 02:05:22 xorgcvs Exp $ */ +#include "X.h" + +#include "misc.h" +#include "scrnintstr.h" +#include "gcstruct.h" +#include "windowstr.h" +#include "pixmap.h" +#include "mi.h" +#include "miline.h" + +/* + +The bresenham error equation used in the mi/mfb/cfb line routines is: + + e = error + dx = difference in raw X coordinates + dy = difference in raw Y coordinates + M = # of steps in X direction + N = # of steps in Y direction + B = 0 to prefer diagonal steps in a given octant, + 1 to prefer axial steps in a given octant + + For X major lines: + e = 2Mdy - 2Ndx - dx - B + -2dx <= e < 0 + + For Y major lines: + e = 2Ndx - 2Mdy - dy - B + -2dy <= e < 0 + +At the start of the line, we have taken 0 X steps and 0 Y steps, +so M = 0 and N = 0: + + X major e = 2Mdy - 2Ndx - dx - B + = -dx - B + + Y major e = 2Ndx - 2Mdy - dy - B + = -dy - B + +At the end of the line, we have taken dx X steps and dy Y steps, +so M = dx and N = dy: + + X major e = 2Mdy - 2Ndx - dx - B + = 2dxdy - 2dydx - dx - B + = -dx - B + Y major e = 2Ndx - 2Mdy - dy - B + = 2dydx - 2dxdy - dy - B + = -dy - B + +Thus, the error term is the same at the start and end of the line. + +Let us consider clipping an X coordinate. There are 4 cases which +represent the two independent cases of clipping the start vs. the +end of the line and an X major vs. a Y major line. In any of these +cases, we know the number of X steps (M) and we wish to find the +number of Y steps (N). Thus, we will solve our error term equation. +If we are clipping the start of the line, we will find the smallest +N that satisfies our error term inequality. If we are clipping the +end of the line, we will find the largest number of Y steps that +satisfies the inequality. In that case, since we are representing +the Y steps as (dy - N), we will actually want to solve for the +smallest N in that equation. + +Case 1: X major, starting X coordinate moved by M steps + + -2dx <= 2Mdy - 2Ndx - dx - B < 0 + 2Ndx <= 2Mdy - dx - B + 2dx 2Ndx > 2Mdy - dx - B + 2Ndx <= 2Mdy + dx - B N > (2Mdy - dx - B) / 2dx + N <= (2Mdy + dx - B) / 2dx + +Since we are trying to find the smallest N that satisfies these +equations, we should use the > inequality to find the smallest: + + N = floor((2Mdy - dx - B) / 2dx) + 1 + = floor((2Mdy - dx - B + 2dx) / 2dx) + = floor((2Mdy + dx - B) / 2dx) + +Case 1b: X major, ending X coordinate moved to M steps + +Same derivations as Case 1, but we want the largest N that satisfies +the equations, so we use the <= inequality: + + N = floor((2Mdy + dx - B) / 2dx) + +Case 2: X major, ending X coordinate moved by M steps + + -2dx <= 2(dx - M)dy - 2(dy - N)dx - dx - B < 0 + -2dx <= 2dxdy - 2Mdy - 2dxdy + 2Ndx - dx - B < 0 + -2dx <= 2Ndx - 2Mdy - dx - B < 0 + 2Ndx >= 2Mdy + dx + B - 2dx 2Ndx < 2Mdy + dx + B + 2Ndx >= 2Mdy - dx + B N < (2Mdy + dx + B) / 2dx + N >= (2Mdy - dx + B) / 2dx + +Since we are trying to find the highest number of Y steps that +satisfies these equations, we need to find the smallest N, so +we should use the >= inequality to find the smallest: + + N = ceiling((2Mdy - dx + B) / 2dx) + = floor((2Mdy - dx + B + 2dx - 1) / 2dx) + = floor((2Mdy + dx + B - 1) / 2dx) + +Case 2b: X major, starting X coordinate moved to M steps from end + +Same derivations as Case 2, but we want the smallest number of Y +steps, so we want the highest N, so we use the < inequality: + + N = ceiling((2Mdy + dx + B) / 2dx) - 1 + = floor((2Mdy + dx + B + 2dx - 1) / 2dx) - 1 + = floor((2Mdy + dx + B + 2dx - 1 - 2dx) / 2dx) + = floor((2Mdy + dx + B - 1) / 2dx) + +Case 3: Y major, starting X coordinate moved by M steps + + -2dy <= 2Ndx - 2Mdy - dy - B < 0 + 2Ndx >= 2Mdy + dy + B - 2dy 2Ndx < 2Mdy + dy + B + 2Ndx >= 2Mdy - dy + B N < (2Mdy + dy + B) / 2dx + N >= (2Mdy - dy + B) / 2dx + +Since we are trying to find the smallest N that satisfies these +equations, we should use the >= inequality to find the smallest: + + N = ceiling((2Mdy - dy + B) / 2dx) + = floor((2Mdy - dy + B + 2dx - 1) / 2dx) + = floor((2Mdy - dy + B - 1) / 2dx) + 1 + +Case 3b: Y major, ending X coordinate moved to M steps + +Same derivations as Case 3, but we want the largest N that satisfies +the equations, so we use the < inequality: + + N = ceiling((2Mdy + dy + B) / 2dx) - 1 + = floor((2Mdy + dy + B + 2dx - 1) / 2dx) - 1 + = floor((2Mdy + dy + B + 2dx - 1 - 2dx) / 2dx) + = floor((2Mdy + dy + B - 1) / 2dx) + +Case 4: Y major, ending X coordinate moved by M steps + + -2dy <= 2(dy - N)dx - 2(dx - M)dy - dy - B < 0 + -2dy <= 2dxdy - 2Ndx - 2dxdy + 2Mdy - dy - B < 0 + -2dy <= 2Mdy - 2Ndx - dy - B < 0 + 2Ndx <= 2Mdy - dy - B + 2dy 2Ndx > 2Mdy - dy - B + 2Ndx <= 2Mdy + dy - B N > (2Mdy - dy - B) / 2dx + N <= (2Mdy + dy - B) / 2dx + +Since we are trying to find the highest number of Y steps that +satisfies these equations, we need to find the smallest N, so +we should use the > inequality to find the smallest: + + N = floor((2Mdy - dy - B) / 2dx) + 1 + +Case 4b: Y major, starting X coordinate moved to M steps from end + +Same analysis as Case 4, but we want the smallest number of Y steps +which means the largest N, so we use the <= inequality: + + N = floor((2Mdy + dy - B) / 2dx) + +Now let's try the Y coordinates, we have the same 4 cases. + +Case 5: X major, starting Y coordinate moved by N steps + + -2dx <= 2Mdy - 2Ndx - dx - B < 0 + 2Mdy >= 2Ndx + dx + B - 2dx 2Mdy < 2Ndx + dx + B + 2Mdy >= 2Ndx - dx + B M < (2Ndx + dx + B) / 2dy + M >= (2Ndx - dx + B) / 2dy + +Since we are trying to find the smallest M, we use the >= inequality: + + M = ceiling((2Ndx - dx + B) / 2dy) + = floor((2Ndx - dx + B + 2dy - 1) / 2dy) + = floor((2Ndx - dx + B - 1) / 2dy) + 1 + +Case 5b: X major, ending Y coordinate moved to N steps + +Same derivations as Case 5, but we want the largest M that satisfies +the equations, so we use the < inequality: + + M = ceiling((2Ndx + dx + B) / 2dy) - 1 + = floor((2Ndx + dx + B + 2dy - 1) / 2dy) - 1 + = floor((2Ndx + dx + B + 2dy - 1 - 2dy) / 2dy) + = floor((2Ndx + dx + B - 1) / 2dy) + +Case 6: X major, ending Y coordinate moved by N steps + + -2dx <= 2(dx - M)dy - 2(dy - N)dx - dx - B < 0 + -2dx <= 2dxdy - 2Mdy - 2dxdy + 2Ndx - dx - B < 0 + -2dx <= 2Ndx - 2Mdy - dx - B < 0 + 2Mdy <= 2Ndx - dx - B + 2dx 2Mdy > 2Ndx - dx - B + 2Mdy <= 2Ndx + dx - B M > (2Ndx - dx - B) / 2dy + M <= (2Ndx + dx - B) / 2dy + +Largest # of X steps means smallest M, so use the > inequality: + + M = floor((2Ndx - dx - B) / 2dy) + 1 + +Case 6b: X major, starting Y coordinate moved to N steps from end + +Same derivations as Case 6, but we want the smallest # of X steps +which means the largest M, so use the <= inequality: + + M = floor((2Ndx + dx - B) / 2dy) + +Case 7: Y major, starting Y coordinate moved by N steps + + -2dy <= 2Ndx - 2Mdy - dy - B < 0 + 2Mdy <= 2Ndx - dy - B + 2dy 2Mdy > 2Ndx - dy - B + 2Mdy <= 2Ndx + dy - B M > (2Ndx - dy - B) / 2dy + M <= (2Ndx + dy - B) / 2dy + +To find the smallest M, use the > inequality: + + M = floor((2Ndx - dy - B) / 2dy) + 1 + = floor((2Ndx - dy - B + 2dy) / 2dy) + = floor((2Ndx + dy - B) / 2dy) + +Case 7b: Y major, ending Y coordinate moved to N steps + +Same derivations as Case 7, but we want the largest M that satisfies +the equations, so use the <= inequality: + + M = floor((2Ndx + dy - B) / 2dy) + +Case 8: Y major, ending Y coordinate moved by N steps + + -2dy <= 2(dy - N)dx - 2(dx - M)dy - dy - B < 0 + -2dy <= 2dxdy - 2Ndx - 2dxdy + 2Mdy - dy - B < 0 + -2dy <= 2Mdy - 2Ndx - dy - B < 0 + 2Mdy >= 2Ndx + dy + B - 2dy 2Mdy < 2Ndx + dy + B + 2Mdy >= 2Ndx - dy + B M < (2Ndx + dy + B) / 2dy + M >= (2Ndx - dy + B) / 2dy + +To find the highest X steps, find the smallest M, use the >= inequality: + + M = ceiling((2Ndx - dy + B) / 2dy) + = floor((2Ndx - dy + B + 2dy - 1) / 2dy) + = floor((2Ndx + dy + B - 1) / 2dy) + +Case 8b: Y major, starting Y coordinate moved to N steps from the end + +Same derivations as Case 8, but we want to find the smallest # of X +steps which means the largest M, so we use the < inequality: + + M = ceiling((2Ndx + dy + B) / 2dy) - 1 + = floor((2Ndx + dy + B + 2dy - 1) / 2dy) - 1 + = floor((2Ndx + dy + B + 2dy - 1 - 2dy) / 2dy) + = floor((2Ndx + dy + B - 1) / 2dy) + +So, our equations are: + + 1: X major move x1 to x1+M floor((2Mdy + dx - B) / 2dx) + 1b: X major move x2 to x1+M floor((2Mdy + dx - B) / 2dx) + 2: X major move x2 to x2-M floor((2Mdy + dx + B - 1) / 2dx) + 2b: X major move x1 to x2-M floor((2Mdy + dx + B - 1) / 2dx) + + 3: Y major move x1 to x1+M floor((2Mdy - dy + B - 1) / 2dx) + 1 + 3b: Y major move x2 to x1+M floor((2Mdy + dy + B - 1) / 2dx) + 4: Y major move x2 to x2-M floor((2Mdy - dy - B) / 2dx) + 1 + 4b: Y major move x1 to x2-M floor((2Mdy + dy - B) / 2dx) + + 5: X major move y1 to y1+N floor((2Ndx - dx + B - 1) / 2dy) + 1 + 5b: X major move y2 to y1+N floor((2Ndx + dx + B - 1) / 2dy) + 6: X major move y2 to y2-N floor((2Ndx - dx - B) / 2dy) + 1 + 6b: X major move y1 to y2-N floor((2Ndx + dx - B) / 2dy) + + 7: Y major move y1 to y1+N floor((2Ndx + dy - B) / 2dy) + 7b: Y major move y2 to y1+N floor((2Ndx + dy - B) / 2dy) + 8: Y major move y2 to y2-N floor((2Ndx + dy + B - 1) / 2dy) + 8b: Y major move y1 to y2-N floor((2Ndx + dy + B - 1) / 2dy) + +We have the following constraints on all of the above terms: + + 0 < M,N <= 2^15 2^15 can be imposed by miZeroClipLine + 0 <= dx/dy <= 2^16 - 1 + 0 <= B <= 1 + +The floor in all of the above equations can be accomplished with a +simple C divide operation provided that both numerator and denominator +are positive. + +Since dx,dy >= 0 and since moving an X coordinate implies that dx != 0 +and moving a Y coordinate implies dy != 0, we know that the denominators +are all > 0. + +For all lines, (-B) and (B-1) are both either 0 or -1, depending on the +bias. Thus, we have to show that the 2MNdxy +/- dxy terms are all >= 1 +or > 0 to prove that the numerators are positive (or zero). + +For X Major lines we know that dx > 0 and since 2Mdy is >= 0 due to the +constraints, the first four equations all have numerators >= 0. + +For the second four equations, M > 0, so 2Mdy >= 2dy so (2Mdy - dy) >= dy +So (2Mdy - dy) > 0, since they are Y major lines. Also, (2Mdy + dy) >= 3dy +or (2Mdy + dy) > 0. So all of their numerators are >= 0. + +For the third set of four equations, N > 0, so 2Ndx >= 2dx so (2Ndx - dx) +>= dx > 0. Similarly (2Ndx + dx) >= 3dx > 0. So all numerators >= 0. + +For the fourth set of equations, dy > 0 and 2Ndx >= 0, so all numerators +are > 0. + +To consider overflow, consider the case of 2 * M,N * dx,dy + dx,dy. This +is bounded <= 2 * 2^15 * (2^16 - 1) + (2^16 - 1) + <= 2^16 * (2^16 - 1) + (2^16 - 1) + <= 2^32 - 2^16 + 2^16 - 1 + <= 2^32 - 1 +Since the (-B) and (B-1) terms are all 0 or -1, the maximum value of +the numerator is therefore (2^32 - 1), which does not overflow an unsigned +32 bit variable. + +*/ + +#define MIOUTCODES(outcode, x, y, xmin, ymin, xmax, ymax) \ +{\ + if (x < xmin) outcode |= OUT_LEFT;\ + if (x > xmax) outcode |= OUT_RIGHT;\ + if (y < ymin) outcode |= OUT_ABOVE;\ + if (y > ymax) outcode |= OUT_BELOW;\ +} + +/* Bit codes for the terms of the 16 clipping equations defined below. */ + +#define T_2NDX (1 << 0) +#define T_2MDY (0) /* implicit term */ +#define T_DXNOTY (1 << 1) +#define T_DYNOTX (0) /* implicit term */ +#define T_SUBDXORY (1 << 2) +#define T_ADDDX (T_DXNOTY) /* composite term */ +#define T_SUBDX (T_DXNOTY | T_SUBDXORY) /* composite term */ +#define T_ADDDY (T_DYNOTX) /* composite term */ +#define T_SUBDY (T_DYNOTX | T_SUBDXORY) /* composite term */ +#define T_BIASSUBONE (1 << 3) +#define T_SUBBIAS (0) /* implicit term */ +#define T_DIV2DX (1 << 4) +#define T_DIV2DY (0) /* implicit term */ +#define T_ADDONE (1 << 5) + +/* Bit masks defining the 16 equations used in miZeroClipLine. */ + +#define EQN1 (T_2MDY | T_ADDDX | T_SUBBIAS | T_DIV2DX) +#define EQN1B (T_2MDY | T_ADDDX | T_SUBBIAS | T_DIV2DX) +#define EQN2 (T_2MDY | T_ADDDX | T_BIASSUBONE | T_DIV2DX) +#define EQN2B (T_2MDY | T_ADDDX | T_BIASSUBONE | T_DIV2DX) + +#define EQN3 (T_2MDY | T_SUBDY | T_BIASSUBONE | T_DIV2DX | T_ADDONE) +#define EQN3B (T_2MDY | T_ADDDY | T_BIASSUBONE | T_DIV2DX) +#define EQN4 (T_2MDY | T_SUBDY | T_SUBBIAS | T_DIV2DX | T_ADDONE) +#define EQN4B (T_2MDY | T_ADDDY | T_SUBBIAS | T_DIV2DX) + +#define EQN5 (T_2NDX | T_SUBDX | T_BIASSUBONE | T_DIV2DY | T_ADDONE) +#define EQN5B (T_2NDX | T_ADDDX | T_BIASSUBONE | T_DIV2DY) +#define EQN6 (T_2NDX | T_SUBDX | T_SUBBIAS | T_DIV2DY | T_ADDONE) +#define EQN6B (T_2NDX | T_ADDDX | T_SUBBIAS | T_DIV2DY) + +#define EQN7 (T_2NDX | T_ADDDY | T_SUBBIAS | T_DIV2DY) +#define EQN7B (T_2NDX | T_ADDDY | T_SUBBIAS | T_DIV2DY) +#define EQN8 (T_2NDX | T_ADDDY | T_BIASSUBONE | T_DIV2DY) +#define EQN8B (T_2NDX | T_ADDDY | T_BIASSUBONE | T_DIV2DY) + +/* miZeroClipLine + * + * returns: 1 for partially clipped line + * -1 for completely clipped line + * + */ +int +miZeroClipLine(xmin, ymin, xmax, ymax, + new_x1, new_y1, new_x2, new_y2, + adx, ady, + pt1_clipped, pt2_clipped, octant, bias, oc1, oc2) + int xmin, ymin, xmax, ymax; + int *new_x1, *new_y1, *new_x2, *new_y2; + int *pt1_clipped, *pt2_clipped; + unsigned int adx, ady; + int octant; + unsigned int bias; + int oc1, oc2; +{ + int swapped = 0; + int clipDone = 0; + CARD32 utmp; + int clip1, clip2; + int x1, y1, x2, y2; + int x1_orig, y1_orig, x2_orig, y2_orig; + int xmajor; + int negslope, anchorval; + unsigned int eqn; + + x1 = x1_orig = *new_x1; + y1 = y1_orig = *new_y1; + x2 = x2_orig = *new_x2; + y2 = y2_orig = *new_y2; + + clip1 = 0; + clip2 = 0; + + xmajor = IsXMajorOctant(octant); + bias = ((bias >> octant) & 1); + + while (1) + { + if ((oc1 & oc2) != 0) /* trivial reject */ + { + clipDone = -1; + clip1 = oc1; + clip2 = oc2; + break; + } + else if ((oc1 | oc2) == 0) /* trivial accept */ + { + clipDone = 1; + if (swapped) + { + SWAPINT_PAIR(x1, y1, x2, y2); + SWAPINT(clip1, clip2); + } + break; + } + else /* have to clip */ + { + /* only clip one point at a time */ + if (oc1 == 0) + { + SWAPINT_PAIR(x1, y1, x2, y2); + SWAPINT_PAIR(x1_orig, y1_orig, x2_orig, y2_orig); + SWAPINT(oc1, oc2); + SWAPINT(clip1, clip2); + swapped = !swapped; + } + + clip1 |= oc1; + if (oc1 & OUT_LEFT) + { + negslope = IsYDecreasingOctant(octant); + utmp = xmin - x1_orig; + if (utmp <= 32767) /* clip based on near endpt */ + { + if (xmajor) + eqn = (swapped) ? EQN2 : EQN1; + else + eqn = (swapped) ? EQN4 : EQN3; + anchorval = y1_orig; + } + else /* clip based on far endpt */ + { + utmp = x2_orig - xmin; + if (xmajor) + eqn = (swapped) ? EQN1B : EQN2B; + else + eqn = (swapped) ? EQN3B : EQN4B; + anchorval = y2_orig; + negslope = !negslope; + } + x1 = xmin; + } + else if (oc1 & OUT_ABOVE) + { + negslope = IsXDecreasingOctant(octant); + utmp = ymin - y1_orig; + if (utmp <= 32767) /* clip based on near endpt */ + { + if (xmajor) + eqn = (swapped) ? EQN6 : EQN5; + else + eqn = (swapped) ? EQN8 : EQN7; + anchorval = x1_orig; + } + else /* clip based on far endpt */ + { + utmp = y2_orig - ymin; + if (xmajor) + eqn = (swapped) ? EQN5B : EQN6B; + else + eqn = (swapped) ? EQN7B : EQN8B; + anchorval = x2_orig; + negslope = !negslope; + } + y1 = ymin; + } + else if (oc1 & OUT_RIGHT) + { + negslope = IsYDecreasingOctant(octant); + utmp = x1_orig - xmax; + if (utmp <= 32767) /* clip based on near endpt */ + { + if (xmajor) + eqn = (swapped) ? EQN2 : EQN1; + else + eqn = (swapped) ? EQN4 : EQN3; + anchorval = y1_orig; + } + else /* clip based on far endpt */ + { + /* + * Technically since the equations can handle + * utmp == 32768, this overflow code isn't + * needed since X11 protocol can't generate + * a line which goes more than 32768 pixels + * to the right of a clip rectangle. + */ + utmp = xmax - x2_orig; + if (xmajor) + eqn = (swapped) ? EQN1B : EQN2B; + else + eqn = (swapped) ? EQN3B : EQN4B; + anchorval = y2_orig; + negslope = !negslope; + } + x1 = xmax; + } + else if (oc1 & OUT_BELOW) + { + negslope = IsXDecreasingOctant(octant); + utmp = y1_orig - ymax; + if (utmp <= 32767) /* clip based on near endpt */ + { + if (xmajor) + eqn = (swapped) ? EQN6 : EQN5; + else + eqn = (swapped) ? EQN8 : EQN7; + anchorval = x1_orig; + } + else /* clip based on far endpt */ + { + /* + * Technically since the equations can handle + * utmp == 32768, this overflow code isn't + * needed since X11 protocol can't generate + * a line which goes more than 32768 pixels + * below the bottom of a clip rectangle. + */ + utmp = ymax - y2_orig; + if (xmajor) + eqn = (swapped) ? EQN5B : EQN6B; + else + eqn = (swapped) ? EQN7B : EQN8B; + anchorval = x2_orig; + negslope = !negslope; + } + y1 = ymax; + } + + if (swapped) + negslope = !negslope; + + utmp <<= 1; /* utmp = 2N or 2M */ + if (eqn & T_2NDX) + utmp = (utmp * adx); + else /* (eqn & T_2MDY) */ + utmp = (utmp * ady); + if (eqn & T_DXNOTY) + if (eqn & T_SUBDXORY) + utmp -= adx; + else + utmp += adx; + else /* (eqn & T_DYNOTX) */ + if (eqn & T_SUBDXORY) + utmp -= ady; + else + utmp += ady; + if (eqn & T_BIASSUBONE) + utmp += bias - 1; + else /* (eqn & T_SUBBIAS) */ + utmp -= bias; + if (eqn & T_DIV2DX) + utmp /= (adx << 1); + else /* (eqn & T_DIV2DY) */ + utmp /= (ady << 1); + if (eqn & T_ADDONE) + utmp++; + + if (negslope) + utmp = -utmp; + + if (eqn & T_2NDX) /* We are calculating X steps */ + x1 = anchorval + utmp; + else /* else, Y steps */ + y1 = anchorval + utmp; + + oc1 = 0; + MIOUTCODES(oc1, x1, y1, xmin, ymin, xmax, ymax); + } + } + + *new_x1 = x1; + *new_y1 = y1; + *new_x2 = x2; + *new_y2 = y2; + + *pt1_clipped = clip1; + *pt2_clipped = clip2; + + return clipDone; +} + + +/* Draw lineSolid, fillStyle-independent zero width lines. + * + * Must keep X and Y coordinates in "ints" at least until after they're + * translated and clipped to accomodate CoordModePrevious lines with very + * large coordinates. + * + * Draws the same pixels regardless of sign(dx) or sign(dy). + * + * Ken Whaley + * + */ + +/* largest positive value that can fit into a component of a point. + * Assumes that the point structure is {type x, y;} where type is + * a signed type. + */ +#define MAX_COORDINATE ((1 << (((sizeof(DDXPointRec) >> 1) << 3) - 1)) - 1) + +#define MI_OUTPUT_POINT(xx, yy)\ +{\ + if ( !new_span && yy == current_y)\ + {\ + if (xx < spans->x)\ + spans->x = xx;\ + ++*widths;\ + }\ + else\ + {\ + ++Nspans;\ + ++spans;\ + ++widths;\ + spans->x = xx;\ + spans->y = yy;\ + *widths = 1;\ + current_y = yy;\ + new_span = FALSE;\ + }\ +} + +void +miZeroLine(pDraw, pGC, mode, npt, pptInit) + DrawablePtr pDraw; + GCPtr pGC; + int mode; /* Origin or Previous */ + int npt; /* number of points */ + DDXPointPtr pptInit; +{ + int Nspans, current_y; + DDXPointPtr ppt; + DDXPointPtr pspanInit, spans; + int *pwidthInit, *widths, list_len; + int xleft, ytop, xright, ybottom; + int new_x1, new_y1, new_x2, new_y2; + int x, y, x1, y1, x2, y2, xstart, ystart; + int oc1, oc2; + int result; + int pt1_clipped, pt2_clipped = 0; + Bool new_span; + int signdx, signdy; + int clipdx, clipdy; + int width, height; + int adx, ady; + int octant; + unsigned int bias = miGetZeroLineBias(pDraw->pScreen); + int e, e1, e2, e3; /* Bresenham error terms */ + int length; /* length of lines == # of pixels on major axis */ + + xleft = pDraw->x; + ytop = pDraw->y; + xright = pDraw->x + pDraw->width - 1; + ybottom = pDraw->y + pDraw->height - 1; + + if (!pGC->miTranslate) + { + /* do everything in drawable-relative coordinates */ + xleft = 0; + ytop = 0; + xright -= pDraw->x; + ybottom -= pDraw->y; + } + + /* it doesn't matter whether we're in drawable or screen coordinates, + * FillSpans simply cannot take starting coordinates outside of the + * range of a DDXPointRec component. + */ + if (xright > MAX_COORDINATE) + xright = MAX_COORDINATE; + if (ybottom > MAX_COORDINATE) + ybottom = MAX_COORDINATE; + + /* since we're clipping to the drawable's boundaries & coordinate + * space boundaries, we're guaranteed that the larger of width/height + * is the longest span we'll need to output + */ + width = xright - xleft + 1; + height = ybottom - ytop + 1; + list_len = (height >= width) ? height : width; + pspanInit = (DDXPointPtr)ALLOCATE_LOCAL(list_len * sizeof(DDXPointRec)); + pwidthInit = (int *)ALLOCATE_LOCAL(list_len * sizeof(int)); + if (!pspanInit || !pwidthInit) + return; + + Nspans = 0; + new_span = TRUE; + spans = pspanInit - 1; + widths = pwidthInit - 1; + ppt = pptInit; + + xstart = ppt->x; + ystart = ppt->y; + if (pGC->miTranslate) + { + xstart += pDraw->x; + ystart += pDraw->y; + } + + /* x2, y2, oc2 copied to x1, y1, oc1 at top of loop to simplify + * iteration logic + */ + x2 = xstart; + y2 = ystart; + oc2 = 0; + MIOUTCODES(oc2, x2, y2, xleft, ytop, xright, ybottom); + + while (--npt > 0) + { + if (Nspans > 0) + (*pGC->ops->FillSpans)(pDraw, pGC, Nspans, pspanInit, + pwidthInit, FALSE); + Nspans = 0; + new_span = TRUE; + spans = pspanInit - 1; + widths = pwidthInit - 1; + + x1 = x2; + y1 = y2; + oc1 = oc2; + ++ppt; + + x2 = ppt->x; + y2 = ppt->y; + if (pGC->miTranslate && (mode != CoordModePrevious)) + { + x2 += pDraw->x; + y2 += pDraw->y; + } + else if (mode == CoordModePrevious) + { + x2 += x1; + y2 += y1; + } + + oc2 = 0; + MIOUTCODES(oc2, x2, y2, xleft, ytop, xright, ybottom); + + CalcLineDeltas(x1, y1, x2, y2, adx, ady, signdx, signdy, 1, 1, octant); + + if (adx > ady) + { + e1 = ady << 1; + e2 = e1 - (adx << 1); + e = e1 - adx; + length = adx; /* don't draw endpoint in main loop */ + + FIXUP_ERROR(e, octant, bias); + + new_x1 = x1; + new_y1 = y1; + new_x2 = x2; + new_y2 = y2; + pt1_clipped = 0; + pt2_clipped = 0; + + if ((oc1 | oc2) != 0) + { + result = miZeroClipLine(xleft, ytop, xright, ybottom, + &new_x1, &new_y1, &new_x2, &new_y2, + adx, ady, + &pt1_clipped, &pt2_clipped, + octant, bias, oc1, oc2); + if (result == -1) + continue; + + length = abs(new_x2 - new_x1); + + /* if we've clipped the endpoint, always draw the full length + * of the segment, because then the capstyle doesn't matter + */ + if (pt2_clipped) + length++; + + if (pt1_clipped) + { + /* must calculate new error terms */ + clipdx = abs(new_x1 - x1); + clipdy = abs(new_y1 - y1); + e += (clipdy * e2) + ((clipdx - clipdy) * e1); + } + } + + /* draw the segment */ + + x = new_x1; + y = new_y1; + + e3 = e2 - e1; + e = e - e1; + + while (length--) + { + MI_OUTPUT_POINT(x, y); + e += e1; + if (e >= 0) + { + y += signdy; + e += e3; + } + x += signdx; + } + } + else /* Y major line */ + { + e1 = adx << 1; + e2 = e1 - (ady << 1); + e = e1 - ady; + length = ady; /* don't draw endpoint in main loop */ + + SetYMajorOctant(octant); + FIXUP_ERROR(e, octant, bias); + + new_x1 = x1; + new_y1 = y1; + new_x2 = x2; + new_y2 = y2; + pt1_clipped = 0; + pt2_clipped = 0; + + if ((oc1 | oc2) != 0) + { + result = miZeroClipLine(xleft, ytop, xright, ybottom, + &new_x1, &new_y1, &new_x2, &new_y2, + adx, ady, + &pt1_clipped, &pt2_clipped, + octant, bias, oc1, oc2); + if (result == -1) + continue; + + length = abs(new_y2 - new_y1); + + /* if we've clipped the endpoint, always draw the full length + * of the segment, because then the capstyle doesn't matter + */ + if (pt2_clipped) + length++; + + if (pt1_clipped) + { + /* must calculate new error terms */ + clipdx = abs(new_x1 - x1); + clipdy = abs(new_y1 - y1); + e += (clipdx * e2) + ((clipdy - clipdx) * e1); + } + } + + /* draw the segment */ + + x = new_x1; + y = new_y1; + + e3 = e2 - e1; + e = e - e1; + + while (length--) + { + MI_OUTPUT_POINT(x, y); + e += e1; + if (e >= 0) + { + x += signdx; + e += e3; + } + y += signdy; + } + } + } + + /* only do the capnotlast check on the last segment + * and only if the endpoint wasn't clipped. And then, if the last + * point is the same as the first point, do not draw it, unless the + * line is degenerate + */ + if ( (! pt2_clipped) && (pGC->capStyle != CapNotLast) && + (((xstart != x2) || (ystart != y2)) || (ppt == pptInit + 1))) + { + MI_OUTPUT_POINT(x, y); + } + + if (Nspans > 0) + (*pGC->ops->FillSpans)(pDraw, pGC, Nspans, pspanInit, + pwidthInit, FALSE); + + DEALLOCATE_LOCAL(pwidthInit); + DEALLOCATE_LOCAL(pspanInit); +} + +void +miZeroDashLine(dst, pgc, mode, nptInit, pptInit) +DrawablePtr dst; +GCPtr pgc; +int mode; +int nptInit; /* number of points in polyline */ +DDXPointRec *pptInit; /* points in the polyline */ +{ + /* XXX kludge until real zero-width dash code is written */ + pgc->lineWidth = 1; + miWideDash (dst, pgc, mode, nptInit, pptInit); + pgc->lineWidth = 0; +} |