/* * Copyright (C) 2007 Google (Evan Stade) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include "windef.h" #include "winbase.h" #include "winuser.h" #include "wingdi.h" #include "gdiplus.h" #include "gdiplus_private.h" #include "wine/debug.h" /* looks-right constant */ #define TENSION_CONST (0.3) static inline INT roundr(REAL x) { return (INT) floor(x+0.5); } static inline REAL deg2rad(REAL degrees) { return (M_PI*2.0) * degrees / 360.0; } /* Converts angle (in degrees) to x/y coordinates */ static void deg2xy(REAL angle, REAL x_0, REAL y_0, REAL *x, REAL *y) { REAL radAngle, hypotenuse; radAngle = deg2rad(angle); hypotenuse = 50.0; /* arbitrary */ *x = x_0 + cos(radAngle) * hypotenuse; *y = y_0 + sin(radAngle) * hypotenuse; } /* GdipDrawPie/GdipFillPie helper function */ static GpStatus draw_pie(GpGraphics *graphics, HBRUSH gdibrush, HPEN gdipen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle) { HGDIOBJ old_pen, old_brush; REAL x_0, y_0, x_1, y_1, x_2, y_2; if(!graphics) return InvalidParameter; old_pen = SelectObject(graphics->hdc, gdipen); old_brush = SelectObject(graphics->hdc, gdibrush); x_0 = x + (width/2.0); y_0 = y + (height/2.0); deg2xy(startAngle+sweepAngle, x_0, y_0, &x_1, &y_1); deg2xy(startAngle, x_0, y_0, &x_2, &y_2); Pie(graphics->hdc, roundr(x), roundr(y), roundr(x+width), roundr(y+height), roundr(x_1), roundr(y_1), roundr(x_2), roundr(y_2)); SelectObject(graphics->hdc, old_pen); SelectObject(graphics->hdc, old_brush); return Ok; } /* GdipDrawCurve helper function. * Calculates Bezier points from cardinal spline points. */ static void calc_curve_bezier(CONST GpPointF *pts, REAL tension, REAL *x1, REAL *y1, REAL *x2, REAL *y2) { REAL xdiff, ydiff; /* calculate tangent */ xdiff = pts[2].X - pts[0].X; ydiff = pts[2].Y - pts[0].Y; /* apply tangent to get control points */ *x1 = pts[1].X - tension * xdiff; *y1 = pts[1].Y - tension * ydiff; *x2 = pts[1].X + tension * xdiff; *y2 = pts[1].Y + tension * ydiff; } /* GdipDrawCurve helper function. * Calculates Bezier points from cardinal spline endpoints. */ static void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj, REAL tension, REAL *x, REAL *y) { /* tangent at endpoints is the line from the endpoint to the adjacent point */ *x = roundr(tension * (xadj - xend) + xend); *y = roundr(tension * (yadj - yend) + yend); } GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics) { if(hdc == NULL) return OutOfMemory; if(graphics == NULL) return InvalidParameter; *graphics = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GpGraphics)); (*graphics)->hdc = hdc; (*graphics)->hwnd = NULL; return Ok; } GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics) { GpStatus ret; if((ret = GdipCreateFromHDC(GetDC(hwnd), graphics)) != Ok) return ret; (*graphics)->hwnd = hwnd; return Ok; } GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics) { if(!graphics) return InvalidParameter; if(graphics->hwnd) ReleaseDC(graphics->hwnd, graphics->hdc); HeapFree(GetProcessHeap(), 0, graphics); return Ok; } GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle) { HGDIOBJ old_pen; REAL x_0, y_0, x_1, y_1, x_2, y_2; if(!graphics || !pen) return InvalidParameter; old_pen = SelectObject(graphics->hdc, pen->gdipen); x_0 = x + (width/2.0); y_0 = y + (height/2.0); deg2xy(startAngle+sweepAngle, x_0, y_0, &x_1, &y_1); deg2xy(startAngle, x_0, y_0, &x_2, &y_2); Arc(graphics->hdc, roundr(x), roundr(y), roundr(x+width), roundr(y+height), roundr(x_1), roundr(y_1), roundr(x_2), roundr(y_2)); SelectObject(graphics->hdc, old_pen); return Ok; } GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1, REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4) { HGDIOBJ old_pen; POINT pt[4] = {{roundr(x1), roundr(y1)}, {roundr(x2), roundr(y2)}, {roundr(x3), roundr(y3)}, {roundr(x4), roundr(y4)}}; if(!graphics || !pen) return InvalidParameter; old_pen = SelectObject(graphics->hdc, pen->gdipen); PolyBezier(graphics->hdc, pt, 4); SelectObject(graphics->hdc, old_pen); return Ok; } /* Approximates cardinal spline with Bezier curves. */ GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF *points, INT count, REAL tension) { HGDIOBJ old_pen; /* PolyBezier expects count*3-2 points. */ int i, len_pt = count*3-2; POINT pt[len_pt]; REAL x1, x2, y1, y2; if(!graphics || !pen) return InvalidParameter; tension = tension * TENSION_CONST; calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y, tension, &x1, &y1); pt[0].x = roundr(points[0].X); pt[0].y = roundr(points[0].Y); pt[1].x = roundr(x1); pt[1].y = roundr(y1); for(i = 0; i < count-2; i++){ calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2); pt[3*i+2].x = roundr(x1); pt[3*i+2].y = roundr(y1); pt[3*i+3].x = roundr(points[i+1].X); pt[3*i+3].y = roundr(points[i+1].Y); pt[3*i+4].x = roundr(x2); pt[3*i+4].y = roundr(y2); } calc_curve_bezier_endp(points[count-1].X, points[count-1].Y, points[count-2].X, points[count-2].Y, tension, &x1, &y1); pt[len_pt-2].x = x1; pt[len_pt-2].y = y1; pt[len_pt-1].x = roundr(points[count-1].X); pt[len_pt-1].y = roundr(points[count-1].Y); old_pen = SelectObject(graphics->hdc, pen->gdipen); PolyBezier(graphics->hdc, pt, len_pt); SelectObject(graphics->hdc, old_pen); return Ok; } GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1, INT y1, INT x2, INT y2) { HGDIOBJ old_obj; if(!pen || !graphics) return InvalidParameter; old_obj = SelectObject(graphics->hdc, pen->gdipen); MoveToEx(graphics->hdc, x1, y1, NULL); LineTo(graphics->hdc, x2, y2); SelectObject(graphics->hdc, old_obj); return Ok; } GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF *points, INT count) { HGDIOBJ old_obj; INT i; if(!pen || !graphics || (count < 2)) return InvalidParameter; old_obj = SelectObject(graphics->hdc, pen->gdipen); MoveToEx(graphics->hdc, roundr(points[0].X), roundr(points[0].Y), NULL); for(i = 1; i < count; i++){ LineTo(graphics->hdc, roundr(points[i].X), roundr(points[i].Y)); } SelectObject(graphics->hdc, old_obj); return Ok; } GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle) { if(!pen) return InvalidParameter; return draw_pie(graphics, GetStockObject(NULL_BRUSH), pen->gdipen, x, y, width, height, startAngle, sweepAngle); } GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x, INT y, INT width, INT height) { LOGBRUSH lb; HPEN hpen; HGDIOBJ old_obj; if(!pen || !graphics) return InvalidParameter; lb.lbStyle = BS_SOLID; lb.lbColor = pen->color; lb.lbHatch = 0; hpen = ExtCreatePen(PS_GEOMETRIC | PS_ENDCAP_SQUARE, (INT) pen->width, &lb, 0, NULL); old_obj = SelectObject(graphics->hdc, hpen); /* assume pen aligment centered */ MoveToEx(graphics->hdc, x, y, NULL); LineTo(graphics->hdc, x+width, y); LineTo(graphics->hdc, x+width, y+height); LineTo(graphics->hdc, x, y+height); LineTo(graphics->hdc, x, y); SelectObject(graphics->hdc, old_obj); DeleteObject(hpen); return Ok; } GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle) { if(!brush) return InvalidParameter; return draw_pie(graphics, brush->gdibrush, GetStockObject(NULL_PEN), x, y, width, height, startAngle, sweepAngle); }