Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -913,9 +913,14 @@ void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight,
/* Refresh Image as per zoom level, if required. */
srcImage.refreshImageForZoom ();

ImageData srcImageData = srcImage.getImageData();
int imgWidth = srcImageData.width;
int imgHeight = srcImageData.height;
int imgWidth = srcImage.width;
int imgHeight = srcImage.height;
if (imgWidth == -1 || imgHeight == -1) {
/* Images wrapped around a native handle carry no dimensions, see Image.gtk_new. */
ImageData srcImageData = srcImage.getImageData();
imgWidth = srcImageData.width;
imgHeight = srcImageData.height;
}
if (srcWidth == 0 && srcHeight == 0) {
srcWidth = imgWidth;
srcHeight = imgHeight;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -970,12 +970,17 @@ void destroy() {

private class CachedImageAtSize {
private Image image;
/** Size in pixels the cached image was requested at; the image itself carries points. */
private int requestedWidth = -1;
private int requestedHeight = -1;

public void destroy() {
if (image != null) {
image.dispose();
image = null;
}
requestedWidth = -1;
requestedHeight = -1;
}

private Optional<Image> refresh(int destWidth, int destHeight) {
Expand All @@ -987,12 +992,16 @@ private Optional<Image> refresh(int destWidth, int destHeight) {
destroy();
Optional<Image> imageAtSize = loadImageAtSize(scaledWidth, scaledHeight);
image = imageAtSize.orElse(null);
if (image != null) {
requestedWidth = scaledWidth;
requestedHeight = scaledHeight;
}
return imageAtSize;
}
}

private boolean isReusable(int width, int height) {
return image != null && image.height == height && image.width == width;
return image != null && requestedHeight == height && requestedWidth == width;
}

private Optional<Image> loadImageAtSize(int destWidth, int destHeight) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

Expand Down Expand Up @@ -59,6 +62,7 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

Expand All @@ -72,6 +76,9 @@ public class Test_org_eclipse_swt_graphics_GC {

private static final int IMAGE_SIZE = 200;

@TempDir
static Path tempFolder;

@BeforeEach
public void setUp() {
display = Display.getDefault();
Expand Down Expand Up @@ -1208,6 +1215,60 @@ RGB getRealRGB(Color color) {
return palette.getRGB(pixel);
}

/**
* Drawing reads the image dimensions from the image itself. Obtaining them through
* ImageData used to decode the file again on every draw at a device zoom other than 100.
*/
@Test
public void test_drawImage_doesNotReReadImageFileAtNonDefaultZoom() throws IOException {
Path file = tempFolder.resolve("volatile-collapseall.png");
Files.copy(SwtTestUtil.getPath("collapseall.png", tempFolder), file);
int previousDeviceZoom = DPIUtil.getDeviceZoom();
Image fileImage = null;
try {
DPIUtil.setDeviceZoom(200);
gc.dispose();
gc = new GC(image);
fileImage = new Image(display, file.toString());
ImageData beforeDelete = drawToFreshTarget(fileImage);
assertFalse(Arrays.equals(blankTargetData(), beforeDelete.data), "the reference draw produced no pixels");

Files.delete(file);

ImageData afterDelete = drawToFreshTarget(fileImage);
ImageDataTestHelper.assertImageDataEqual(beforeDelete, afterDelete, beforeDelete);
gc.drawImage(fileImage, 0, 0);
} finally {
if (fileImage != null) {
fileImage.dispose();
}
DPIUtil.setDeviceZoom(previousDeviceZoom);
Files.deleteIfExists(file);
}
}

private byte[] blankTargetData() {
Image target = new Image(display, IMAGE_SIZE, IMAGE_SIZE);
try {
return target.getImageData().data;
} finally {
target.dispose();
}
}

private ImageData drawToFreshTarget(Image source) {
Rectangle bounds = source.getBounds();
Image target = new Image(display, IMAGE_SIZE, IMAGE_SIZE);
GC targetGc = new GC(target);
try {
targetGc.drawImage(source, 0, 0, bounds.width, bounds.height, 0, 0, bounds.width * 2, bounds.height * 2);
return target.getImageData();
} finally {
targetGc.dispose();
target.dispose();
}
}

private void executeWithNonDefaultDeviceZoom(Runnable executable) {
int previousDeviceZoom = DPIUtil.getDeviceZoom();
DPIUtil.setDeviceZoom(200);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1224,5 +1224,43 @@ public void test_gcOnImageGcDrawer_imageDataAtNonDeviceZoom() {
}
}

/**
* The size cache must hit at any device zoom. Comparing the requested size in pixels
* against the cached image's size in points made every scaled draw a miss at zoom != 100.
*/
@Test
public void test_drawImageAtSize_cacheIsReusedAtNonDefaultZoom() throws IOException {
Path file = tempFolder.resolve("cached-collapseall.svg");
Files.copy(Path.of(getPath("collapseall.svg")), file);
int originalDeviceZoom = DPIUtil.getDeviceZoom();
Image image = null;
Image target = null;
GC gc = null;
try {
DPIUtil.setDeviceZoom(200);
image = new Image(display, file.toString());
target = new Image(display, 64, 64);
gc = new GC(target);
gc.drawImage(image, 0, 0, 20, 20);

// a second draw at the same size must come from the cache, so the file is not needed
Files.delete(file);

gc.drawImage(image, 0, 0, 20, 20);
} finally {
if (gc != null) {
gc.dispose();
}
if (target != null) {
target.dispose();
}
if (image != null) {
image.dispose();
}
DPIUtil.setDeviceZoom(originalDeviceZoom);
Files.deleteIfExists(file);
}
}

}

Loading