1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.image;
24  
25  import com.liferay.portal.kernel.image.ImageBag;
26  import com.liferay.portal.kernel.image.ImageProcessor;
27  import com.liferay.portal.kernel.log.Log;
28  import com.liferay.portal.kernel.log.LogFactoryUtil;
29  import com.liferay.portal.kernel.util.JavaProps;
30  import com.liferay.portal.util.FileImpl;
31  
32  import com.sun.media.jai.codec.ImageCodec;
33  import com.sun.media.jai.codec.ImageDecoder;
34  
35  import java.awt.Graphics2D;
36  import java.awt.Graphics;
37  import java.awt.Image;
38  import java.awt.image.BufferedImage;
39  import java.awt.image.DataBuffer;
40  import java.awt.image.IndexColorModel;
41  import java.awt.image.RenderedImage;
42  import java.awt.image.SampleModel;
43  
44  import java.io.ByteArrayInputStream;
45  import java.io.File;
46  import java.io.IOException;
47  import java.io.OutputStream;
48  
49  import java.util.Enumeration;
50  
51  import javax.imageio.ImageIO;
52  
53  import javax.media.jai.RenderedImageAdapter;
54  
55  import net.jmge.gif.Gif89Encoder;
56  
57  /**
58   * <a href="ImageProcessorImpl.java.html"><b><i>View Source</i></b></a>
59   *
60   * @author Brian Wing Shun Chan
61   *
62   */
63  public class ImageProcessorImpl implements ImageProcessor {
64  
65      public static ImageProcessorImpl getInstance() {
66          return _instance;
67      }
68  
69      public BufferedImage convertImageType(BufferedImage sourceImage, int type) {
70          BufferedImage targetImage = new BufferedImage(
71              sourceImage.getWidth(), sourceImage.getHeight(), type);
72  
73          Graphics2D graphics = targetImage.createGraphics();
74  
75          graphics.drawRenderedImage(sourceImage, null);
76  
77          graphics.dispose();
78  
79          return targetImage;
80      }
81  
82      public void encodeGIF(RenderedImage renderedImage, OutputStream os)
83          throws IOException {
84  
85          if (JavaProps.isJDK6()) {
86              ImageIO.write(renderedImage, "GIF", os);
87          }
88          else {
89              BufferedImage bufferedImage = getBufferedImage(renderedImage);
90  
91              if (!(bufferedImage.getColorModel() instanceof IndexColorModel)) {
92                  bufferedImage = convertImageType(
93                      bufferedImage, BufferedImage.TYPE_BYTE_INDEXED);
94              }
95  
96              Gif89Encoder encoder = new Gif89Encoder(bufferedImage);
97  
98              encoder.encode(os);
99          }
100     }
101 
102     public void encodeWBMP(RenderedImage renderedImage, OutputStream os)
103         throws IOException {
104 
105         BufferedImage bufferedImage = getBufferedImage(renderedImage);
106 
107         SampleModel sampleModel = bufferedImage.getSampleModel();
108 
109         int type = sampleModel.getDataType();
110 
111         if ((bufferedImage.getType() != BufferedImage.TYPE_BYTE_BINARY) ||
112             (type < DataBuffer.TYPE_BYTE) || (type > DataBuffer.TYPE_INT) ||
113             (sampleModel.getNumBands() != 1) ||
114             (sampleModel.getSampleSize(0) != 1)) {
115 
116             BufferedImage binaryImage = new BufferedImage(
117                 bufferedImage.getWidth(), bufferedImage.getHeight(),
118                 BufferedImage.TYPE_BYTE_BINARY);
119 
120             Graphics graphics = binaryImage.getGraphics();
121 
122             graphics.drawImage(bufferedImage, 0, 0, null);
123 
124             renderedImage = binaryImage;
125         }
126 
127         if (!ImageIO.write(renderedImage, "wbmp", os)) {
128 
129             // See http://www.jguru.com/faq/view.jsp?EID=127723
130 
131             os.write(0);
132             os.write(0);
133             os.write(_toMultiByte(bufferedImage.getWidth()));
134             os.write(_toMultiByte(bufferedImage.getHeight()));
135 
136             DataBuffer dataBuffer = bufferedImage.getData().getDataBuffer();
137 
138             int size = dataBuffer.getSize();
139 
140             for (int i = 0; i < size; i++) {
141                 os.write((byte)dataBuffer.getElem(i));
142             }
143         }
144     }
145 
146     public BufferedImage getBufferedImage(RenderedImage renderedImage) {
147         if (renderedImage instanceof BufferedImage) {
148             return (BufferedImage)renderedImage;
149         }
150         else {
151             RenderedImageAdapter adapter = new RenderedImageAdapter(
152                 renderedImage);
153 
154             return adapter.getAsBufferedImage();
155         }
156     }
157 
158     public ImageBag read(File file) throws IOException {
159         return read(_fileUtil.getBytes(file));
160     }
161 
162     public ImageBag read(byte[] bytes) {
163         RenderedImage renderedImage = null;
164         String type = TYPE_NOT_AVAILABLE;
165 
166         Enumeration<ImageCodec> enu = ImageCodec.getCodecs();
167 
168         while (enu.hasMoreElements()) {
169             ImageCodec codec = enu.nextElement();
170 
171             if (codec.isFormatRecognized(bytes)) {
172                 type = codec.getFormatName();
173 
174                 ImageDecoder decoder = ImageCodec.createImageDecoder(
175                     type, new ByteArrayInputStream(bytes), null);
176 
177                 try {
178                     renderedImage = decoder.decodeAsRenderedImage();
179                 }
180                 catch (IOException ioe) {
181                     if (_log.isDebugEnabled()) {
182                         _log.debug(type + ": " + ioe.getMessage());
183                     }
184                 }
185 
186                 break;
187             }
188         }
189 
190         if (type.equals("jpeg")) {
191             type = ImageProcessor.TYPE_JPEG;
192         }
193 
194         return new ImageBag(renderedImage, type);
195     }
196 
197     public RenderedImage scale(
198         RenderedImage renderedImage, int maxHeight, int maxWidth) {
199 
200         int imageHeight = renderedImage.getHeight();
201         int imageWidth = renderedImage.getWidth();
202 
203         if (maxHeight == 0) {
204             maxHeight = imageHeight;
205         }
206 
207         if (maxWidth == 0) {
208             maxWidth = imageWidth;
209         }
210 
211         if ((imageHeight <= maxHeight) && (imageWidth <= maxWidth)) {
212             return renderedImage;
213         }
214 
215         double factor = Math.min(
216             (double)maxHeight / imageHeight, (double)maxWidth / imageWidth);
217 
218         int scaledHeight = Math.max(1, (int)(factor * imageHeight));
219         int scaledWidth = Math.max(1, (int)(factor * imageWidth));
220 
221         BufferedImage bufferedImage = getBufferedImage(renderedImage);
222 
223         Image scaledImage = bufferedImage.getScaledInstance(
224             scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
225 
226         BufferedImage scaledBufferedImage = new BufferedImage(
227             scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
228 
229         scaledBufferedImage.getGraphics().drawImage(scaledImage, 0, 0, null);
230 
231         return scaledBufferedImage;
232     }
233 
234     private byte[] _toMultiByte(int intValue) {
235         int numBits = 32;
236         int mask = 0x80000000;
237 
238         while (mask != 0 && (intValue & mask) == 0) {
239             numBits--;
240             mask >>>= 1;
241         }
242 
243         int numBitsLeft = numBits;
244         byte[] multiBytes = new byte[(numBitsLeft + 6) / 7];
245 
246         int maxIndex = multiBytes.length - 1;
247 
248         for (int b = 0; b <= maxIndex; b++) {
249             multiBytes[b] = (byte)((intValue >>> ((maxIndex - b) * 7)) & 0x7f);
250 
251             if (b != maxIndex) {
252                 multiBytes[b] |= (byte)0x80;
253             }
254         }
255 
256         return multiBytes;
257     }
258 
259     private static Log _log = LogFactoryUtil.getLog(ImageProcessorImpl.class);
260 
261     private static ImageProcessorImpl _instance = new ImageProcessorImpl();
262 
263     private static FileImpl _fileUtil = FileImpl.getInstance();
264 
265 }