VERSION
[fanfix.git] / src / be / nikiroo / utils / Image.java
CommitLineData
80500544
NR
1package be.nikiroo.utils;
2
59864f77
NR
3import java.io.ByteArrayInputStream;
4import java.io.Closeable;
5import java.io.File;
6import java.io.FileInputStream;
80500544
NR
7import java.io.IOException;
8import java.io.InputStream;
9
f04d5e49
NR
10import be.nikiroo.utils.streams.MarkableFileInputStream;
11
80500544
NR
12/**
13 * This class represents an image data.
14 *
15 * @author niki
16 */
59864f77 17public class Image implements Closeable {
82fcfcde 18 static private File tempRoot;
59864f77
NR
19 static private TempFiles tmpRepository;
20 static private long count = 0;
21 static private Object lock = new Object();
22
23 private File data;
80500544
NR
24
25 /**
26 * Do not use -- for serialisation purposes only.
27 */
28 @SuppressWarnings("unused")
29 private Image() {
30 }
31
32 /**
33 * Create a new {@link Image} with the given data.
34 *
35 * @param data
36 * the data
37 */
38 public Image(byte[] data) {
59864f77
NR
39 ByteArrayInputStream in = new ByteArrayInputStream(data);
40 try {
41 this.data = getTemporaryFile();
42 IOUtils.write(in, this.data);
43 } catch (IOException e) {
44 throw new RuntimeException(e);
45 } finally {
46 try {
47 in.close();
48 } catch (IOException e) {
49 throw new RuntimeException(e);
50 }
51 }
80500544
NR
52 }
53
54 /**
55 * Create a new {@link Image} from its Base64 representation.
56 *
57 * @param base64
58 * the {@link Image} in Base64 format
59 *
60 * @throws IOException
61 * in case of I/O error
62 */
63 public Image(String base64) throws IOException {
64 this(Base64.decode(base64));
65 }
66
67 /**
68 * Create a new {@link Image} from a stream.
69 *
70 * @param in
71 * the stream
72 *
73 * @throws IOException
74 * in case of I/O error
75 */
76 public Image(InputStream in) throws IOException {
59864f77
NR
77 data = getTemporaryFile();
78 IOUtils.write(in, data);
80500544
NR
79 }
80
7b42695f
NR
81 /**
82 * Generate an {@link InputStream} for this {@link Image}.
83 * <p>
84 * This {@link InputStream} will (always) be a new one, and <b>you</b> are
85 * responsible for it.
86 * <p>
87 * Note: take care that the {@link InputStream} <b>must not</b> live past
88 * the {@link Image} life time!
89 *
90 * @return the stream
91 *
92 * @throws IOException
93 * in case of I/O error
94 */
95 public InputStream newInputStream() throws IOException {
f04d5e49 96 return new MarkableFileInputStream(new FileInputStream(data));
7b42695f
NR
97 }
98
80500544 99 /**
59864f77 100 * <b>Read</b> the actual image data, as a byte array.
7b42695f
NR
101 * <p>
102 * Note: if possible, prefer the {@link Image#newInputStream()} method, as
103 * it can be more efficient.
80500544
NR
104 *
105 * @return the image data
106 */
107 public byte[] getData() {
59864f77 108 try {
7b42695f 109 InputStream in = newInputStream();
59864f77
NR
110 try {
111 return IOUtils.toByteArray(in);
112 } finally {
113 in.close();
114 }
115 } catch (IOException e) {
116 throw new RuntimeException(e);
117 }
80500544
NR
118 }
119
120 /**
121 * Convert the given {@link Image} object into a Base64 representation of
122 * the same {@link Image} object.
7b42695f
NR
123 * <p>
124 * Note: if possible, prefer the {@link Image#newInputStream()} method, as
125 * it can be more efficient.
80500544
NR
126 *
127 * @return the Base64 representation
128 */
129 public String toBase64() {
7b42695f
NR
130 try {
131 return StringUtils.base64(getData(), false);
132 } catch (IOException e) {
133 throw new RuntimeException(e);
134 }
80500544 135 }
59864f77
NR
136
137 /**
138 * Closing the {@link Image} will delete the associated temporary file on
139 * disk.
140 * <p>
141 * Note that even if you don't, the program will still <b>try</b> to delete
142 * all the temporary files at JVM termination.
143 */
144 @Override
145 public void close() throws IOException {
146 data.delete();
147 synchronized (lock) {
148 count--;
149 if (count <= 0) {
150 count = 0;
151 tmpRepository.close();
152 tmpRepository = null;
153 }
154 }
155 }
156
157 @Override
158 protected void finalize() throws Throwable {
159 try {
160 close();
161 } finally {
162 super.finalize();
163 }
164 }
165
166 /**
167 * Return a newly created temporary file to work on.
168 *
169 * @return the file
170 *
171 * @throws IOException
172 * in case of I/O error
173 */
174 private File getTemporaryFile() throws IOException {
175 synchronized (lock) {
176 if (tmpRepository == null) {
82fcfcde 177 tmpRepository = new TempFiles(tempRoot, "images");
59864f77
NR
178 count = 0;
179 }
180
181 count++;
182
183 return tmpRepository.createTempFile("image");
184 }
185 }
82fcfcde
NR
186
187 /**
188 * Change the temporary root directory used by the program.
189 * <p>
190 * Caution: the directory will be <b>owned</b> by the system, all its files
191 * now belong to us (and will most probably be deleted).
192 * <p>
193 * Note: it may take some time until the new temporary root is used, we
194 * first need to make sure the previous one is not used anymore (i.e., we
195 * must reach a point where no unclosed {@link Image} remains in memory) to
196 * switch the temporary root.
197 *
198 * @param root
199 * the new temporary root, which will be <b>owned</b> by the
200 * system
201 */
202 public static void setTemporaryFilesRoot(File root) {
203 tempRoot = root;
204 }
80500544 205}