From 5252bfcb32ae2bfaf8995bc177e7309df467f609 Mon Sep 17 00:00:00 2001 From: Andy Goryachev Date: Mon, 20 Jul 2026 13:09:14 -0700 Subject: [PATCH 1/7] image format handler --- .../richtext/editor/RichEditorDemoWindow.java | 6 + .../control/richtext/util/RichUtils.java | 31 ++- .../richtext/model/ImageFormatHandler.java | 216 ++++++++++++++++++ .../control/richtext/model/RichTextModel.java | 1 + 4 files changed, 249 insertions(+), 5 deletions(-) create mode 100644 modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java diff --git a/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/editor/RichEditorDemoWindow.java b/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/editor/RichEditorDemoWindow.java index 6143748c7a6..fac34f3b08f 100644 --- a/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/editor/RichEditorDemoWindow.java +++ b/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/editor/RichEditorDemoWindow.java @@ -67,6 +67,7 @@ public class RichEditorDemoWindow extends Stage { public RichEditorDemoWindow() { editor = new RichTextArea(); + editor.getInputMap().registerFunction(RichTextArea.Tag.ERROR_FEEDBACK, this::errorFeedback); toolbar = new RichEditorToolbar(); status = new Label(); @@ -193,4 +194,9 @@ private String titleString(File f, boolean modified) { } return sb.toString(); } + + private void errorFeedback() { + // TODO beep + IO.println("Error!"); + } } diff --git a/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/RichUtils.java b/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/RichUtils.java index 5d0bf47e35f..c928d2c653b 100644 --- a/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/RichUtils.java +++ b/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/RichUtils.java @@ -434,6 +434,31 @@ public static String readString(InputStream in) throws IOException { return sb.toString(); } + + private static byte[] writeImage(Image im, String format) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(65536); + // using disk cache slows things down + boolean old = ImageIO.getUseCache(); + ImageIO.setUseCache(false); + try { + ImageIO.write(ImgUtil.fromFXImage(im, null), format, out); + } finally { + ImageIO.setUseCache(old); + } + return out.toByteArray(); + } + + /** + * Writes an Image to a byte array in JPG format. + * + * @param im source image + * @return byte array containing JPG image + * @throws IOException if an I/O error occurs + */ + public static byte[] writeJPG(Image im) throws IOException { + return writeImage(im, "JPG"); + } + /** * Writes an Image to a byte array in PNG format. * @@ -442,11 +467,7 @@ public static String readString(InputStream in) throws IOException { * @throws IOException if an I/O error occurs */ public static byte[] writePNG(Image im) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(65536); - // this might conflict with user-set value - ImageIO.setUseCache(false); - ImageIO.write(ImgUtil.fromFXImage(im, null), "PNG", out); - return out.toByteArray(); + return writeImage(im, "PNG"); } /** diff --git a/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java b/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java new file mode 100644 index 00000000000..149ab0357bf --- /dev/null +++ b/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jfx.incubator.scene.control.richtext.model; + +import java.io.IOException; +import java.io.OutputStream; +import javafx.scene.image.Image; +import javafx.scene.image.PixelReader; +import javafx.scene.input.DataFormat; +import com.sun.jfx.incubator.scene.control.richtext.EmbeddedImageHelper; +import com.sun.jfx.incubator.scene.control.richtext.SegmentStyledInput; +import com.sun.jfx.incubator.scene.control.richtext.util.RichUtils; +import jfx.incubator.scene.control.richtext.StyleResolver; +import jfx.incubator.scene.control.richtext.TextPos; + +/** + * Facilitates importing of images into the RichTextModel. + * @since 28 + */ +public class ImageFormatHandler extends DataFormatHandler { + + private static final int SMALL_SIZE = 128; + private static final int SMALL_CHANGE_THRESHOLD = 130050; // 1/8th of full scale + private static final ImageFormatHandler instance = new ImageFormatHandler(); + + /** + * Constructor. + */ + private ImageFormatHandler() { + super(DataFormat.IMAGE); + } + + /** + * Returns the singleton instance of {@code ImageFormatHandler}. + * @return the singleton instance of {@code ImageFormatHandler} + */ + public static final ImageFormatHandler getInstance() { + return instance; + } + + /** + * Creates an ImageFormatHandler instance which imports images using either + * a lossless (PNG) or lossy (JPEG) encoding. + * + * @param lossless determines whether to create a lossless handler + * @return the instance + */ + public static final ImageFormatHandler create(boolean lossless) { + return new ImageFormatHandler() { + @Override + byte[] toByteArray(Image im) throws IOException { + return lossless ? + RichUtils.writePNG(im) : + RichUtils.writeJPG(im); + + } + }; + } + + /** + * {@inheritDoc} + * + *

The type of {@code input} must be {@code Image}. + */ + @Override + public StyledInput createStyledInput(Object input, StyleAttributeMap attr) throws IOException { + Image im = (Image)input; + double w = im.getWidth(); + double h = im.getHeight(); + byte[] b = toByteArray(im); + if ((b == null) || (b.length == 0)) { + // TODO there seems to be a bug writing certain images to JPG + throw new IOException("Failed to store image"); + } + EmbeddedImage em = EmbeddedImageHelper.create(b, w, h, EmbeddedImage.AUTO, EmbeddedImage.AUTO, true); + StyleAttributeMap a = StyleAttributeMap.of(StyleAttributeMap.EMBEDDED_IMAGE, em); + return new SegmentStyledInput(StyledSegment.of(" ", a)); + } + + @Override + public Object copy(StyledTextModel m, StyleResolver r, TextPos start, TextPos end) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void save(StyledTextModel m, StyleResolver r, TextPos start, TextPos end, OutputStream out) throws IOException { + throw new UnsupportedOperationException(); + } + + byte[] toByteArray(Image im) throws IOException { + boolean isPhoto = detectPhoto(im); + RichUtils.log("photo={0} fmt={1}", isPhoto, im.getPixelReader().getPixelFormat()); + + // FIX storing as jpg is broken, see JDK-8388450 + isPhoto = false; + + return isPhoto ? + RichUtils.writeJPG(im) : + RichUtils.writePNG(im); + } + + private static int diff(int a, int b) { + int d = diffChannel(a, b); // r + a >>= 8; + b >>= 8; + d += diffChannel(a, b); // g + a >>= 8; + b >>= 8; + d += diffChannel(a, b); // b + return d; + } + + private static int diffChannel(int a, int b) { + int d = (a & 0xff) - (b & 0xff); + return d < 0 ? -d : d; + } + + private boolean detectPhoto(Image im) { + int w = (int)im.getWidth(); + int h = (int)im.getHeight(); + + // small image -> png + if ((w < SMALL_SIZE) && (h < SMALL_SIZE)) { + return false; + } + + PixelReader rd = im.getPixelReader(); + + // TODO we could check the presence of alpha channel, but JDK-8388511 + + int parts = 4; + int stepx = w / parts; + int stepy = h / parts; + int x; + int y; + + int same = 0; + int jump = 0; + + x = stepx; + for (int ix = 1; ix < parts; ix++) { + int prev = 0; + for (y = 0; y < h; y++) { + int argb = rd.getArgb(x, y); + + // alpha + if((argb & 0xff000000) != 0xff000000) { + // alpha channel -> png + return false; + } + + if (prev == argb) { + same++; + } else { + int diff = diff(argb, prev); + if (diff < SMALL_CHANGE_THRESHOLD) { + jump++; + } + } + prev = argb; + } + x += stepx; + } + + y = stepy; + for (int iy = 1; iy < parts; iy++) { + int prev = 0; + for (x = 0; x < w; x++) { + int argb = rd.getArgb(x, y); + + // alpha + if((argb & 0xff000000) != 0xff000000) { + // alpha channel -> png + return false; + } + + if (prev == argb) { + same++; + } else { + int diff = diff(argb, prev); + if (diff < SMALL_CHANGE_THRESHOLD) { + jump++; + } + } + prev = argb; + } + y += stepy; + } + + // more small changes than same color pixels -> jpg + return jump > same; + } +} diff --git a/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/RichTextModel.java b/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/RichTextModel.java index 5fd972baaf7..be54cf1178e 100644 --- a/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/RichTextModel.java +++ b/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/RichTextModel.java @@ -77,6 +77,7 @@ public RichTextModel() { registerDataFormatHandler(RtfFormatHandler.getInstance(), true, true, 300); registerDataFormatHandler(HtmlExportFormatHandler.getInstance(), true, false, 200); registerDataFormatHandler(FileListFormatHandler.getInstance(), false, true, 100); + registerDataFormatHandler(ImageFormatHandler.getInstance(), false, true, 50); registerDataFormatHandler(PlainTextFormatHandler.getInstance(), true, true, 0); // always has at least one paragraph paragraphs.add(new RParagraph()); From 2b7f1fc0c84df4007467c906080a01a5b2036cb5 Mon Sep 17 00:00:00 2001 From: Andy Goryachev Date: Fri, 24 Jul 2026 14:27:41 -0700 Subject: [PATCH 2/7] undo image paste --- .../incubator/scene/control/richtext/model/StyledTextModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/StyledTextModel.java b/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/StyledTextModel.java index 1b3cf3b5e78..905f344389b 100644 --- a/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/StyledTextModel.java +++ b/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/StyledTextModel.java @@ -450,10 +450,10 @@ public final DataFormatHandler getDataFormatHandler(DataFormat format, boolean f */ public void fireChangeEvent(TextPos start, TextPos end, int charsTop, int linesAdded, int charsBottom) { ContentChange ch = ContentChange.ofEdit(start, end, charsTop, linesAdded, charsBottom); - markers.update(start, end, charsTop, linesAdded, charsBottom); for (Listener li : listeners) { li.onContentChange(ch); } + markers.update(start, end, charsTop, linesAdded, charsBottom); } /** From 2f2b5eb8c52b7c1f40a8a1c9af2e6199058cfa49 Mon Sep 17 00:00:00 2001 From: Andy Goryachev Date: Tue, 28 Jul 2026 10:09:35 -0700 Subject: [PATCH 3/7] test --- tests/system/src/test/.classpath | 148 +++++++------ tests/system/src/test/addExports | 1 + .../test/com/sun/javafx/images/TestData.java | 35 +++ .../richtext/TestImageFormatHandler.java | 209 ++++++++++++++++++ .../sun/javafx/images/banana-slug-small.jpg | Bin 0 -> 1274 bytes .../com/sun/javafx/images/banana-slug.jpg | Bin 0 -> 53928 bytes .../sun/javafx/images/underlines-small.png | Bin 0 -> 2279 bytes .../test/com/sun/javafx/images/underlines.png | Bin 0 -> 20153 bytes 8 files changed, 320 insertions(+), 73 deletions(-) create mode 100644 tests/system/src/test/java/test/com/sun/javafx/images/TestData.java create mode 100644 tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java create mode 100644 tests/system/src/test/resources/test/com/sun/javafx/images/banana-slug-small.jpg create mode 100644 tests/system/src/test/resources/test/com/sun/javafx/images/banana-slug.jpg create mode 100644 tests/system/src/test/resources/test/com/sun/javafx/images/underlines-small.png create mode 100644 tests/system/src/test/resources/test/com/sun/javafx/images/underlines.png diff --git a/tests/system/src/test/.classpath b/tests/system/src/test/.classpath index 55bde31606a..7e87e1b5480 100644 --- a/tests/system/src/test/.classpath +++ b/tests/system/src/test/.classpath @@ -1,73 +1,75 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/system/src/test/addExports b/tests/system/src/test/addExports index b5a66c287cd..d97818e1b74 100644 --- a/tests/system/src/test/addExports +++ b/tests/system/src/test/addExports @@ -30,6 +30,7 @@ --add-exports javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED --add-exports=javafx.graphics/com.sun.javafx.tk.quantum=ALL-UNNAMED --add-exports javafx.graphics/com.sun.prism.impl=ALL-UNNAMED +--add-exports jfx.incubator.richtext/com.sun.jfx.incubator.scene.control.richtext=ALL-UNNAMED # --add-exports=javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED # diff --git a/tests/system/src/test/java/test/com/sun/javafx/images/TestData.java b/tests/system/src/test/java/test/com/sun/javafx/images/TestData.java new file mode 100644 index 00000000000..2f1b07de6bd --- /dev/null +++ b/tests/system/src/test/java/test/com/sun/javafx/images/TestData.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test.com.sun.javafx.images; + +import java.net.URL; + +public class TestData { + public static String getURI(String name) { + URL u = TestData.class.getResource(name); + return u == null ? null : u.toExternalForm(); + } +} diff --git a/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java b/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java new file mode 100644 index 00000000000..d455bd0c8cf --- /dev/null +++ b/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test.jfx.incubator.scene.control.richtext; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HexFormat; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import javafx.scene.image.Image; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import com.sun.jfx.incubator.scene.control.richtext.EmbeddedImageHelper; +import com.sun.jfx.incubator.scene.control.richtext.SegmentStyledOutput; +import jfx.incubator.scene.control.richtext.RichTextArea; +import jfx.incubator.scene.control.richtext.TextPos; +import jfx.incubator.scene.control.richtext.model.EmbeddedImage; +import jfx.incubator.scene.control.richtext.model.ImageFormatHandler; +import jfx.incubator.scene.control.richtext.model.RichTextModel; +import jfx.incubator.scene.control.richtext.model.StyleAttributeMap; +import jfx.incubator.scene.control.richtext.model.StyledInput; +import jfx.incubator.scene.control.richtext.model.StyledSegment; +import jfx.incubator.scene.control.richtext.model.StyledTextModel; +import test.com.sun.javafx.images.TestData; +import test.robot.testharness.RobotTestBase; +import test.util.Util; + +// Tests ImageFormatHandler +public class TestImageFormatHandler extends RobotTestBase { + + private enum Size { SMALL, LARGE } + private enum Type { JPG, PNG } + private static final int SMALL_SIZE = 128; // ImageFormatHandler.SMALL_SIZE + + private static final ImageFormatHandler handler = ImageFormatHandler.getInstance(); + private static final byte[] JPG_SIGNATURE = mkSignature("ffd8ffe1"); + private static final byte[] PNG_SIGNATURE = mkSignature("89504e47"); + private RichTextArea rta; + + @BeforeEach + public void beforeEach() { + rta = new RichTextArea(); + setContent(rta); + } + + @Test + public void smallJpg() { + test(Size.SMALL, Type.JPG, Type.PNG); + } + + @Test + public void smallPng() { + test(Size.SMALL, Type.PNG, Type.PNG); + } + + @Disabled("JDK-8388450") // TODO + @Test + public void largeJpg() { + test(Size.LARGE, Type.JPG, Type.JPG); + } + + @Test + public void largePng() { + test(Size.LARGE, Type.PNG, Type.PNG); + } + + @Test + public void copy() { + RichTextModel m = new RichTextModel(); + assertThrows(UnsupportedOperationException.class, () -> { + handler.copy(m, null, TextPos.ZERO, TextPos.ZERO); + }); + } + + @Test + public void save() { + RichTextModel m = new RichTextModel(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + assertThrows(UnsupportedOperationException.class, () -> { + handler.save(m, null, TextPos.ZERO, TextPos.ZERO, out); + }); + } + + private static List load(StyledInput in) { + ArrayList ss = new ArrayList<>(); + StyledSegment s; + while ((s = in.nextSegment()) != null) { + ss.add(s); + } + return ss; + } + + private static String getName(Size size, Type source) { + return switch(size) { + case LARGE -> + switch(source) { + case JPG -> "banana-slug.jpg"; + case PNG -> "underlines.png"; + }; + case SMALL -> + switch(source) { + case JPG -> "banana-slug-small.jpg"; + case PNG -> "underlines-small.png"; + }; + }; + } + + private void test(Size size, Type source, Type expectedType) { + String name = getName(size, source); + String url = TestData.getURI(name); + Image im = new Image(url); + StyledInput in = null; + try { + in = handler.createStyledInput(im, null); + } catch (IOException e) { + fail(e); + } + + List ss = load(in); + assertEquals(1, ss.size()); + + StyledSegment s = ss.getFirst(); + assertEquals(1, s.getText().length()); + StyleAttributeMap a = s.getStyleAttributeMap(null); + EmbeddedImage em = a.get(StyleAttributeMap.EMBEDDED_IMAGE); + assertNotNull(em); + byte[] b = EmbeddedImageHelper.getBytes(em); + assertNotNull(b); + assertSignature(expectedType, b); + + // let's see if it pastes + Util.runAndWait(() -> { + rta.clear(); + ClipboardContent cc = new ClipboardContent(); + cc.putImage(im); + Clipboard.getSystemClipboard().setContent(cc); + }); + + AtomicBoolean pasted = new AtomicBoolean(); + Util.runAndWait(() -> { + rta.select(TextPos.ZERO); + rta.paste(); + StyledTextModel m = rta.getModel(); + SegmentStyledOutput out = new SegmentStyledOutput(8); + try { + m.export(TextPos.ZERO, m.getDocumentEnd(), out); + } catch (Exception e) { + fail(e); + } + StyledSegment[] exported = out.getSegments(); + for (StyledSegment seg : exported) { + StyleAttributeMap att = seg.getStyleAttributeMap(null); + if (att != null) { + EmbeddedImage em2 = att.get(StyleAttributeMap.EMBEDDED_IMAGE); + if (em2 != null) { + pasted.set(true); + } + } + } + assertTrue(pasted.get()); + }); + } + + private void assertSignature(Type expected, byte[] bytes) { + assertNotNull(bytes); + byte[] exp = (expected == Type.JPG) ? JPG_SIGNATURE : PNG_SIGNATURE; + assertTrue(bytes.length > exp.length); + byte[] b = Arrays.copyOf(bytes, exp.length); + assertArrayEquals(exp, b, "wrong image signature, expected " + expected); + } + + private static byte[] mkSignature(String hexPattern) { + return HexFormat.of().parseHex(hexPattern); + } +} diff --git a/tests/system/src/test/resources/test/com/sun/javafx/images/banana-slug-small.jpg b/tests/system/src/test/resources/test/com/sun/javafx/images/banana-slug-small.jpg new file mode 100644 index 0000000000000000000000000000000000000000..896d5b515440fd11855a2aa56d5216da8e7d36ba GIT binary patch literal 1274 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

I;#~=)L{f#V(1*HWH1=D$- zl+(n{_xSwH8^&>!^OZN1AyG1K<=+*_-}XkXYMkDVyKhsB-#*dl@aS;N%CIXbmtEud z#NiPGORErpo9DT1{QR5FzFd4#tjD9F!{U=n71P3StElv0s5I4Y;nzZo`&x5#v($5I zxrntjY&cn!nbW)dT!yjIksaZt+)$Yb!=8ImYEMou3mf~YEFw6 z+HJQgnT?sR-7tzui=ip|;=J3b;m@qvLfRrgrB!mSM{wZM&Kx_S-?Z z8S;U-oBgT%{R85hgzTc1_W_4&t<;+770pJ0^FIdJLf9DC%1Taj)vG>PI>UB?E$OEC zN=-^QbZ@5{w|9pUn&ND=ly%EAm(>aMMw>f6L;G>4*{WE}K3;3=FMg@F-|iMn2}t3z zzL;Mq@A@J?n{cV!rEP14wKSz^t*+aeR^MW93#SSZReESgWVz&b3{|FOxX8IUPj*hF z7CV=fl<*d7t42<-?RdK6AGFpp87MpYOF?T}{3Q5Vfd#s5lTU zMD;NZ+`ycEOenvijHRfcNVuWlRAnoxmbfgzA0moqE}E>a*(pDO5y}3R{Sk>x$pN18 zRq!ajblW%%wjQ6i$w=~A?e9y9rON-Kly=Rql}MN`oFAEfEI#frb-I`3xaX+2xi}

RZ8*CQUW!?+@maq5v(csOdv!m$B-l`Po4dE( zo-v@e-8OfgIG}HRKYD+ux7mU?$~!AT%>Q)VbIo67YVkcWoRS^u`Jr5!@ku<`yOlRe zkegGrlNv~k*zhSp|R~}6HYg4yGK4qU^fvUX>H_oV@TwPr` zU3oce9n83RgoTB1+d1K;e+WNvFi^T^lm zv#qm}7#-bXMgRHyOHLCv^Z%{M#_`|H0vgEm_=byzlbh>5yn&+N$EPAH=58idT5rs) zfj$H35P!wX2mXit|Mup8Yy7X0n*UW&NZ`Lq{@0uTYe{uS69*|$(bA>a%kqQUB#WVd7BOA+{oDAAB{LESvBaN*} zANiBvRW4mZAN|^7+xsA}X~M}nkNmN?=!CGfG)ZY0R+h^)NPf-mo~O^{((=8};dQH! zXUXAZx~JZ){l((t!s#5_I*Qh}mwu05@plHf#%?#v`vI(SG;-~7G-T<&(B)9%P`nc* zUV*(y+$EDc0(>yebOC}h-8Bru$4Xv-<*nP;6)gespO#F5))_YrC|0JMoThX?^ zwDrcqHv{M_ITW^+BfQY6v3G1HwmzpRRC%)0-h8+Fw!SH1S1bM?UULmyXLXrY!@#=c zb&lCG-+Rkx(OWq6gQon398+2x$ERxM+v7;zy%Pqg8e3`H)rUk05P{Fb?eZp_@?|Vq znaZgzkhu#WRO)3?NLo47@wr|P1Vdd;vq@TdM_Ico2+W=D1Pf_Q-aU8w@s=jE&nyA;(qxo>OtBc)-ZL&MQ9NJccBhL%Wv!c31yl zr@JpnVa7OC(PDerV|T75abLgNvd-T6s!pheV}*jMX*1ok5@5`g^2+4LpaA27?Y-aE zy#47*WsdFRE9DaN@w^bLkL@h^sjK6-;#Vs~7_Yzc!A9@J&kK~F7A!9N>}oHcoLk&G zPpf~_%KgfIH*q(hkBDj86g%vbw)biYxLj}?)_FFEWwIa6mrj942;CfDq3Y!MD3hLa z)Q*a`c7zJrj*iggcaO^xJ*dx#qHcwpW#O$4drVb%fd+3EHpK-qAih3`8KX#|emmZ+ z3u-BqM1sYWHsih+9qa?rWFBIV{hH|wF3;HkOmi^g;i?DHts;zm@Ao}o&K7%;gYFMn zS#cqr?-f+fjrzJ*7E`OV=|Chy!2M)R@@ETy{Nd?6(9v~rC4YEuB8%aDb#oSM%K74M z9*aJ=`~S=!qb(DBpF~}k0`~m%KB*;53Eg40ZeB{gZLM(a{nPYIaAM-CgoxXNCa%Qu zPw+AQ?uX2IU!6P(<1f$$*Vfat_YKDqWfk4zx#mn6UJibRueb5uI8FFGE!19`AOjU2 zndhe^K<>AdT~XEsxiYk#hOH&@0b_WyU$?CHc2kC!<;H0#{o{6z@d3AFWBD~8R^o6n zZ_mAzk!5O&&yhbhyFvL~k>AYcP>z#fU&QHwtR}p{XPr%-C2lilDQu9}`^M>#@%1(O zJl{$8lj}3*^KpTbWEHJ*cqHV&(>|`Eya(jzMsB`)*>dQ8y`7(t&ZSvSvx#H9jpte9 zZ{s^4TL%-kTL$K>-lZPmB+*lA8|yXk@&+mS{Cujgqp9D->m6m;eT~e<^>FW_HBWI_ z=DVa%n;aJL2fP{A3M{oP7jpxfP?bQtjbyVMCvhzg3~SSxm2;{?H(40+h9RTUI!#eC z)|%0qXmwc3R#&-GZ1I_{48y-APTQJmwD?=I9dq=f2{rjkGckR_#2Je@`RRsmWMU(zL$co5kw zh7UmQj-Y{7fN)R$kS}zu=#Oeb{rze^zWIsX?o}-wj>&UP(p_hsqe+t2W#o%Uwv&UF zya<_;oFKP-*_gLOuB}y0U+u3?))9_-$-`cz2w)8yQ0q?mj;r|!pPaR1n3QFt`)DFu zH9F71KtTew8p?HX3^PmJfW7;fkgvdA{!WTiXyq#^nDdW>mUtqKZ*`v(o><(&{h{yl zZyt@Mv3G5M$6kyD7u;h7@Hd+ev}IQWnH?-Nkn4IH2*PGI?eMs*MS1&`{9gpXEFyxMwDZ?tozRkLJwtKp7fHlC-{2{8; z=Aa2i4W2sc1+~lk28QmPaqPuz3QRTISXZ`nWQ&H{ z_X-w#>-Nmb6UQ1G?@t-ZJoiOM&TX+5uVrVu^{g$hNL>6Q&CqV#O|$!{P7N`M;a9#b zq4d;6C0w-X{gj)zY^nsfTw+h=nLzDHV6BS`+v7qV&uv$f(}*1TE2aIUcIc(Ow(p_Z%$rF5ShwrdkB#SyD|H*( z%-;~%<*N3!nBu?xP#CbhlW6qd5oyRC> zSi{6EDC=IV0|%Ao`mp;5+aK}!ceiDmA1UY-$8E+in587Ji0bVUuppjXwqCaAY4PaX zBMDFZg=1@wwcRcr4hb&UNnMWfbAjL7tT^*_8<{a^_6nibuYALz#49Rm^=JiF;ie<6 z+0}NurSJ6N-0mDBx9<^J!>UhRK9=<#bz1offWGdo<=CUdBuibiD#(}ecq91oXqfl95GmSXy!|jscWAD3A>MtD~YKG1v^;J zKYXW;U|vDdpS7@)H%hTnKs51yp_!16KF<5oyin*0F`DfY zqR0`_2eyUXA8!YW_Aqg_%mrRt#RqShLe}7+aZQ-ZkF*aEHCo)W(5eE>T(=R)EibSy2H!6 zWAm|-TE@U88ra(xmAuP&$13r!=Px$TP(y>uDf{(4dAc|b_wL#o#&fM5HIcJkK z`X8WD({q?|P*Uv1r4;MhWNqkpA30@2;)sVP^_Z=J!jI!0<#pYtF8~Xo8SfXOnec4& z4Dw=J++qFKxM$3o-wbf{h|+D~`;v-H{tGroFO|v4RihWTTwt_LWi?ZuY(Aw8^A14F zfnmCtuZDQ$JDI+B{%s@eMg~diu7S0Mz$!f9J6|Y%Av7Bc=eP=C73fBRmrI_uwr(CJ ze9nAT>;4^0`u95;V!P&XuuT{&HCF#_mBgkA+217xX(U87@F2yek>vqUG-rust>>ww zdj1xWrzd&N?~wlJ@}-JkH20s%D7*;uI4j_@af(v@p%AdS(}K9!tpJMq*OZ;} zz>{mP^NXRC1Es?C@0sQFS*6F{>hH+ACjQ_OoBasK9O!)0?Y@hN$_p+kt$puuw@txJ zWmKBR`o6G?`43ad-N|0XUfjcceebUZ&&uO@TW^8vFygq1-n}s1A-nTFA#;~0Ewk&! z;GrW*kFBg^c7*`}-3Ll#<5-C{3RV^m@1jndNQ@$IG>4F5&bwa)Vyx2e=iQc(H)Dfp z=hyKq@yTKP*WVm8y#c2O!p9&ee@6{jU%L{YEv0EXExnh~ukfbYD*s3Fj#}US;mWb=oe0+?fL4Ext_|nck1l%mb`` zYPYubOD@)-*#j5lS$JCp_z@^Qb?tN36SkO;ixa7V%Hr-_$Wa-ByeD8z-)@12F6N(9S6f3^S`FB%?M+XrxK*v>7 z=wnKhd~?V_x5AYG44(dxFQ+G5C!k$xrNx*(X*R*6M^&jPFx%;U6Q^#o`%>3@rCz?V zeYN6`v;X?ab>%AO(3IawXRJRIjl(2UsDG?6Bs4@H7c>l@+U2VpBiLqqnOAPClwh^< zr)PRsAJ*4|?R%5@VCTt{AN92q<6+ys?yc5ewXPn8>T{C}vbZ^1dFCQ!Dg#7wGhQi* z*JOrfNLo>id5Bv37I{vnN+%AIY*d)n3@aCRl%av#+TH73c$}8hEqN?h^hxRYP`jJv(X7cBK z9&+1neT!HR`9O6bhooBiUJ<{d!r)}O@A=p!LZk1e6Q{%23fng!K#p&BP4q$j$l3KPtw4CPO%IQ z$~}ezVZ!x+1WsL6>thIhSjl3neQWV6B`$dH8~ex@UBHfzT#q+ZoP6cpA0e{C z?5dm`%GttO!I=(nZK9qDtJIVQbP2L6~KWvHr!YD2Qt_OPO?*}Hl~X+9{ao!8 zCmyobXKXLfoI;lMZQxzvwH%YuHrBPRJjCn6xf?N;UTTi)4{cm=joKVtDFiV2*&b{> zz_j$vHx!du)Oxt;gJml3EXg<9KWtclpG}IYQz4EUT*|k>J{|HHv*>=wIJnQd7 zp#)hpeT1PUItS{QxHXzX1|Xd2MORFU+tqhMpGMps>W3uo4U&vknX26>Vk-;on;pY98Zh6o!Iu-t zV)cpcSFmt~Pu2%cS=92}yc7yo7OP1{;t+-Z5(MPIH6P1EdU0&jhRD`NdH`|3u0p*+4iSSu=QMKK{yTx_nfM49whUv zXS}M&Wbt|SVcY$rs^xjn)nc{2+MW8HAcwv*#GyJUm;<%xps^^ih8=CY;C46fOyk&g z9r8X1jp1`1T5h_aZTk3T&9vS;Rywx?xx4XEeWn|yDuP=8od8t|QZpUw_xZ@g@lflPc2~H?v_uYy zuh27re$gGBGFRL#vv{aI!6W-UzWmz=OhcTQt5jgZ*r(-6)Tn3jT2G1mat+6?E+p*i z7ENkET{jlk#5!t!)Me)#NGcv{OfG#q=0LD|W58K*3;@|6bhgx8|(>fsTNq1l?GCezn1p;eOKtch%; z6&C}4u9g*w<-rU&W)rk+gbyNT83Sw2)Rc82gUgV$Y_hkq^;5@=R2F5#3LiIw!eAaj;%%FW-DDE32F;adOLgE9dG8bQC$(aIXJEpsURPRw22^3KHtuc7MS} zUm-A*P?j=Tn4QsI0yAXCKpeC^ApSg$YD|>m@Poe;$ZfOu7)qw+jWAX=W)K+T%dl=M zmqi#O>Tm>J8dNBB*$VNoqOf%kGs| zy3Qq$Wc*m9REkD7Z6qTsh}vDDxswrNs=xG+;FtkMT`tiJBbI7wNN7e^F3d-~3#vev zA$C_H&_1mFuokO4q?I*f;nX##KBUQ+89C~cpYCCS9K3hx*gODY7fdO%bvmlT z=ME4!Q`1O;!Jb-qdWPMX3#*+A{>n|ct=)f^Ec2*M%deOG+FC3t;T{)kYZ^aFF?|t%^JdUj=z%rpctzjR}v+%(`qJ_XUDT*=8%>eUwin5~aSXGMZ37}dM_=b=AoPBb#Qg!EOs95J)x+Ml0r3 zo~3x6cvd@7d%qpHUF?{=py@u?hb&MMfp~XsJ@7@#z47WDX^IHZ-m~G#VWbmUq1ofK zd1<`~0G?Skm^+IP@5v~S#o3DwzTk2%Y)_D#-TfL~6ebvmMaJ{BZoxrbca+(Dab*(* zA7pB3Z2^Da?2ML4*F&~;yF2blOz(lVuaTLhs{Rl$y{!<@!v#@5Az~J_3KK2djk(l$ z-Mrv63~HH4{jtvy02m#R0-L@FQkba@f4vE&y}g^+EDO6}y?353%{?0N<7*WaQ_9oj zO@2DCh7&I*=x*TDKVi!#)1RitB$lRttF^<%vACLOjX}nAg*?LHzv`W%{bIBxi`jwE z=xMSStJ$lqdBHoHn2r$z;@n0TZKH7h1&p|p*@f>xhM8>yS^UX!)esTa+x@zxxzUsm zLZ&H$LS@Wz*DpBAViYX=05&m*1AJ)1%yoL%VsZv=9@F0ibm&C?zkB|RL8UgnWnO3& zBeiYiDc4aOm(Ou%u6jjFx>srlooK%NY1pSV!(gh7>mWflN`9fU5h*ONlSl8Y9~x+D zz|mBmC@Rn6Bt+ROn^k)m4k(tc-zaEO0poNcKg`hYw`|6oe`+WBW|{ZCrTq08*m&$Z z+!dr`{#1&zFPXKUaJrEpeeM7gk6spI3wWe;&oc%+e1EK22s){22#Vubj494Phc);E&9P!M<|u zIy9ERVAU=7e+aLF`i`pCA#2Vfmy{oRc)$1#BydphY2o?~>r0`!(KmAR2nIYQJ;Z#7+In3NPnabGJFLdnG1S|ntr_;ECWi0_nDApZUzOTwn_o8 zF|PM$q0aqC5~{Z0M-MKCkfkrxh{-k=`b}G=wUsZazN+0wrvQ=4By+rk8s+lnz>=)0(y)5dNd(+D>X>7i8pQnRGC9rdyusz10E2g$Hmi=vX|7}#8=B+yvp48S+G z5lYPA0QoaD99g0UrJN{%q&CvJQAU~TxJ^2kXgph8`#XEb1i22mB`BpxExMVA;q^Uz zH2GgY`Y#tm%Gsdi@J3jh^)+J<8kwdrtJ3~4jjc2MPkjiA;haC8Gp2@_+uR9Wc>Q`; zBJt48+JZhq?#Wz`FB{<>b%WDDcL2eSW;}W$Gq?@E#a^Cmk30JGf&>+X7>d8uUZo@) zCc`?=hz|4(E{Mw9r2U4EvGIO{Q!@*zEXL>K-V?}vN=8<(&Hd@x%$ELg;A^9@5G;Vw zHpURJ|9%XD@PXNhW&j=8L1}B+!)?Wm!?B0TH7?7=e(|J?sFnq?*ftZ1VQjnup5n+2z|PZm`ia5ql>tx6l?ff5J4Yus z*!oDjgIDyIu5M8jxhW1|YoXK^|BS}Dgfh>g-zmwQ>&UJX&EZ7VJyMM#-Gq3w!ake! zObRo31h_cL+BYnbhN|Sdr9wqm)6vcYzDxrwg=df;#20PN2dA5^%u1n+BqKg;*c`Dh z)4Hdrn`ZHuKlNQZ+gE2ZZ3eer61I|0JDumM&4&mXmY8dres8bUeARc>SsQwEa&@MK zl*z;Q;ydc_CGS=`WeMAk0a4k)0jqwgm&BbQPQ!g-{xp7IT}r=dt-( z1}LT9u8Ls|fQd@1tl4{?VkR#zD4*lLSIcBLo`J}XAQ!DOG%7GuAWfiZgyRA5gO;8u z6gua?)m3sIc+O<)=kyqcpF(m=`Zsm7@2KK6B#m@9K8y3X-IfPE3o04-d^`0k4CVUK zWyMAFLhK245@u9$Tmj0y#i{%F6UjSSko#KF=T3E4-Cb<{U+sL3NKxHghKiGPElQ2}7rQ{Yc$&yOZ3+-Wb z?&xbHtZ#d`!D%~@CLc3I*RnDzE9`%EPmV2zBV0Z#(b-VcexEEHN)w%CL-B`x_1T3| zn!HE=pv`G^ris#ih+1GC*rYI14Qh0J%JLyFgHPQibonH*g3!#u=xvUYHe$}Q?A)Cl zVRL1x4Uz=h1EE93%C~?{-ZO56I(1ZTL55e{8$z^qc85L=f2p``_Bi@v5>p;UYiKP~ z@wJnXvAHAL`L+_+wv2BkwZ!2E+$#YMn&M-HxB!Fg!)Ly->r!jh2OOWNKCE@TH{Bde z*(@xxxC-TJo!KnWwl(O9U{-sYMh*&X+v9zEJabM!+_vs}vex(0`|o5z8(`)4YV2CZ z0s2qs?bQm0ek(Ww@LJoFc$v-X{bbSuJbRgUBzkUHT=^mrsovbhFmrj|@e}o%=>*?- zgf)(`;7yLTpHe|HDM-!sTsE3%a`u0ZOOa?Obpl>9v~aKmWPHy#*o8@W*?sWU}GJpQR#?>z1b=jEqWyfuI3}jM5XBZ6RbjI$yoFT1Kc}?M4eUa zbck>32GtAa?|BNztNn|1P@K3_0=h_~H&oO_J8t!Njn2C}8-ppb2*8|>`1V@-7J74i zws{g-0kQGk>O%t=D!>3M6^Q_Y-Qc|QY^8}nnR?MjT(-{Fz4Yk|$DNZ90Nh{aaf7<%<~S-ftY z-w8b0zak%yJAXgf7ZQht;04-K;_<7L;)@Vo@8x@8df)WaJW7&iy`t-VK4~mz+)FjQ z+7)V@JiyuXw}yX}W{sd!I~Ld@eOvc4^5EA%ubxMU9&8;w+GhA42oBT>Ka5fc+Enkq z_C1+gp7#A$|5RZt*|y!YlX@Pu)PY2texp8f{vJar)QM$C*`rpbCl)U1RJt6iEWZ85 zv}&2?6@fmq>mNCi29Nj3p|~ES+0qMf5~-r;m&?A(8!dprvhicdr;A`Abay_aTE?E( z0fg5)M6rk0b_pEJVYr92?6J!+4?1JgzxTec%e;BX7s8#gGMXbRDk0*uwX*tKD(;ZC z(KbBbN=5TGyiY^?7%zBsZ_~v4W|Iw4ax=oS=Y}i1hQHY2={l_kMBR+QoCzw!MbllE z+=CydqRgRpazG+ zb#r8JbOC>4JD&MV&(+QlPhYrR^SCGkj~RxAr}R8V_E(&Jtx48JO;G~n= z?+{!&KyKe9pH{@_=`ds$2;!QY{gx)EWWOgn4@`y0lOVNQAQ(W%l0ugJPfqOo_+r9&+F45PzU?@6*u!<~;@};tlw^vuJNVX1&00nS z2`4h)S;v%dDd^+6^h%aqo7cHund=SU6Uv zSNBu2HTr{8pvCJyx`2`JX5#llC!t(-rD?KS5q+k(Ry-H?z-{`s3M9tDY9$W0Ui}9sbEbu!=u@{8dip{+b+Z z4!x=ta89U4Qi*=7vzmQ1QVUEEtZHh`)tShf@qA^x-uCD)dwBQuZduzBXfV@9)^Ipo zjLN6^LB$7{B-YD*x@n@WxJNR+8+?u%3W>{gw)*Kr-{~W-a&T33uAfdaWyu9R4w)bJ zf=Ta}fZtFI*Opi%5f+u8#C)OtOe@Zv(VXB!5>o}uwv~V;Mrw(dT$BP96U=aKs=R&8 zFr38j#%}nhIE4Lvq&8%}9aRbD_jG>6OM$LlT=Tc33WjNij)Uw?R#gJtI;E(to$p~q#W(D0!>k5_K^ zJO(%uEGBNr8E$4t)%lWB;Y;~QiS?uZnnw(iJT5Pi^FMle!N zjBI2nB++2KYFNG*H@|};3dl`i3k0ndtN)*2J{X*6P1Lun1l8wGwq0SomB!fNHtn{ zkWOp2R5QWSc&juL3Aqp|8m`mDXWou{>g6wAt1uI>c9m;4Lznw`a?XM|D0W@xkD%H6 zK%k%bAjW|7=QubV3Z(7^m!)zm;;XsdGUkLJi)B1RV6oa8we(gJCXe z!ybYfG&e)Wa55|C}lWacSd$o zOa`ZqPz!!$H_&fVPW#$2vfL_ffXOJsCX1-M*>1?kY1^k9{<-K`-~nfBuzYjEad}+s zXL0&4#LvHa%&VQ>kCaom#ILHJ{@|f{_u)3e;-T-7c|6PDTds~v68ySf$XI=y8a{b} zwZ!d=S!bk{a$x~qf%X3#iPOUWD}SIr#}fa-r;{tOMJ|ySRU!GQ>bya)Wy0LE*R(~w6*sSa;uhXfi4-upE}+%K z*@JfiN%X$6qcwyKxlM^3mm_)?OcLrKZysu$D(-0`_|^#I8oG$-JEU*0sHypkw$zHc zmtRFBEc5sE>$=%_-|MG|67&0HH64q(bjMn!GHtl6d=dJ-YTS9s_^hWwlsop8{vkF1N|GA6&^7j+ib-%8-)7QC^n};Ow zh939Nyr548*~v;m*j#S{Pq#ny6Fl!wvBOw>tug#tK{naW2Ql@`fNkfDFEBLEW`dK;A(6_HNE3YLe zBhg6ZhykW&`1n1spxQrL`1nu2=-=VTNc4RGNfZcW)ol#=Q3wm=+gCJXdoal069bUYhyg`^1((=2P2OFfs;7xK>& zH=K>hPXd92YZHev6*(S99cYU+Z~9UFmRdZE0E-sa*bI+R_isN`G?|U-=Z~H@XCRt< zb`OiQULp+$1!~O>%-wxq{s?t|oqO(!vF~^K{lo0KWuN(NNfGapJ|^Epk&u2WzAq~6 zwMLO*0;-5VR=X0JiYWqqKET;W7)Ra0`zQiniyQ~^Yx}#$n70cT1t)Onmo_=?<^ix^|BTh7XS~KzqXmd~-2&jpMzt%TfZp`dM>@HL z!22*}1+-TFU~yG@Q{Gdy0ZK`uxax0shi0z5JS44d1#&m+`i{ z6M4G&+#REqsHg--kwd0rW7tWurT3L<8yzmQFA&ZS_E`h0pRfM zzM{<4^SK7Xn;c9YLw*a%`7LJn5IyYRQ+f4*_Y9ua#MKi$)V@}`AiH*C3&5a(6L+c$ zM6Tl1rXwr!+bn1%7i4o-OeswOIQ*i#DE8cygQb>kH zij?*cP@8I8=xkDpJb+UG`08D&XbY`Zt5`J->+c5g0hO*FjL`D7lI)}&qZWaaNUwxa zGix{eZVddDb(rjo8R&zQI)YIA$#=Z-bmoD_s(Qd5rE0`CyTXCxoTxp zSdD8(7LBHUtLE_e$YkFSriltlZ9F2?6II-*n8UUQz02Kc<&+>-_wO0bQ(7JXWFP}G z!vGa~nkmgGMnK}}PMX(ApNo3y2Q#oTfN{2pn<)j7!m9!vl~d z&vL!cFO-wGU#Fyec*bRwaJD}`z2trZQ@2Y7bW7nZW$AI3B|^{(`**UWHBv!cmU?->UzwZ=4QHg*dR_w5A0Gp35dLU*Q;#;g`{N-cb8zRX z7?U`h78|n*uZpZNbOg+(efO1i6^v&h3{wggC6B)b@ReI_c$sF!pLL@P0vA0pB1{D# zco;9GZ|ZB|`?K+Lz}}kG6G>ghBSpK2A7rLY?R^#vMXdPaxtJ8G7n#D7c%Y-zHj8uX zSs9K${HZr%KQycK7=ak5o^4s)F~$vt1rK}p@k9hD{N(G4Fbo?6g5Gzz_F`Uf|3x!X zrXi(@{w~6xoC6Mk0Slu^`nWCu;QdM41dMxj?%xe=%Pb}fIUjzom7=fZ#KH-}cIep| zdI(jfbVfc9MTT9g=-hpc6zAk!w|E!$SsmW>PoPW%>~MjORzcW!CW{s6-Pg`GzW0^n!7zbUdD437cX_ zV>*KkE3k;!?P@n1z%Yv8^+bJ;so8C4i=WBb%c%B43o*|NCrX0+XQ7Q0Cw~4wz`da) zdC6jX-`(|QMk#taGz0>wa|02ro~ycOJA=Dcg83tKu8`Fb0Ax%B`QF+0_}7WW!+2qU zMLjNt5CbA;={X_DJswK8HH;Tks^2DFn50Wj$G(#3rvJ_V@gT~**!hH7sn5T}V;ws_ z5=1R0?&X1R%rH@;M}1gmUWSP*^H1x*XXuo|&6nsMwqL*^nC8imA>kor9(0_7cchXg z_$N2>g>aToKPBBKv(ik$5hy9$zoO%e4a!AC}n$xnL zStlwqIc&=up4it?6Y=|c{)^u~a9{WRxvu-Z?$7JK-wn19h=fg8-?h)29D23D_LmA> zoxl#lOd+akbu_QxCknfd2in@9wZf@w(zrS+t(JH(tiU-ItPAJeym2IFu+Ft6qV1Wz z7BGnjntfkq^qHw5=BDW~*V<;u1MJ=RbeWHJW`S zflBK~O+zNM4~`G5jRI#&%necQ(y!DGN&lG(Wi+q|qR@5lKpPzS5CZyE=mo5<4Fi2t zJ+7CH*o|(alQ~le#X(;IvNg(nm9k{e>DFTziirezVVA(2s!NU5*n2@lI)4Y7a$t(X&c8`8{YKwMiey z?GKKYc5EL4vT$qUSJd~@(<&60asV6HM5DeNpCQ+lUZ)YV6kA;WOnVfjV`+MtLs&h& zEq0Jd3eKhMkVaxGIk~%mB!|@~8bS6&_sF zq+3-#^llBwm;L~sl@;>W#A>*Dd50$<9}Yj2WZWiSgN%2b!1eOtK*58FxlPqsNSrvg zK$xE4dtR28DV$6dz<0D=T&9#l)YzKFprW3LwU!kkUA^nF*!CCh@TUjKQ8lqk4m zl$`~GSW_Iox`^ilIXfx@z6XIP{b6d$B{w5HCs!{a`z|#5B}Z34JFm^3S^h+)t+SD> zJSg|#ANFRLf!cSyVmSd?i&dW==Kh)4D2$fGV8%|!_IAC&t@CmZ>xGY@qT*7WuL<3A zz)aWBT7*PpFBHc*l63p`$U7*#l16OE7|JJZrrPr1^B9wr`z)ID6qXz0ncE16R%-M0 z60o0@&WY#3EXK%JD?h|&XQlDW!&XQ3QM-#pwI}BGOm8Ci9ytk%QQir6id0m_afH?m zmwmjVTob0VUjaphN?+&HqRqE7dYu|!hdG*NIUss=Ser5u5nryXXn-?GP?it!ygwF{ z%)rL~3?`=i>rgrdw#_CNC8b8n&inJfCmgpQY6u=Qv$BTA>E3ts}wN8Dh2 zNA&@E>AqEqc6wMa&Gq`DpE6NhKyLrV0@C)ie}&o&3m)~vKAK3RuuE0Tex(bP;YU5B zx&6a-lPTSA>pf9JDjo3MPCf$@4~%DzqYAVA8Rc?XR@bYpW)>3hNNK^7GxQ9kQ&}<+ z$coe3$a%??C{BI7sgHxtjCXbl?J$#126V9fw&Ff>wEkHjGNnA)rl?AYhv6D-HAhN+ z1q7kOH%&bgZf!oy22z}jHonAf{*MN=2=BcrZ)JiCe9f5oot@!_6`-;!l?kIs@>p>> zl{L6o;l}*v2&;w`qd-x*6N_dyYgmPTJbGSR<@#@RKIYHl@>Scrh{+;>(JsEk=HQx_ z{5Y1?0Lq`hYIRFbwXjJed|U zeG|m;5k$~yCFCz1Z^)u&UW8w%xRO{TE8b(=C-wx|(>$Hz7Q9lA^mU|azpog!lR&6Z zh<}M0%pR1Qj}Jx0{TI7e0Bj|N51QE~YTB*t*UQI&iKIX7J}5|Cg5)5SO`0yF2zz9c z4Qght+xzYHdFK2iC$ifYx*6I3%g>xKd@7Sfi|I3i&k&~i6)^Nq<3DboQA!R4nK^7! z3Bz{LxPP()Jz6H&{=+s)r-E1{VZo@)dHp-sy=uaXQch7VX+eG#ekQY}J&o&)mMi>wD@&ju#O)O49CUxYcHrfR@rb%0tcq*uvH;&*cXF zdla-4Tx3Vjo|oB>m;vy$wVfhA`y|hUi!u90|8|IZuV*a4yafPrG)}bb`74K+f7;Mm zocOQhbm1f_<9f#=`Miv_tbQvqGFdaE9a$M~Nh>v7h?=zI+Cx4~JSYk0YFO9OGfTny zxhsFD?3^ue_U(_S!UVe-;##LkNJM)TrxO5W$-&PLxBdCq)5 zRA>LUT5qDoF^Bpguq~tii&l4IbC4DcN*<`6+?q51!iDq96I!s&w!B>+4{#sUy>PLq zhz8f;iVo8*-X<3SB175ZYau3XDu)mtCgeGJ-#M6kVg#g$8e*~6PPHwkH#DPqQ2l=T gewY8Jp_A4XpU{#+N?qc Date: Tue, 28 Jul 2026 10:11:44 -0700 Subject: [PATCH 4/7] whitespace --- .../scene/control/richtext/model/ImageFormatHandler.java | 1 - .../scene/control/richtext/TestImageFormatHandler.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java b/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java index 149ab0357bf..3d030d222d0 100644 --- a/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java +++ b/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java @@ -75,7 +75,6 @@ byte[] toByteArray(Image im) throws IOException { return lossless ? RichUtils.writePNG(im) : RichUtils.writeJPG(im); - } }; } diff --git a/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java b/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java index d455bd0c8cf..5dba9f5e7fa 100644 --- a/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java +++ b/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java @@ -70,7 +70,7 @@ private enum Type { JPG, PNG } private static final byte[] JPG_SIGNATURE = mkSignature("ffd8ffe1"); private static final byte[] PNG_SIGNATURE = mkSignature("89504e47"); private RichTextArea rta; - + @BeforeEach public void beforeEach() { rta = new RichTextArea(); From cec7b49d45070838f216fd1dc8436acae4e82da7 Mon Sep 17 00:00:00 2001 From: Andy Goryachev Date: Fri, 31 Jul 2026 14:30:39 -0700 Subject: [PATCH 5/7] lossless --- .../richtext/model/ImageFormatHandler.java | 133 +----------------- .../richtext/TestImageFormatHandler.java | 61 ++------ .../system/src/test/java/test/util/Util.java | 14 +- .../sun/javafx/images/banana-slug-small.jpg | Bin 1274 -> 0 bytes .../sun/javafx/images/underlines-small.png | Bin 2279 -> 0 bytes 5 files changed, 27 insertions(+), 181 deletions(-) delete mode 100644 tests/system/src/test/resources/test/com/sun/javafx/images/banana-slug-small.jpg delete mode 100644 tests/system/src/test/resources/test/com/sun/javafx/images/underlines-small.png diff --git a/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java b/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java index 3d030d222d0..628397c1c65 100644 --- a/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java +++ b/modules/jfx.incubator.richtext/src/main/java/jfx/incubator/scene/control/richtext/model/ImageFormatHandler.java @@ -28,7 +28,6 @@ import java.io.IOException; import java.io.OutputStream; import javafx.scene.image.Image; -import javafx.scene.image.PixelReader; import javafx.scene.input.DataFormat; import com.sun.jfx.incubator.scene.control.richtext.EmbeddedImageHelper; import com.sun.jfx.incubator.scene.control.richtext.SegmentStyledInput; @@ -38,12 +37,12 @@ /** * Facilitates importing of images into the RichTextModel. + * The image is imported via lossless compression (PNG). + * * @since 28 */ public class ImageFormatHandler extends DataFormatHandler { - private static final int SMALL_SIZE = 128; - private static final int SMALL_CHANGE_THRESHOLD = 130050; // 1/8th of full scale private static final ImageFormatHandler instance = new ImageFormatHandler(); /** @@ -61,24 +60,6 @@ public static final ImageFormatHandler getInstance() { return instance; } - /** - * Creates an ImageFormatHandler instance which imports images using either - * a lossless (PNG) or lossy (JPEG) encoding. - * - * @param lossless determines whether to create a lossless handler - * @return the instance - */ - public static final ImageFormatHandler create(boolean lossless) { - return new ImageFormatHandler() { - @Override - byte[] toByteArray(Image im) throws IOException { - return lossless ? - RichUtils.writePNG(im) : - RichUtils.writeJPG(im); - } - }; - } - /** * {@inheritDoc} * @@ -89,11 +70,7 @@ public StyledInput createStyledInput(Object input, StyleAttributeMap attr) throw Image im = (Image)input; double w = im.getWidth(); double h = im.getHeight(); - byte[] b = toByteArray(im); - if ((b == null) || (b.length == 0)) { - // TODO there seems to be a bug writing certain images to JPG - throw new IOException("Failed to store image"); - } + byte[] b = RichUtils.writePNG(im); EmbeddedImage em = EmbeddedImageHelper.create(b, w, h, EmbeddedImage.AUTO, EmbeddedImage.AUTO, true); StyleAttributeMap a = StyleAttributeMap.of(StyleAttributeMap.EMBEDDED_IMAGE, em); return new SegmentStyledInput(StyledSegment.of(" ", a)); @@ -108,108 +85,4 @@ public Object copy(StyledTextModel m, StyleResolver r, TextPos start, TextPos en public void save(StyledTextModel m, StyleResolver r, TextPos start, TextPos end, OutputStream out) throws IOException { throw new UnsupportedOperationException(); } - - byte[] toByteArray(Image im) throws IOException { - boolean isPhoto = detectPhoto(im); - RichUtils.log("photo={0} fmt={1}", isPhoto, im.getPixelReader().getPixelFormat()); - - // FIX storing as jpg is broken, see JDK-8388450 - isPhoto = false; - - return isPhoto ? - RichUtils.writeJPG(im) : - RichUtils.writePNG(im); - } - - private static int diff(int a, int b) { - int d = diffChannel(a, b); // r - a >>= 8; - b >>= 8; - d += diffChannel(a, b); // g - a >>= 8; - b >>= 8; - d += diffChannel(a, b); // b - return d; - } - - private static int diffChannel(int a, int b) { - int d = (a & 0xff) - (b & 0xff); - return d < 0 ? -d : d; - } - - private boolean detectPhoto(Image im) { - int w = (int)im.getWidth(); - int h = (int)im.getHeight(); - - // small image -> png - if ((w < SMALL_SIZE) && (h < SMALL_SIZE)) { - return false; - } - - PixelReader rd = im.getPixelReader(); - - // TODO we could check the presence of alpha channel, but JDK-8388511 - - int parts = 4; - int stepx = w / parts; - int stepy = h / parts; - int x; - int y; - - int same = 0; - int jump = 0; - - x = stepx; - for (int ix = 1; ix < parts; ix++) { - int prev = 0; - for (y = 0; y < h; y++) { - int argb = rd.getArgb(x, y); - - // alpha - if((argb & 0xff000000) != 0xff000000) { - // alpha channel -> png - return false; - } - - if (prev == argb) { - same++; - } else { - int diff = diff(argb, prev); - if (diff < SMALL_CHANGE_THRESHOLD) { - jump++; - } - } - prev = argb; - } - x += stepx; - } - - y = stepy; - for (int iy = 1; iy < parts; iy++) { - int prev = 0; - for (x = 0; x < w; x++) { - int argb = rd.getArgb(x, y); - - // alpha - if((argb & 0xff000000) != 0xff000000) { - // alpha channel -> png - return false; - } - - if (prev == argb) { - same++; - } else { - int diff = diff(argb, prev); - if (diff < SMALL_CHANGE_THRESHOLD) { - jump++; - } - } - prev = argb; - } - y += stepy; - } - - // more small changes than same color pixels -> jpg - return jump > same; - } } diff --git a/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java b/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java index 5dba9f5e7fa..ecf4148fcd6 100644 --- a/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java +++ b/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java @@ -35,14 +35,12 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HexFormat; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import javafx.scene.image.Image; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import com.sun.jfx.incubator.scene.control.richtext.EmbeddedImageHelper; import com.sun.jfx.incubator.scene.control.richtext.SegmentStyledOutput; @@ -62,13 +60,8 @@ // Tests ImageFormatHandler public class TestImageFormatHandler extends RobotTestBase { - private enum Size { SMALL, LARGE } - private enum Type { JPG, PNG } - private static final int SMALL_SIZE = 128; // ImageFormatHandler.SMALL_SIZE - + private static final byte[] PNG_SIGNATURE = Util.hexToByteArray("89504e47"); private static final ImageFormatHandler handler = ImageFormatHandler.getInstance(); - private static final byte[] JPG_SIGNATURE = mkSignature("ffd8ffe1"); - private static final byte[] PNG_SIGNATURE = mkSignature("89504e47"); private RichTextArea rta; @BeforeEach @@ -78,24 +71,13 @@ public void beforeEach() { } @Test - public void smallJpg() { - test(Size.SMALL, Type.JPG, Type.PNG); - } - - @Test - public void smallPng() { - test(Size.SMALL, Type.PNG, Type.PNG); + public void processJpg() { + test("banana-slug.jpg"); } - @Disabled("JDK-8388450") // TODO @Test - public void largeJpg() { - test(Size.LARGE, Type.JPG, Type.JPG); - } - - @Test - public void largePng() { - test(Size.LARGE, Type.PNG, Type.PNG); + public void processPng() { + test("underlines.png"); } @Test @@ -124,23 +106,7 @@ private static List load(StyledInput in) { return ss; } - private static String getName(Size size, Type source) { - return switch(size) { - case LARGE -> - switch(source) { - case JPG -> "banana-slug.jpg"; - case PNG -> "underlines.png"; - }; - case SMALL -> - switch(source) { - case JPG -> "banana-slug-small.jpg"; - case PNG -> "underlines-small.png"; - }; - }; - } - - private void test(Size size, Type source, Type expectedType) { - String name = getName(size, source); + private void test(String name) { String url = TestData.getURI(name); Image im = new Image(url); StyledInput in = null; @@ -160,7 +126,7 @@ private void test(Size size, Type source, Type expectedType) { assertNotNull(em); byte[] b = EmbeddedImageHelper.getBytes(em); assertNotNull(b); - assertSignature(expectedType, b); + assertSignature(b); // let's see if it pastes Util.runAndWait(() -> { @@ -195,15 +161,10 @@ private void test(Size size, Type source, Type expectedType) { }); } - private void assertSignature(Type expected, byte[] bytes) { + private void assertSignature(byte[] bytes) { assertNotNull(bytes); - byte[] exp = (expected == Type.JPG) ? JPG_SIGNATURE : PNG_SIGNATURE; - assertTrue(bytes.length > exp.length); - byte[] b = Arrays.copyOf(bytes, exp.length); - assertArrayEquals(exp, b, "wrong image signature, expected " + expected); - } - - private static byte[] mkSignature(String hexPattern) { - return HexFormat.of().parseHex(hexPattern); + assertTrue(bytes.length > PNG_SIGNATURE.length); + byte[] b = Arrays.copyOf(bytes, PNG_SIGNATURE.length); + assertArrayEquals(PNG_SIGNATURE, b, "wrong image signature"); } } diff --git a/tests/system/src/test/java/test/util/Util.java b/tests/system/src/test/java/test/util/Util.java index baaf6c5481b..4084bd35e63 100644 --- a/tests/system/src/test/java/test/util/Util.java +++ b/tests/system/src/test/java/test/util/Util.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ import java.io.IOException; import java.net.URL; import java.util.ArrayList; +import java.util.HexFormat; import java.util.Iterator; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -464,4 +465,15 @@ public static boolean isOnWayland() { public static boolean isNear(double a, double b) { return Math.abs(a - b) < 0.1; } + + /** + * Parses hex string into a byte array. + * Example: "0001" -> byte[] { 0x00, 0x01 } + * No spaces or symbols other than upper/lower case hex digits is allosed. + * @param hexPattern hex string + * @return the byte array + */ + public static byte[] hexToByteArray(String hexPattern) { + return HexFormat.of().parseHex(hexPattern); + } } diff --git a/tests/system/src/test/resources/test/com/sun/javafx/images/banana-slug-small.jpg b/tests/system/src/test/resources/test/com/sun/javafx/images/banana-slug-small.jpg deleted file mode 100644 index 896d5b515440fd11855a2aa56d5216da8e7d36ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1274 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 Date: Fri, 31 Jul 2026 14:34:21 -0700 Subject: [PATCH 6/7] images --- .../sun/javafx/images/{TestData.java => Images.java} | 12 +++++++++--- .../control/richtext/TestImageFormatHandler.java | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) rename tests/system/src/test/java/test/com/sun/javafx/images/{TestData.java => Images.java} (81%) diff --git a/tests/system/src/test/java/test/com/sun/javafx/images/TestData.java b/tests/system/src/test/java/test/com/sun/javafx/images/Images.java similarity index 81% rename from tests/system/src/test/java/test/com/sun/javafx/images/TestData.java rename to tests/system/src/test/java/test/com/sun/javafx/images/Images.java index 2f1b07de6bd..05332e65e00 100644 --- a/tests/system/src/test/java/test/com/sun/javafx/images/TestData.java +++ b/tests/system/src/test/java/test/com/sun/javafx/images/Images.java @@ -27,9 +27,15 @@ import java.net.URL; -public class TestData { - public static String getURI(String name) { - URL u = TestData.class.getResource(name); +/// Provides test images. +public class Images { + /** + * Returns the test image URL to pass to the {@code Image} constructor. + * @param name resource name + * @return the URL string + */ + public static String getURL(String name) { + URL u = Images.class.getResource(name); return u == null ? null : u.toExternalForm(); } } diff --git a/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java b/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java index ecf4148fcd6..91b3bac1625 100644 --- a/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java +++ b/tests/system/src/test/java/test/jfx/incubator/scene/control/richtext/TestImageFormatHandler.java @@ -53,7 +53,7 @@ import jfx.incubator.scene.control.richtext.model.StyledInput; import jfx.incubator.scene.control.richtext.model.StyledSegment; import jfx.incubator.scene.control.richtext.model.StyledTextModel; -import test.com.sun.javafx.images.TestData; +import test.com.sun.javafx.images.Images; import test.robot.testharness.RobotTestBase; import test.util.Util; @@ -107,7 +107,7 @@ private static List load(StyledInput in) { } private void test(String name) { - String url = TestData.getURI(name); + String url = Images.getURL(name); Image im = new Image(url); StyledInput in = null; try { From 1c4471d137c2ddd1abb13fbfd7c246584def8f16 Mon Sep 17 00:00:00 2001 From: Andy Goryachev Date: Thu, 13 Aug 2026 09:39:57 -0700 Subject: [PATCH 7/7] copied from swing fx utils --- .../scene/control/richtext/util/ImgUtil.java | 25 +++++++++++-------- .../control/richtext/util/RichUtils.java | 17 ++++++++----- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/ImgUtil.java b/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/ImgUtil.java index 55d6ed2d2c6..26300363c25 100644 --- a/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/ImgUtil.java +++ b/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/ImgUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -153,6 +153,9 @@ public static WritableImage toFXImage(BufferedImage bimg, WritableImage wimg) { return bimgType; } } + if (isOpaque) { + return BufferedImage.TYPE_INT_RGB; + } switch (fxFormat.getType()) { default: case BYTE_BGRA_PRE: @@ -198,10 +201,12 @@ public static WritableImage toFXImage(BufferedImage bimg, WritableImage wimg) { } private static boolean checkFXImageOpaque(PixelReader pr, int iw, int ih) { - for (int x = 0; x < iw; x++) { - for (int y = 0; y < ih; y++) { - Color color = pr.getColor(x,y); - if (color.getOpacity() != 1.0) { + int[] pixels = new int[iw]; + WritablePixelFormat format = PixelFormat.getIntArgbPreInstance(); + for (int y = 0; y < ih; y++) { + pr.getPixels(0, y, iw, 1, format, pixels, 0, iw); + for (int pixel : pixels) { + if ((pixel >>> 24) != 0xff) { return false; } } @@ -251,11 +256,11 @@ public static BufferedImage fromFXImage(Image img, BufferedImage bimg) { case INT_ARGB: case BYTE_BGRA_PRE: case BYTE_BGRA: - // Check fx image opacity only if - // supplied BufferedImage is without alpha channel - if (bimg != null && - (bimg.getType() == BufferedImage.TYPE_INT_BGR || - bimg.getType() == BufferedImage.TYPE_INT_RGB)) { + case BYTE_INDEXED: + boolean opacityMatters = bimg == null || + bimg.getType() == BufferedImage.TYPE_INT_BGR || + bimg.getType() == BufferedImage.TYPE_INT_RGB; + if (opacityMatters) { srcPixelsAreOpaque = checkFXImageOpaque(pr, iw, ih); } break; diff --git a/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/RichUtils.java b/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/RichUtils.java index c928d2c653b..68033a0f0c0 100644 --- a/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/RichUtils.java +++ b/modules/jfx.incubator.richtext/src/main/java/com/sun/jfx/incubator/scene/control/richtext/util/RichUtils.java @@ -25,6 +25,7 @@ package com.sun.jfx.incubator.scene.control.richtext.util; +import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -434,16 +435,20 @@ public static String readString(InputStream in) throws IOException { return sb.toString(); } - private static byte[] writeImage(Image im, String format) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(65536); - // using disk cache slows things down - boolean old = ImageIO.getUseCache(); - ImageIO.setUseCache(false); try { - ImageIO.write(ImgUtil.fromFXImage(im, null), format, out); + // using disk cache slows things down + boolean old = ImageIO.getUseCache(); + ImageIO.setUseCache(false); + try { + BufferedImage bi = ImgUtil.fromFXImage(im, null); + ImageIO.write(bi, format, out); + } finally { + ImageIO.setUseCache(old); + } } finally { - ImageIO.setUseCache(old); + out.close(); } return out.toByteArray(); }