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