1 package be
.nikiroo
.utils
;
3 import java
.awt
.Dimension
;
5 import java
.awt
.geom
.AffineTransform
;
6 import java
.awt
.image
.AffineTransformOp
;
7 import java
.awt
.image
.BufferedImage
;
8 import java
.io
.ByteArrayInputStream
;
9 import java
.io
.ByteArrayOutputStream
;
11 import java
.io
.FileInputStream
;
12 import java
.io
.IOException
;
13 import java
.io
.InputStream
;
15 import javax
.imageio
.ImageIO
;
17 import be
.nikiroo
.utils
.ImageText
.Mode
;
20 * This class offer some utilities based around images.
24 public class ImageUtils
{
26 * Convert the given {@link Image} object into a Base64 representation of
27 * the same {@link Image} object.
30 * the {@link Image} object to convert
32 * @return the Base64 representation
37 static public String
toBase64(BufferedImage image
) throws IOException
{
38 return toBase64(image
, null);
42 * Convert the given {@link Image} object into a Base64 representation of
43 * the same {@link Image}. object.
46 * the {@link Image} object to convert
48 * the image format to use to serialise it (default is PNG)
50 * @return the Base64 representation
55 static public String
toBase64(BufferedImage image
, String format
)
61 String imageString
= null;
62 ByteArrayOutputStream out
= new ByteArrayOutputStream();
64 ImageIO
.write(image
, format
, out
);
65 byte[] imageBytes
= out
.toByteArray();
67 imageString
= new String(Base64
.encodeBytes(imageBytes
));
75 * Convert the given image into a Base64 representation of the same
79 * the image to convert
81 * @return the Base64 representation
86 static public String
toBase64(InputStream in
) throws IOException
{
87 String fileString
= null;
88 ByteArrayOutputStream out
= new ByteArrayOutputStream();
90 byte[] buf
= new byte[8192];
93 while ((c
= in
.read(buf
, 0, buf
.length
)) > 0) {
99 fileString
= new String(Base64
.encodeBytes(out
.toByteArray()));
106 * Convert the given Base64 representation of an image into an {@link Image}
110 * the {@link Image} in Base64 format
112 * @return the {@link Image} object
114 * @throws IOException
115 * in case of IO error
117 static public BufferedImage
fromBase64(String b64data
) throws IOException
{
118 ByteArrayInputStream in
= new ByteArrayInputStream(
119 Base64
.decode(b64data
));
120 return fromStream(in
);
124 * A shorthand method to create an {@link ImageText} and return its output.
127 * the source {@link Image}
129 * the final text size to target
131 * the mode of conversion
133 * TRUE to invert colours rendering
135 * @return the text image
137 static public String
toAscii(Image image
, Dimension size
, Mode mode
,
139 return new ImageText(image
, size
, mode
, invert
).toString();
143 * Convert the given {@link InputStream} (which should allow calls to
144 * {@link InputStream#reset()} for better perfs) into an {@link Image}
145 * object, respecting the EXIF transformations if any.
148 * the {@link InputStream}
150 * @return the {@link Image} object
152 * @throws IOException
153 * in case of IO error
155 static public BufferedImage
fromStream(InputStream in
) throws IOException
{
156 MarkableFileInputStream tmpIn
= null;
159 boolean resetable
= in
.markSupported();
163 } catch (IOException e
) {
169 return fromResetableStream(in
);
172 tmp
= File
.createTempFile(".tmp-image", ".tmp");
174 IOUtils
.write(in
, tmp
);
175 tmpIn
= new MarkableFileInputStream(new FileInputStream(tmp
));
176 return fromResetableStream(tmpIn
);
189 * Convert the given resetable {@link InputStream} into an {@link Image}
190 * object, respecting the EXIF transformations if any.
193 * the 'resetable' (this is mandatory) {@link InputStream}
195 * @return the {@link Image} object
197 * @throws IOException
198 * in case of IO error
200 static private BufferedImage
fromResetableStream(InputStream in
)
205 orientation
= getExifTransorm(in
);
206 } catch (Exception e
) {
207 // no EXIF transform, ok
212 BufferedImage image
= ImageIO
.read(in
);
215 throw new IOException("Failed to convert input to image");
218 // Note: this code has been found on Internet;
219 // thank you anonymous coder.
220 int width
= image
.getWidth();
221 int height
= image
.getHeight();
222 AffineTransform affineTransform
= new AffineTransform();
224 switch (orientation
) {
226 affineTransform
= null;
229 affineTransform
.scale(-1.0, 1.0);
230 affineTransform
.translate(-width
, 0);
232 case 3: // PI rotation
233 affineTransform
.translate(width
, height
);
234 affineTransform
.rotate(Math
.PI
);
237 affineTransform
.scale(1.0, -1.0);
238 affineTransform
.translate(0, -height
);
240 case 5: // - PI/2 and Flip X
241 affineTransform
.rotate(-Math
.PI
/ 2);
242 affineTransform
.scale(-1.0, 1.0);
244 case 6: // -PI/2 and -width
245 affineTransform
.translate(height
, 0);
246 affineTransform
.rotate(Math
.PI
/ 2);
248 case 7: // PI/2 and Flip
249 affineTransform
.scale(-1.0, 1.0);
250 affineTransform
.translate(-height
, 0);
251 affineTransform
.translate(0, width
);
252 affineTransform
.rotate(3 * Math
.PI
/ 2);
255 affineTransform
.translate(0, width
);
256 affineTransform
.rotate(3 * Math
.PI
/ 2);
259 affineTransform
= null;
263 if (affineTransform
!= null) {
264 AffineTransformOp affineTransformOp
= new AffineTransformOp(
265 affineTransform
, AffineTransformOp
.TYPE_BILINEAR
);
267 BufferedImage transformedImage
= new BufferedImage(width
, height
,
269 transformedImage
= affineTransformOp
270 .filter(image
, transformedImage
);
272 image
= transformedImage
;
280 * Return the EXIF transformation flag of this image if any.
283 * Note: this code has been found on internet; thank you anonymous coder.
287 * the data {@link InputStream}
289 * @return the transformation flag if any
291 * @throws IOException
292 * in case of IO error
294 static private int getExifTransorm(InputStream in
) throws IOException
{
295 int[] exif_data
= new int[100];
299 /* Read File head, check for JPEG SOI + Exif APP1 */
300 for (int i
= 0; i
< 4; i
++)
301 exif_data
[i
] = in
.read();
303 if (exif_data
[0] != 0xFF || exif_data
[1] != 0xD8
304 || exif_data
[2] != 0xFF || exif_data
[3] != 0xE1)
307 /* Get the marker parameter length count */
308 int length
= (in
.read() << 8 | in
.read());
310 /* Length includes itself, so must be at least 2 */
311 /* Following Exif data length must be at least 6 */
315 /* Read Exif head, check for "Exif" */
316 for (int i
= 0; i
< 6; i
++)
317 exif_data
[i
] = in
.read();
319 if (exif_data
[0] != 0x45 || exif_data
[1] != 0x78
320 || exif_data
[2] != 0x69 || exif_data
[3] != 0x66
321 || exif_data
[4] != 0 || exif_data
[5] != 0)
325 length
= length
> exif_data
.length ? exif_data
.length
: length
;
326 for (int i
= 0; i
< length
; i
++)
327 exif_data
[i
] = in
.read();
330 return -1; /* Length of an IFD entry */
332 /* Discover byte order */
333 if (exif_data
[0] == 0x49 && exif_data
[1] == 0x49)
335 else if (exif_data
[0] == 0x4D && exif_data
[1] == 0x4D)
341 if (is_motorola
== 1) {
342 if (exif_data
[2] != 0)
344 if (exif_data
[3] != 0x2A)
347 if (exif_data
[3] != 0)
349 if (exif_data
[2] != 0x2A)
353 /* Get first IFD offset (offset to IFD0) */
355 if (is_motorola
== 1) {
356 if (exif_data
[4] != 0)
358 if (exif_data
[5] != 0)
360 offset
= exif_data
[6];
362 offset
+= exif_data
[7];
364 if (exif_data
[7] != 0)
366 if (exif_data
[6] != 0)
368 offset
= exif_data
[5];
370 offset
+= exif_data
[4];
372 if (offset
> length
- 2)
373 return -1; /* check end of data segment */
375 /* Get the number of directory entries contained in this IFD */
377 if (is_motorola
== 1) {
378 number_of_tags
= exif_data
[offset
];
379 number_of_tags
<<= 8;
380 number_of_tags
+= exif_data
[offset
+ 1];
382 number_of_tags
= exif_data
[offset
+ 1];
383 number_of_tags
<<= 8;
384 number_of_tags
+= exif_data
[offset
];
386 if (number_of_tags
== 0)
390 /* Search for Orientation Tag in IFD0 */
392 if (offset
> length
- 12)
393 return -1; /* check end of data segment */
396 if (is_motorola
== 1) {
397 tagnum
= exif_data
[offset
];
399 tagnum
+= exif_data
[offset
+ 1];
401 tagnum
= exif_data
[offset
+ 1];
403 tagnum
+= exif_data
[offset
];
405 if (tagnum
== 0x0112)
406 break; /* found Orientation Tag */
407 if (--number_of_tags
== 0)
412 /* Get the Orientation value */
413 if (is_motorola
== 1) {
414 if (exif_data
[offset
+ 8] != 0)
416 set_flag
= exif_data
[offset
+ 9];
418 if (exif_data
[offset
+ 9] != 0)
420 set_flag
= exif_data
[offset
+ 8];