Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ImageReaderWriterHelper |
|
| 4.75;4,75 |
1 | // | |
2 | // $Id$ | |
3 | // | |
4 | // jupload - A file upload applet. | |
5 | // | |
6 | // Copyright 2008 The JUpload Team | |
7 | // | |
8 | // Created: 12 feb. 08 | |
9 | // Creator: etienne_sf | |
10 | // Last modified: $Date$ | |
11 | // | |
12 | // This program is free software; you can redistribute it and/or modify | |
13 | // it under the terms of the GNU General Public License as published by | |
14 | // the Free Software Foundation; either version 2 of the License, or | |
15 | // (at your option) any later version. | |
16 | // | |
17 | // This program is distributed in the hope that it will be useful, | |
18 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | // GNU General Public License for more details. | |
21 | // | |
22 | // You should have received a copy of the GNU General Public License | |
23 | // along with this program; if not, write to the Free Software | |
24 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
25 | ||
26 | package wjhk.jupload2.filedata.helper; | |
27 | ||
28 | import java.awt.image.BufferedImage; | |
29 | import java.io.File; | |
30 | import java.io.IOException; | |
31 | import java.util.Iterator; | |
32 | ||
33 | import javax.imageio.IIOImage; | |
34 | import javax.imageio.ImageIO; | |
35 | import javax.imageio.ImageReader; | |
36 | import javax.imageio.ImageWriteParam; | |
37 | import javax.imageio.ImageWriter; | |
38 | import javax.imageio.metadata.IIOMetadata; | |
39 | import javax.imageio.plugins.jpeg.JPEGImageWriteParam; | |
40 | import javax.imageio.stream.FileImageInputStream; | |
41 | import javax.imageio.stream.FileImageOutputStream; | |
42 | ||
43 | import wjhk.jupload2.exception.JUploadIOException; | |
44 | import wjhk.jupload2.filedata.DefaultFileData; | |
45 | import wjhk.jupload2.filedata.PictureFileData; | |
46 | import wjhk.jupload2.policies.PictureUploadPolicy; | |
47 | ||
48 | /** | |
49 | * This package provides low level methods, for picture management. It is used by {@link PictureFileData} to simplify | |
50 | * its processing. | |
51 | * | |
52 | * @author etienne_sf | |
53 | */ | |
54 | public class ImageReaderWriterHelper { | |
55 | ||
56 | /** | |
57 | * File input, from which the original picture should be read. | |
58 | */ | |
59 | 7 | FileImageInputStream fileImageInputStream = null; |
60 | ||
61 | /** | |
62 | * File output stream for the current transformation. | |
63 | */ | |
64 | FileImageOutputStream fileImageOutputStream; | |
65 | ||
66 | /** | |
67 | * The {@link PictureFileData} that this helper will have to help. | |
68 | */ | |
69 | PictureFileData pictureFileData; | |
70 | ||
71 | /** | |
72 | * Current ImageReader. Initialized by {@link #initImageReader()} | |
73 | */ | |
74 | 7 | ImageReader imageReader = null; |
75 | ||
76 | /** | |
77 | * Current ImageWriter. Initialized by {@link #initImageWriter()} | |
78 | */ | |
79 | 7 | ImageWriter imageWriter = null; |
80 | ||
81 | /** | |
82 | * Current ImageWriter. Initialized by {@link #initImageWriter()} | |
83 | */ | |
84 | 7 | ImageWriteParam imageWriterParam = null; |
85 | ||
86 | /** | |
87 | * Contains the target picture format (in lowercase): gif, jpg... It is used to find an ImageWriter, and to define | |
88 | * the target filename. | |
89 | */ | |
90 | String targetPictureFormat; | |
91 | ||
92 | /** | |
93 | * The current upload policy must be a {@link PictureUploadPolicy} | |
94 | */ | |
95 | PictureUploadPolicy uploadPolicy; | |
96 | ||
97 | // //////////////////////////////////////////////////////////////////// | |
98 | // //////////////////// CONSTRUCTOR | |
99 | // //////////////////////////////////////////////////////////////////// | |
100 | ||
101 | /** | |
102 | * Standard constructor. | |
103 | * | |
104 | * @param uploadPolicy The current upload policy. | |
105 | * @param pictureFileData The file data to be 'helped'. | |
106 | */ | |
107 | 7 | public ImageReaderWriterHelper(PictureUploadPolicy uploadPolicy, PictureFileData pictureFileData) { |
108 | 7 | this.uploadPolicy = uploadPolicy; |
109 | 7 | this.pictureFileData = pictureFileData; |
110 | ||
111 | 14 | this.targetPictureFormat = uploadPolicy.getImageFileConversionInfo().getTargetFormat( |
112 | 7 | pictureFileData.getFileExtension()); |
113 | 7 | } |
114 | ||
115 | // //////////////////////////////////////////////////////////////////// | |
116 | // //////////////////// PUBLIC METHODS | |
117 | // //////////////////////////////////////////////////////////////////// | |
118 | ||
119 | /** | |
120 | * returns the target picture format (lowercase, may be the same as the file extension) | |
121 | * | |
122 | * @return the target picture format (lowercase, may be the same as the file extension) | |
123 | */ | |
124 | public String getTargetPictureFormat() { | |
125 | 5 | return this.targetPictureFormat; |
126 | } | |
127 | ||
128 | /** | |
129 | * Creates a FileImageOutputStream, and assign it as the output to the imageWriter. | |
130 | * | |
131 | * @param file The file where the output stream must write. | |
132 | * @throws JUploadIOException Any error... | |
133 | */ | |
134 | public void setOutput(File file) throws JUploadIOException { | |
135 | // We first initialize internal variable. | |
136 | 5 | initImageWriter(); |
137 | ||
138 | try { | |
139 | 5 | this.fileImageOutputStream = new FileImageOutputStream(file); |
140 | 0 | } catch (IOException e) { |
141 | 0 | throw new JUploadIOException("ImageReaderWriterHelper.setOutput()", e); |
142 | 5 | } |
143 | 5 | this.imageWriter.setOutput(this.fileImageOutputStream); |
144 | 5 | } |
145 | ||
146 | /** | |
147 | * Free all reserved resource by this helper. Includes closing of any input or output stream. | |
148 | * | |
149 | * @throws JUploadIOException Any IO Exception | |
150 | */ | |
151 | public void dispose() throws JUploadIOException { | |
152 | // First: let's free any resource used by ImageWriter. | |
153 | 7 | if (this.imageWriter != null) { |
154 | // An imageWriter was initialized. Let's free it. | |
155 | 5 | this.imageWriter.dispose(); |
156 | 5 | if (this.fileImageOutputStream != null) { |
157 | try { | |
158 | 5 | this.fileImageOutputStream.close(); |
159 | 0 | } catch (IOException e) { |
160 | 0 | throw new JUploadIOException("ImageReaderWriterHelper.dispose() [fileImageOutputStream]", e); |
161 | 5 | } |
162 | } | |
163 | 5 | this.imageWriter = null; |
164 | 5 | this.fileImageOutputStream = null; |
165 | } | |
166 | ||
167 | // Then, all about ImageReader | |
168 | 7 | if (this.imageReader != null) { |
169 | // An imageReader was initialized. Let's free it. | |
170 | 7 | this.imageReader.dispose(); |
171 | try { | |
172 | 7 | this.fileImageInputStream.close(); |
173 | 0 | } catch (IOException e) { |
174 | 0 | throw new JUploadIOException("ImageReaderWriterHelper.dispose() [fileImageInputStream]", e); |
175 | 7 | } |
176 | 7 | this.imageReader = null; |
177 | 7 | this.fileImageInputStream = null; |
178 | } | |
179 | 7 | } |
180 | ||
181 | /** | |
182 | * Call to imageReader.getNumImages(boolean). Caution: may be long, for big files. | |
183 | * | |
184 | * @param allowSearch | |
185 | * @return The number of image into this file. | |
186 | * @throws JUploadIOException | |
187 | */ | |
188 | public int getNumImages(boolean allowSearch) throws JUploadIOException { | |
189 | 0 | initImageReader(); |
190 | try { | |
191 | 0 | return this.imageReader.getNumImages(allowSearch); |
192 | 0 | } catch (IOException e) { |
193 | 0 | throw new JUploadIOException("ImageReaderWriterHelper.getNumImages() [fileImageInputStream]", e); |
194 | } | |
195 | } | |
196 | ||
197 | /** | |
198 | * Call to ImageIO.read(fileImageInputStream). | |
199 | * | |
200 | * @return The BufferedImage read | |
201 | * @throws JUploadIOException public BufferedImage imageIORead() throws JUploadIOException { try { return | |
202 | * ImageIO.read(this.pictureFileData.getWorkingSourceFile()); } catch (IOException e) { throw new | |
203 | * JUploadIOException( "ImageReaderWriterHelper.ImageIORead()", e); } } | |
204 | */ | |
205 | ||
206 | /** | |
207 | * Read an image, from the pictureFileData. | |
208 | * | |
209 | * @param imageIndex The index number of the picture, in the file. 0 for the first picture (only valid value for | |
210 | * picture containing one picture) | |
211 | * @return The image corresponding to this index, in the picture file. | |
212 | * @throws JUploadIOException | |
213 | * @throws IndexOutOfBoundsException Occurs when the imageIndex is wrong. | |
214 | */ | |
215 | public BufferedImage readImage(int imageIndex) throws JUploadIOException, IndexOutOfBoundsException { | |
216 | 7 | initImageReader(); |
217 | try { | |
218 | 14 | this.uploadPolicy.displayDebug("ImageReaderWriterHelper: reading picture number " + imageIndex |
219 | 7 | + " of file " + this.pictureFileData.getFileName(), 30); |
220 | 7 | return this.imageReader.read(imageIndex); |
221 | 0 | } catch (IndexOutOfBoundsException e) { |
222 | // The IndexOutOfBoundsException is transmitted to the caller. It | |
223 | // indicates that the index is out of bound. It's the good way for | |
224 | // the caller to stop the loop through available pictures. | |
225 | 0 | throw e; |
226 | 0 | } catch (IOException e) { |
227 | 0 | throw new JUploadIOException("ImageReaderWriterHelper.readImage(" + imageIndex + ")", e); |
228 | } | |
229 | } | |
230 | ||
231 | /** | |
232 | * Read an image, from the pictureFileData. | |
233 | * | |
234 | * @param imageIndex The index number of the picture, in the file. 0 for the first picture (only valid value for | |
235 | * picture containing one picture) | |
236 | * @return The full image data for this index | |
237 | * @throws JUploadIOException | |
238 | * @throws IndexOutOfBoundsException Occurs when the imageIndex is wrong. | |
239 | */ | |
240 | public IIOImage readAll(int imageIndex) throws JUploadIOException, IndexOutOfBoundsException { | |
241 | 0 | initImageReader(); |
242 | try { | |
243 | 0 | this.uploadPolicy.displayDebug("ImageReaderWriterHelper: reading picture number " + imageIndex |
244 | 0 | + " of file " + this.pictureFileData.getFileName(), 30); |
245 | 0 | return this.imageReader.readAll(imageIndex, this.imageReader.getDefaultReadParam()); |
246 | 0 | } catch (IndexOutOfBoundsException e) { |
247 | // The IndexOutOfBoundsException is transmitted to the caller. It | |
248 | // indicates that the index is out of bound. It's the good way for | |
249 | // the caller to stop the loop through available pictures. | |
250 | 0 | throw e; |
251 | 0 | } catch (IOException e) { |
252 | 0 | throw new JUploadIOException("ImageReaderWriterHelper.readAll(" + imageIndex + ")", e); |
253 | } | |
254 | } | |
255 | ||
256 | /** | |
257 | * Load the metadata associated with one picture in the picture file. | |
258 | * | |
259 | * @param imageIndex | |
260 | * @return The metadata loaded | |
261 | * @throws JUploadIOException Any IOException is encapsulated in this exception | |
262 | */ | |
263 | public IIOMetadata getImageMetadata(int imageIndex) throws JUploadIOException { | |
264 | // We must have the reader initialized | |
265 | 5 | initImageReader(); |
266 | ||
267 | // Ok, let's go | |
268 | try { | |
269 | 10 | this.uploadPolicy.displayDebug("ImageReaderWriterHelper: reading metadata for picture number " + imageIndex |
270 | 5 | + " of file " + this.pictureFileData.getFileName(), 30); |
271 | 5 | return this.imageReader.getImageMetadata(imageIndex); |
272 | 0 | } catch (IOException e) { |
273 | 0 | throw new JUploadIOException("ImageReaderWriterHelper.getImageMetadata()", e); |
274 | } | |
275 | } | |
276 | ||
277 | /** | |
278 | * Write a picture in the output picture file. Called just before an upload. | |
279 | * | |
280 | * @param numIndex The index of the image in the transformed picture file. | |
281 | * @param iioImage The image to write. | |
282 | * @param iwp The parameter to use to write this image. | |
283 | * @throws JUploadIOException | |
284 | */ | |
285 | public void writeInsert(int numIndex, IIOImage iioImage, ImageWriteParam iwp) throws JUploadIOException { | |
286 | 0 | initImageWriter(); |
287 | try { | |
288 | 0 | this.imageWriter.writeInsert(numIndex, iioImage, iwp); |
289 | 0 | } catch (IOException e) { |
290 | 0 | throw new JUploadIOException("ImageReaderWriterHelper.writeInsert()", e); |
291 | 0 | } |
292 | 0 | } |
293 | ||
294 | /** | |
295 | * Write a picture in the output picture file. Called just before an upload. | |
296 | * | |
297 | * @param iioImage The image to write. | |
298 | * @throws JUploadIOException | |
299 | */ | |
300 | public void write(IIOImage iioImage) throws JUploadIOException { | |
301 | 5 | initImageWriter(); |
302 | try { | |
303 | 5 | this.imageWriter.write(null, iioImage, this.imageWriterParam); |
304 | 0 | } catch (IOException e) { |
305 | 0 | throw new JUploadIOException("ImageReaderWriterHelper.write()", e); |
306 | 5 | } |
307 | 5 | } |
308 | ||
309 | // //////////////////////////////////////////////////////////////////// | |
310 | // //////////////////// PRIVATE METHODS | |
311 | // //////////////////////////////////////////////////////////////////// | |
312 | ||
313 | /** | |
314 | * Initialize the ImageWriter and the ImageWriteParam for the current picture helper. | |
315 | * | |
316 | * @throws JUploadIOException | |
317 | */ | |
318 | private void initImageWriter() throws JUploadIOException { | |
319 | 10 | if (this.imageWriter == null) { |
320 | // Get the writer (to choose the compression quality) | |
321 | // In the windows world, file extension may be in uppercase, which | |
322 | // is not compatible with the core Java API. | |
323 | 5 | Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName(this.targetPictureFormat); |
324 | 5 | if (!iter.hasNext()) { |
325 | // Too bad: no writer for the selected picture format | |
326 | ||
327 | // A particular case: no gif support in JRE 1.5. A JRE upgrade | |
328 | // must be done. | |
329 | 0 | if (this.targetPictureFormat.equals("gif") && System.getProperty("java.version").startsWith("1.5")) { |
330 | 0 | throw new JUploadIOException( |
331 | "gif pictures are not supported in Java 1.5. Please switch to JRE 1.6."); | |
332 | } | |
333 | ||
334 | 0 | throw new JUploadIOException("No writer for the '" + this.targetPictureFormat + "' picture format."); |
335 | } else { | |
336 | 5 | this.imageWriter = iter.next(); |
337 | 5 | this.imageWriterParam = this.imageWriter.getDefaultWriteParam(); |
338 | ||
339 | // For jpeg pictures, we force the compression level. | |
340 | 5 | if (this.targetPictureFormat.equalsIgnoreCase("jpg") |
341 | 5 | || this.targetPictureFormat.equalsIgnoreCase("jpeg")) { |
342 | 0 | this.imageWriterParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); |
343 | // Let's select a good compromise between picture size | |
344 | // and quality. | |
345 | 0 | this.imageWriterParam.setCompressionQuality(this.uploadPolicy.getPictureCompressionQuality()); |
346 | // In some case, we need to force the Huffman tables: | |
347 | 0 | ((JPEGImageWriteParam) this.imageWriterParam).setOptimizeHuffmanTables(true); |
348 | } | |
349 | ||
350 | // | |
351 | try { | |
352 | 5 | this.uploadPolicy.displayDebug( |
353 | 5 | "ImageWriter1 (used), CompressionQuality=" + this.imageWriterParam.getCompressionQuality(), |
354 | 50); | |
355 | 5 | } catch (Exception e2) { |
356 | // If we come here, compression is not supported for | |
357 | // this picture format, or parameters are not explicit | |
358 | // mode, or ... (etc). May trigger several different | |
359 | // errors. We just ignore them: this par of code is only | |
360 | // to write some debug info. | |
361 | 5 | this.uploadPolicy.displayWarn(e2.getClass().getName() + " in ImageReaderWriterHelper.java"); |
362 | 0 | } |
363 | } | |
364 | } | |
365 | 10 | }// initImageWriter |
366 | ||
367 | /** | |
368 | * Initialize the ImageReader for the current helper. | |
369 | * | |
370 | * @throws JUploadIOException | |
371 | */ | |
372 | private void initImageReader() throws JUploadIOException { | |
373 | // First: we open a ImageInputStream | |
374 | try { | |
375 | 12 | this.fileImageInputStream = new FileImageInputStream(this.pictureFileData.getWorkingSourceFile()); |
376 | 0 | } catch (IOException e) { |
377 | 0 | throw new JUploadIOException("ImageReaderWriterHelper.initImageReader()", e); |
378 | 12 | } |
379 | ||
380 | // Then: we create an ImageReader, and assign the ImageInputStream to | |
381 | // it. | |
382 | 12 | if (this.imageReader == null) { |
383 | 7 | String ext = DefaultFileData.getExtension(this.pictureFileData.getFileName()); |
384 | 7 | Iterator<ImageReader> iterator = ImageIO.getImageReadersBySuffix(ext); |
385 | 7 | if (iterator.hasNext()) { |
386 | 7 | this.imageReader = iterator.next(); |
387 | 7 | this.imageReader.setInput(this.fileImageInputStream); |
388 | 7 | this.uploadPolicy.displayDebug("Foud one reader for " + ext + " extension", 50); |
389 | }// while | |
390 | ||
391 | // Did we find our reader ? | |
392 | 7 | if (this.imageReader == null) { |
393 | 0 | this.uploadPolicy.displayErr("Found no reader for " + ext + " extension"); |
394 | 7 | } else if (this.uploadPolicy.getDebugLevel() >= 50) { |
395 | // This call may be long, so we do it only if useful. | |
396 | try { | |
397 | 14 | this.uploadPolicy.displayDebug("Nb images in " + this.pictureFileData.getFileName() + ": " |
398 | 7 | + this.imageReader.getNumImages(true), 50); |
399 | 0 | } catch (IOException e) { |
400 | // We mask the error, was just for debug... | |
401 | 7 | } |
402 | } | |
403 | } | |
404 | 12 | } |
405 | } |