win32-printing: fix rounding problems when using pattern matrix

When using meta surface patterns, the win32 CTM is changed to the
inverted pattern matrix then the meta surface is replayed to the
surface. The problem with this is that win32 uses integer coordinates
for GDI functions. A pattern matrix that scale the CTM up will cause
rounding errors in the position of each path in the pattern.

This is fixed by always keeping the win32 CTM set to the identity
matrix. The CTM is stored in the surface and all coordinates are
transformed by the CTM before calling GDI functions.
This commit is contained in:
Adrian Johnson 2007-10-21 23:45:40 +09:30
parent af01d9b8fa
commit 89fe7b2ff0
2 changed files with 138 additions and 31 deletions

View file

@ -312,6 +312,22 @@ _cairo_win32_printing_surface_done_solid_brush (cairo_win32_surface_t *surface)
}
}
static cairo_status_t
_cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_surface_t *surface,
RECT *clip)
{
XFORM xform;
_cairo_matrix_to_win32_xform (&surface->ctm, &xform);
if (!SetWorldTransform (surface->dc, &xform))
return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:SetWorldTransform");
GetClipBox (surface->dc, clip);
if (!ModifyWorldTransform (surface->dc, &xform, MWT_IDENTITY))
return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform");
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_surface_t *surface,
cairo_pattern_t *pattern)
@ -335,6 +351,8 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surfa
cairo_surface_pattern_t *pattern)
{
cairo_content_t old_content;
cairo_matrix_t old_ctm;
cairo_bool_t old_has_ctm;
cairo_rectangle_int_t meta_extents;
cairo_status_t status;
cairo_extend_t extend;
@ -351,18 +369,21 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surfa
/* _cairo_pattern_set_matrix guarantees invertibility */
assert (status == CAIRO_STATUS_SUCCESS);
old_ctm = surface->ctm;
old_has_ctm = surface->has_ctm;
cairo_matrix_multiply (&p2d, &p2d, &surface->ctm);
surface->ctm = p2d;
SaveDC (surface->dc);
SetGraphicsMode (surface->dc, GM_ADVANCED);
_cairo_matrix_to_win32_xform (&p2d, &xform);
if (!SetWorldTransform (surface->dc, &xform))
return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_meta_pattern_set_world_transform_1");
status = _cairo_surface_get_extents (meta_surface, &meta_extents);
if (status)
return status;
GetClipBox (surface->dc, &clip);
status = _cairo_win32_printing_surface_get_ctm_clip_box (surface, &clip);
if (status)
return status;
if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) {
left = (int) floor((double)clip.left/meta_extents.width);
right = (int) ceil((double)clip.right/meta_extents.width);
@ -389,6 +410,7 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surfa
for (y_tile = top; y_tile < bottom; y_tile++) {
for (x_tile = left; x_tile < right; x_tile++) {
cairo_matrix_t m;
double x, y;
SaveDC (surface->dc);
m = p2d;
@ -405,22 +427,47 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surfa
cairo_matrix_scale (&m, 1, -1);
}
}
_cairo_matrix_to_win32_xform (&m, &xform);
if (!SetWorldTransform (surface->dc, &xform))
return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_meta_pattern_set_world_transform_2");
surface->ctm = m;
surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm);
/* Set clip path around bbox of the pattern. */
BeginPath (surface->dc);
Rectangle (surface->dc, 0, 0, meta_extents.width, meta_extents.height);
x = 0;
y = 0;
cairo_matrix_transform_point (&surface->ctm, &x, &y);
MoveToEx (surface->dc, (int) x, (int) y, NULL);
x = meta_extents.width;
y = 0;
cairo_matrix_transform_point (&surface->ctm, &x, &y);
LineTo (surface->dc, (int) x, (int) y);
x = meta_extents.width;
y = meta_extents.height;
cairo_matrix_transform_point (&surface->ctm, &x, &y);
LineTo (surface->dc, (int) x, (int) y);
x = 0;
y = meta_extents.height;
cairo_matrix_transform_point (&surface->ctm, &x, &y);
LineTo (surface->dc, (int) x, (int) y);
CloseFigure (surface->dc);
EndPath (surface->dc);
SelectClipPath (surface->dc, RGN_AND);
status = _cairo_meta_surface_replay (meta_surface, &surface->base);
if (status)
return status;
RestoreDC (surface->dc, -1);
}
}
surface->content = old_content;
surface->ctm = old_ctm;
surface->has_ctm = old_has_ctm;
RestoreDC (surface->dc, -1);
return status;
@ -528,8 +575,8 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf
/* _cairo_pattern_set_matrix guarantees invertibility */
assert (status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (&m, &m, &surface->ctm);
SaveDC (surface->dc);
SetGraphicsMode (surface->dc, GM_ADVANCED);
_cairo_matrix_to_win32_xform (&m, &xform);
if (!SetWorldTransform (surface->dc, &xform))
@ -631,6 +678,8 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa
/* _cairo_pattern_set_matrix guarantees invertibility */
assert (status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (&mat, &surface->ctm, &mat);
p1x = _cairo_fixed_to_double (pattern->p1.x);
p1y = _cairo_fixed_to_double (pattern->p1.y);
p2x = _cairo_fixed_to_double (pattern->p2.x);
@ -650,7 +699,6 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa
_cairo_matrix_to_win32_xform (&mat, &xform);
SetGraphicsMode (surface->dc, GM_ADVANCED);
if (!SetWorldTransform (surface->dc, &xform))
return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:SetWorldTransform2");
GetWorldTransform(surface->dc, &xform);
@ -660,6 +708,7 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa
p2y = 0;
GetClipBox (surface->dc, &clip);
if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) {
range_start = (int) floor(clip.left/d);
range_stop = (int) ceil(clip.right/d);
@ -803,10 +852,19 @@ _cairo_win32_printing_surface_path_move_to (void *closure, cairo_point_t *point)
path_info->last_move_to_point = *point;
path_info->has_sub_path = FALSE;
MoveToEx (path_info->surface->dc,
_cairo_fixed_integer_part (point->x),
_cairo_fixed_integer_part (point->y),
NULL);
if (path_info->surface->has_ctm) {
double x, y;
x = _cairo_fixed_to_double (point->x);
y = _cairo_fixed_to_double (point->y);
cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
MoveToEx (path_info->surface->dc, (int) x, (int) y, NULL);
} else {
MoveToEx (path_info->surface->dc,
_cairo_fixed_integer_part (point->x),
_cairo_fixed_integer_part (point->y),
NULL);
}
return CAIRO_STATUS_SUCCESS;
}
@ -816,9 +874,18 @@ _cairo_win32_printing_surface_path_line_to (void *closure, cairo_point_t *point)
{
win32_path_info_t *path_info = closure;
LineTo (path_info->surface->dc,
_cairo_fixed_integer_part (point->x),
_cairo_fixed_integer_part (point->y));
if (path_info->surface->has_ctm) {
double x, y;
x = _cairo_fixed_to_double (point->x);
y = _cairo_fixed_to_double (point->y);
cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
LineTo (path_info->surface->dc, (int) x, (int) y);
} else {
LineTo (path_info->surface->dc,
_cairo_fixed_integer_part (point->x),
_cairo_fixed_integer_part (point->y));
}
return CAIRO_STATUS_SUCCESS;
}
@ -832,12 +899,34 @@ _cairo_win32_printing_surface_path_curve_to (void *closure,
win32_path_info_t *path_info = closure;
POINT points[3];
points[0].x = _cairo_fixed_integer_part (b->x);
points[0].y = _cairo_fixed_integer_part (b->y);
points[1].x = _cairo_fixed_integer_part (c->x);
points[1].y = _cairo_fixed_integer_part (c->y);
points[2].x = _cairo_fixed_integer_part (d->x);
points[2].y = _cairo_fixed_integer_part (d->y);
if (path_info->surface->has_ctm) {
double x, y;
x = _cairo_fixed_to_double (b->x);
y = _cairo_fixed_to_double (b->y);
cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
points[0].x = (LONG) x;
points[0].y = (LONG) y;
x = _cairo_fixed_to_double (c->x);
y = _cairo_fixed_to_double (c->y);
cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
points[1].x = (LONG) x;
points[1].y = (LONG) y;
x = _cairo_fixed_to_double (d->x);
y = _cairo_fixed_to_double (d->y);
cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
points[2].x = (LONG) x;
points[2].y = (LONG) y;
} else {
points[0].x = _cairo_fixed_integer_part (b->x);
points[0].y = _cairo_fixed_integer_part (b->y);
points[1].x = _cairo_fixed_integer_part (c->x);
points[1].y = _cairo_fixed_integer_part (c->y);
points[2].x = _cairo_fixed_integer_part (d->x);
points[2].y = _cairo_fixed_integer_part (d->y);
}
PolyBezierTo (path_info->surface->dc, points, 3);
return CAIRO_STATUS_SUCCESS;
@ -1079,7 +1168,6 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface,
* Switch to user space to set line parameters
*/
SaveDC (surface->dc);
SetGraphicsMode (surface->dc, GM_ADVANCED);
_cairo_matrix_to_win32_xform (ctm, &xform);
xform.eDx = 0.0f;
xform.eDy = 0.0f;
@ -1179,6 +1267,8 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac
cairo_scaled_glyph_t *scaled_glyph;
cairo_pattern_t *opaque = NULL;
int i;
cairo_matrix_t old_ctm;
cairo_bool_t old_has_ctm;
XFORM xform;
cairo_solid_pattern_t clear;
@ -1228,11 +1318,14 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac
}
SaveDC (surface->dc);
SetGraphicsMode (surface->dc, GM_ADVANCED);
xform.eM11 = 1.0f;
xform.eM21 = 0.0f;
xform.eM12 = 0.0f;
xform.eM22 = 1.0f;
old_ctm = surface->ctm;
old_has_ctm = surface->has_ctm;
surface->has_ctm = TRUE;
BeginPath (surface->dc);
for (i = 0; i < num_glyphs; i++) {
status = _cairo_scaled_glyph_lookup (scaled_font,
@ -1241,13 +1334,13 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac
&scaled_glyph);
if (status)
break;
xform.eDx = (FLOAT) glyphs[i].x;
xform.eDy = (FLOAT) glyphs[i].y;
if (!SetWorldTransform (surface->dc, &xform))
return _cairo_win32_print_gdi_error ("_win32_surface_print_show_glyphs:SetWorldTransform");
surface->ctm = old_ctm;
cairo_matrix_translate (&surface->ctm, glyphs[i].x, glyphs[i].y);
status = _cairo_win32_printing_surface_emit_path (surface, scaled_glyph->path);
}
EndPath (surface->dc);
surface->ctm = old_ctm;
surface->has_ctm = old_has_ctm;
if (status == CAIRO_STATUS_SUCCESS) {
SelectClipPath (surface->dc, RGN_AND);
status = _cairo_win32_printing_surface_paint_pattern (surface, source);
@ -1273,8 +1366,20 @@ static cairo_int_status_t
_cairo_win32_printing_surface_start_page (void *abstract_surface)
{
cairo_win32_surface_t *surface = abstract_surface;
XFORM xform;
SaveDC (surface->dc);
SetGraphicsMode (surface->dc, GM_ADVANCED);
GetWorldTransform(surface->dc, &xform);
surface->ctm.xx = xform.eM11;
surface->ctm.xy = xform.eM21;
surface->ctm.yx = xform.eM12;
surface->ctm.yy = xform.eM22;
surface->ctm.x0 = xform.eDx;
surface->ctm.y0 = xform.eDy;
surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm);
if (!ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY))
return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform");
return CAIRO_STATUS_SUCCESS;
}

View file

@ -83,6 +83,8 @@ typedef struct _cairo_win32_surface {
/* printing surface bits */
cairo_paginated_mode_t paginated_mode;
cairo_content_t content;
cairo_bool_t has_ctm;
cairo_matrix_t ctm;
int clip_saved_dc;
HBRUSH brush, old_brush;
} cairo_win32_surface_t;