gdiplus: Add a software path for thin line drawing.
Signed-off-by: Vincent Povirk <vincent@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
092531df71
commit
6e054e43c6
|
@ -3401,12 +3401,336 @@ end:
|
|||
return retval;
|
||||
}
|
||||
|
||||
GpStatus SOFTWARE_GdipDrawThinPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
|
||||
{
|
||||
GpStatus stat;
|
||||
GpPath* flat_path;
|
||||
GpMatrix* transform;
|
||||
GpRectF gp_bound_rect;
|
||||
GpRect gp_output_area;
|
||||
RECT output_area;
|
||||
INT output_height, output_width;
|
||||
DWORD *output_bits, *brush_bits=NULL;
|
||||
int i;
|
||||
static const BYTE static_dash_pattern[] = {1,1,1,0,1,0,1,0};
|
||||
const BYTE *dash_pattern;
|
||||
INT dash_pattern_size;
|
||||
BYTE *dyn_dash_pattern = NULL;
|
||||
|
||||
stat = GdipClonePath(path, &flat_path);
|
||||
|
||||
if (stat != Ok)
|
||||
return stat;
|
||||
|
||||
stat = GdipCreateMatrix(&transform);
|
||||
|
||||
if (stat == Ok)
|
||||
{
|
||||
stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
|
||||
CoordinateSpaceWorld, transform);
|
||||
|
||||
if (stat == Ok)
|
||||
stat = GdipFlattenPath(flat_path, transform, 1.0);
|
||||
|
||||
GdipDeleteMatrix(transform);
|
||||
}
|
||||
|
||||
/* estimate the output size in pixels, can be larger than necessary */
|
||||
if (stat == Ok)
|
||||
{
|
||||
output_area.left = floorf(flat_path->pathdata.Points[0].X);
|
||||
output_area.right = ceilf(flat_path->pathdata.Points[0].X);
|
||||
output_area.top = floorf(flat_path->pathdata.Points[0].Y);
|
||||
output_area.bottom = ceilf(flat_path->pathdata.Points[0].Y);
|
||||
|
||||
for (i=1; i<flat_path->pathdata.Count; i++)
|
||||
{
|
||||
REAL x, y;
|
||||
x = flat_path->pathdata.Points[i].X;
|
||||
y = flat_path->pathdata.Points[i].Y;
|
||||
|
||||
if (floorf(x) < output_area.left) output_area.left = floorf(x);
|
||||
if (floorf(y) < output_area.top) output_area.top = floorf(y);
|
||||
if (ceilf(x) > output_area.right) output_area.right = ceilf(x);
|
||||
if (ceilf(y) > output_area.bottom) output_area.bottom = ceilf(y);
|
||||
}
|
||||
|
||||
stat = get_graphics_bounds(graphics, &gp_bound_rect);
|
||||
}
|
||||
|
||||
if (stat == Ok)
|
||||
{
|
||||
output_area.left = max(output_area.left, floorf(gp_bound_rect.X));
|
||||
output_area.top = max(output_area.top, floorf(gp_bound_rect.Y));
|
||||
output_area.right = min(output_area.right, ceilf(gp_bound_rect.X + gp_bound_rect.Width));
|
||||
output_area.bottom = min(output_area.bottom, ceilf(gp_bound_rect.Y + gp_bound_rect.Height));
|
||||
|
||||
output_width = output_area.right - output_area.left + 1;
|
||||
output_height = output_area.bottom - output_area.top + 1;
|
||||
|
||||
if (output_width <= 0 || output_height <= 0)
|
||||
{
|
||||
GdipDeletePath(flat_path);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
gp_output_area.X = output_area.left;
|
||||
gp_output_area.Y = output_area.top;
|
||||
gp_output_area.Width = output_width;
|
||||
gp_output_area.Height = output_height;
|
||||
|
||||
output_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
|
||||
if (!output_bits)
|
||||
stat = OutOfMemory;
|
||||
}
|
||||
|
||||
if (stat == Ok)
|
||||
{
|
||||
if (pen->brush->bt != BrushTypeSolidColor)
|
||||
{
|
||||
/* allocate and draw brush output */
|
||||
brush_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
|
||||
|
||||
if (brush_bits)
|
||||
{
|
||||
stat = brush_fill_pixels(graphics, pen->brush, brush_bits,
|
||||
&gp_output_area, output_width);
|
||||
}
|
||||
else
|
||||
stat = OutOfMemory;
|
||||
}
|
||||
|
||||
if (stat == Ok)
|
||||
{
|
||||
/* convert dash pattern to bool array */
|
||||
switch (pen->dash)
|
||||
{
|
||||
case DashStyleCustom:
|
||||
{
|
||||
dash_pattern_size = 0;
|
||||
|
||||
for (i=0; i < pen->numdashes; i++)
|
||||
dash_pattern_size += gdip_round(pen->dashes[i]);
|
||||
|
||||
if (dash_pattern_size != 0)
|
||||
{
|
||||
dash_pattern = dyn_dash_pattern = heap_alloc(dash_pattern_size);
|
||||
|
||||
if (dyn_dash_pattern)
|
||||
{
|
||||
int j=0;
|
||||
for (i=0; i < pen->numdashes; i++)
|
||||
{
|
||||
int k;
|
||||
for (k=0; k < gdip_round(pen->dashes[i]); k++)
|
||||
dyn_dash_pattern[j++] = (i&1)^1;
|
||||
}
|
||||
}
|
||||
else
|
||||
stat = OutOfMemory;
|
||||
|
||||
break;
|
||||
}
|
||||
/* else fall through */
|
||||
}
|
||||
case DashStyleSolid:
|
||||
default:
|
||||
dash_pattern = static_dash_pattern;
|
||||
dash_pattern_size = 1;
|
||||
break;
|
||||
case DashStyleDash:
|
||||
dash_pattern = static_dash_pattern;
|
||||
dash_pattern_size = 4;
|
||||
break;
|
||||
case DashStyleDot:
|
||||
dash_pattern = &static_dash_pattern[4];
|
||||
dash_pattern_size = 2;
|
||||
break;
|
||||
case DashStyleDashDot:
|
||||
dash_pattern = static_dash_pattern;
|
||||
dash_pattern_size = 6;
|
||||
break;
|
||||
case DashStyleDashDotDot:
|
||||
dash_pattern = static_dash_pattern;
|
||||
dash_pattern_size = 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (stat == Ok)
|
||||
{
|
||||
/* trace path */
|
||||
GpPointF subpath_start = flat_path->pathdata.Points[0];
|
||||
INT prev_x = INT_MAX, prev_y = INT_MAX;
|
||||
int dash_pos = dash_pattern_size - 1;
|
||||
|
||||
for (i=0; i < flat_path->pathdata.Count; i++)
|
||||
{
|
||||
BYTE type, type2;
|
||||
GpPointF start_point, end_point;
|
||||
GpPoint start_pointi, end_pointi;
|
||||
|
||||
type = flat_path->pathdata.Types[i];
|
||||
if (i+1 < flat_path->pathdata.Count)
|
||||
type2 = flat_path->pathdata.Types[i+1];
|
||||
else
|
||||
type2 = PathPointTypeStart;
|
||||
|
||||
start_point = flat_path->pathdata.Points[i];
|
||||
|
||||
if ((type & PathPointTypePathTypeMask) == PathPointTypeStart)
|
||||
subpath_start = start_point;
|
||||
|
||||
if ((type & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
|
||||
end_point = subpath_start;
|
||||
else if ((type2 & PathPointTypePathTypeMask) == PathPointTypeStart)
|
||||
continue;
|
||||
else
|
||||
end_point = flat_path->pathdata.Points[i+1];
|
||||
|
||||
start_pointi.X = floorf(start_point.X);
|
||||
start_pointi.Y = floorf(start_point.Y);
|
||||
end_pointi.X = floorf(end_point.X);
|
||||
end_pointi.Y = floorf(end_point.Y);
|
||||
|
||||
/* draw line segment */
|
||||
if (abs(start_pointi.Y - end_pointi.Y) > abs(start_pointi.X - end_pointi.X))
|
||||
{
|
||||
INT x, y, start_y, end_y, step;
|
||||
|
||||
if (start_pointi.Y < end_pointi.Y)
|
||||
{
|
||||
step = 1;
|
||||
start_y = ceilf(start_point.Y) - output_area.top;
|
||||
end_y = end_pointi.Y - output_area.top;
|
||||
}
|
||||
else
|
||||
{
|
||||
step = -1;
|
||||
start_y = start_point.Y - output_area.top;
|
||||
end_y = ceilf(end_point.Y) - output_area.top;
|
||||
}
|
||||
|
||||
for (y=start_y; y != (end_y+step); y+=step)
|
||||
{
|
||||
x = gdip_round( start_point.X +
|
||||
(end_point.X - start_point.X) * (y + output_area.top - start_point.Y) / (end_point.Y - start_point.Y) )
|
||||
- output_area.left;
|
||||
|
||||
if (x == prev_x && y == prev_y)
|
||||
continue;
|
||||
|
||||
prev_x = x;
|
||||
prev_y = y;
|
||||
dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
|
||||
|
||||
if (!dash_pattern[dash_pos])
|
||||
continue;
|
||||
|
||||
if (x < 0 || x >= output_width || y < 0 || y >= output_height)
|
||||
continue;
|
||||
|
||||
if (brush_bits)
|
||||
output_bits[x + y*output_width] = brush_bits[x + y*output_width];
|
||||
else
|
||||
output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
INT x, y, start_x, end_x, step;
|
||||
|
||||
if (start_pointi.X < end_pointi.X)
|
||||
{
|
||||
step = 1;
|
||||
start_x = ceilf(start_point.X) - output_area.left;
|
||||
end_x = end_pointi.X - output_area.left;
|
||||
}
|
||||
else
|
||||
{
|
||||
step = -1;
|
||||
start_x = start_point.X - output_area.left;
|
||||
end_x = ceilf(end_point.X) - output_area.left;
|
||||
}
|
||||
|
||||
for (x=start_x; x != (end_x+step); x+=step)
|
||||
{
|
||||
y = gdip_round( start_point.Y +
|
||||
(end_point.Y - start_point.Y) * (x + output_area.left - start_point.X) / (end_point.X - start_point.X) )
|
||||
- output_area.top;
|
||||
|
||||
if (x == prev_x && y == prev_y)
|
||||
continue;
|
||||
|
||||
prev_x = x;
|
||||
prev_y = y;
|
||||
dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
|
||||
|
||||
if (!dash_pattern[dash_pos])
|
||||
continue;
|
||||
|
||||
if (x < 0 || x >= output_width || y < 0 || y >= output_height)
|
||||
continue;
|
||||
|
||||
if (brush_bits)
|
||||
output_bits[x + y*output_width] = brush_bits[x + y*output_width];
|
||||
else
|
||||
output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* draw output image */
|
||||
if (stat == Ok)
|
||||
{
|
||||
stat = alpha_blend_pixels(graphics, output_area.left, output_area.top,
|
||||
(BYTE*)output_bits, output_width, output_height, output_width * 4,
|
||||
PixelFormat32bppARGB);
|
||||
}
|
||||
|
||||
heap_free(brush_bits);
|
||||
heap_free(dyn_dash_pattern);
|
||||
heap_free(output_bits);
|
||||
}
|
||||
|
||||
GdipDeletePath(flat_path);
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
|
||||
{
|
||||
GpStatus stat;
|
||||
GpPath *wide_path;
|
||||
GpMatrix *transform=NULL;
|
||||
|
||||
/* Check if the final pen thickness in pixels is too thin. */
|
||||
if (pen->unit == UnitPixel)
|
||||
{
|
||||
if (pen->width < 1.415)
|
||||
return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
GpPointF points[3] = {{0,0}, {1,0}, {0,1}};
|
||||
|
||||
points[1].X = pen->width;
|
||||
points[2].Y = pen->width;
|
||||
|
||||
stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
|
||||
CoordinateSpaceWorld, points, 3);
|
||||
|
||||
if (stat != Ok)
|
||||
return stat;
|
||||
|
||||
if (((points[1].X-points[0].X)*(points[1].X-points[0].X) +
|
||||
(points[1].Y-points[0].Y)*(points[1].Y-points[0].Y) < 2.0001) &&
|
||||
((points[2].X-points[0].X)*(points[2].X-points[0].X) +
|
||||
(points[2].Y-points[0].Y)*(points[2].Y-points[0].Y) < 2.0001))
|
||||
return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
|
||||
}
|
||||
|
||||
stat = GdipClonePath(path, &wide_path);
|
||||
|
||||
if (stat != Ok)
|
||||
|
|
Loading…
Reference in New Issue