Bonjour,
J'ai un JDesktopPane dans lequel j'affiche des fenetres (extends JInternalFrame) qui ne contiennent qu'une image (BufferedImage) d'un décor (un meuble par exemple) et un menu contextuel qui permet de réduire/augmenter leur taille.
Les images sont des PNG avec de la transparence.
J'ai donc :
- Une classe Decor qui contient :
. l'image de base du décor
. l'échelle d'affichage de son image
- Une classe FenetreSceneDecor qui "extends" JInternalFrame et qui contient
. le décor à afficher
. l'image à afficher : cette image est une copie de l'image du décor après un "resize" si l'échelle demandée est différente de 1.0
. la méthode paint redéfinie qui fait un simple drawimage(image, 0, 0, null) de l'image calculée avec l'échelle
. la méthode reGenererImage qui recalcule l'image à afficher en fonction de l'échelle et qui est appelée depuis mon menu contextuel
Or j'ai 2 problèmes :
1/ Lorsque je déplace la fenêtre, c'est lent et le rafraichissement est mauvais (saccadé)
2/ Le redimensionnement de l'image amène à une dégradation de la qualité de l'image. J'ai trouvé 3 méthodes différentes mais chacune a son propre inconvénient soit sur la transparence soit sur la finesse de l'image obtenue.
Vous trouverez ci-dessous une version simplifiée du code de ma classe FenetreSceneDecor ainsi que le code des 3 méthodes de redimensionnement que j'ai trouvé et implémenté et à côté de chacune les problèmes rencontrés.
Je met également quelques copies d'écran pour illustrer le problème.
nb : l'image en exemple un fichier PNG de taille 649x663 pour 998 ko.
Code de ma classe principale :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 public class FenetreSceneDecor extends FenetreScene { private BufferedImage image; private Decor decor; public void paint(Graphics g) { Graphics2D g2d = (Graphics2D)g; g2d.drawImage(image, 0, 0, null); } private void reGenererImage(int largeur, int hauteur) { // METHODE 1 image = OutilsImage.getImageRetaillée1(decor.getImage(), (int)(decor.getLargeur()*decor.getScale()), (int)(decor.getHauteur()*decor.getScale())); // METHODE 2 //image = OutilsImage.getImageRetaillée2(decor.getImage(), decor.getScale()); // METHODE 3 //image = OutilsImage.getImageRetaillée3(decor.getImage(), (int)(decor.getLargeur()*decor.getScale()), (int)(decor.getHauteur()*decor.getScale())); } }
Pièce jointe : "origine.jpg"
Cette image montre l'application avec le JDesktopPane et une instance de la classe FenetreSceneDecor qui affiche l'image (BufferedImage) d'une hutte vue de haut. L'image de la hutte a de la transparence (le marron sur les coins est la couleur de fond du JDesktopPane) et est à l'échelle 1.
=> la qualité de l'image est nickel (finesse et transparence) : ok
=> le deplacement de la fenetre est lent et saccadé : ko
Méthode 1 :
=> perte de la transparence (noir) : ko
=> finesse de l'image après redimensionnement : ok
Illustration : pièce jointe "1_diminuer.jpg"
Méthode 2 :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 public static BufferedImage getImageRetaillée1(BufferedImage image, int largeur, int hauteur) { BufferedImage imageToSave = new BufferedImage(largeur, hauteur, BufferedImage.TYPE_INT_BGR); Graphics2D g2d = imageToSave.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.drawImage(image, 0, 0, largeur, hauteur, null); g2d.dispose(); return imageToSave; }
=> perte de la transparence (on retrouve le dessin d'origine) : ko
=> finesse de l'image après redimensionnement : ok
Illustration : pièce jointe "2_diminuer.jpg"
Méthode 3 :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 public static BufferedImage getImageRetaillée2(BufferedImage image, double scale) { AffineTransform tx = new AffineTransform(); tx.scale(scale, scale); AffineTransformOp op = new AffineTransformOp(tx,AffineTransformOp.TYPE_BICUBIC); int type = image.getType() == 0? BufferedImage.TYPE_INT_BGR : image.getType(); BufferedImage newImage = new BufferedImage((int)(image.getWidth(null)*scale), (int)(image.getHeight(null)*scale), type); return op.filter(image, newImage); }
=> transparence : ok
=> image très floue après redimensionnement : ko
Illustration : pièce jointe "3_diminuer.jpg"
Cela fait 2 jours que je me bagarre avec ça, je ne suis pas du tout doué dans la manipulation des images :-(
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public static BufferedImage getImageRetaillée3(BufferedImage image, int width, int height) { image = createCompatibleImage(image); image = resize(image, 100, 100); image = blurImage(image); image = resize(image, width, height); return image; } 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; } private static BufferedImage blurImage(BufferedImage image) { float ninth = 1.0f/9.0f; float[] blurKernel = { ninth, ninth, ninth, ninth, ninth, ninth, ninth, ninth, ninth }; Map<Key, Object> map = new HashMap<Key, Object>(); map.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); map.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); RenderingHints hints = new RenderingHints(map); BufferedImageOp op = new ConvolveOp(new Kernel(3, 3, blurKernel), ConvolveOp.EDGE_NO_OP, hints); return op.filter(image, null); } private static BufferedImage resize(BufferedImage image, int width, int height) { int type = image.getType() == 0? BufferedImage.TYPE_INT_ARGB : image.getType(); BufferedImage resizedImage = new BufferedImage(width, height, type); Graphics2D g = resizedImage.createGraphics(); g.setComposite(AlphaComposite.Src); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.drawImage(image, 0, 0, width, height, null); g.dispose(); return resizedImage; }
Si quelqu'un a des suggestions d'amélioration, merci d'avance :-)
<EDIT>24/08 : J'ai retiré les pièces jointes car j'ai besoin de place pour en mettre d'autres sur un autre thread</EDIT>
Partager