Skip to content

[GTK] GC#copyArea() produces wrong results when source and target area are overlapping #1756

Closed
@HeikoKlare

Description

@HeikoKlare

The GC provides an operation copyArea() that is supposed to copy an area inside the GC's surface, i.e., from a source area of the surface into a target area of the same surface. When the source and target area are overlapping, the result is not as expected.

Expected Behavior Example

Take the following original surface data:
Image

Let's assume we want to copy the red-marked area down to the yellow-marked area:
Image

Then we would expect the following result:
Image

Actual Behavior Example

The expected result is only achieved under very specific conditions:

  • You are not using GTK4 or Wayland
  • You do not create the GC on an Image instance or the Image you pass was created via the Image constructor just accepting width/height or a size

The reason for the latter is that for that exact case (creating Image via width/height or size) a very specific workaround has been implemented via
https://bugs.eclipse.org/bugs/show_bug.cgi?id=571166:

if (GTK.GTK4) {
surface = Cairo.cairo_image_surface_create(Cairo.CAIRO_FORMAT_RGB24, width, height);
} else {
surface = GDK.gdk_window_create_similar_surface(GDK.gdk_get_default_root_window(), Cairo.CAIRO_CONTENT_COLOR, width, height);
}

In all other cases (using GTK4, using Wayland, using an Image created in a different way) you will see this result:
Image

Since this is a derivation from the existing behavior of GTK and the behavior on Windows and MacOS, consumers may experience inconsistent cross-platform behavior, such as in eclipse-platform/eclipse.platform.ui#2740

Cause

The cause for the mentioned issue is in different behavior of copy operations inside the same surface depending on the used API and environment on which you create a surface. The usage of GDK.gdk_window_create_similar_surface() on X11 seems to perform some sufficient buffering of the data to be copied inside the same surface. In all other cases, the operation already overwrites its own source region while still reading from in.

In the example show above, the area is linewise/chunkwise copied from top-left downwards. When the copy operation reaches the overlapping red/yellow region, is reads already overwritten data to copy and copies that over again. This leads to duplications of the data in the non-overlapping area.

This can be easily experienced in the LineNumberRuler, which we have already seen several years ago in https://bugs.eclipse.org/bugs/show_bug.cgi?id=571166:
Image

Potential Fix

Completely buffering the data to be copied to avoid concurrent overwrites would solve the problem. I will submit a PR for this. There might be better ways directly via the Cairo API.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Linux/GTKHappens on LinuxWaylandbugSomething isn't workinggtk4GTK4 issues

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions