This is an automated email from the ASF dual-hosted git repository.
mattcasters pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new 8c2135a883 Fix image notepad rendering issue #8869 (#8945)
8c2135a883 is described below
commit 8c2135a88375581605599b686dc41e39160c18b9
Author: William Evan Lomanto <[email protected]>
AuthorDate: Mon Oct 19 19:57:41 2026 +0700
Fix image notepad rendering issue #8869 (#8945)
* Fix image notepad rendering issue
Reviewer Changes Suggestion
* spotless
---------
Co-authored-by: Hans Van Akelyen <[email protected]>
---
.../hop/core/gui/markdown/NoteImageSupport.java | 37 +++++++++++++++++++---
.../core/gui/markdown/NoteImageSupportTest.java | 20 ++++++++++++
.../org/apache/hop/ui/hopgui/shared/SwtGc.java | 16 +++++++---
3 files changed, 64 insertions(+), 9 deletions(-)
diff --git
a/engine/src/main/java/org/apache/hop/core/gui/markdown/NoteImageSupport.java
b/engine/src/main/java/org/apache/hop/core/gui/markdown/NoteImageSupport.java
index 164fc1c3b7..144d3dc5f9 100644
---
a/engine/src/main/java/org/apache/hop/core/gui/markdown/NoteImageSupport.java
+++
b/engine/src/main/java/org/apache/hop/core/gui/markdown/NoteImageSupport.java
@@ -42,6 +42,12 @@ public final class NoteImageSupport {
/** Soft cap on rendered image height in canvas units. */
public static final int DEFAULT_MAX_HEIGHT = 200;
+ /** Minimum rendered image dimension in canvas units; avoids degenerate 1px
rasters. */
+ public static final int MIN_RENDERED_SIZE = 8;
+
+ /** Hard cap on any rendered image dimension in canvas units; guards against
huge rasters. */
+ public static final int MAX_RENDERED_SIZE = 2048;
+
private static final Map<String, ImageInfo> SIZE_CACHE = new
ConcurrentHashMap<>();
private NoteImageSupport() {}
@@ -177,22 +183,45 @@ public final class NoteImageSupport {
return lower.endsWith(".svg");
}
- /** Fit image into max box preserving aspect ratio. */
+ /**
+ * Fit an image into the given max box, preserving aspect ratio. The result
never exceeds the box
+ * or {@link #MAX_RENDERED_SIZE}. A very wide or tall image keeps its thin
axis short rather than
+ * being grown past the box.
+ */
public static Point fit(int srcW, int srcH, int maxWidth, int maxHeight) {
if (srcW <= 0 || srcH <= 0) {
- return new Point(Math.max(1, maxWidth), Math.max(1, maxHeight));
+ return new Point(bound(maxWidth), bound(maxHeight));
}
- int mw = Math.max(1, maxWidth);
- int mh = Math.max(1, maxHeight);
+ int mw = bound(maxWidth);
+ int mh = bound(maxHeight);
double scale = Math.min((double) mw / srcW, (double) mh / srcH);
if (scale > 1.0) {
scale = 1.0; // do not upscale small icons excessively on canvas
}
int w = Math.max(1, (int) Math.round(srcW * scale));
int h = Math.max(1, (int) Math.round(srcH * scale));
+
+ if (w < MIN_RENDERED_SIZE || h < MIN_RENDERED_SIZE) {
+ double up = Math.max((double) MIN_RENDERED_SIZE / w, (double)
MIN_RENDERED_SIZE / h);
+ up = Math.min(up, Math.min((double) mw / w, (double) mh / h));
+ if (up > 1.0) {
+ w = Math.clamp(Math.round(w * up), 1, mw);
+ h = Math.clamp(Math.round(h * up), 1, mh);
+ }
+ }
+ // Never rasterise an enormous bitmap on the canvas
+ if (w > MAX_RENDERED_SIZE || h > MAX_RENDERED_SIZE) {
+ double down = Math.min((double) MAX_RENDERED_SIZE / w, (double)
MAX_RENDERED_SIZE / h);
+ w = Math.max(1, (int) Math.round(w * down));
+ h = Math.max(1, (int) Math.round(h * down));
+ }
return new Point(w, h);
}
+ private static int bound(int value) {
+ return Math.max(1, Math.min(value, MAX_RENDERED_SIZE));
+ }
+
/** Test helper / memory bound. */
static void clearCache() {
SIZE_CACHE.clear();
diff --git
a/engine/src/test/java/org/apache/hop/core/gui/markdown/NoteImageSupportTest.java
b/engine/src/test/java/org/apache/hop/core/gui/markdown/NoteImageSupportTest.java
index 83c2d40980..109e39a9a5 100644
---
a/engine/src/test/java/org/apache/hop/core/gui/markdown/NoteImageSupportTest.java
+++
b/engine/src/test/java/org/apache/hop/core/gui/markdown/NoteImageSupportTest.java
@@ -91,6 +91,26 @@ class NoteImageSupportTest {
assertEquals(50, shrink.y);
}
+ @Test
+ void fitBoundsExtremeDimensions() {
+ Point wide = NoteImageSupport.fit(2000, 100, 20, 200);
+ assertTrue(wide.x <= 20 && wide.y <= 200);
+
+ Point banner = NoteImageSupport.fit(2000, 40, 100, 200);
+ assertTrue(banner.x <= 100 && banner.y <= 200);
+
+ Point sliver = NoteImageSupport.fit(100000, 1, 500, 200);
+ assertTrue(sliver.x <= 500 && sliver.y <= 200);
+
+ Point max = NoteImageSupport.fit(100000, 100000, 100000, 100000);
+ assertTrue(max.x <= NoteImageSupport.MAX_RENDERED_SIZE);
+ assertTrue(max.y <= NoteImageSupport.MAX_RENDERED_SIZE);
+
+ Point tall = NoteImageSupport.fit(3, 10, 20, 200);
+ assertTrue(tall.x <= 20 && tall.y <= 200);
+ assertEquals(0.3, (double) tall.x / tall.y, 0.05);
+ }
+
private static void writePng(Path path, int w, int h) throws Exception {
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/shared/SwtGc.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/shared/SwtGc.java
index 59aeb296e3..a4a6822ad9 100644
--- a/ui/src/main/java/org/apache/hop/ui/hopgui/shared/SwtGc.java
+++ b/ui/src/main/java/org/apache/hop/ui/hopgui/shared/SwtGc.java
@@ -617,19 +617,25 @@ public class SwtGc implements IGc {
}
try (java.io.InputStream in =
org.apache.hop.core.vfs.HopVfs.getInputStream(path)) {
org.eclipse.swt.graphics.ImageData data = new
org.eclipse.swt.graphics.ImageData(in);
+ // Scale the decoded raster to the logical size; the DPI provider only
applies monitor zoom.
+ int w = Math.max(1, width);
+ int h = Math.max(1, height);
+ org.eclipse.swt.graphics.ImageData scaled =
+ (data.width == w && data.height == h) ? data : data.scaledTo(w, h);
Image img =
SwtUniversalImage.createDpiAwareImage(
gc.getDevice(),
zoom -> {
if (zoom == 100) {
- return data;
+ return scaled;
}
- int w = Math.max(1, data.width * zoom / 100);
- int h = Math.max(1, data.height * zoom / 100);
- return data.scaledTo(w, h);
+ int zw = Math.max(1, Math.round(scaled.width * (zoom /
100f)));
+ int zh = Math.max(1, Math.round(scaled.height * (zoom /
100f)));
+
+ return scaled.scaledTo(zw, zh);
});
try {
- gc.drawImage(img, 0, 0, data.width, data.height, x, y, width,
height);
+ gc.drawImage(img, 0, 0, scaled.width, scaled.height, x, y, w, h);
} finally {
img.dispose();
}