9412c93cd93b52c00d4fbc33cc93dc8c9860c17a
[fanfix.git] / ui / ImageUtilsAwt.java
1 package be.nikiroo.utils.ui;
2
3 import java.awt.Dimension;
4 import java.awt.Graphics2D;
5 import java.awt.geom.AffineTransform;
6 import java.awt.image.AffineTransformOp;
7 import java.awt.image.BufferedImage;
8 import java.io.File;
9 import java.io.IOException;
10 import java.io.InputStream;
11
12 import javax.imageio.ImageIO;
13
14 import be.nikiroo.utils.IOUtils;
15 import be.nikiroo.utils.Image;
16 import be.nikiroo.utils.ImageUtils;
17 import be.nikiroo.utils.StringUtils;
18
19 /**
20 * This class offer some utilities based around images and uses java.awt.
21 *
22 * @author niki
23 */
24 public class ImageUtilsAwt extends ImageUtils {
25 /**
26 * A rotation to perform on an image.
27 *
28 * @author niki
29 */
30 public enum Rotation {
31 /** No rotation */
32 NONE,
33 /** Rotate the image to the right */
34 RIGHT,
35 /** Rotate the image to the left */
36 LEFT,
37 /** Rotate the image by 180° */
38 UTURN
39 }
40 @Override
41 protected boolean check() {
42 // Will not work if ImageIO is not available
43 ImageIO.getCacheDirectory();
44 return true;
45 }
46
47 @Override
48 public void saveAsImage(Image img, File target, String format)
49 throws IOException {
50 try {
51 BufferedImage image = fromImage(img);
52
53 boolean ok = false;
54 try {
55
56 ok = ImageIO.write(image, format, target);
57 } catch (IOException e) {
58 ok = false;
59 }
60
61 // Some formats are not reliable
62 // Second chance: PNG
63 if (!ok && !format.equals("png")) {
64 try {
65 ok = ImageIO.write(image, "png", target);
66 } catch (IllegalArgumentException e) {
67 throw e;
68 } catch (Exception e) {
69 throw new IOException("Undocumented exception occured, "
70 + "converting to IOException", e);
71 }
72 }
73
74 if (!ok) {
75 throw new IOException(
76 "Cannot find a writer for this image and format: "
77 + format);
78 }
79 } catch (IOException e) {
80 throw new IOException("Cannot write image to " + target, e);
81 }
82 }
83
84 /**
85 * Convert the given {@link Image} into a {@link BufferedImage} object,
86 * respecting the EXIF transformations if any.
87 *
88 * @param img
89 * the {@link Image}
90 *
91 * @return the {@link Image} object
92 *
93 * @throws IOException
94 * in case of IO error
95 */
96 public static BufferedImage fromImage(Image img) throws IOException {
97 return fromImage(img, Rotation.NONE);
98 }
99
100 /**
101 * Convert the given {@link Image} into a {@link BufferedImage} object,
102 * respecting the EXIF transformations if any.
103 *
104 * @param img
105 * the {@link Image}
106 * @param rotation
107 * the rotation to apply, if any (can be null, same as
108 * {@link Rotation#NONE})
109 *
110 * @return the {@link Image} object
111 *
112 * @throws IOException
113 * in case of IO error
114 */
115 public static BufferedImage fromImage(Image img, Rotation rotation) throws IOException {
116 InputStream in = img.newInputStream();
117 BufferedImage image;
118 try {
119 int orientation;
120 try {
121 orientation = getExifTransorm(in);
122 } catch (Exception e) {
123 // no EXIF transform, ok
124 orientation = -1;
125 }
126
127 in.reset();
128
129 try {
130 image = ImageIO.read(in);
131 } catch (IllegalArgumentException e) {
132 throw e;
133 } catch (Exception e) {
134 throw new IOException("Undocumented exception occured, "
135 + "converting to IOException", e);
136 }
137
138 if (image == null) {
139 String extra = "";
140 if (img.getSize() <= 2048) {
141 try {
142 byte[] data = null;
143 InputStream inData = img.newInputStream();
144 try {
145 data = IOUtils.toByteArray(inData);
146 } finally {
147 inData.close();
148 }
149 extra = ", content: "
150 + new String(data, "UTF-8");
151 } catch (Exception e) {
152 extra = ", content unavailable";
153 }
154 }
155 String ssize = StringUtils.formatNumber(img.getSize());
156 throw new IOException(
157 "Failed to convert input to image, size was: " + ssize
158 + extra);
159 }
160
161 // Note: this code has been found on Internet;
162 // thank you anonymous coder.
163 int width = image.getWidth();
164 int height = image.getHeight();
165 AffineTransform affineTransform = new AffineTransform();
166
167 switch (orientation) {
168 case 1:
169 affineTransform = null;
170 break;
171 case 2: // Flip X
172 affineTransform.scale(-1.0, 1.0);
173 affineTransform.translate(-width, 0);
174 break;
175 case 3: // PI rotation
176 affineTransform.translate(width, height);
177 affineTransform.rotate(Math.PI);
178 break;
179 case 4: // Flip Y
180 affineTransform.scale(1.0, -1.0);
181 affineTransform.translate(0, -height);
182 break;
183 case 5: // - PI/2 and Flip X
184 affineTransform.rotate(-Math.PI / 2);
185 affineTransform.scale(-1.0, 1.0);
186 break;
187 case 6: // -PI/2 and -width
188 affineTransform.translate(height, 0);
189 affineTransform.rotate(Math.PI / 2);
190 break;
191 case 7: // PI/2 and Flip
192 affineTransform.scale(-1.0, 1.0);
193 affineTransform.translate(-height, 0);
194 affineTransform.translate(0, width);
195 affineTransform.rotate(3 * Math.PI / 2);
196 break;
197 case 8: // PI / 2
198 affineTransform.translate(0, width);
199 affineTransform.rotate(3 * Math.PI / 2);
200 break;
201 default:
202 affineTransform = null;
203 break;
204 }
205
206 if (rotation == null)
207 rotation = Rotation.NONE;
208
209 switch (rotation) {
210 case LEFT:
211 if (affineTransform == null) {
212 affineTransform = new AffineTransform();
213 }
214 affineTransform.translate(height, 0);
215 affineTransform.rotate(Math.PI / 2);
216 break;
217 case RIGHT:
218 if (affineTransform == null) {
219 affineTransform = new AffineTransform();
220 }
221 affineTransform.translate(0, width);
222 affineTransform.rotate(3 * Math.PI / 2);
223 break;
224 case UTURN:
225 if (affineTransform == null) {
226 affineTransform = new AffineTransform();
227 }
228 affineTransform.translate(width, height);
229 affineTransform.rotate(Math.PI);
230 break;
231 default:
232 break;
233 }
234
235 if (affineTransform != null) {
236 AffineTransformOp affineTransformOp = new AffineTransformOp(
237 affineTransform, AffineTransformOp.TYPE_BILINEAR);
238
239 BufferedImage transformedImage = new BufferedImage(width,
240 height, image.getType());
241 transformedImage = affineTransformOp.filter(image,
242 transformedImage);
243
244 image = transformedImage;
245 }
246 //
247 } finally {
248 in.close();
249 }
250
251 return image;
252 }
253
254 /**
255 * Resize the given image.
256 *
257 * @param areaSize
258 * the base size of the target dimension for snap sizes
259 * @param image
260 * the image to resize
261 * @param zoom
262 * the zoom factor or -1 for snap size
263 * @param zoomSnapWidth
264 * if snap size, TRUE to snap to width (and FALSE, snap to
265 * height)
266 *
267 * @return a new, resized image
268 */
269 public static BufferedImage scaleImage(Dimension areaSize,
270 BufferedImage image, double zoom, boolean zoomSnapWidth) {
271 Integer scaledSize[] = scaleSize(areaSize.width, areaSize.height,
272 image.getWidth(), image.getHeight(), zoom, zoomSnapWidth);
273 int width = scaledSize[0];
274 int height = scaledSize[1];
275 BufferedImage resizedImage = new BufferedImage(width, height,
276 BufferedImage.TYPE_4BYTE_ABGR);
277 Graphics2D g = resizedImage.createGraphics();
278 try {
279 g.drawImage(image, 0, 0, width, height, null);
280 } finally {
281 g.dispose();
282 }
283
284 return resizedImage;
285 }
286 }