[1/2] gdiplus: rendering of linecaps [try2]

Laurent Vromman laurent at vromman.org
Sat Jul 7 02:04:06 CDT 2007


Why do you use M_PI / 2.0 or M_PI / 4.0 and not directly M_PI_2 and M_PI_4?

Laurent

Evan Stade a écrit :
> Hi,
>
> [try2] Removed non-portable constructs.
>
> Changelog:
> * added helpers that render line caps
> * added end cap drawing for GdipDrawLineI
>
> dlls/gdiplus/graphics.c |  220 
> ++++++++++++++++++++++++++++++++++++++++++++++-
> dlls/gdiplus/pen.c      |    5 -
> 2 files changed, 217 insertions(+), 8 deletions(-)
>
> ------------------------------------------------------------------------
>
> diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c
> index 4765e33..6c40aec 100644
> --- a/dlls/gdiplus/graphics.c
> +++ b/dlls/gdiplus/graphics.c
> @@ -27,8 +27,11 @@ #include "gdiplus.h"
>  #include "gdiplus_private.h"
>  #include "wine/debug.h"
>  
> -/* looks-right constant */
> +WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
> +
> +/* looks-right constants */
>  #define TENSION_CONST (0.3)
> +#define ANCHOR_WIDTH (2.0)
>  
>  static inline INT roundr(REAL x)
>  {
> @@ -108,6 +111,212 @@ static void calc_curve_bezier_endp(REAL 
>      *y = roundr(tension * (yadj - yend) + yend);
>  }
>  
> +/* Draws the linecap the specified color and size on the hdc.  The linecap is in
> + * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. */
> +static void draw_cap(HDC hdc, COLORREF color, GpLineCap cap, REAL size,
> +    REAL x1, REAL y1, REAL x2, REAL y2)
> +{
> +    HGDIOBJ oldbrush, oldpen;
> +    HBRUSH brush;
> +    HPEN pen;
> +    POINT pt[4];
> +    REAL theta, dsmall, dbig, dx, dy, invert;
> +
> +    if(x2 != x1)
> +        theta = atan((y2 - y1) / (x2 - x1));
> +    else if(y2 != y1){
> +        theta = M_PI / 2.0 * (y2 > y1 ? 1.0 : -1.0);
> +    }
> +    else
> +        return;
> +
> +    invert = ((x2 - x1) >= 0.0 ? 1.0 : -1.0);
> +    brush = CreateSolidBrush(color);
> +    pen = CreatePen(PS_SOLID, 1, color);
> +    oldbrush = SelectObject(hdc, brush);
> +    oldpen = SelectObject(hdc, pen);
> +
> +    switch(cap){
> +        case LineCapFlat:
> +            break;
> +        case LineCapSquare:
> +        case LineCapSquareAnchor:
> +        case LineCapDiamondAnchor:
> +            size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
> +            if(cap == LineCapDiamondAnchor){
> +                dsmall = cos(theta + M_PI / 2.0) * size;
> +                dbig = sin(theta + M_PI / 2.0) * size;
> +            }
> +            else{
> +                dsmall = cos(theta + M_PI / 4.0) * size;
> +                dbig = sin(theta + M_PI / 4.0) * size;
> +            }
> +
> +            /* calculating the latter points from the earlier points makes them
> +             * look a little better because of rounding issues */
> +            pt[0].x = roundr(x2 - dsmall);
> +            pt[1].x = roundr(((REAL)pt[0].x) + dbig + dsmall);
> +
> +            pt[0].y = roundr(y2 - dbig);
> +            pt[3].y = roundr(((REAL)pt[0].y) + dsmall + dbig);
> +
> +            pt[1].y = roundr(y2 - dsmall);
> +            pt[2].y = roundr(dbig + dsmall + ((REAL)pt[1].y));
> +
> +            pt[3].x = roundr(x2 - dbig);
> +            pt[2].x = roundr(((REAL)pt[3].x) + dsmall + dbig);
> +
> +            Polygon(hdc, pt, 4);
> +
> +            break;
> +        case LineCapArrowAnchor:
> +            size = size * 4.0 / sqrt(3.0);
> +
> +            dx = cos(M_PI / 6.0 + theta) * size * invert;
> +            dy = sin(M_PI / 6.0 + theta) * size * invert;
> +
> +            pt[0].x = roundr(x2 - dx);
> +            pt[0].y = roundr(y2 - dy);
> +
> +            dx = cos(- M_PI / 6.0 + theta) * size * invert;
> +            dy = sin(- M_PI / 6.0 + theta) * size * invert;
> +
> +            pt[1].x = roundr(x2 - dx);
> +            pt[1].y = roundr(y2 - dy);
> +
> +            pt[2].x = roundr(x2);
> +            pt[2].y = roundr(y2);
> +
> +            Polygon(hdc, pt, 3);
> +
> +            break;
> +        case LineCapRoundAnchor:
> +            dx = dy = ANCHOR_WIDTH * size / 2.0;
> +
> +            x2 = (REAL) roundr(x2 - dx);
> +            y2 = (REAL) roundr(y2 - dy);
> +
> +            Ellipse(hdc, (INT) x2, (INT) y2, roundr(x2 + 2.0 * dx),
> +                roundr(y2 + 2.0 * dy));
> +            break;
> +        case LineCapTriangle:
> +            size = size / 2.0;
> +            dx = cos(M_PI / 2.0 + theta) * size;
> +            dy = sin(M_PI / 2.0 + theta) * size;
> +
> +            /* Using roundr here can make the triangle float off the end of the
> +             * line. */
> +            pt[0].x = ((x2 - x1) >= 0 ? floor(x2 - dx) : ceil(x2 - dx));
> +            pt[0].y = ((y2 - y1) >= 0 ? floor(y2 - dy) : ceil(y2 - dy));
> +            pt[1].x = roundr(pt[0].x + 2.0 * dx);
> +            pt[1].y = roundr(pt[0].y + 2.0 * dy);
> +
> +            dx = cos(theta) * size * invert;
> +            dy = sin(theta) * size * invert;
> +
> +            pt[2].x = roundr(x2 + dx);
> +            pt[2].y = roundr(y2 + dy);
> +
> +            Polygon(hdc, pt, 3);
> +
> +            break;
> +        case LineCapRound:
> +            dx = -cos(M_PI / 2.0 + theta) * size * invert;
> +            dy = -sin(M_PI / 2.0 + theta) * size * invert;
> +
> +            pt[0].x = ((x2 - x1) >= 0 ? floor(x2 - dx) : ceil(x2 - dx));
> +            pt[0].y = ((y2 - y1) >= 0 ? floor(y2 - dy) : ceil(y2 - dy));
> +            pt[1].x = roundr(pt[0].x + 2.0 * dx);
> +            pt[1].y = roundr(pt[0].y + 2.0 * dy);
> +
> +            dx = dy = size / 2.0;
> +
> +            x2 = (REAL) roundr(x2 - dx);
> +            y2 = (REAL) roundr(y2 - dy);
> +
> +            Pie(hdc, (INT) x2, (INT) y2, roundr(x2 + 2.0 * dx),
> +                roundr(y2 + 2.0 * dy), pt[0].x, pt[0].y, pt[1].x, pt[1].y);
> +            break;
> +        case LineCapCustom:
> +            FIXME("line cap not implemented\n");
> +        default:
> +            break;
> +    }
> +
> +    SelectObject(hdc, oldbrush);
> +    SelectObject(hdc, oldpen);
> +    DeleteObject(brush);
> +    DeleteObject(pen);
> +}
> +
> +/* Shortens the line by the given percent by changing x2, y2.
> + * If percent is > 1.0 then the line will change direction. */
> +static void shorten_line_percent(REAL x1, REAL  y1, REAL *x2, REAL *y2, REAL percent)
> +{
> +    REAL dist, theta, dx, dy;
> +
> +    if((y1 == *y2) && (x1 == *x2))
> +        return;
> +
> +    dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * percent;
> +    theta = (*x2 == x1 ? M_PI / 2.0 : atan((*y2 - y1) / (*x2 - x1)));
> +    dx = cos(theta) * dist;
> +    dy = sin(theta) * dist;
> +
> +    *x2 = *x2 + fabs(dx) * (*x2 > x1 ? -1.0 : 1.0);
> +    *y2 = *y2 + fabs(dy) * (*y2 > y1 ? -1.0 : 1.0);
> +}
> +
> +/* Shortens the line by the given amount by changing x2, y2.
> + * If the amount is greater than the distance, the line will become length 0. */
> +static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
> +{
> +    REAL dx, dy, percent;
> +
> +    dx = *x2 - x1;
> +    dy = *y2 - y1;
> +    if(dx == 0 && dy == 0)
> +        return;
> +
> +    percent = amt / sqrt(dx * dx + dy * dy);
> +    if(percent >= 1.0){
> +        *x2 = x1;
> +        *y2 = y1;
> +        return;
> +    }
> +
> +    shorten_line_percent(x1, y1, x2, y2, percent);
> +}
> +
> +/* Draws lines between the given points, and if caps is true then draws an endcap
> + * at the end of the last line.  FIXME: Startcaps not implemented. */
> +static void draw_polyline(HDC hdc, GpPen *pen, GDIPCONST GpPointF * pt,
> +    INT count, BOOL caps)
> +{
> +    POINT *pti = GdipAlloc(count * sizeof(POINT));
> +    REAL x = pt[count - 1].X, y = pt[count - 1].Y;
> +    INT i;
> +
> +    if(caps){
> +        if(pen->endcap == LineCapArrowAnchor)
> +            shorten_line_amt(pt[count-2].X, pt[count-2].Y, &x, &y, pen->width);
> +
> +        draw_cap(hdc, pen->color, pen->endcap, pen->width, pt[count-2].X,
> +            pt[count-2].Y, pt[count - 1].X, pt[count - 1].Y);
> +    }
> +
> +    for(i = 0; i < count - 1; i ++){
> +        pti[i].x = roundr(pt[i].X);
> +        pti[i].y = roundr(pt[i].Y);
> +    }
> +
> +    pti[i].x = roundr(x);
> +    pti[i].y = roundr(y);
> +
> +    Polyline(hdc, pti, count);
> +    GdipFree(pti);
> +}
> +
>  GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
>  {
>      if(hdc == NULL)
> @@ -248,16 +457,21 @@ GpStatus WINGDIPAPI GdipDrawLineI(GpGrap
>      INT y1, INT x2, INT y2)
>  {
>      INT save_state;
> +    GpPointF pt[2];
>  
>      if(!pen || !graphics)
>          return InvalidParameter;
>  
> +    pt[0].X = (REAL)x1;
> +    pt[0].Y = (REAL)y1;
> +    pt[1].X = (REAL)x2;
> +    pt[1].Y = (REAL)y2;
> +
>      save_state = SaveDC(graphics->hdc);
>      EndPath(graphics->hdc);
>      SelectObject(graphics->hdc, pen->gdipen);
>  
> -    MoveToEx(graphics->hdc, x1, y1, NULL);
> -    LineTo(graphics->hdc, x2, y2);
> +    draw_polyline(graphics->hdc, pen, pt, 2, TRUE);
>  
>      RestoreDC(graphics->hdc, save_state);
>  
> diff --git a/dlls/gdiplus/pen.c b/dlls/gdiplus/pen.c
> index 0fcbb69..52515d5 100644
> --- a/dlls/gdiplus/pen.c
> +++ b/dlls/gdiplus/pen.c
> @@ -77,11 +77,6 @@ GpStatus WINGDIPAPI GdipSetPenEndCap(GpP
>  {
>      if(!pen)    return InvalidParameter;
>  
> -    if(cap != LineCapFlat){
> -        FIXME("Not implemented for non-flat EndCap\n");
> -        return NotImplemented;
> -    }
> -
>      pen->endcap = cap;
>  
>      return Ok;
>   
> ------------------------------------------------------------------------
>
>
>   




More information about the wine-devel mailing list