From 4d7d62eded81b194247b7052366fb8f89978dc36 Mon Sep 17 00:00:00 2001 From: James Tabor Date: Sun, 29 Jun 2008 01:19:46 +0000 Subject: [PATCH] Arc: - Ported GraphApp draw arc fill algorithm. It was faster than X11. Include copyright license in header. - Now we can draw pies and arcs and fill them, need to test chord fill. svn path=/trunk/; revision=34169 --- reactos/subsystems/win32/win32k/objects/arc.c | 236 +++-- .../subsystems/win32/win32k/objects/drawing.c | 810 ++++++++++++++++++ reactos/subsystems/win32/win32k/win32k.rbuild | 1 + 3 files changed, 952 insertions(+), 95 deletions(-) create mode 100755 reactos/subsystems/win32/win32k/objects/drawing.c diff --git a/reactos/subsystems/win32/win32k/objects/arc.c b/reactos/subsystems/win32/win32k/objects/arc.c index b0b32749f4f..3138e2521a9 100644 --- a/reactos/subsystems/win32/win32k/objects/arc.c +++ b/reactos/subsystems/win32/win32k/objects/arc.c @@ -25,6 +25,8 @@ #define Rsin(d) ((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0))) #define Rcos(d) ((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0))) +BOOL FASTCALL IntFillArc( PDC dc, INT XLeft, INT YLeft, INT Width, INT Height, double StartArc, double EndArc, ARCTYPE arctype); + static BOOL FASTCALL @@ -41,10 +43,11 @@ IntArc( DC *dc, { PDC_ATTR Dc_Attr; RECTL RectBounds; - PGDIBRUSHOBJ PenBrushObj, FillBrushObj; - GDIBRUSHINST FillBrushInst, PenBrushInst; + PGDIBRUSHOBJ PenBrushObj; + GDIBRUSHINST PenBrushInst; BITMAPOBJ *BitmapObj; BOOL ret = TRUE; + LONG PenWidth, PenOrigWidth; double AngleStart, AngleEnd; LONG RadiusX, RadiusY, CenterX, CenterY; LONG SfCx, SfCy, EfCx, EfCy; @@ -60,50 +63,58 @@ IntArc( DC *dc, 0|___________________| 0 bottom + */ - if (Right <= Left || Top <= Bottom) + if (Right < Left) { - DPRINT1("Arc Fail 1\n"); - SetLastWin32Error(ERROR_INVALID_PARAMETER); - return FALSE; + INT tmp = Right; Right = Left; Left = tmp; } -/* - if (Right - Left != Bottom - Top) + if (Bottom < Top) { - UNIMPLEMENTED; + INT tmp = Bottom; Bottom = Top; Top = tmp; } -*/ + if ((Left == Right) || + (Top == Bottom) || + (((arctype != GdiTypeArc) || (arctype != GdiTypeArcTo)) && + ((Right - Left == 1) || + (Bottom - Top == 1)))) + return TRUE; + + if (dc->DcLevel.flPath & DCPATH_CLOCKWISE) + { + INT X, Y; + X = XRadialStart; + Y = YRadialStart; + XRadialStart = XRadialEnd; + YRadialStart = YRadialEnd; + XRadialEnd = X; + YRadialEnd = Y; + } + Dc_Attr = dc->pDc_Attr; if(!Dc_Attr) Dc_Attr = &dc->Dc_Attr; - FillBrushObj = BRUSHOBJ_LockBrush(Dc_Attr->hbrush); - if (NULL == FillBrushObj) - { - DPRINT1("Arc Fail 2\n"); - SetLastWin32Error(ERROR_INTERNAL_ERROR); - return FALSE; - } - PenBrushObj = PENOBJ_LockPen(Dc_Attr->hpen); if (NULL == PenBrushObj) { - DPRINT1("Arc Fail 3\n"); - BRUSHOBJ_UnlockBrush(FillBrushObj); + DPRINT1("Arc Fail 1\n"); SetLastWin32Error(ERROR_INTERNAL_ERROR); return FALSE; } - BitmapObj = BITMAPOBJ_LockBitmap(dc->w.hBitmap); - if (NULL == BitmapObj) + PenOrigWidth = PenWidth = PenBrushObj->ptPenWidth.x; + if (PenBrushObj->ulPenStyle == PS_NULL) PenWidth = 0; + + if (PenBrushObj->ulPenStyle == PS_INSIDEFRAME) { - DPRINT1("Arc Fail 4\n"); - BRUSHOBJ_UnlockBrush(FillBrushObj); - PENOBJ_UnlockPen(PenBrushObj); - SetLastWin32Error(ERROR_INTERNAL_ERROR); - return FALSE; + if (2*PenWidth > (Right - Left)) PenWidth = (Right -Left + 1)/2; + if (2*PenWidth > (Bottom - Top)) PenWidth = (Bottom -Top + 1)/2; + Left += PenWidth / 2; + Right -= (PenWidth - 1) / 2; + Top += PenWidth / 2; + Bottom -= (PenWidth - 1) / 2; } - IntGdiInitBrushInstance(&FillBrushInst, FillBrushObj, dc->XlateBrush); - IntGdiInitBrushInstance(&PenBrushInst, PenBrushObj, dc->XlatePen); + if (!PenWidth) PenWidth = 1; + PenBrushObj->ptPenWidth.x = PenWidth; Left += dc->ptlDCOrig.x; Right += dc->ptlDCOrig.x; @@ -125,56 +136,103 @@ IntArc( DC *dc, DPRINT1("1: Left: %d, Top: %d, Right: %d, Bottom: %d\n", RectBounds.left,RectBounds.top,RectBounds.right,RectBounds.bottom); - if (Left == Right) - { - DPRINT1("Arc Good Exit\n"); - PUTPIXEL(Left, Top, PenBrushInst); - BITMAPOBJ_UnlockBitmap(BitmapObj); - BRUSHOBJ_UnlockBrush(FillBrushObj); - PENOBJ_UnlockPen(PenBrushObj); - return ret; - } - RadiusX = (RectBounds.right - RectBounds.left)/2; - RadiusY = (RectBounds.bottom - RectBounds.top)/2; - CenterX = RectBounds.left + RadiusX; - CenterY = RectBounds.top + RadiusY; - - AngleEnd = atan2(-(YRadialEnd - CenterY), XRadialEnd - CenterX)* (180.0 / M_PI); - AngleStart = atan2(-(YRadialStart - CenterY), XRadialStart - CenterX)* (180.0 / M_PI); + RadiusX = (RectBounds.right - RectBounds.left) / 2; + RadiusY = (RectBounds.bottom - RectBounds.top) / 2; + CenterX = (RectBounds.right + RectBounds.left) / 2; + CenterY = (RectBounds.bottom + RectBounds.top) / 2; + AngleEnd = atan2((YRadialEnd - CenterY), XRadialEnd - CenterX)*(360.0/(M_PI*2)); + AngleStart = atan2((YRadialStart - CenterY), XRadialStart - CenterX)*(360.0/(M_PI*2)); SfCx = (Rcos(AngleStart) * RadiusX); SfCy = (Rsin(AngleStart) * RadiusY); - EfCx = (Rcos(AngleEnd) * RadiusX); EfCy = (Rsin(AngleEnd) * RadiusY); + + if ((arctype == GdiTypePie) || (arctype == GdiTypeChord)) { - FLOAT AngS = AngleStart, Factor = 1; - int x,y, ox = 0, oy = 0; - - if (arctype == GdiTypePie) - { - PUTLINE(SfCx + CenterX, SfCy + CenterY, CenterX, CenterY, PenBrushInst); - } - - for(; AngS < AngleEnd; AngS += Factor) - { - x = (Rcos(AngS) * RadiusX); - y = (Rsin(AngS) * RadiusY); - - if (arctype == GdiTypePie) - PUTLINE((x + CenterX) - 1, (y + CenterY) - 1, CenterX, CenterY, FillBrushInst); - - PUTPIXEL (x + CenterX, y + CenterY, PenBrushInst); - ox = x; - oy = y; - } - - if (arctype == GdiTypePie) - PUTLINE(EfCx + CenterX, EfCy + CenterY, CenterX, CenterY, PenBrushInst); - + ret = IntFillArc( dc, + RectBounds.left, + RectBounds.top, + fabs(RectBounds.right-RectBounds.left), // Width + fabs(RectBounds.bottom-RectBounds.top), // Height + AngleStart, + AngleEnd, + arctype); } + + BitmapObj = BITMAPOBJ_LockBitmap(dc->w.hBitmap); + if (NULL == BitmapObj) + { + DPRINT1("Arc Fail 2\n"); + PENOBJ_UnlockPen(PenBrushObj); + SetLastWin32Error(ERROR_INTERNAL_ERROR); + return FALSE; + } + + IntGdiInitBrushInstance(&PenBrushInst, PenBrushObj, dc->XlatePen); + + if (arctype == GdiTypePie) + { + PUTLINE(CenterX, CenterY, SfCx + CenterX, SfCy + CenterY, PenBrushInst); + } + { + double AngS = AngleStart, AngT = AngleEnd, + Factor = fabs(RadiusX) < 25 ? 1.0 : (25/fabs(RadiusX)); + int x,y, ox = 0, oy = 0; + BOOL Start = TRUE; + + if (dc->DcLevel.flPath & DCPATH_CLOCKWISE) + { + DPRINT1("Arc CW\n"); + for (; AngS < AngT; AngS += Factor) + { + x = (RadiusX * Rcos(AngS)); + y = (RadiusY * Rsin(AngS)); + + DPRINT("Arc CW -> %d, X: %d Y: %d\n",(INT)AngS,x,y); + if (Start) + { + PUTPIXEL(x + CenterX, y + CenterY, PenBrushInst); + ox = x; + oy = y; + Start = FALSE; + continue; + } + PUTLINE(ox + CenterX, oy + CenterY, x + CenterX, y + CenterY, PenBrushInst); + ox = x; + oy = y; + } + } + else + { + DPRINT1("Arc CCW\n"); + for (; AngT < AngS; AngS -= Factor) + { + x = (RadiusX * Rcos(AngS)); + y = (RadiusY * Rsin(AngS)); + + DPRINT("Arc CCW -> %d, X: %d Y: %d\n",(INT)AngS,x,y); + if (Start) + { + PUTPIXEL(x + CenterX, y + CenterY, PenBrushInst); + ox = x; + oy = y; + Start = FALSE; + continue; + } + PUTLINE(ox + CenterX, oy + CenterY, x + CenterX, y + CenterY, PenBrushInst); + ox = x; + oy = y; + } + } + } + if (arctype == GdiTypePie) + PUTLINE(EfCx + CenterX, EfCy + CenterY, CenterX, CenterY, PenBrushInst); + if (arctype == GdiTypeChord) + PUTLINE(EfCx + CenterX, EfCy + CenterY, SfCx + CenterX, SfCy + CenterY, PenBrushInst); + + PenBrushObj->ptPenWidth.x = PenOrigWidth; BITMAPOBJ_UnlockBitmap(BitmapObj); - BRUSHOBJ_UnlockBrush(FillBrushObj); PENOBJ_UnlockPen(PenBrushObj); DPRINT1("IntArc Exit.\n"); return ret; @@ -196,9 +254,6 @@ IntGdiArcInternal( { BOOL Ret; RECT rc, rc1; - double AngleStart, AngleEnd; - LONG RadiusX, RadiusY, CenterX, CenterY, Width, Height; - LONG SfCx, SfCy, EfCx = 0, EfCy = 0; DPRINT1("StartX: %d, StartY: %d, EndX: %d, EndY: %d\n", XStartArc,YStartArc,XEndArc,YEndArc); @@ -221,22 +276,10 @@ IntGdiArcInternal( if (arctype == GdiTypeArcTo) { - Width = fabs(RightRect - LeftRect); - Height = fabs(BottomRect - TopRect); - RadiusX = Width/2; - RadiusY = Height/2; - CenterX = RightRect > LeftRect ? LeftRect + RadiusX : RightRect + RadiusX; - CenterY = BottomRect > TopRect ? TopRect + RadiusY : BottomRect + RadiusY; - - AngleStart = atan2((YStartArc - CenterY)/Height, (XStartArc - CenterX)/Width); - AngleEnd = atan2((YEndArc - CenterY)/Height, (XEndArc - CenterX)/Width); - - EfCx = GDI_ROUND(CenterX+cos(AngleEnd) * RadiusX); - EfCy = GDI_ROUND(CenterY+sin(AngleEnd) * RadiusY); - SfCx = GDI_ROUND(CenterX+cos(AngleStart) * RadiusX); - SfCy = GDI_ROUND(CenterY+sin(AngleStart) * RadiusY); - - IntGdiLineTo(dc, SfCx, SfCy); + if (dc->DcLevel.flPath & DCPATH_CLOCKWISE) + IntGdiLineTo(dc, XEndArc, YEndArc); + else + IntGdiLineTo(dc, XStartArc, YStartArc); } IntGdiSetRect(&rc, LeftRect, TopRect, RightRect, BottomRect); @@ -245,6 +288,7 @@ IntGdiArcInternal( // IntLPtoDP(dc, (LPPOINT)&rc, 2); // IntLPtoDP(dc, (LPPOINT)&rc1, 2); + Ret = IntArc( dc, rc.left, rc.top, @@ -258,9 +302,11 @@ IntGdiArcInternal( if (arctype == GdiTypeArcTo) { - IntGdiMoveToEx(dc, EfCx, EfCy, NULL); + if (dc->DcLevel.flPath & DCPATH_CLOCKWISE) + IntGdiMoveToEx(dc, XStartArc, YStartArc, NULL); + else + IntGdiMoveToEx(dc, XEndArc, YEndArc, NULL); } - return Ret; } @@ -292,9 +338,9 @@ IntGdiAngleArc( PDC pDC, result = IntGdiArcInternal( GdiTypeArcTo, pDC, x-dwRadius, - y+dwRadius, - x+dwRadius, y-dwRadius, + x+dwRadius, + y+dwRadius, x1, y1, x2, @@ -304,7 +350,7 @@ IntGdiAngleArc( PDC pDC, if (result) { - IntGdiMoveToEx(pDC, x2, y2, NULL); + IntGdiMoveToEx(pDC, x2, y2, NULL); // Dont forget Path. } return result; } diff --git a/reactos/subsystems/win32/win32k/objects/drawing.c b/reactos/subsystems/win32/win32k/objects/drawing.c new file mode 100755 index 00000000000..14fa7dd1935 --- /dev/null +++ b/reactos/subsystems/win32/win32k/objects/drawing.c @@ -0,0 +1,810 @@ +/* +App Software Licence +-------------------- +This package includes software which is copyright (c) L. Patrick. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. You may not sell this software package. +4. You may include this software in a distribution of other software, + and you may charge a nominal fee for the media used. +5. You may sell derivative programs, providing that such programs + simply use this software in a compiled form. +6. You may sell derivative programs which use a compiled, modified + version of this software, provided that you have attempted as + best as you can to propagate all modifications made to the source + code files of this software package back to the original author(s) + of this package. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS AS IS, AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. +*/ +/* Copyright (c) L. Patrick + + This file is part of the App cross-platform programming package. + You may redistribute it and/or modify it under the terms of the + App Software License. See the file LICENSE.TXT for details. + + http://enchantia.com/software/graphapp/ + http://www.it.usyd.edu.au/~graphapp/ +*/ +/* + Modified for ReactOS + */ + +#include + +#define NDEBUG +#include + + +#define DEGREES_TO_RADIANS(deg) ((deg)*2*M_PI/360) + +typedef struct _Rect +{ + int x, y; /* top-left point inside rect */ + int width, height; /* width and height of rect */ +} Rect, *PRect; + +static +POINT +INTERNAL_CALL +app_new_point(int x, int y) +{ + POINT p; + p.x = x; + p.y = y; + return p; +} +#define pt(x,y) app_new_point((x),(y)) + +static +Rect +INTERNAL_CALL +rect(int x, int y, int width, int height) +{ + Rect r; + + r.x = x; + r.y = y; + r.width = width; + r.height = height; + return r; +} + + +/* + * app_window_fill_rect: + * + * Fill a rectangle with colour, in a window. + * + * This function implements client-side clipping, so that + * we never rely on the GDI system to do clipping, except if + * the destination is a window which is partially obscured. + * In that situation we must rely on the GDI system because there + * is no way for the program to know which portions of the + * window are currently obscured. + */ +static +int +INTERNAL_CALL +app_fill_rect(DC *dc, Rect r, PGDIBRUSHOBJ BrushObj) +{ + RECTL DestRect; + BITMAPOBJ *BitmapObj; + GDIBRUSHINST BrushInst; + POINTL BrushOrigin; + BOOL Ret = TRUE; + + ASSERT(BrushObj); + + BitmapObj = BITMAPOBJ_LockBitmap(dc->w.hBitmap); + if (BitmapObj == NULL) + { + SetLastWin32Error(ERROR_INVALID_HANDLE); + return 0; + } + + if (!(BrushObj->flAttrs & GDIBRUSH_IS_NULL)) + { + + /* fix negative spaces */ + if (r.width < 0) + { + r.x += r.width; + r.width = 0 - r.width; + } + if (r.height < 0) + { + r.y += r.height; + r.height = 0 - r.height; + } + + DestRect.left = r.x; + DestRect.right = r.x + r.width; + + DestRect.top = r.y; + DestRect.bottom = r.y + r.height; + + BrushOrigin.x = BrushObj->ptOrigin.x; + BrushOrigin.y = BrushObj->ptOrigin.y; + + IntGdiInitBrushInstance(&BrushInst, BrushObj, dc->XlateBrush); + + Ret = IntEngBitBlt( + &BitmapObj->SurfObj, + NULL, + NULL, + dc->CombinedClip, + NULL, + &DestRect, + NULL, + NULL, + &BrushInst.BrushObject, // use pDC->eboFill + &BrushOrigin, + ROP3_TO_ROP4(PATCOPY)); + } + + BITMAPOBJ_UnlockBitmap(BitmapObj); + return (int)Ret; +} + +/* + * Draw an arc of an ellipse from start_angle anti-clockwise to + * end_angle. If the angles coincide, draw nothing; if they + * differ by 360 degrees or more, draw a full ellipse. + * The shape is drawn with the current line thickness, + * completely within the bounding rectangle. The shape is also + * axis-aligned, so that the ellipse would be horizontally and + * vertically symmetric is it was complete. + * + * The draw_arc algorithm is based on draw_ellipse, but unlike + * that algorithm is not symmetric in the general case, since + * an angular portion is clipped from the shape. + * This clipping is performed by keeping track of two hypothetical + * lines joining the centre point to the enclosing rectangle, + * at the angles start_angle and end_angle, using a line-intersection + * algorithm. Essentially the algorithm just fills the spaces + * which are within the arc and also between the angles, going + * in an anti-clockwise direction from start_angle to end_angle. + * In the top half of the ellipse, this amounts to drawing + * to the left of the start_angle line and to the right of + * the end_angle line, while in the bottom half of the ellipse, + * it involves drawing to the right of the start_angle and to + * the left of the end_angle. + */ + +/* + * Fill a rectangle within an arc, given the centre point p0, + * and the two end points of the lines corresponding to the + * start_angle and the end_angle. This function takes care of + * the logic needed to swap the fill direction below + * the central point, and also performs the calculations + * needed to intersect the current Y value with each line. + */ +static +int +FASTCALL +app_fill_arc_rect(DC *g, + Rect r, // top, left, width, height + POINT p0, // Center + POINT p1, // Start + POINT p2, // End + int start_angle, + int end_angle, + PGDIBRUSHOBJ BrushObj) +{ + int x1, x2; + int start_above, end_above; + long rise1, run1, rise2, run2; + + rise1 = p1.y - p0.y; + run1 = p1.x - p0.x; + rise2 = p2.y - p0.y; + run2 = p2.x - p0.x; + + if (r.y <= p0.y) // + { + /* in top half of arc ellipse */ + + if (p1.y <= r.y) + { + /* start_line is in the top half and is */ + /* intersected by the current Y scan line */ + if (rise1 == 0) + x1 = p1.x; + else + x1 = p0.x + (r.y-p0.y)*run1/rise1; + start_above = 1; + } + else if ((start_angle >= 0) && (start_angle <= 180)) + { + /* start_line is above middle */ + x1 = p1.x; + start_above = 1; + } + else + { + /* start_line is below middle */ + x1 = r.x + r.width; + start_above = 0; + } + if (x1 < r.x) + x1 = r.x; + if (x1 > r.x+r.width) + x1 = r.x+r.width; + + if (p2.y <= r.y) + { + /* end_line is in the top half and is */ + /* intersected by the current Y scan line */ + if (rise2 == 0) + x2 = p2.x; + else + x2 = p0.x + (r.y-p0.y)*run2/rise2; + end_above = 1; + } + else if ((end_angle >= 0) && (end_angle <= 180)) + { + /* end_line is above middle */ + x2 = p2.x; + end_above = 1; + } + else + { + /* end_line is below middle */ + x2 = r.x; + end_above = 0; + } + + if (x2 < r.x) x2 = r.x; + + if (x2 > r.x+r.width) x2 = r.x+r.width; + + if (start_above && end_above) + { + if (start_angle > end_angle) + { + /* fill outsides of wedge */ + if (! app_fill_rect(g, rect(r.x, r.y, + x1-r.x, r.height), BrushObj)) + return 0; + return app_fill_rect(g, rect(x2, r.y, + r.x+r.width-x2, r.height), BrushObj); + } + else + { + /* fill inside of wedge */ + r.width = x1-x2; + r.x = x2; + return app_fill_rect(g, r, BrushObj); + } + } + else if (start_above) + { + /* fill to the left of the start_line */ + r.width = x1-r.x; + return app_fill_rect(g, r, BrushObj); + } + else if (end_above) + { + /* fill right of end_line */ + r.width = r.x+r.width-x2; + r.x = x2; + return app_fill_rect(g, r, BrushObj); + } + else + { + if (start_angle > end_angle) + return app_fill_rect(g,r, BrushObj); + else + return 1; + } + } + else + { + /* in lower half of arc ellipse */ + + if (p1.y >= r.y) + { + /* start_line is in the lower half and is */ + /* intersected by the current Y scan line */ + if (rise1 == 0) + x1 = p1.x; + else + x1 = p0.x + (r.y-p0.y)*run1/rise1; + start_above = 0; + } + else if ((start_angle >= 180) && (start_angle <= 360)) + { + /* start_line is below middle */ + x1 = p1.x; + start_above = 0; + } + else + { + /* start_line is above middle */ + x1 = r.x; + start_above = 1; + } + if (x1 < r.x) + x1 = r.x; + if (x1 > r.x+r.width) + x1 = r.x+r.width; + + if (p2.y >= r.y) + { + /* end_line is in the lower half and is */ + /* intersected by the current Y scan line */ + if (rise2 == 0) + x2 = p2.x; + else + x2 = p0.x + (r.y-p0.y)*run2/rise2; + end_above = 0; + } + else if ((end_angle >= 180) && (end_angle <= 360)) + { + /* end_line is below middle */ + x2 = p2.x; + end_above = 0; + } + else + { + /* end_line is above middle */ + x2 = r.x + r.width; + end_above = 1; + } + if (x2 < r.x) + x2 = r.x; + if (x2 > r.x+r.width) + x2 = r.x+r.width; + + if (start_above && end_above) + { + if (start_angle > end_angle) + return app_fill_rect(g,r, BrushObj); + else + return 1; + } + else if (start_above) + { + /* fill to the left of end_line */ + r.width = x2-r.x; + return app_fill_rect(g,r, BrushObj); + } + else if (end_above) + { + /* fill right of start_line */ + r.width = r.x+r.width-x1; + r.x = x1; + return app_fill_rect(g,r, BrushObj); + } + else + { + if (start_angle > end_angle) + { + /* fill outsides of wedge */ + if (! app_fill_rect(g, rect(r.x, r.y, + x2-r.x, r.height), BrushObj)) + return 0; + return app_fill_rect(g, rect(x1, r.y, + r.x+r.width-x1, r.height), BrushObj); + } + else + { + /* fill inside of wedge */ + r.width = x2-x1; + r.x = x1; + return app_fill_rect(g, r, BrushObj); + } + } + } +} + +/* + * To fill an axis-aligned ellipse, we use a scan-line algorithm. + * We walk downwards from the top Y co-ordinate, calculating + * the width of the ellipse using incremental integer arithmetic. + * To save calculation, we observe that the top and bottom halves + * of the ellipsoid are mirror-images, therefore we can draw the + * top and bottom halves by reflection. As a result, this algorithm + * draws rectangles inwards from the top and bottom edges of the + * bounding rectangle. + * + * To save rendering time, draw as few rectangles as possible. + * Other ellipse-drawing algorithms assume we want to draw each + * line, using a draw_pixel operation, or a draw_horizontal_line + * operation. This approach is slower than it needs to be in + * circumstances where a fill_rect operation is more efficient + * (such as in X-Windows, where there is a communication overhead + * to the X-Server). For this reason, the algorithm accumulates + * rectangles on adjacent lines which have the same width into a + * single larger rectangle. + * + * This algorithm forms the basis of the later, more complex, + * draw_ellipse algorithm, which renders the rectangular spaces + * between an outer and inner ellipse, and also the draw_arc and + * fill_arc operations which additionally clip drawing between + * a start_angle and an end_angle. + * + */ +static +int +FASTCALL +app_fill_ellipse(DC *g, Rect r, PGDIBRUSHOBJ BrushObj) +{ + /* e(x,y) = b*b*x*x + a*a*y*y - a*a*b*b */ + + int a = r.width / 2; + int b = r.height / 2; + int x = 0; + int y = b; + long a2 = a*a; + long b2 = b*b; + long xcrit = (3 * a2 / 4) + 1; + long ycrit = (3 * b2 / 4) + 1; + long t = b2 + a2 - 2*a2*b; /* t = e(x+1,y-1) */ + long dxt = b2*(3+x+x); + long dyt = a2*(3-y-y); + int d2xt = b2+b2; + int d2yt = a2+a2; + Rect r1, r2; + int result = 1; + +// START_DEBUG(); + + if ((r.width <= 2) || (r.height <= 2)) + return app_fill_rect(g, r, BrushObj); + + r1.x = r.x + a; + r1.y = r.y; + r1.width = r.width & 1; /* i.e. if width is odd */ + r1.height = 1; + + r2 = r1; + r2.y = r.y + r.height - 1; + + while (y > 0) + { + if (t + a2*y < xcrit) { /* e(x+1,y-1/2) <= 0 */ + /* move outwards to encounter edge */ + x += 1; + t += dxt; + dxt += d2xt; + + /* move outwards */ + r1.x -= 1; r1.width += 2; + r2.x -= 1; r2.width += 2; + } + else if (t - b2*x >= ycrit) { /* e(x+1/2,y-1) > 0 */ + /* drop down one line */ + y -= 1; + t += dyt; + dyt += d2yt; + + /* enlarge rectangles */ + r1.height += 1; + r2.height += 1; r2.y -= 1; + } + else { + /* drop diagonally down and out */ + x += 1; + y -= 1; + t += dxt + dyt; + dxt += d2xt; + dyt += d2yt; + + if ((r1.width > 0) && (r1.height > 0)) + { + /* draw rectangles first */ + + if (r1.y+r1.height < r2.y) { + /* distinct rectangles */ + result &= app_fill_rect(g, r1, BrushObj); + result &= app_fill_rect(g, r2, BrushObj); + } + + /* move down */ + r1.y += r1.height; r1.height = 1; + r2.y -= 1; r2.height = 1; + } + else { + /* skipped pixels on initial diagonal */ + + /* enlarge, rather than moving down */ + r1.height += 1; + r2.height += 1; r2.y -= 1; + } + + /* move outwards */ + r1.x -= 1; r1.width += 2; + r2.x -= 1; r2.width += 2; + } + } + if (r1.y < r2.y) { + /* overlap */ + r1.x = r.x; + r1.width = r.width; + r1.height = r2.y+r2.height-r1.y; + result &= app_fill_rect(g, r1, BrushObj); + } + else if (x <= a) { + /* crossover, draw final line */ + r1.x = r.x; + r1.width = r.width; + r1.height = r1.y+r1.height-r2.y; + r1.y = r2.y; + result &= app_fill_rect(g, r1, BrushObj); + } + return result; +} + +static +FASTCALL +POINT app_boundary_point(Rect r, int angle) +{ + int cx, cy; + double tangent; + + cx = r.width; + cx /= 2; + cx += r.x; + + cy = r.height; + cy /= 2; + cy += r.y; + + if (angle == 0) + return pt(r.x+r.width, cy); + else if (angle == 45) + return pt(r.x+r.width, r.y); + else if (angle == 90) + return pt(cx, r.y); + else if (angle == 135) + return pt(r.x, r.y); + else if (angle == 180) + return pt(r.x, cy); + else if (angle == 225) + return pt(r.x, r.y+r.height); + else if (angle == 270) + return pt(cx, r.y+r.height); + else if (angle == 315) + return pt(r.x+r.width, r.y+r.height); + + tangent = tan(DEGREES_TO_RADIANS(angle)); + + if ((angle > 45) && (angle < 135)) + return pt((int)(cx+r.height/tangent/2), r.y); + else if ((angle > 225) && (angle < 315)) + return pt((int)(cx-r.height/tangent/2), r.y+r.height); + else if ((angle > 135) && (angle < 225)) + return pt(r.x, (int)(cy+r.width*tangent/2)); + else + return pt(r.x+r.width, (int)(cy-r.width*tangent/2)); +} + +int +FASTCALL +app_fill_arc(DC *g, Rect r, int start_angle, int end_angle, PGDIBRUSHOBJ BrushObj, BOOL Chord) +{ + /* e(x,y) = b*b*x*x + a*a*y*y - a*a*b*b */ + + int a = r.width / 2; + int b = r.height / 2; + int x = 0; + int y = b; + long a2 = a*a; + long b2 = b*b; + long xcrit = (3 * a2 / 4) + 1; + long ycrit = (3 * b2 / 4) + 1; + long t = b2 + a2 - 2*a2*b; /* t = e(x+1,y-1) */ + long dxt = b2*(3+x+x); + long dyt = a2*(3-y-y); + int d2xt = b2+b2; + int d2yt = a2+a2; + Rect r1, r2; + int movedown, moveout; + int result = 1; + + /* line descriptions */ + POINT p0, p1, p2; + +// START_DEBUG(); + + /* if angles differ by 360 degrees or more, close the shape */ + if ((start_angle + 360 <= end_angle) || + (start_angle - 360 >= end_angle)) + { + return app_fill_ellipse(g, r, BrushObj); + } + + /* make start_angle >= 0 and <= 360 */ + while (start_angle < 0) + start_angle += 360; + start_angle %= 360; + + /* make end_angle >= 0 and <= 360 */ + while (end_angle < 0) + end_angle += 360; + end_angle %= 360; + + /* draw nothing if the angles are equal */ + if (start_angle == end_angle) + return 1; + + /* find arc wedge line end points */ + p1 = app_boundary_point(r, start_angle); + p2 = app_boundary_point(r, end_angle); + p0 = pt(r.x + r.width/2, r.y + r.height/2); + if (Chord) + { + int zx, zy; + zx = (p0.x-p1.x)/2; + zy = (p2.y-p1.y)/2; + p0 = pt(p1.x+zx,p0.y+zy); + } + /* initialise rectangles to be drawn */ + r1.x = r.x + a; + r1.y = r.y; + r1.width = r.width & 1; /* i.e. if width is odd */ + r1.height = 1; + + r2 = r1; + r2.y = r.y + r.height - 1; + + while (y > 0) + { + moveout = movedown = 0; + + if (t + a2*y < xcrit) { /* e(x+1,y-1/2) <= 0 */ + /* move outwards to encounter edge */ + x += 1; + t += dxt; + dxt += d2xt; + + moveout = 1; + } + else if (t - b2*x >= ycrit) { /* e(x+1/2,y-1) > 0 */ + /* drop down one line */ + y -= 1; + t += dyt; + dyt += d2yt; + + movedown = 1; + } + else { + /* drop diagonally down and out */ + x += 1; + y -= 1; + t += dxt + dyt; + dxt += d2xt; + dyt += d2yt; + + moveout = 1; + movedown = 1; + } + + if (movedown) { + if (r1.width == 0) { + r1.x -= 1; r1.width += 2; + r2.x -= 1; r2.width += 2; + moveout = 0; + } + + if (r1.x < r.x) + r1.x = r2.x = r.x; + if (r1.width > r.width) + r1.width = r2.width = r.width; + if (r1.y == r2.y-1) { + r1.x = r2.x = r.x; + r1.width = r2.width = r.width; + } + + if ((r1.width > 0) && (r1.y+r1.height < r2.y)) { + /* distinct rectangles */ + result &= app_fill_arc_rect(g, r1, + p0, p1, p2, + start_angle, end_angle, BrushObj); + result &= app_fill_arc_rect(g, r2, + p0, p1, p2, + start_angle, end_angle, BrushObj); + } + + /* move down */ + r1.y += 1; + r2.y -= 1; + } + + if (moveout) { + /* move outwards */ + r1.x -= 1; r1.width += 2; + r2.x -= 1; r2.width += 2; + } + } + if (r1.y < r2.y) { + /* overlap */ + r1.x = r.x; + r1.width = r.width; + r1.height = r2.y+r2.height-r1.y; + while (r1.height > 0) { + result &= app_fill_arc_rect(g, + rect(r1.x, r1.y, r1.width, 1), + p0, p1, p2, start_angle, end_angle, BrushObj); + r1.y += 1; + r1.height -= 1; + } + } + else if (x <= a) { + /* crossover, draw final line */ + r1.x = r.x; + r1.width = r.width; + r1.height = r1.y+r1.height-r2.y; + r1.y = r2.y; + while (r1.height > 0) { + result &= app_fill_arc_rect(g, + rect(r1.x, r1.y, r1.width, 1), + p0, p1, p2, start_angle, end_angle, BrushObj); + r1.y += 1; + r1.height -= 1; + } + } + return result; +} + +/* ReactOS Interface *********************************************************/ + +BOOL +FASTCALL +IntFillArc( PDC dc, + INT XLeft, + INT YLeft, + INT Width, + INT Height, + double StartArc, + double EndArc, + ARCTYPE arctype) +{ + PDC_ATTR Dc_Attr; + PGDIBRUSHOBJ FillBrushObj; + int Start = ceill(StartArc); + int End = ceill(EndArc); + Rect r; + BOOL Chord = (arctype == GdiTypeChord); + + Dc_Attr = dc->pDc_Attr; + if(!Dc_Attr) Dc_Attr = &dc->Dc_Attr; + + FillBrushObj = BRUSHOBJ_LockBrush(Dc_Attr->hbrush); + if (NULL == FillBrushObj) + { + DPRINT1("FillArc Fail\n"); + SetLastWin32Error(ERROR_INTERNAL_ERROR); + return FALSE; + } + r.x = XLeft; + r.y = YLeft; + r.width = Width; + r.height = Height; + app_fill_arc(dc, r, Start-90, End-90, FillBrushObj, Chord); + + BRUSHOBJ_UnlockBrush(FillBrushObj); + return TRUE; +} + diff --git a/reactos/subsystems/win32/win32k/win32k.rbuild b/reactos/subsystems/win32/win32k/win32k.rbuild index 10b4b804908..7063b673f93 100644 --- a/reactos/subsystems/win32/win32k/win32k.rbuild +++ b/reactos/subsystems/win32/win32k/win32k.rbuild @@ -155,6 +155,7 @@ dc.c dcutil.c dibobj.c + drawing.c fillshap.c gdibatch.c gdiobj.c