Sun.awt.image Exception

Ich habe ein Programm, das lange Zeit unter java-8 lief. Ich wollte jetzt das Programm unter openJDK19 laufen lassen.

Leider bekomme ich eine Exception:

Exception in thread „AWT-EventQueue-0“ java.lang.IllegalAccessError: class core.AttachmentManager (in unnamed module @0x5b03fd8f) cannot access class sun.awt.image.BufferedImageGraphicsConfig (in module java.desktop) because module java.desktop does not export sun.awt.image to unnamed module @0x5b03fd8f
at core.AttachmentManager.createCompatibleImage(AttachmentManager.java:522)
at core.AttachmentManager.addAttachment(AttachmentManager.java:144)
at control.MainControl.addAttachment(MainControl.java:727)
at view.ToolBar$10.actionPerformed(ToolBar.java:169)

Wenn ich das richtig verstehe ist das ein Problem mit der Modularisierung von Java. Ich müsste herausfinden in welchem Modul das Package sun.awt.image liegt und dann das entsprechende Modul in module-info.java eintragen, oder?

Hier ist der Code-Abschnitt in dem die Exception auftritt:

‚‘’
private static BufferedImage createCompatibleImage(BufferedImage image) {

	GraphicsConfiguration gc = BufferedImageGraphicsConfig.getConfig(image);
	
	int w = image.getWidth();
	int h = image.getHeight();
	
	BufferedImage result = gc.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
	Graphics2D g2 = result.createGraphics();
	g2.drawRenderedImage(image, null);
	g2.dispose();
	
	return result;
	
	
}

‚‘’
In der Zeile:

GraphicsConfiguration gc = BufferedImageGraphicsConfig.getConfig(image);

tritt die Exception auf.

Da kommen mehrere Dinge zusammen.
Ja das hängt mit der Modularisierung zusammen. Du müsstest dem Modul java.desktop sagen dass das entsprechende Package für dein Modul geöffnet wird. Da ist das Problem dass dein Modul kein Namen hat, also du hast keine module-info.java.

Dann ist das auch gar nicht so „einfach“ es geht, also man kann beim Kompilieren und Starten sagen, fremde Module bestimmte Sachen öffnen oder exportieren sollen aber das ist denke ich keine schöne Lösung.

Ich sehe eher das Problem, dass du ein sun Package verwendest. Das sollte man nie machen und einen Grund dafür merkst du gerade.

Ich weiß jetzt nicht wofür du diese Klasse brauchst und ob es anders geht.

Ich dachte ich hätte eine module-info.java

Die liegt im Root Verzeichnis des Projekts.
Darin steht:

requires java.desktop;

Ach so, ich bin davon ausgegangen wegen dem „unnamed module“. Die Klasse core.AttachmentManager ist von dir, aus dem gleichen Projekt?

Ja,
AttachmentManager

ist von mir aus dem gleichen Projekt.

Dann gäbe es die Möglichkeit beim Kompillieren und beim Ausführen das ganze per --add-exports java.desktop/sun.awt.image=ALL-UNNAMED zu versuchen, aber da kann ich auch nicht garantieren dass es klappt

Ich weiß jetzt nicht genau wo ich dieses Argument eintragen soll.

Ich verwende Eclipse. Dort gibt es die Möglichkeit unter:

run - run Configurations - Arguments - Program arguments / VM arguments

Argumente anzugeben.

Wird das dort eingetragen?

Ja, müssten VM Arguments sein wenn ich das richtig sehe. Habe ewig nicht mehr Eclipse eingesetzt und noch länger her dass ich ohne Maven/Gradle gearbeitet habe

Ja, als VM Argument funktioniert es. Vielen Dank.

Sehr gut. Aber wie gesagt, du solltest schauen, das ganze ohne diese Klasse zu machen, da die Sun Pakete auch einfach verschwinden können

Mir stellt sich im Moment eher die Frage, warum du die Klasse überhaupt verwendest. Wenn ich das richtig sehe, soll, diese Methode ja in erster Linie „das Bild kopieren“, und da könnte man auch

private static BufferedImage createCompatibleImage(BufferedImage image) {
	int w = image.getWidth();
	int h = image.getHeight();
	
	BufferedImage result = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
	Graphics2D g2 = result.createGraphics();
	g2.drawRenderedImage(image, null);
	g2.dispose();
	
	return result;
}

verwenden. Falls das irgendwo „nicht funktioniert“ oder einen Nachteil hat, wäre ich gespannt, welcher das sein sollte…

Ich habe mir das damals zusammengegoogled.

Letztendlich geht es darum von einem beliebig großen .png Bild ein Thumbnail zu erstellen.

Da gibt’s natürlich 1000 Freiheitsgrade. Das, was du bisher gepostet hast, macht ja eine „Kopie“ des Bildes. Wenn es nur darum geht, einen Thumbnail zu erstellen, brauch man ja nicht das komplette Bild nochmal in ein neues, gleichgroßes Bild zu zeichnen. Je nachdem, wie „schön“ der Thumbnail sein soll, gibt es auch verschiedene Möglichkeiten. Aber eine einfache und effiziente Möglichkeit, einen Thumbnail zu erstellen, ist das hier:

package bytewelt;

import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class ImageThumbnail
{
    public static void main(String[] args) throws IOException
    {
        BufferedImage image = ImageIO.read(new File("./data/image.png"));
        BufferedImage thumbnail = createScaledInstance(image, 100);
        ImageIO.write(thumbnail, "png", new File("./data/thumbnail.png"));
        
    }
    private static BufferedImage createScaledInstance(
        BufferedImage image, int w)
    {
        double scale = (double) w / image.getWidth();
        int h = (int) (scale * image.getHeight());
        return createScaledInstance(image, w, h);
    }

    private static BufferedImage createScaledInstance(
        BufferedImage image, int w, int h)
    {
        BufferedImage scaledImage = new BufferedImage(w, h, image.getType());
        double scaleX = (double) w / image.getWidth();
        double scaleY = (double) h / image.getHeight();
        AffineTransform affineTransform =
            AffineTransform.getScaleInstance(scaleX, scaleY);
        AffineTransformOp affineTransformOp = new AffineTransformOp(
            affineTransform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
        return affineTransformOp.filter(image, scaledImage);
    }

}

Ansonsten hatte ich dazu z.B. in Java - resize image without losing quality - Stack Overflow und java - How to improve the performance of g.drawImage() method for resizing images - Stack Overflow mal was zu Qualität und Performance geschrieben…

(BTW: Wenn du auf dem Bild, das du da erstellst, dann getScaledInstance aufrufst, um den Thumbnail zu erstellen: Nein, mach’ das nicht…)

Vielen Dank für deine ausführlichen Infos. Ich bleibe im Moment noch bei meiner Version. Die Bildqualität ist ganz gut und das Erstellen dauert auch nicht so lange.