Vous devez être membre et vous identifier pour publier un article.
Les visiteurs peuvent toutefois commenter chaque article par une réponse.

Swing - Comment faire pivoter une image en Swing?

Astuces de l’Infobrol (Java)Article publié le 31/01/2007 08:41:01


Salut à toutes et à tous!


Aujourd'hui, une autre petite astuce qui va certainement plaireaux fanas de graphisme 2D....


Ne vous est-il jamais arrivé de devoir afficher une image sur un objet Graphics? Sûrement que oui.
Mais vous avez peut-être déjà éprouvé le besoin de devori faire pivoter cette image de x degrés à gauche ou à droite. Par exemple un vaisseau spatial dans un jeu d'arcade dont la direction du nez doit correspondre à la direction dans laquelle il se dirige.

La soultion est d'utiliser un objet AffineTransfrom, mais cela paraît parfois ardu. De plus, il peut être intéressant (toujours deans le cas du vaisseau de précalculer x images décalées de y degrés chacune de manière à disposer plus rapidement de l'image pivotée.


Dans ce but, j'ai écrit il y a quelques années une classe (RotatingImage) qui étend BufferedImage et qui peut donc être affichée sur un objet Graphics en uitlisant la méthode
  1. drawImage()
de la cette classe.

L'emploi de cette classe est très simple. En voici le mode d'emploi:

1. Instanciez un objet BufferedImage:
  1. URL imageURL = ...;
  2. BufferedImage buffImage = ImageIO.read(imageURL);

ou
  1. File imageFile = new File("someFilePath.jpg");
  2. BufferedImage buffImage = ImageIO.read(imageFile);


2. Instanciez un objet RotatingImage:
  1. rotatingImage = new RotatingImage(buffImage,true);

RotatingImage compte 3 constructeurs:
Le premier prend 3 arguments:
  1. public RotatingImage(BufferedImage sourceImage, boolean preCalc, int numberOgImages)

- sourceImage: la BufferedImage que vous venez d'instancier
- preCalc: une variable de type boolean qui spécifie si il faut précalculer les diférents images pivotées.
- numberOfImages: le nombre d'images a générer. Si par exemple cet argument vaut 10, alors 10 images pivotées de 36 degrés l'une par rapport à l'autre seront générées.

Le second prend 2 arguments:
  1. public RotatingImage(BufferedImage sourceImage, boolean preCalc)

En fait, il appelle le premier avec preCalc=false et numberOfImages=36

Le second n'en prend qu'un seul.
  1. public RotatingImage(BufferedImage sourceImage)

En fait, il appelle le second avec preCalc=false.

3. Spécifiez l'angle de rotation par rapport à l'image originale en utilisant la méthode
  1. setAngle(double angle)
de RotatingImage
  1. rotatingImage.setAngle(Math.PI*3/2);


4. Afficher votre image sur un Graphics ou dans un label, par exemple:
- dans un Graphics:
  1. JPanel panel = new JPanel();
  2. Graphics gr = panel.getGraphics();
  3. gr.drawImage(rotatingImage, 100,100, null);

- dans un JLabel:
  1. JLabel label = new JLabel(rotatingIamge);
  2. panel.add(label);


Remarques:
- Les images calculées sont stockées dans un objet Map. Les angles doivent être spécifiés en radians.
A chaque fois que vous utilisez la méthode
  1. setAngle(double angle)
, la classe RotatingImage calcule l'angle le plus proche de celui spécifié en fonction du nombre d'image demandées.
Par exemple, imaginons que vous ayez spécifié 30 images, elles seront alors espacées de 12 degrés chacune.
Si vous utilisez
  1. setAngle(Math.Pi/4)
(ce qui équivaut à une rotation de 45 degrés), l'image la plus proche sera celle pivotée de 4 x 12 = 48 degrés.
C'est donc cette image qui sera affichée.
- N'utilisez cette classe qu'avec de images de petite taille ou en spécifiant un nombre d'iamges pas trop élévé, sans quoi vous obtindrez une Overflow exception... faites quelques tests.

