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 | } | |
10b6023d NR |
40 | @Override |
41 | protected boolean check() { | |
a2c1d5fe NR |
42 | // Will not work if ImageIO is not available |
43 | ImageIO.getCacheDirectory(); | |
10b6023d NR |
44 | return true; |
45 | } | |
46 | ||
80500544 NR |
47 | @Override |
48 | public void saveAsImage(Image img, File target, String format) | |
49 | throws IOException { | |
50 | try { | |
e8aa5bf9 | 51 | BufferedImage image = fromImage(img); |
80500544 NR |
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 | |
e8aa5bf9 | 62 | // Second chance: PNG |
80500544 | 63 | if (!ok && !format.equals("png")) { |
89aa5782 NR |
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 | } | |
80500544 NR |
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 | */ | |
e704a414 | 96 | public static BufferedImage fromImage(Image img) throws IOException { |
b3424395 NR |
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 { | |
7b42695f | 116 | InputStream in = img.newInputStream(); |
35466644 NR |
117 | BufferedImage image; |
118 | try { | |
7b42695f NR |
119 | int orientation; |
120 | try { | |
121 | orientation = getExifTransorm(in); | |
122 | } catch (Exception e) { | |
123 | // no EXIF transform, ok | |
124 | orientation = -1; | |
125 | } | |
80500544 | 126 | |
7b42695f | 127 | in.reset(); |
80500544 | 128 | |
7b42695f NR |
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) { | |
c48600a5 | 139 | String extra = ""; |
59654e2a | 140 | if (img.getSize() <= 2048) { |
c48600a5 | 141 | try { |
b3424395 NR |
142 | byte[] data = null; |
143 | InputStream inData = img.newInputStream(); | |
144 | try { | |
145 | data = IOUtils.toByteArray(inData); | |
146 | } finally { | |
147 | inData.close(); | |
148 | } | |
c48600a5 | 149 | extra = ", content: " |
b3424395 | 150 | + new String(data, "UTF-8"); |
c48600a5 NR |
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); | |
7b42695f NR |
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 | } | |
b3424395 NR |
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 | } | |
80500544 | 234 | |
7b42695f NR |
235 | if (affineTransform != null) { |
236 | AffineTransformOp affineTransformOp = new AffineTransformOp( | |
237 | affineTransform, AffineTransformOp.TYPE_BILINEAR); | |
80500544 | 238 | |
7b42695f NR |
239 | BufferedImage transformedImage = new BufferedImage(width, |
240 | height, image.getType()); | |
241 | transformedImage = affineTransformOp.filter(image, | |
242 | transformedImage); | |
80500544 | 243 | |
7b42695f NR |
244 | image = transformedImage; |
245 | } | |
246 | // | |
247 | } finally { | |
248 | in.close(); | |
80500544 | 249 | } |
80500544 NR |
250 | |
251 | return image; | |
252 | } | |
b3424395 NR |
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 | } | |
80500544 | 286 | } |