001package net.gdface.image;
002
003import java.awt.Rectangle;
004import java.awt.image.BufferedImage;
005import java.io.ByteArrayInputStream;
006import java.io.File;
007import java.io.FileInputStream;
008import java.io.FileNotFoundException;
009import java.io.IOException;
010import java.util.Iterator;
011
012import javax.imageio.ImageIO;
013import javax.imageio.ImageReadParam;
014import javax.imageio.ImageReader;
015import javax.imageio.ImageTypeSpecifier;
016import javax.imageio.stream.ImageInputStream;
017import javax.imageio.stream.MemoryCacheImageInputStream;
018
019import net.gdface.utils.Assert;
020import net.gdface.utils.FaceUtilits;
021
022/**
023 * 图像数据处理对象<br>
024 * {@link #open()}可以在不将图像全部解码加载到内存而获取图像的基本信息<br>
025 * {@link #read(ImageReadParam)}使用内存做cache读取(不使用临时文件做cache)<br>
026 * {@link #read(Rectangle, ImageTypeSpecifier)}可以对图像指定区域解码
027 * @author guyadong
028 *
029 */
030public class LazyImage extends BaseLazyImage implements ImageMatrix{
031        private Rectangle rectangle=null;
032        private ImageReader imageReader;
033        private MemoryCacheImageInputStream imageInputstream;
034        private BufferedImage bufferedImage=null;
035        /**
036         * 通过{@link ImageReader}来读取图像基本信息,检查图像数据有效性
037         * @return 
038         * @throws UnsupportedFormatException 
039         * @throws NotImageException 
040         */
041        @SuppressWarnings("unchecked")
042        @Override
043        public LazyImage open() throws UnsupportedFormatException, NotImageException {
044                try {
045                        if(bufferedImage == null){
046                                Iterator<ImageReader> it = ImageIO.getImageReaders(getImageInputstream());
047                                if (it.hasNext())
048                                        try {
049                                                imageReader = it.next();
050                                                imageReader.setInput(getImageInputstream(), true, true);
051                                                this.suffix = imageReader.getFormatName().trim().toLowerCase();
052                                                this.width = imageReader.getWidth(0);
053                                                this.height = imageReader.getHeight(0);
054                                        } catch (Exception e) {                                         
055                                                throw new UnsupportedFormatException(e);
056                                        } 
057                                else {
058                                        // 没有找到对应的图像解码则招聘异常
059                                        throw new NotImageException("NOT FOUND MATCHED ImageReader");
060                                }
061                        }else{
062                                this.width = bufferedImage.getWidth();
063                                this.height = bufferedImage.getHeight();
064                        }
065                        return this;
066                } finally {
067                        if (autoClose)
068                                try {
069                                        close();
070                                } catch (IOException e) {
071                                        throw new RuntimeException(e);
072                                }
073                }
074        }
075        
076        
077        /**
078         * 对图像数据解码生成 {@link BufferedImage}对象
079         * 
080         * @param param
081         *            图像读取参数对象,为null使用默认参数<br>
082         *            参见 {@link ImageReader#getDefaultReadParam()}
083         * @return
084         * @throws UnsupportedFormatException
085         * @see ImageReadParam
086         */
087        protected BufferedImage read(ImageReadParam param) throws UnsupportedFormatException {
088                try {
089                        if(null==this.bufferedImage||null!=param){
090                                ImageReader imageReader=getImageReader();
091                                if(null==imageReader.getInput())
092                                        imageReader.setInput(getImageInputstream(), true,true);                         
093                                BufferedImage bi = imageReader.read(0, null==param?imageReader.getDefaultReadParam():param);
094                                if (null==param)
095                                        this.bufferedImage = bi;
096                                else
097                                        return bi;
098                        }
099                        return this.bufferedImage;
100                } catch (Exception e) {
101                        throw new UnsupportedFormatException(e);
102                } finally {
103                        if(autoClose)
104                                try {
105                                        close();
106                                } catch (IOException e) {
107                                        throw new RuntimeException(e);
108                                }
109                }
110        }
111        public BufferedImage read() throws UnsupportedFormatException{
112                return read(null);
113        }
114        /**
115         * 对图像数据指定的区域解码
116         * 
117         * @param rect
118         *            解码区域对象, 默认({@code null})全图解码<br>
119         *            参见 {@link ImageReadParam#setSourceRegion(Rectangle)}
120         * @param destinationType
121         *            目标图像的所需图像类型,默认为null, <br>
122         *            例如用此参数可以在解码时指定输出的图像类型为RGB,<br>
123         *            如下代码 生成 destinationType参数对象:<br>
124         *            {@code 
125         *                      ImageTypeSpecifier destinationType=ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
126         *            }<br>
127         *            用上面的ImageTypeSpecifier对象来调用此方法返回的BufferedImage对象中的raster成员(通过BufferedImage.getData()获取)
128         *            的getDataElements()方法返回的就是包含RGB数据byte[]类型的数组<br>
129         *            而直接用BufferedImage.getRGB()方式只能获取ARGB类型的int[]数组 参见
130         *            {@link ImageReadParam#setDestinationType(ImageTypeSpecifier) }
131         * @return
132         * @throws UnsupportedFormatException
133         * @see #read(ImageReadParam)
134         */
135        public BufferedImage read(Rectangle rect, ImageTypeSpecifier destinationType) throws UnsupportedFormatException {
136                ImageReadParam param = getImageReader().getDefaultReadParam();
137                if (rect != null && !rect.equals(getRectangle()))
138                        param.setSourceRegion(rect);
139                param.setDestinationType(destinationType);              
140                if(null!=destinationType)
141                        param.setDestination(destinationType.createBufferedImage(width, height));
142                return read(param);
143        }
144        @Override
145        public byte[] getMatrixRGBA() throws UnsupportedFormatException{
146                if (matrixRGBA==null){
147                        matrixRGBA=ImageUtil.getMatrixRGBA(read());
148                }
149                return matrixRGBA;
150        }
151
152        @Override
153        public byte[] getMatrixRGB() throws UnsupportedFormatException{
154                if (matrixRGB==null){
155                        matrixRGB=ImageUtil.getMatrixRGB(read());
156                }
157                return matrixRGB;
158        }
159
160        @Override
161        public byte[] getMatrixBGR() throws UnsupportedFormatException{
162                if (matrixBGR==null){   
163                        matrixBGR=ImageUtil.getMatrixBGR(read());
164                }
165                return matrixBGR;
166        }
167        /**
168         *  对图像数据指定的区域解码返回灰度图像数据
169         * @return 灰度图像矩阵数据
170         * @throws UnsupportedFormatException
171         */
172        @Override
173        public byte[] getMatrixGray() throws UnsupportedFormatException{                
174                if(null==matrixGray){
175                        matrixGray = ImageUtil.getMatrixGRAY(read());
176                }
177                return matrixGray;
178
179        }
180        @Override
181        public byte[] wirtePNGBytes(){
182                try {
183                        if("PNG".equalsIgnoreCase(getSuffix())){
184                                if(getImgBytes() != null){
185                                        return getImgBytes();
186                                }
187                        }
188                        return ImageUtil.wirtePNGBytes(read());
189                } catch (UnsupportedFormatException e) {
190                        throw new RuntimeException(e);
191                }
192        }
193        @Override
194        public byte[] wirteJPEGBytes(){
195                try {
196                        if("JPEG".equalsIgnoreCase(getSuffix())){
197                                if(getImgBytes() != null){
198                                        return getImgBytes();
199                                }
200                        }
201                        return ImageUtil.wirteJPEGBytes(read(),0.9f);
202                } catch (UnsupportedFormatException e) {
203                        throw new RuntimeException(e);
204                }
205        }
206        /**
207         * 创建并打开对象
208         * @param imgBytes
209         * @return
210         * @throws NotImageException
211         * @throws UnsupportedFormatException
212         * @see #LazyImage(byte[])
213         * @see #open()
214         */
215        public static LazyImage create(final byte[] imgBytes) throws NotImageException, UnsupportedFormatException {
216                return new LazyImage(imgBytes).open();
217        }
218        /**
219         * 创建对象
220         * @param bufferedImage
221         * @see #LazyImage(BufferedImage)
222         * @see #open()
223         * @return
224         */
225        public static LazyImage create(final BufferedImage bufferedImage) {
226                try {
227                        return new LazyImage(bufferedImage).open();
228                } catch (ImageErrorException e) {
229                        throw new RuntimeException(e);
230                }
231        }
232        /**
233         * 用本地图像文件创建对象
234         * @param file
235         * @param md5 {@code file}的MD5较验码,可以为null
236         * @return
237         * @throws NotImageException
238         * @throws UnsupportedFormatException
239         */
240        public static LazyImage create(final File file, String md5) throws NotImageException, UnsupportedFormatException {
241                try {
242                        return new LazyImage(file, md5).open();
243                } catch (FileNotFoundException e) {
244                        throw new RuntimeException(e);
245                }
246        }
247        
248        /**
249         * 多源创建对象
250         * @param src
251         * @return
252         * @throws NotImageException
253         * @throws UnsupportedFormatException
254         * @see #LazyImage(Object)
255         * @see FaceUtilits#getBytesNotEmpty(Object)
256         */
257        public static <T> LazyImage create(final T src) throws NotImageException, UnsupportedFormatException {
258                try {
259                        return new LazyImage(src).open();
260                } catch (IOException e) {
261                        throw new RuntimeException(e);
262                }
263        }
264        
265        /**
266         * @param bufferedImage 已经解码的图像数据,为null或为空,则抛出 {@link IllegalArgumentException}
267         */
268        public LazyImage(BufferedImage bufferedImage)
269        {
270                Assert.notNull(bufferedImage, "bufferedImage");
271                this.bufferedImage = bufferedImage;
272        }
273        /**
274         * @param imgBytes 图像数据,{@code imgBytes}为null或为空,则抛出 {@link IllegalArgumentException}
275         */
276        public LazyImage(byte[] imgBytes) {
277                super(imgBytes);
278        }
279
280        /**
281         * 用本地图像文件创建对象
282         * @param src
283         * @param md5
284         * @throws FileNotFoundException
285         */
286        public LazyImage(File src, String md5) throws FileNotFoundException {
287                super(src, md5);
288        }
289        
290        /**
291         * 多源创建对象
292         * @param src
293         * @throws IOException
294         * @see FaceUtilits#getBytesNotEmpty(Object)
295         */
296        public <T>LazyImage(T src) throws IOException {
297                this(FaceUtilits.getBytesNotEmpty(src));                
298        }
299
300        /**
301         * 返回{@link ImageInputStream}对象<br>
302         * 如果 {@link #imageInputstream} 为{@code null},则根据 {@link #imgBytes}或 {@link #localFile}创建
303         * @return imageInputstream
304         */
305        private ImageInputStream getImageInputstream() {
306                if (null == imageInputstream) {
307                        if (null == imgBytes) {
308                                if (null == localFile)
309                                        throw new IllegalArgumentException(
310                                                        "while isValidImage be true localFile & imgBytes can't be NULL all");
311                                try {
312                                        this.imageInputstream = new MemoryCacheImageInputStream(new FileInputStream(localFile));
313                                } catch (FileNotFoundException e) {
314                                        throw new RuntimeException(e);
315                                }
316                        }else{
317                                this.imageInputstream = new MemoryCacheImageInputStream(new ByteArrayInputStream(imgBytes));
318                        }
319                }
320                return imageInputstream;
321        }
322        
323        /**
324         * 返回{@link ImageReader}对象<br>
325         * 如果 {@link #imageReader}为{@code null},则根据 {@link #suffix}创建,失败抛出
326         * @return imageReader {@link ImageReader}对象
327         * @throws IllegalStateException 无法根据{@link #suffix}获取{@link ImageReader}
328         */
329        private ImageReader getImageReader() throws IllegalStateException {
330                if (null == imageReader) {
331                        Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(suffix);
332                        if (it.hasNext())
333                                imageReader = it.next();
334                        else
335                                throw new IllegalStateException(String.format("invalid suffix %s", suffix));
336                }
337                return imageReader;
338        }
339
340        /**
341         * 释放资源
342         * @throws IOException
343         */
344        public void close() throws IOException{
345                if(null!=imageReader){
346                        imageReader.dispose();
347                        imageReader=null;
348                }
349                if(null!=imageInputstream){
350                        imageInputstream.close();
351                        imageInputstream=null;
352                }
353                super.close();
354        }
355        @Override
356        public void finalize() throws Throwable {
357                this.bufferedImage=null;
358                this.rectangle=null;
359                super.finalize();
360        }
361
362
363        /**
364         * 获取图像矩形对象
365         * @return rectangle
366         */
367        public Rectangle getRectangle() {
368                if(null==rectangle)
369                        rectangle=new Rectangle(0,0,width,height);
370                return rectangle;
371        }       
372}