Voici le code de la classe RotatingImage:

  1. package be.fery.swing.rotatingImage;
  2.  
  3. /**
  4.  * This class enable image rotations.
  5.  *
  6.  * As it extends BufferedImage, it may be used
  7.  * to be displayed on a Component or on a
  8.  * Graphics object.
  9.  *
  10.  * @author Philippe FERY(philippe.fery@gmail.com)
  11.  * @version 1.0
  12.  *
  13.  * Created : November 2005
  14.  */
  15. import java.awt.Graphics;
  16. import java.awt.Point;
  17. import java.awt.image.BufferedImage;
  18. import java.util.HashMap;
  19.  
  20. public class RotatingImage extends BufferedImage {
  21. private BufferedImage sourceImage;
  22.  
  23. private double angle;
  24.  
  25. private int numberOfImages;
  26.  
  27. private int sourceWidth;
  28.  
  29. private int sourceHeight;
  30.  
  31. private int rotatedWidth;
  32.  
  33. private int rotatedHeight;
  34.  
  35. private HashMap images;
  36.  
  37. public RotatingImage(BufferedImage sourceImage, boolean preCalc, int numberOfImages) {
  38. super((int) Math.sqrt(sourceImage.getWidth() * sourceImage.getWidth() + sourceImage.getHeight() * sourceImage.getHeight()),
  39. (int) Math.sqrt(sourceImage.getWidth() * sourceImage.getWidth() + sourceImage.getHeight() * sourceImage.getHeight()),
  40. BufferedImage.TYPE_INT_ARGB);
  41. this.sourceImage = sourceImage;
  42. this.sourceWidth = sourceImage.getWidth();
  43. this.sourceHeight = sourceImage.getHeight();
  44. this.angle = 0;
  45. this.numberOfImages = numberOfImages;
  46. this.images = new HashMap();
  47. if (preCalc)
  48. preCalculateImages();
  49.  
  50. }
  51.  
  52. public RotatingImage(BufferedImage sourceImage, boolean preCalc) {
  53. this(sourceImage, preCalc, 36);
  54.  
  55. }
  56.  
  57. private void preCalculateImages() {
  58. long begin = System.currentTimeMillis();
  59. for (int i = 0; i ‹ numberOfImages; i++) {
  60. setAngle(i * (2 * Math.PI) / numberOfImages);
  61. rotate();
  62. }
  63. long end = System.currentTimeMillis();
  64. long duration = end - begin;
  65. System.out.println("Duration [" + images.keySet().size() + "]:" + duration);
  66. }
  67.  
  68. public RotatingImage(BufferedImage sourceImage) {
  69. this(sourceImage, false);
  70. }
  71.  
  72. /**
  73. * Rotate the source image
  74. *
  75. */
  76. public void rotate() {
  77. double hypotenuseLength = Math.sqrt(sourceWidth * sourceWidth + sourceHeight * sourceHeight);
  78. int rotatedWidth = (int) hypotenuseLength + 1;
  79. int rotatedHeight = (int) hypotenuseLength + 1;
  80.  
  81. BufferedImage rotatedImage = new BufferedImage(rotatedWidth, rotatedHeight, BufferedImage.TYPE_INT_ARGB);
  82.  
  83. int x = -1;
  84. int y = -1;
  85. int sector = (int) (Math.round(angle / ((2 * Math.PI) / (double) numberOfImages)));
  86. if (images.get(new Integer(sector)) != null) {
  87. rotatedImage = (BufferedImage) images.get(new Integer(sector));
  88. } else {
  89. for (int xRot = 0; xRot ‹ rotatedWidth; xRot++) {
  90. for (int yRot = 0; yRot ‹ rotatedHeight; yRot++) {
  91. Point xyCoord = getSourceXY(xRot - (rotatedWidth / 2), yRot - (rotatedHeight / 2));
  92. x = xyCoord.x;
  93. y = xyCoord.y;
  94. if (x ›= 0 && x ‹ sourceWidth && y ›= 0 && y ‹ sourceHeight) {
  95. int rgbColor = sourceImage.getRGB(x, y);
  96. try {
  97. rotatedImage.setRGB(xRot, yRot, rgbColor);
  98. } catch (Exception e) {
  99. System.out.print(" =› Error");
  100. }
  101. }
  102. }
  103. }
  104. images.put(new Integer(sector), rotatedImage);
  105. }
  106. Graphics g = getGraphics();
  107. g.clearRect(0, 0, rotatedWidth, rotatedHeight);
  108. g.drawImage(rotatedImage, 0, 0, null);
  109. }
  110.  
  111. /**
  112. * Get the corresponding coordinates in the source image for the specified
  113. * coordinates in the rotated image using the following transformation
  114. * matrix: | xSrc | = | xRot | | cos(angle) sin(angle) | | | | | * | | |
  115. * ySrc | = | yRot | | -sin(angle) cos(angle) |
  116. *
  117. * @param xRot
  118. * The x ccordinate in the rotated image
  119. * @param yRot
  120. * The y coordinate in the rotated image
  121. *
  122. * @return The corresponding (x,y) coordinate in the source image
  123. */
  124. private Point getSourceXY(int xRot, int yRot) {
  125. int dx = xRot;
  126. int dy = yRot;
  127. int x = ((int) (dx * Math.cos(angle) + dy * Math.sin(angle))) + (sourceWidth / 2);
  128. int y = ((int) (dy * Math.cos(angle) - dx * Math.sin(angle))) + (sourceHeight / 2);
  129. return new Point(x, y);
  130. }
  131.  
  132. /**
  133. * Get the corresponding coordinates in the rotated image for the specified
  134. * coordinates in the source image using the following transformation
  135. * matrix: | xSrc | = | xRot | | cos(angle) -sin(angle) | | | | | * | | |
  136. * ySrc | = | yRot | | sin(angle) cos(angle) |
  137. *
  138. * @param xRot
  139. * The x ccordinate in the source image
  140. * @param yRot
  141. * The y coordinate in the source image
  142. *
  143. * @return The corresponding (x,y) coordinate in the rotated image
  144. */
  145. private Point getRotatedXY(int xSrc, int ySrc) {
  146. int dx = xSrc;
  147. int dy = ySrc;
  148. int x = (int) (dx * Math.cos(angle) - dy * Math.sin(angle) + (rotatedWidth / 2));
  149. int y = (int) (dy * Math.cos(angle) + dx * Math.sin(angle) + (rotatedHeight / 2));
  150. return new Point(x, y);
  151. }
  152.  
  153. /**
  154. * Get the angle value
  155. *
  156. * @return The rotation angle value
  157. */
  158. public double getAngle() {
  159. return angle;
  160. }
  161.  
  162. /**
  163. * Set the angle value
  164. *
  165. * @param angle
  166. * The rotation angle value
  167. */
  168. public void setAngle(double angle) {
  169. this.angle = angle;
  170. rotate();
  171. }
  172.  
  173. /**
  174. * Get the source image
  175. *
  176. * @return The source image (not rotated)
  177. */
  178. public BufferedImage getSourceImage() {
  179. return sourceImage;
  180. }
  181.  
  182. /**
  183. * Get the number of images
  184. *
  185. * @return The number of images to generate
  186. */
  187. public int getNumberOfImage() {
  188. return numberOfImages;
  189. }
  190.  
  191. /**
  192. * Set the number of images
  193. *
  194. * @param numberOfImages
  195. * The number of images to generate
  196. */
  197. public void setNumberOfImage(int numberOfImages) {
  198. this.numberOfImages = numberOfImages;
  199. }
  200.  
  201. }



