Commit | Line | Data |
---|---|---|
80500544 NR |
1 | package be.nikiroo.utils.ui; |
2 | ||
b3424395 NR |
3 | import java.awt.Dimension; |
4 | import java.awt.Graphics2D; | |
80500544 NR |
5 | import java.awt.geom.AffineTransform; |
6 | import java.awt.image.AffineTransformOp; | |
7 | import java.awt.image.BufferedImage; | |
80500544 NR |
8 | import java.io.File; |
9 | import java.io.IOException; | |
10 | import java.io.InputStream; | |
11 | ||
12 | import javax.imageio.ImageIO; | |
13 | ||
b3424395 | 14 | import be.nikiroo.utils.IOUtils; |
80500544 NR |
15 | import be.nikiroo.utils.Image; |
16 | import be.nikiroo.utils.ImageUtils; | |
c48600a5 | 17 | import be.nikiroo.utils.StringUtils; |
80500544 NR |
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 { | |
b3424395 NR |
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 | } | |
113a5500 | 40 | |
10b6023d NR |
41 | @Override |
42 | protected boolean check() { | |
a2c1d5fe NR |
43 | // Will not work if ImageIO is not available |
44 | ImageIO.getCacheDirectory(); | |
10b6023d NR |
45 | return true; |
46 | } | |
47 | ||
80500544 NR |
48 | @Override |
49 | public void saveAsImage(Image img, File target, String format) | |
50 | throws IOException { | |
51 | try { | |
e8aa5bf9 | 52 | BufferedImage image = fromImage(img); |
80500544 NR |
53 | |
54 | boolean ok = false; | |
55 | try { | |
56 | ||
57 | ok = ImageIO.write(image, format, target); | |
58 | } catch (IOException e) { | |
59 | ok = false; | |
60 | } | |
61 | ||
62 | // Some formats are not reliable | |
e8aa5bf9 | 63 | // Second chance: PNG |
80500544 | 64 | if (!ok && !format.equals("png")) { |
89aa5782 NR |
65 | try { |
66 | ok = ImageIO.write(image, "png", target); | |
67 | } catch (IllegalArgumentException e) { | |
68 | throw e; | |
69 | } catch (Exception e) { | |
70 | throw new IOException("Undocumented exception occured, " | |
71 | + "converting to IOException", e); | |
72 | } | |
80500544 NR |
73 | } |
74 | ||
75 | if (!ok) { | |
76 | throw new IOException( | |
77 | "Cannot find a writer for this image and format: " | |
78 | + format); | |
79 | } | |
80 | } catch (IOException e) { | |
81 | throw new IOException("Cannot write image to " + target, e); | |
82 | } | |
83 | } | |
84 | ||
85 | /** | |
86 | * Convert the given {@link Image} into a {@link BufferedImage} object, | |
87 | * respecting the EXIF transformations if any. | |
88 | * | |
89 | * @param img | |
90 | * the {@link Image} | |
91 | * | |
92 | * @return the {@link Image} object | |
93 | * | |
94 | * @throws IOException | |
95 | * in case of IO error | |
96 | */ | |
e704a414 | 97 | public static BufferedImage fromImage(Image img) throws IOException { |
b3424395 NR |
98 | return fromImage(img, Rotation.NONE); |
99 | } | |
113a5500 | 100 | |
b3424395 NR |
101 | /** |
102 | * Convert the given {@link Image} into a {@link BufferedImage} object, | |
103 | * respecting the EXIF transformations if any. | |
104 | * | |
105 | * @param img | |
106 | * the {@link Image} | |
107 | * @param rotation | |
108 | * the rotation to apply, if any (can be null, same as | |
109 | * {@link Rotation#NONE}) | |
110 | * | |
111 | * @return the {@link Image} object | |
112 | * | |
113 | * @throws IOException | |
114 | * in case of IO error | |
115 | */ | |
113a5500 NR |
116 | public static BufferedImage fromImage(Image img, Rotation rotation) |
117 | throws IOException { | |
7b42695f | 118 | InputStream in = img.newInputStream(); |
35466644 NR |
119 | BufferedImage image; |
120 | try { | |
7b42695f NR |
121 | int orientation; |
122 | try { | |
123 | orientation = getExifTransorm(in); | |
124 | } catch (Exception e) { | |
125 | // no EXIF transform, ok | |
126 | orientation = -1; | |
127 | } | |
80500544 | 128 | |
7b42695f | 129 | in.reset(); |
80500544 | 130 | |
7b42695f NR |
131 | try { |
132 | image = ImageIO.read(in); | |
133 | } catch (IllegalArgumentException e) { | |
134 | throw e; | |
135 | } catch (Exception e) { | |
136 | throw new IOException("Undocumented exception occured, " | |
137 | + "converting to IOException", e); | |
138 | } | |
139 | ||
140 | if (image == null) { | |
c48600a5 | 141 | String extra = ""; |
59654e2a | 142 | if (img.getSize() <= 2048) { |
c48600a5 | 143 | try { |
b3424395 NR |
144 | byte[] data = null; |
145 | InputStream inData = img.newInputStream(); | |
146 | try { | |
147 | data = IOUtils.toByteArray(inData); | |
148 | } finally { | |
149 | inData.close(); | |
150 | } | |
113a5500 | 151 | extra = ", content: " + new String(data, "UTF-8"); |
c48600a5 NR |
152 | } catch (Exception e) { |
153 | extra = ", content unavailable"; | |
154 | } | |
155 | } | |
156 | String ssize = StringUtils.formatNumber(img.getSize()); | |
157 | throw new IOException( | |
158 | "Failed to convert input to image, size was: " + ssize | |
159 | + extra); | |
7b42695f NR |
160 | } |
161 | ||
162 | // Note: this code has been found on Internet; | |
163 | // thank you anonymous coder. | |
164 | int width = image.getWidth(); | |
165 | int height = image.getHeight(); | |
166 | AffineTransform affineTransform = new AffineTransform(); | |
167 | ||
168 | switch (orientation) { | |
169 | case 1: | |
170 | affineTransform = null; | |
171 | break; | |
172 | case 2: // Flip X | |
173 | affineTransform.scale(-1.0, 1.0); | |
174 | affineTransform.translate(-width, 0); | |
175 | break; | |
176 | case 3: // PI rotation | |
177 | affineTransform.translate(width, height); | |
178 | affineTransform.rotate(Math.PI); | |
179 | break; | |
180 | case 4: // Flip Y | |
181 | affineTransform.scale(1.0, -1.0); | |
182 | affineTransform.translate(0, -height); | |
183 | break; | |
184 | case 5: // - PI/2 and Flip X | |
185 | affineTransform.rotate(-Math.PI / 2); | |
186 | affineTransform.scale(-1.0, 1.0); | |
187 | break; | |
188 | case 6: // -PI/2 and -width | |
189 | affineTransform.translate(height, 0); | |
190 | affineTransform.rotate(Math.PI / 2); | |
191 | break; | |
192 | case 7: // PI/2 and Flip | |
193 | affineTransform.scale(-1.0, 1.0); | |
194 | affineTransform.translate(-height, 0); | |
195 | affineTransform.translate(0, width); | |
196 | affineTransform.rotate(3 * Math.PI / 2); | |
197 | break; | |
198 | case 8: // PI / 2 | |
199 | affineTransform.translate(0, width); | |
200 | affineTransform.rotate(3 * Math.PI / 2); | |
201 | break; | |
202 | default: | |
203 | affineTransform = null; | |
204 | break; | |
205 | } | |
113a5500 | 206 | |
b3424395 NR |
207 | if (rotation == null) |
208 | rotation = Rotation.NONE; | |
113a5500 | 209 | |
b3424395 | 210 | switch (rotation) { |
113a5500 | 211 | case RIGHT: |
b3424395 NR |
212 | if (affineTransform == null) { |
213 | affineTransform = new AffineTransform(); | |
214 | } | |
215 | affineTransform.translate(height, 0); | |
216 | affineTransform.rotate(Math.PI / 2); | |
a71e8e38 NR |
217 | |
218 | int tmp = width; | |
219 | width = height; | |
220 | height = tmp; | |
221 | ||
b3424395 | 222 | break; |
113a5500 | 223 | case LEFT: |
b3424395 NR |
224 | if (affineTransform == null) { |
225 | affineTransform = new AffineTransform(); | |
226 | } | |
227 | affineTransform.translate(0, width); | |
228 | affineTransform.rotate(3 * Math.PI / 2); | |
a71e8e38 NR |
229 | |
230 | int temp = width; | |
231 | width = height; | |
232 | height = temp; | |
233 | ||
b3424395 NR |
234 | break; |
235 | case UTURN: | |
236 | if (affineTransform == null) { | |
237 | affineTransform = new AffineTransform(); | |
238 | } | |
239 | affineTransform.translate(width, height); | |
240 | affineTransform.rotate(Math.PI); | |
241 | break; | |
242 | default: | |
243 | break; | |
244 | } | |
80500544 | 245 | |
7b42695f NR |
246 | if (affineTransform != null) { |
247 | AffineTransformOp affineTransformOp = new AffineTransformOp( | |
248 | affineTransform, AffineTransformOp.TYPE_BILINEAR); | |
80500544 | 249 | |
7b42695f NR |
250 | BufferedImage transformedImage = new BufferedImage(width, |
251 | height, image.getType()); | |
252 | transformedImage = affineTransformOp.filter(image, | |
253 | transformedImage); | |
80500544 | 254 | |
7b42695f NR |
255 | image = transformedImage; |
256 | } | |
257 | // | |
258 | } finally { | |
259 | in.close(); | |
80500544 | 260 | } |
80500544 NR |
261 | |
262 | return image; | |
263 | } | |
b3424395 | 264 | |
303244ee NR |
265 | /** |
266 | * Scale a dimension. | |
267 | * | |
268 | * @param imageSize | |
269 | * the actual image size | |
270 | * @param areaSize | |
271 | * the base size of the target to get snap sizes for | |
272 | * @param zoom | |
273 | * the zoom factor (ignored on snap mode) | |
274 | * @param snapMode | |
275 | * NULL for no snap mode, TRUE to snap to width and FALSE for | |
276 | * snap to height) | |
277 | * | |
278 | * @return the scaled (minimum is 1x1) | |
279 | */ | |
280 | public static Dimension scaleSize(Dimension imageSize, Dimension areaSize, | |
281 | double zoom, Boolean snapMode) { | |
282 | Integer[] sz = scaleSize(imageSize.width, imageSize.height, | |
283 | areaSize.width, areaSize.height, zoom, snapMode); | |
284 | return new Dimension(sz[0], sz[1]); | |
285 | } | |
286 | ||
b3424395 NR |
287 | /** |
288 | * Resize the given image. | |
289 | * | |
303244ee NR |
290 | * @param image |
291 | * the image to resize | |
b3424395 NR |
292 | * @param areaSize |
293 | * the base size of the target dimension for snap sizes | |
303244ee NR |
294 | * @param zoom |
295 | * the zoom factor (ignored on snap mode) | |
296 | * @param snapMode | |
297 | * NULL for no snap mode, TRUE to snap to width and FALSE for | |
298 | * snap to height) | |
299 | * | |
300 | * @return a new, resized image | |
301 | */ | |
302 | public static BufferedImage scaleImage(BufferedImage image, | |
303 | Dimension areaSize, double zoom, Boolean snapMode) { | |
304 | Dimension scaledSize = scaleSize( | |
305 | new Dimension(image.getWidth(), image.getHeight()), areaSize, | |
306 | zoom, snapMode); | |
307 | ||
308 | return scaleImage(image, scaledSize); | |
309 | } | |
310 | ||
311 | /** | |
312 | * Resize the given image. | |
313 | * | |
b3424395 NR |
314 | * @param image |
315 | * the image to resize | |
303244ee NR |
316 | * @param targetSize |
317 | * the target size | |
b3424395 NR |
318 | * |
319 | * @return a new, resized image | |
320 | */ | |
303244ee NR |
321 | public static BufferedImage scaleImage(BufferedImage image, |
322 | Dimension targetSize) { | |
323 | BufferedImage resizedImage = new BufferedImage(targetSize.width, | |
324 | targetSize.height, BufferedImage.TYPE_4BYTE_ABGR); | |
b3424395 NR |
325 | Graphics2D g = resizedImage.createGraphics(); |
326 | try { | |
303244ee | 327 | g.drawImage(image, 0, 0, targetSize.width, targetSize.height, null); |
b3424395 NR |
328 | } finally { |
329 | g.dispose(); | |
330 | } | |
113a5500 | 331 | |
b3424395 NR |
332 | return resizedImage; |
333 | } | |
80500544 | 334 | } |