1   /**
2    * Copyright (c) 2000-2008 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.util.JavaProps;
28  import com.liferay.util.FileUtil;
29  
30  import com.sun.media.jai.codec.ImageCodec;
31  import com.sun.media.jai.codec.ImageDecoder;
32  
33  import java.awt.Graphics2D;
34  import java.awt.Image;
35  import java.awt.image.BufferedImage;
36  import java.awt.image.DataBuffer;
37  import java.awt.image.IndexColorModel;
38  import java.awt.image.RenderedImage;
39  import java.awt.image.SampleModel;
40  
41  import java.io.ByteArrayInputStream;
42  import java.io.File;
43  import java.io.IOException;
44  import java.io.OutputStream;
45  
46  import java.util.Enumeration;
47  
48  import javax.imageio.ImageIO;
49  
50  import javax.media.jai.RenderedImageAdapter;
51  
52  import net.jmge.gif.Gif89Encoder;
53  
54  import org.apache.commons.logging.Log;
55  import org.apache.commons.logging.LogFactory;
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 BufferedImage convertImageType(BufferedImage sourceImage, int type) {
66          BufferedImage targetImage = new BufferedImage(
67              sourceImage.getWidth(), sourceImage.getHeight(), type);
68  
69          Graphics2D graphics = targetImage.createGraphics();
70  
71          graphics.drawRenderedImage(sourceImage, null);
72          graphics.dispose();
73  
74          return targetImage;
75      }
76  
77      public void encodeGIF(RenderedImage renderedImage, OutputStream out)
78          throws IOException {
79  
80          if (JavaProps.isJDK6()) {
81              ImageIO.write(renderedImage, "GIF", out);
82          }
83          else {
84              BufferedImage bufferedImage = getBufferedImage(renderedImage);
85  
86              if (!(bufferedImage.getColorModel() instanceof IndexColorModel)) {
87                  bufferedImage = convertImageType(
88                      bufferedImage, BufferedImage.TYPE_BYTE_INDEXED);
89              }
90  
91              Gif89Encoder encoder = new Gif89Encoder(bufferedImage);
92  
93              encoder.encode(out);
94          }
95      }
96  
97      public void encodeWBMP(RenderedImage renderedImage, OutputStream out)
98          throws InterruptedException, IOException {
99  
100         BufferedImage bufferedImage = getBufferedImage(renderedImage);
101 
102         SampleModel sampleModel = bufferedImage.getSampleModel();
103 
104         int type = sampleModel.getDataType();
105 
106         if ((bufferedImage.getType() != BufferedImage.TYPE_BYTE_BINARY) ||
107             (type < DataBuffer.TYPE_BYTE) || (type > DataBuffer.TYPE_INT) ||
108             (sampleModel.getNumBands() != 1) ||
109             (sampleModel.getSampleSize(0) != 1)) {
110 
111             BufferedImage binaryImage = new BufferedImage(
112                 bufferedImage.getWidth(), bufferedImage.getHeight(),
113                 BufferedImage.TYPE_BYTE_BINARY);
114 
115             binaryImage.getGraphics().drawImage(bufferedImage, 0, 0, null);
116 
117             renderedImage = binaryImage;
118         }
119 
120         if (!ImageIO.write(renderedImage, "wbmp", out)) {
121 
122             // See http://www.jguru.com/faq/view.jsp?EID=127723
123 
124             out.write(0);
125             out.write(0);
126             out.write(_toMultiByte(bufferedImage.getWidth()));
127             out.write(_toMultiByte(bufferedImage.getHeight()));
128 
129             DataBuffer dataBuffer = bufferedImage.getData().getDataBuffer();
130 
131             int size = dataBuffer.getSize();
132 
133             for (int i = 0; i < size; i++) {
134                 out.write((byte)dataBuffer.getElem(i));
135             }
136         }
137     }
138 
139     public BufferedImage getBufferedImage(RenderedImage renderedImage) {
140         if (renderedImage instanceof BufferedImage) {
141             return (BufferedImage)renderedImage;
142         }
143         else {
144             RenderedImageAdapter adapter = new RenderedImageAdapter(
145                 renderedImage);
146 
147             return adapter.getAsBufferedImage();
148         }
149     }
150 
151     public ImageBag read(File file) throws IOException {
152         return read(FileUtil.getBytes(file));
153     }
154 
155     public ImageBag read(byte[] bytes) throws IOException {
156         RenderedImage renderedImage = null;
157         String type = TYPE_NOT_AVAILABLE;
158 
159         Enumeration<ImageCodec> enu = ImageCodec.getCodecs();
160 
161         while (enu.hasMoreElements()) {
162             ImageCodec codec = enu.nextElement();
163 
164             if (codec.isFormatRecognized(bytes)) {
165                 type = codec.getFormatName();
166 
167                 ImageDecoder decoder = ImageCodec.createImageDecoder(
168                     type, new ByteArrayInputStream(bytes), null);
169 
170                 try {
171                     renderedImage = decoder.decodeAsRenderedImage();
172                 }
173                 catch (IOException ioe) {
174                     if (_log.isDebugEnabled()) {
175                         _log.debug(type + ": " + ioe.getMessage());
176                     }
177                 }
178 
179                 break;
180             }
181         }
182 
183         if (type.equals("jpeg")) {
184             type = ImageProcessor.TYPE_JPEG;
185         }
186 
187         return new ImageBag(renderedImage, type);
188     }
189 
190     public RenderedImage scale(
191         RenderedImage renderedImage, int maxHeight, int maxWidth) {
192 
193         int imageHeight = renderedImage.getHeight();
194         int imageWidth = renderedImage.getWidth();
195 
196         if (maxHeight == 0) {
197             maxHeight = imageHeight;
198         }
199 
200         if (maxWidth == 0) {
201             maxWidth = imageWidth;
202         }
203 
204         if ((imageHeight <= maxHeight) && (imageWidth <= maxWidth)) {
205             return renderedImage;
206         }
207 
208         double factor = Math.min(
209             (double)maxHeight / imageHeight, (double)maxWidth / imageWidth);
210 
211         int scaledHeight = (int)(factor * imageHeight);
212         int scaledWidth = (int)(factor * imageWidth);
213 
214         BufferedImage bufferedImage = getBufferedImage(renderedImage);
215 
216         Image scaledImage = bufferedImage.getScaledInstance(
217             scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
218 
219         BufferedImage scaledBufferedImage = new BufferedImage(
220             scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
221 
222         scaledBufferedImage.getGraphics().drawImage(scaledImage, 0, 0, null);
223 
224         return scaledBufferedImage;
225     }
226 
227     private byte[] _toMultiByte(int intValue) {
228         int numBits = 32;
229         int mask = 0x80000000;
230 
231         while (mask != 0 && (intValue & mask) == 0) {
232             numBits--;
233             mask >>>= 1;
234         }
235 
236         int numBitsLeft = numBits;
237         byte[] multiBytes = new byte[(numBitsLeft + 6) / 7];
238 
239         int maxIndex = multiBytes.length - 1;
240 
241         for (int b = 0; b <= maxIndex; b++) {
242             multiBytes[b] = (byte)((intValue >>> ((maxIndex - b) * 7)) & 0x7f);
243 
244             if (b != maxIndex) {
245                 multiBytes[b] |= (byte)0x80;
246             }
247         }
248 
249         return multiBytes;
250     }
251 
252     private static Log _log = LogFactory.getLog(ImageProcessorImpl.class);
253 
254 }