Le code de cette classe peut évidemment être amélioré, mais je n'en ai pas le temps actuellement. Si le coeur vous en dit...
Voici quelques pistes:
- permettre de spécifier l'angle en degrés
- utiliser un objet AffineTransform pour effectuer les rotations plutôt que le calcule matriciel
- gérer la transparence
Vous êtes libres d'utilise ce code ou de transformer, mais en y laissant les commentaires en tête de classe.

Je vous demande juste de publier le code transformé ici même si vous l'avez amélioré. Ajoutez alors un tag
  1. @author
avec votre nom et votre adresse e-mail sous le mien.

;-)
HackTrack

Avatar :: HackTrack Un article de HackTrackModifié 1 fois. (dernière modification le 31/01/2007 08:42:28 par HackTrack)



Source : indéterminée


Seuls les membres peuvent laisser un commentaire pour cet article (mesure automatique temporaire due aux attaques de spam par robots dont cet article fait actuellement l'objet).

Commentaires


31/01/2007 10:09:17 : Positionnement carto

Merci à toi pour cet article bien commenté.

Je vois déjà en quoi il peut m'être utile, car je vais bientôt développer un client riche pour une application carto. Je pense donc l'utiliser pour générer une sorte d'indicateur de direction vers le point de référence sur la carte.

Dès que j'ai quelque chôse, je vous en fait profiter.

- Akira Sato -


15/01/2010 15:51:25 : Convertir les radians en degrés

Bonjour,

Ta source est intéressante et j'aimerais vous faire profiter d'un ajout que l'on peut appliquer à celle-ci.

Pour passer des degrés en radians, il vous suffit de faire :
radian = degree * ((2 * Math.PI) / 360); // we translate degree to radian

Pour passer des radians en degrés, il vous suffit de faire :
degree = radian * (360 / (2 * Math.PI)); // we translate radian to degree

En espérant des développeurs(ses) en détresse ;)

Pierre-Yves (Pyrrah)
www.pyrrah.eu

- invité -


18/01/2010 21:08:17 : RE: Convertir les radians en degrés

Merci pour ces infos :-)

- Steph -

Sélection, tri et recherche d'articles
FILTRER :
TRIER :1er critère : 2e critère :
CHERCHER : Dans les titres Dans le contenu


[Afficher les liens en fonction des critères du formulaire ci-dessus]

English translation

You have asked to visit this site in English. For now, only the interface is translated, but not all the content yet.

If you want to help me in translations, your contribution is welcome. All you need to do is register on the site, and send me a message asking me to add you to the group of translators, which will give you the opportunity to translate the pages you want. A link at the bottom of each translated page indicates that you are the translator, and has a link to your profile.

Thank you in advance.

Document created the 13/09/2004, last modified the 26/10/2018
Source of the printed document:https://www.gaudry.be/en/ast-rf-382.html

The infobrol is a personal site whose content is my sole responsibility. The text is available under CreativeCommons license (BY-NC-SA). More info on the terms of use and the author.