gdiplus: Rendering of linecaps.
This commit is contained in:
parent
0a6f6874af
commit
5128e5dc58
|
@ -27,8 +27,11 @@
|
||||||
#include "gdiplus_private.h"
|
#include "gdiplus_private.h"
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
|
|
||||||
/* looks-right constant */
|
WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
|
||||||
|
|
||||||
|
/* looks-right constants */
|
||||||
#define TENSION_CONST (0.3)
|
#define TENSION_CONST (0.3)
|
||||||
|
#define ANCHOR_WIDTH (2.0)
|
||||||
|
|
||||||
static inline INT roundr(REAL x)
|
static inline INT roundr(REAL x)
|
||||||
{
|
{
|
||||||
|
@ -108,6 +111,212 @@ static void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj,
|
||||||
*y = roundr(tension * (yadj - yend) + yend);
|
*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 * (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) * size;
|
||||||
|
dbig = sin(theta + M_PI_2) * size;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
dsmall = cos(theta + M_PI_4) * size;
|
||||||
|
dbig = sin(theta + M_PI_4) * 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 + theta) * size;
|
||||||
|
dy = sin(M_PI_2 + 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 + theta) * size * invert;
|
||||||
|
dy = -sin(M_PI_2 + 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 : 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)
|
GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
|
||||||
{
|
{
|
||||||
if(hdc == NULL)
|
if(hdc == NULL)
|
||||||
|
@ -248,16 +457,21 @@ GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
|
||||||
INT y1, INT x2, INT y2)
|
INT y1, INT x2, INT y2)
|
||||||
{
|
{
|
||||||
INT save_state;
|
INT save_state;
|
||||||
|
GpPointF pt[2];
|
||||||
|
|
||||||
if(!pen || !graphics)
|
if(!pen || !graphics)
|
||||||
return InvalidParameter;
|
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);
|
save_state = SaveDC(graphics->hdc);
|
||||||
EndPath(graphics->hdc);
|
EndPath(graphics->hdc);
|
||||||
SelectObject(graphics->hdc, pen->gdipen);
|
SelectObject(graphics->hdc, pen->gdipen);
|
||||||
|
|
||||||
MoveToEx(graphics->hdc, x1, y1, NULL);
|
draw_polyline(graphics->hdc, pen, pt, 2, TRUE);
|
||||||
LineTo(graphics->hdc, x2, y2);
|
|
||||||
|
|
||||||
RestoreDC(graphics->hdc, save_state);
|
RestoreDC(graphics->hdc, save_state);
|
||||||
|
|
||||||
|
|
|
@ -77,11 +77,6 @@ GpStatus WINGDIPAPI GdipSetPenEndCap(GpPen *pen, GpLineCap cap)
|
||||||
{
|
{
|
||||||
if(!pen) return InvalidParameter;
|
if(!pen) return InvalidParameter;
|
||||||
|
|
||||||
if(cap != LineCapFlat){
|
|
||||||
FIXME("Not implemented for non-flat EndCap\n");
|
|
||||||
return NotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
pen->endcap = cap;
|
pen->endcap = cap;
|
||||||
|
|
||||||
return Ok;
|
return Ok;
|
||||||
|
|
Loading…
Reference in New Issue