001    /**
002     * Copyright (c) 2000-2012 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.ImageTool;
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    import java.awt.image.WritableRaster;
039    
040    import java.io.File;
041    import java.io.IOException;
042    import java.io.OutputStream;
043    
044    import java.util.Enumeration;
045    
046    import javax.imageio.ImageIO;
047    
048    import javax.media.jai.RenderedImageAdapter;
049    
050    import net.jmge.gif.Gif89Encoder;
051    
052    /**
053     * @author Brian Wing Shun Chan
054     */
055    public class ImageToolImpl implements ImageTool {
056    
057            public static ImageTool getInstance() {
058                    return _instance;
059            }
060    
061            public BufferedImage convertImageType(BufferedImage sourceImage, int type) {
062                    BufferedImage targetImage = new BufferedImage(
063                            sourceImage.getWidth(), sourceImage.getHeight(), type);
064    
065                    Graphics2D graphics = targetImage.createGraphics();
066    
067                    graphics.drawRenderedImage(sourceImage, null);
068    
069                    graphics.dispose();
070    
071                    return targetImage;
072            }
073    
074            public void encodeGIF(RenderedImage renderedImage, OutputStream os)
075                    throws IOException {
076    
077                    if (JavaProps.isJDK6()) {
078                            ImageIO.write(renderedImage, TYPE_GIF, os);
079                    }
080                    else {
081                            BufferedImage bufferedImage = getBufferedImage(renderedImage);
082    
083                            if (!(bufferedImage.getColorModel() instanceof IndexColorModel)) {
084                                    bufferedImage = convertImageType(
085                                            bufferedImage, BufferedImage.TYPE_BYTE_INDEXED);
086                            }
087    
088                            Gif89Encoder encoder = new Gif89Encoder(bufferedImage);
089    
090                            encoder.encode(os);
091                    }
092            }
093    
094            public void encodeWBMP(RenderedImage renderedImage, OutputStream os)
095                    throws IOException {
096    
097                    BufferedImage bufferedImage = getBufferedImage(renderedImage);
098    
099                    SampleModel sampleModel = bufferedImage.getSampleModel();
100    
101                    int type = sampleModel.getDataType();
102    
103                    if ((bufferedImage.getType() != BufferedImage.TYPE_BYTE_BINARY) ||
104                            (type < DataBuffer.TYPE_BYTE) || (type > DataBuffer.TYPE_INT) ||
105                            (sampleModel.getNumBands() != 1) ||
106                            (sampleModel.getSampleSize(0) != 1)) {
107    
108                            BufferedImage binaryImage = new BufferedImage(
109                                    bufferedImage.getWidth(), bufferedImage.getHeight(),
110                                    BufferedImage.TYPE_BYTE_BINARY);
111    
112                            Graphics graphics = binaryImage.getGraphics();
113    
114                            graphics.drawImage(bufferedImage, 0, 0, null);
115    
116                            renderedImage = binaryImage;
117                    }
118    
119                    if (!ImageIO.write(renderedImage, "wbmp", os)) {
120    
121                            // See http://www.jguru.com/faq/view.jsp?EID=127723
122    
123                            os.write(0);
124                            os.write(0);
125                            os.write(_toMultiByte(bufferedImage.getWidth()));
126                            os.write(_toMultiByte(bufferedImage.getHeight()));
127    
128                            DataBuffer dataBuffer = bufferedImage.getData().getDataBuffer();
129    
130                            int size = dataBuffer.getSize();
131    
132                            for (int i = 0; i < size; i++) {
133                                    os.write((byte)dataBuffer.getElem(i));
134                            }
135                    }
136            }
137    
138            public BufferedImage getBufferedImage(RenderedImage renderedImage) {
139                    if (renderedImage instanceof BufferedImage) {
140                            return (BufferedImage)renderedImage;
141                    }
142                    else {
143                            RenderedImageAdapter adapter = new RenderedImageAdapter(
144                                    renderedImage);
145    
146                            return adapter.getAsBufferedImage();
147                    }
148            }
149    
150            public byte[] getBytes(RenderedImage renderedImage, String contentType)
151                    throws IOException {
152    
153                    UnsyncByteArrayOutputStream baos = new UnsyncByteArrayOutputStream();
154    
155                    write(renderedImage, contentType, baos);
156    
157                    return baos.toByteArray();
158            }
159    
160            public ImageBag read(byte[] bytes) {
161                    RenderedImage renderedImage = null;
162                    String type = TYPE_NOT_AVAILABLE;
163    
164                    Enumeration<ImageCodec> enu = ImageCodec.getCodecs();
165    
166                    while (enu.hasMoreElements()) {
167                            ImageCodec codec = enu.nextElement();
168    
169                            if (codec.isFormatRecognized(bytes)) {
170                                    type = codec.getFormatName();
171    
172                                    ImageDecoder decoder = ImageCodec.createImageDecoder(
173                                            type, new UnsyncByteArrayInputStream(bytes), null);
174    
175                                    try {
176                                            renderedImage = decoder.decodeAsRenderedImage();
177                                    }
178                                    catch (IOException ioe) {
179                                            if (_log.isDebugEnabled()) {
180                                                    _log.debug(type + ": " + ioe.getMessage());
181                                            }
182                                    }
183    
184                                    break;
185                            }
186                    }
187    
188                    if (type.equals("jpeg")) {
189                            type = TYPE_JPEG;
190                    }
191    
192                    return new ImageBag(renderedImage, type);
193            }
194    
195            public ImageBag read(File file) throws IOException {
196                    return read(_fileUtil.getBytes(file));
197            }
198    
199            public RenderedImage scale(RenderedImage renderedImage, int width) {
200                    if (width <= 0) {
201                            return renderedImage;
202                    }
203    
204                    int imageHeight = renderedImage.getHeight();
205                    int imageWidth = renderedImage.getWidth();
206    
207                    double factor = (double) width / imageWidth;
208    
209                    int scaledHeight = (int)(factor * imageHeight);
210                    int scaledWidth = width;
211    
212                    BufferedImage bufferedImage = getBufferedImage(renderedImage);
213    
214                    int type = bufferedImage.getType();
215    
216                    if (type == 0) {
217                            type = BufferedImage.TYPE_INT_ARGB;
218                    }
219    
220                    BufferedImage scaledBufferedImage = new BufferedImage(
221                            scaledWidth, scaledHeight, type);
222    
223                    Graphics graphics = scaledBufferedImage.getGraphics();
224    
225                    Image scaledImage = bufferedImage.getScaledInstance(
226                            scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
227    
228                    graphics.drawImage(scaledImage, 0, 0, null);
229    
230                    return scaledBufferedImage;
231            }
232    
233            public RenderedImage scale(
234                    RenderedImage renderedImage, int maxHeight, int maxWidth) {
235    
236                    int imageHeight = renderedImage.getHeight();
237                    int imageWidth = renderedImage.getWidth();
238    
239                    if (maxHeight == 0) {
240                            maxHeight = imageHeight;
241                    }
242    
243                    if (maxWidth == 0) {
244                            maxWidth = imageWidth;
245                    }
246    
247                    if ((imageHeight <= maxHeight) && (imageWidth <= maxWidth)) {
248                            return renderedImage;
249                    }
250    
251                    double factor = Math.min(
252                            (double)maxHeight / imageHeight, (double)maxWidth / imageWidth);
253    
254                    int scaledHeight = Math.max(1, (int)(factor * imageHeight));
255                    int scaledWidth = Math.max(1, (int)(factor * imageWidth));
256    
257                    BufferedImage bufferedImage = getBufferedImage(renderedImage);
258    
259                    int type = bufferedImage.getType();
260    
261                    if (type == 0) {
262                            type = BufferedImage.TYPE_INT_ARGB;
263                    }
264    
265                    BufferedImage scaledBufferedImage = null;
266    
267                    if ((type == BufferedImage.TYPE_BYTE_BINARY) ||
268                            (type == BufferedImage.TYPE_BYTE_INDEXED)) {
269    
270                            IndexColorModel indexColorModel =
271                                    (IndexColorModel)bufferedImage.getColorModel();
272    
273                            BufferedImage tempBufferedImage = new BufferedImage(
274                                    1, 1, type, indexColorModel);
275    
276                            int bits = indexColorModel.getPixelSize();
277                            int size = indexColorModel.getMapSize();
278    
279                            byte[] reds = new byte[size];
280    
281                            indexColorModel.getReds(reds);
282    
283                            byte[] greens = new byte[size];
284    
285                            indexColorModel.getGreens(greens);
286    
287                            byte[] blues = new byte[size];
288    
289                            indexColorModel.getBlues(blues);
290    
291                            WritableRaster writableRaster = tempBufferedImage.getRaster();
292    
293                            int pixel = writableRaster.getSample(0, 0, 0);
294    
295                            IndexColorModel scaledIndexColorModel = new IndexColorModel(
296                                    bits, size, reds, greens, blues, pixel);
297    
298                            scaledBufferedImage = new BufferedImage(
299                                    scaledWidth, scaledHeight, type, scaledIndexColorModel);
300                    }
301                    else {
302                            scaledBufferedImage = new BufferedImage(
303                                    scaledWidth, scaledHeight, type);
304                    }
305    
306                    Graphics graphics = scaledBufferedImage.getGraphics();
307    
308                    Image scaledImage = bufferedImage.getScaledInstance(
309                            scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
310    
311                    graphics.drawImage(scaledImage, 0, 0, null);
312    
313                    return scaledBufferedImage;
314            }
315    
316            public void write(
317                            RenderedImage renderedImage, String contentType, OutputStream os)
318                    throws IOException {
319    
320                    if (contentType.contains(TYPE_BMP)) {
321                            ImageEncoder imageEncoder = ImageCodec.createImageEncoder(
322                                    TYPE_BMP, os, null);
323    
324                            imageEncoder.encode(renderedImage);
325                    }
326                    else if (contentType.contains(TYPE_GIF)) {
327                            encodeGIF(renderedImage, os);
328                    }
329                    else if ((contentType.contains(TYPE_JPEG)) ||
330                                     (contentType.contains("jpeg"))) {
331    
332                            ImageIO.write(renderedImage, "jpeg", os);
333                    }
334                    else if (contentType.contains(TYPE_PNG)) {
335                            ImageIO.write(renderedImage, TYPE_PNG, os);
336                    }
337                    else if (contentType.contains(TYPE_TIFF) ||
338                                     contentType.contains("tif")) {
339    
340                            ImageEncoder imageEncoder = ImageCodec.createImageEncoder(
341                                    TYPE_TIFF, os, null);
342    
343                            imageEncoder.encode(renderedImage);
344                    }
345            }
346    
347            private byte[] _toMultiByte(int intValue) {
348                    int numBits = 32;
349                    int mask = 0x80000000;
350    
351                    while (mask != 0 && (intValue & mask) == 0) {
352                            numBits--;
353                            mask >>>= 1;
354                    }
355    
356                    int numBitsLeft = numBits;
357                    byte[] multiBytes = new byte[(numBitsLeft + 6) / 7];
358    
359                    int maxIndex = multiBytes.length - 1;
360    
361                    for (int b = 0; b <= maxIndex; b++) {
362                            multiBytes[b] = (byte)((intValue >>> ((maxIndex - b) * 7)) & 0x7f);
363    
364                            if (b != maxIndex) {
365                                    multiBytes[b] |= (byte)0x80;
366                            }
367                    }
368    
369                    return multiBytes;
370            }
371    
372            private static Log _log = LogFactoryUtil.getLog(ImageToolImpl.class);
373    
374            private static ImageTool _instance = new ImageToolImpl();
375    
376            private static FileImpl _fileUtil = FileImpl.getInstance();
377    
378    }