001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.image;
016    
017    import com.liferay.portal.kernel.image.ImageBag;
018    import com.liferay.portal.kernel.image.ImageProcessor;
019    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
020    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.util.JavaProps;
024    import com.liferay.portal.util.FileImpl;
025    
026    import com.sun.media.jai.codec.ImageCodec;
027    import com.sun.media.jai.codec.ImageDecoder;
028    import com.sun.media.jai.codec.ImageEncoder;
029    
030    import java.awt.Graphics2D;
031    import java.awt.Graphics;
032    import java.awt.Image;
033    import java.awt.image.BufferedImage;
034    import java.awt.image.DataBuffer;
035    import java.awt.image.IndexColorModel;
036    import java.awt.image.RenderedImage;
037    import java.awt.image.SampleModel;
038    
039    import java.io.File;
040    import java.io.IOException;
041    import java.io.OutputStream;
042    
043    import java.util.Enumeration;
044    
045    import javax.imageio.ImageIO;
046    
047    import javax.media.jai.RenderedImageAdapter;
048    
049    import net.jmge.gif.Gif89Encoder;
050    
051    /**
052     * @author Brian Wing Shun Chan
053     */
054    public class ImageProcessorImpl implements ImageProcessor {
055    
056            public static ImageProcessorImpl getInstance() {
057                    return _instance;
058            }
059    
060            public BufferedImage convertImageType(BufferedImage sourceImage, int type) {
061                    BufferedImage targetImage = new BufferedImage(
062                            sourceImage.getWidth(), sourceImage.getHeight(), type);
063    
064                    Graphics2D graphics = targetImage.createGraphics();
065    
066                    graphics.drawRenderedImage(sourceImage, null);
067    
068                    graphics.dispose();
069    
070                    return targetImage;
071            }
072    
073            public void encodeGIF(RenderedImage renderedImage, OutputStream os)
074                    throws IOException {
075    
076                    if (JavaProps.isJDK6()) {
077                            ImageIO.write(renderedImage, TYPE_GIF, os);
078                    }
079                    else {
080                            BufferedImage bufferedImage = getBufferedImage(renderedImage);
081    
082                            if (!(bufferedImage.getColorModel() instanceof IndexColorModel)) {
083                                    bufferedImage = convertImageType(
084                                            bufferedImage, BufferedImage.TYPE_BYTE_INDEXED);
085                            }
086    
087                            Gif89Encoder encoder = new Gif89Encoder(bufferedImage);
088    
089                            encoder.encode(os);
090                    }
091            }
092    
093            public void encodeWBMP(RenderedImage renderedImage, OutputStream os)
094                    throws IOException {
095    
096                    BufferedImage bufferedImage = getBufferedImage(renderedImage);
097    
098                    SampleModel sampleModel = bufferedImage.getSampleModel();
099    
100                    int type = sampleModel.getDataType();
101    
102                    if ((bufferedImage.getType() != BufferedImage.TYPE_BYTE_BINARY) ||
103                            (type < DataBuffer.TYPE_BYTE) || (type > DataBuffer.TYPE_INT) ||
104                            (sampleModel.getNumBands() != 1) ||
105                            (sampleModel.getSampleSize(0) != 1)) {
106    
107                            BufferedImage binaryImage = new BufferedImage(
108                                    bufferedImage.getWidth(), bufferedImage.getHeight(),
109                                    BufferedImage.TYPE_BYTE_BINARY);
110    
111                            Graphics graphics = binaryImage.getGraphics();
112    
113                            graphics.drawImage(bufferedImage, 0, 0, null);
114    
115                            renderedImage = binaryImage;
116                    }
117    
118                    if (!ImageIO.write(renderedImage, "wbmp", os)) {
119    
120                            // See http://www.jguru.com/faq/view.jsp?EID=127723
121    
122                            os.write(0);
123                            os.write(0);
124                            os.write(_toMultiByte(bufferedImage.getWidth()));
125                            os.write(_toMultiByte(bufferedImage.getHeight()));
126    
127                            DataBuffer dataBuffer = bufferedImage.getData().getDataBuffer();
128    
129                            int size = dataBuffer.getSize();
130    
131                            for (int i = 0; i < size; i++) {
132                                    os.write((byte)dataBuffer.getElem(i));
133                            }
134                    }
135            }
136    
137            public BufferedImage getBufferedImage(RenderedImage renderedImage) {
138                    if (renderedImage instanceof BufferedImage) {
139                            return (BufferedImage)renderedImage;
140                    }
141                    else {
142                            RenderedImageAdapter adapter = new RenderedImageAdapter(
143                                    renderedImage);
144    
145                            return adapter.getAsBufferedImage();
146                    }
147            }
148    
149            public byte[] getBytes(RenderedImage renderedImage, String contentType)
150                    throws IOException {
151    
152                    UnsyncByteArrayOutputStream baos = new UnsyncByteArrayOutputStream();
153    
154                    if (contentType.indexOf(TYPE_BMP) != -1) {
155                            ImageEncoder encoder = ImageCodec.createImageEncoder(
156                                    TYPE_BMP, baos, null);
157    
158                            encoder.encode(renderedImage);
159                    }
160                    else if (contentType.indexOf(TYPE_GIF) != -1) {
161                            encodeGIF(renderedImage, baos);
162                    }
163                    else if ((contentType.indexOf(TYPE_JPEG) != -1) ||
164                                     (contentType.indexOf("jpeg") != -1)) {
165    
166                            ImageIO.write(renderedImage, "jpeg", baos);
167                    }
168                    else if (contentType.indexOf(TYPE_PNG) != -1) {
169                            ImageIO.write(renderedImage, TYPE_PNG, baos);
170                    }
171                    else if (contentType.indexOf("tif") != -1) {
172                            ImageEncoder encoder = ImageCodec.createImageEncoder(
173                                    TYPE_TIFF, baos, null);
174    
175                            encoder.encode(renderedImage);
176                    }
177    
178                    return baos.toByteArray();
179            }
180    
181            public ImageBag read(File file) throws IOException {
182                    return read(_fileUtil.getBytes(file));
183            }
184    
185            public ImageBag read(byte[] bytes) {
186                    RenderedImage renderedImage = null;
187                    String type = TYPE_NOT_AVAILABLE;
188    
189                    Enumeration<ImageCodec> enu = ImageCodec.getCodecs();
190    
191                    while (enu.hasMoreElements()) {
192                            ImageCodec codec = enu.nextElement();
193    
194                            if (codec.isFormatRecognized(bytes)) {
195                                    type = codec.getFormatName();
196    
197                                    ImageDecoder decoder = ImageCodec.createImageDecoder(
198                                            type, new UnsyncByteArrayInputStream(bytes), null);
199    
200                                    try {
201                                            renderedImage = decoder.decodeAsRenderedImage();
202                                    }
203                                    catch (IOException ioe) {
204                                            if (_log.isDebugEnabled()) {
205                                                    _log.debug(type + ": " + ioe.getMessage());
206                                            }
207                                    }
208    
209                                    break;
210                            }
211                    }
212    
213                    if (type.equals("jpeg")) {
214                            type = TYPE_JPEG;
215                    }
216    
217                    return new ImageBag(renderedImage, type);
218            }
219    
220            public RenderedImage scale(
221                    RenderedImage renderedImage, int maxHeight, int maxWidth) {
222    
223                    int imageHeight = renderedImage.getHeight();
224                    int imageWidth = renderedImage.getWidth();
225    
226                    if (maxHeight == 0) {
227                            maxHeight = imageHeight;
228                    }
229    
230                    if (maxWidth == 0) {
231                            maxWidth = imageWidth;
232                    }
233    
234                    if ((imageHeight <= maxHeight) && (imageWidth <= maxWidth)) {
235                            return renderedImage;
236                    }
237    
238                    double factor = Math.min(
239                            (double)maxHeight / imageHeight, (double)maxWidth / imageWidth);
240    
241                    int scaledHeight = Math.max(1, (int)(factor * imageHeight));
242                    int scaledWidth = Math.max(1, (int)(factor * imageWidth));
243    
244                    BufferedImage bufferedImage = getBufferedImage(renderedImage);
245    
246                    Image scaledImage = bufferedImage.getScaledInstance(
247                            scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
248    
249                    BufferedImage scaledBufferedImage = new BufferedImage(
250                            scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
251    
252                    scaledBufferedImage.getGraphics().drawImage(scaledImage, 0, 0, null);
253    
254                    return scaledBufferedImage;
255            }
256    
257            private byte[] _toMultiByte(int intValue) {
258                    int numBits = 32;
259                    int mask = 0x80000000;
260    
261                    while (mask != 0 && (intValue & mask) == 0) {
262                            numBits--;
263                            mask >>>= 1;
264                    }
265    
266                    int numBitsLeft = numBits;
267                    byte[] multiBytes = new byte[(numBitsLeft + 6) / 7];
268    
269                    int maxIndex = multiBytes.length - 1;
270    
271                    for (int b = 0; b <= maxIndex; b++) {
272                            multiBytes[b] = (byte)((intValue >>> ((maxIndex - b) * 7)) & 0x7f);
273    
274                            if (b != maxIndex) {
275                                    multiBytes[b] |= (byte)0x80;
276                            }
277                    }
278    
279                    return multiBytes;
280            }
281    
282            private static Log _log = LogFactoryUtil.getLog(ImageProcessorImpl.class);
283    
284            private static ImageProcessorImpl _instance = new ImageProcessorImpl();
285    
286            private static FileImpl _fileUtil = FileImpl.getInstance();
287    
288    }