Skip to content

Commit 7458170

Browse files
committed
[GTK] Fix GC#copyArea() for overlapping source and target areas eclipse-platform#1756
The GC provides a #copyArea() method that copies an area inside the surface of the GC to another area in the same surface. When using a surface directly created via the Cairo API (or with the GDK API on Wayland), this operation is broken. When the source and target area are overlapping and the source is above-left of the target area, the source area will appear replicated in the target area, as Cairo does not perform any buffering but does a linewise/chunkwise write from the source area to the target area. This change fixes the behavior of GC#copyArea() by explicitly buffering the area to be copied. It also removes the existing workaround in images that avoids the instantiation of a surface via the Cairo API, as the reason for that workaround was the issue fixed by this change. Fixes eclipse-platform#1756
1 parent 413426e commit 7458170

File tree

2 files changed

+21
-6
lines changed

2 files changed

+21
-6
lines changed

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/GC.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,10 +545,29 @@ void copyAreaInPixels(int srcX, int srcY, int width, int height, int destX, int
545545
if (deltaX == 0 && deltaY == 0) return;
546546
long drawable = data.drawable;
547547
if (data.image != null) {
548-
Cairo.cairo_set_source_surface(handle, data.image.surface, deltaX, deltaY);
548+
// When filling a part of a surface with another part of a surface, Cairo does not buffer
549+
// the whole data to copy but performs some chunk-wise copy from the top left to the bottom right.
550+
// Thus if the source and the target area are overlapping, Cairo may copy a chunk into a target
551+
// area that is then copied on later, leading to unwanted replications of that source chunk in
552+
// the target area.
553+
// To avoid this, we buffer the area to copy in a separate surface.
554+
int imageFormat = Cairo.cairo_image_surface_get_format(data.image.surface);
555+
long tempSurface = Cairo.cairo_image_surface_create(imageFormat, width, height);
556+
long tempGC = Cairo.cairo_create(tempSurface);
557+
int sourceX = deltaX > 0 ? 0 : deltaX;
558+
int sourceY = deltaY > 0 ? 0 : deltaY;
559+
Cairo.cairo_set_source_surface(tempGC, data.image.surface, sourceX, sourceY);
560+
Cairo.cairo_rectangle(tempGC, 0, 0, width, height);
561+
Cairo.cairo_set_operator(tempGC, Cairo.CAIRO_OPERATOR_SOURCE);
562+
Cairo.cairo_fill(tempGC);
563+
Cairo.cairo_destroy(tempGC);
564+
int targetX = deltaX < 0 ? 0 : deltaX;
565+
int targetY = deltaY < 0 ? 0 : deltaY;
566+
Cairo.cairo_set_source_surface(handle, tempSurface, targetX, targetY);
549567
Cairo.cairo_rectangle(handle, destX, destY, width, height);
550568
Cairo.cairo_set_operator(handle, Cairo.CAIRO_OPERATOR_SOURCE);
551569
Cairo.cairo_fill(handle);
570+
Cairo.cairo_surface_destroy(tempSurface);
552571
} else if (drawable != 0) {
553572
Cairo.cairo_save(handle);
554573
Cairo.cairo_rectangle(handle, destX, destY, width, height);

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,11 +1260,7 @@ void init(int width, int height) {
12601260
this.type = SWT.BITMAP;
12611261

12621262
/* Create the pixmap */
1263-
if (GTK.GTK4) {
1264-
surface = Cairo.cairo_image_surface_create(Cairo.CAIRO_FORMAT_RGB24, width, height);
1265-
} else {
1266-
surface = GDK.gdk_window_create_similar_surface(GDK.gdk_get_default_root_window(), Cairo.CAIRO_CONTENT_COLOR, width, height);
1267-
}
1263+
surface = Cairo.cairo_image_surface_create(Cairo.CAIRO_FORMAT_ARGB32, width, height);
12681264
if (surface == 0) SWT.error(SWT.ERROR_NO_HANDLES);
12691265
// When we create a blank image we need to set it to 100 in GTK3 as we draw using 100% scale.
12701266
// Cairo will take care of scaling for us when image needs to be scaled.

0 commit comments

Comments
 (0)