Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply PageUnit to clip regions. #745

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/graphics-cairo.c
Original file line number Diff line number Diff line change
Expand Up @@ -835,11 +835,16 @@ cairo_SetGraphicsClip (GpGraphics *graphics)
if (gdip_is_InfiniteRegion (graphics->overall_clip))
return Ok;

if (gdip_is_matrix_empty (graphics->clip_matrix)) {
/* Clip region is in device coordinates but we're drawing in page coordinates
* so we need to draw with an inverse page transform */
GpMatrix page;
gdip_get_inverse_page_transform(graphics, &page);

if (gdip_is_matrix_empty (&page)) {
work = graphics->overall_clip;
} else {
GdipCloneRegion (graphics->overall_clip, &work);
GdipTransformRegion (work, graphics->clip_matrix);
GdipTransformRegion (work, &page);
}

switch (work->type) {
Expand Down
2 changes: 2 additions & 0 deletions src/graphics-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ typedef struct _Graphics {
} Graphics;

float gdip_unit_conversion (Unit from, Unit to, float dpi, GraphicsType type, float nSrc) GDIP_INTERNAL;
void gdip_get_page_transform(GpGraphics* graphics, GpMatrix* matrix) GDIP_INTERNAL;
void gdip_get_inverse_page_transform(GpGraphics* graphics, GpMatrix* matrix) GDIP_INTERNAL;

void gdip_set_cairo_clipping (GpGraphics *graphics) GDIP_INTERNAL;
GpStatus gdip_calculate_overall_clipping (GpGraphics *graphics) GDIP_INTERNAL;
Expand Down
70 changes: 48 additions & 22 deletions src/graphics.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,24 @@ gdip_unit_conversion (Unit from, Unit to, float dpi, GraphicsType type, float nS
}
}

void
gdip_get_page_transform(GpGraphics* graphics, GpMatrix* matrix)
{
/* The page transformaion converts page coordinates to device coordinates */
double scaleX = gdip_unit_conversion(graphics->page_unit, UnitDisplay, graphics->dpi_x, graphics->type, 1);
double scaleY = gdip_unit_conversion(graphics->page_unit, UnitDisplay, graphics->dpi_y, graphics->type, 1);

cairo_matrix_init_scale(matrix, scaleX, scaleY);
cairo_matrix_multiply(matrix, graphics->copy_of_ctm, matrix);
}

void
gdip_get_inverse_page_transform(GpGraphics* graphics, GpMatrix* matrix)
{
gdip_get_page_transform(graphics, matrix);
cairo_matrix_invert(matrix);
}

static void
gdip_graphics_reset (GpGraphics *graphics)
{
Expand Down Expand Up @@ -2097,19 +2115,17 @@ GdipSetClipPath (GpGraphics *graphics, GpPath *path, CombineMode combineMode)
if (!path || combineMode > CombineModeComplement)
return InvalidParameter;

GpMatrix page;
gdip_get_page_transform(graphics, &page);

/* if the matrix is empty, avoid path cloning and transform */
if (gdip_is_matrix_empty (graphics->clip_matrix)) {
if (gdip_is_matrix_empty (&page)) {
work = path;
} else {
cairo_matrix_t inverted;

gdip_cairo_matrix_copy (&inverted, graphics->clip_matrix);
cairo_matrix_invert (&inverted);

status = GdipClonePath (path, &work);
if (status != Ok)
return status;
GdipTransformPath (work, &inverted);
GdipTransformPath (work, &page);
}

status = GdipCombineRegionPath (graphics->clip, work, combineMode);
Expand Down Expand Up @@ -2152,17 +2168,15 @@ GdipSetClipRegion (GpGraphics *graphics, GpRegion *region, CombineMode combineMo
if (!region || combineMode > CombineModeComplement)
return InvalidParameter;

GpMatrix page;
gdip_get_page_transform(graphics, &page);

/* if the matrix is empty, avoid region cloning and transform */
if (gdip_is_matrix_empty (graphics->clip_matrix)) {
if (gdip_is_matrix_empty (&page)) {
work = region;
} else {
cairo_matrix_t inverted;

gdip_cairo_matrix_copy (&inverted, graphics->clip_matrix);
cairo_matrix_invert (&inverted);

GdipCloneRegion (region, &work);
GdipTransformRegion (work, &inverted);
GdipTransformRegion (work, &page);
}

status = GdipCombineRegionRegion (graphics->clip, work, combineMode);
Expand Down Expand Up @@ -2292,9 +2306,12 @@ GdipGetClip (GpGraphics *graphics, GpRegion *region)
gdip_clear_region (region);
gdip_copy_region (graphics->clip, region);

if (gdip_is_matrix_empty (graphics->clip_matrix))
GpMatrix page;
gdip_get_inverse_page_transform(graphics, &page);

if (gdip_is_matrix_empty (&page))
return Ok;
return GdipTransformRegion (region, graphics->clip_matrix);
return GdipTransformRegion (region, &page);
}

GpStatus WINGDIPAPI
Expand Down Expand Up @@ -2322,12 +2339,15 @@ GdipGetClipBounds (GpGraphics *graphics, GpRectF *rect)
return Ok;
}

GpMatrix page;
gdip_get_inverse_page_transform(graphics, &page);

/* if the matrix is empty, avoid region cloning and transform */
if (gdip_is_matrix_empty (graphics->clip_matrix)) {
if (gdip_is_matrix_empty (&page)) {
work = graphics->clip;
} else {
GdipCloneRegion (graphics->clip, &work);
GdipTransformRegion (work, graphics->clip_matrix);
GdipTransformRegion (work, &page);
}

status = GdipGetRegionBounds (work, graphics, rect);
Expand Down Expand Up @@ -2398,8 +2418,11 @@ GpStatus gdip_get_visible_clip (GpGraphics *graphics, GpRegion **visible_clip)
if (status != Ok)
return status;

if (!gdip_is_matrix_empty (graphics->clip_matrix)) {
GdipTransformRegion (clip, graphics->clip_matrix);
GpMatrix page;
gdip_get_inverse_page_transform(graphics, &page);

if (!gdip_is_matrix_empty (&page)) {
GdipTransformRegion (clip, &page);
}

status = GdipCombineRegionRectI (clip, &graphics->bounds, CombineModeIntersect);
Expand Down Expand Up @@ -2436,10 +2459,13 @@ GdipGetVisibleClipBounds (GpGraphics *graphics, GpRectF *rect)
rect->X += graphics->clip_matrix->x0;
rect->Y += graphics->clip_matrix->y0;
} else if (!gdip_is_InfiniteRegion (clip)) {
GpMatrix page;
gdip_get_inverse_page_transform(graphics, &page);

/* if the matrix is empty, avoid region cloning and transform */
if (!gdip_is_matrix_empty (graphics->clip_matrix)) {
if (!gdip_is_matrix_empty (&page)) {
GdipCloneRegion (graphics->overall_clip, &clip);
GdipTransformRegion (clip, graphics->clip_matrix);
GdipTransformRegion (clip, &page);
}

RectF clipbound;
Expand Down
161 changes: 161 additions & 0 deletions tests/testclip.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ using namespace Gdiplus;
using namespace DllExports;
#endif

static void assertClipBounds(GpGraphics* graphics, double x, double y, double width, double height);

static void
test_gdip_clip()
{
Expand Down Expand Up @@ -547,6 +549,163 @@ test_gdip_clip_path ()
C (GdipDisposeImage (bitmap));
}

static void
test_gdip_clip_page_units ()
{
GpBitmap *bitmap = 0;
GpGraphics *graphics;
GpMatrix* matrix;
BOOL contains;

C (GdipCreateMatrix(&matrix));
C (GdipCreateBitmapFromScan0 (612, 792, 0, PixelFormat32bppARGB, NULL, &bitmap));
C (GdipGetImageGraphicsContext (bitmap, &graphics));

/* Set the clip rect in Display units. */
C (GdipSetClipRect (graphics, 20, 30, 40, 50, CombineModeReplace));

/* Clip bounds should be as requested */
assertClipBounds(graphics, 20, 30, 40, 50);

/* Change to Document units */
C (GdipSetPageUnit(graphics, UnitDocument));

/* Clip bounds should be scaled */
assertClipBounds(graphics, 62.5, 93.75, 125, 156.25);

/* Hit testing should be correct */
C (GdipIsVisiblePoint(graphics, 70, 100, &contains));
assert (contains);
C (GdipIsVisiblePoint(graphics, 60, 90, &contains));
assert (!contains);

/* Set the clip rect in Document units. */
C (GdipSetPageUnit(graphics, UnitDocument));
C (GdipSetClipRect (graphics, 62.5, 93.75, 125, 156.25, CombineModeReplace));

/* Clip bounds should be equal to requested */
assertClipBounds (graphics, 62.5, 93.75, 125, 156.25);

/* Hit testing should be correct */
C (GdipIsVisiblePoint(graphics, 70, 100, &contains));
assert (contains);
C (GdipIsVisiblePoint(graphics, 60, 90, &contains));
assert (!contains);

/* Set the clip in Display units with translate transform */
C (GdipSetPageUnit(graphics, UnitDisplay));
C (GdipTranslateWorldTransform(graphics, 20, 30, MatrixOrderAppend));
C (GdipSetClipRect (graphics, 0, 0, 40, 50, CombineModeReplace));

/* Clip bounds and matrix should be as requested */
assertClipBounds (graphics, 0, 0, 40, 50);
C (GdipGetWorldTransform(graphics, matrix));
verifyMatrix(matrix, 1, 0, 0, 1, 20, 30);

/* Change to Document units */
C (GdipSetPageUnit(graphics, UnitDocument));

/* Clip bounds should be scaled and matrix the same */
assertClipBounds (graphics, 42.5, 63.75, 125, 156.25);
C (GdipGetWorldTransform(graphics, matrix));
verifyMatrix(matrix, 1, 0, 0, 1, 20, 30);

GdipDeleteMatrix(matrix);
}

static void
test_gdip_clip_path_page_units ()
{
GpBitmap *bitmap = 0;
GpGraphics *graphics;
GpMatrix* matrix;
GpPath* path;
BOOL contains;

C (GdipCreateMatrix(&matrix));
C (GdipCreateBitmapFromScan0 (612, 792, 0, PixelFormat32bppARGB, NULL, &bitmap));
C (GdipGetImageGraphicsContext (bitmap, &graphics));

/* Set the clip path in Display units. */
C (GdipCreatePath(FillModeWinding, &path));
C (GdipAddPathRectangle(path, 20, 30, 40, 50));
C (GdipSetClipPath (graphics, path, CombineModeReplace));

/* Clip bounds should be as requested */
assertClipBounds(graphics, 20, 30, 40, 50);

/* Change to Document units */
C (GdipSetPageUnit(graphics, UnitDocument));

/* Clip bounds should be scaled */
assertClipBounds(graphics, 62.5, 93.75, 125, 156.25);

/* Hit testing should be correct */
C (GdipIsVisiblePoint(graphics, 70, 100, &contains));
assert (contains);
C (GdipIsVisiblePoint(graphics, 60, 90, &contains));
assert (!contains);

/* Set the clip rect in Document units. */
C (GdipSetPageUnit(graphics, UnitDocument));
C (GdipResetPath(path));
C (GdipAddPathRectangle(path, 62.5, 93.75, 125, 156.25));
C (GdipSetClipPath (graphics, path, CombineModeReplace));

/* Clip bounds should be equal to requested */
assertClipBounds (graphics, 62.5, 93.75, 125, 156.25);

/* Hit testing should be correct */
C (GdipIsVisiblePoint(graphics, 70, 100, &contains));
assert (contains);
C (GdipIsVisiblePoint(graphics, 60, 90, &contains));
assert (!contains);

/* Set the clip in Display units with translate transform */
C (GdipSetPageUnit(graphics, UnitDisplay));
C (GdipTranslateWorldTransform(graphics, 20, 30, MatrixOrderAppend));
C (GdipResetPath(path));
C (GdipAddPathRectangle(path, 0, 0, 40, 50));
C (GdipSetClipPath (graphics, path, CombineModeReplace));

/* Clip bounds and matrix should be as requested */
assertClipBounds (graphics, 0, 0, 40, 50);
C (GdipGetWorldTransform(graphics, matrix));
verifyMatrix(matrix, 1, 0, 0, 1, 20, 30);

/* Change to Document units */
C (GdipSetPageUnit(graphics, UnitDocument));

/* Clip bounds should be scaled and matrix the same */
assertClipBounds (graphics, 42.5, 63.75, 125, 156.25);
C (GdipGetWorldTransform(graphics, matrix));
verifyMatrix(matrix, 1, 0, 0, 1, 20, 30);

GdipDeleteMatrix(matrix);
GdipDeletePath(path);
}

void
assertClipBounds(GpGraphics* graphics, double x, double y, double width, double height)
{
GpRectF bounds;
GpRegion* region;

C (GdipCreateRegion(&region));

C (GdipGetClipBounds(graphics, &bounds));
assertEqualRectFInline (bounds, x, y, width, height);

C (GdipGetClip(graphics, region));
C (GdipGetRegionBounds(region, graphics, &bounds));
assertEqualRectFInline (bounds, x, y, width, height);

C (GdipGetVisibleClipBounds(graphics, &bounds));
assertEqualRectFInline (bounds, x, y, width, height);

GdipDeleteRegion(region);
}

int
main(int argc, char**argv)
{
Expand All @@ -557,6 +716,8 @@ main(int argc, char**argv)
test_gdip_clip_containers ();
test_gdip_clip_visible ();
test_gdip_clip_path ();
test_gdip_clip_page_units ();
test_gdip_clip_path_page_units ();

SHUTDOWN;
return 0;
Expand Down