1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights 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  public class ImageProcessorImpl implements ImageProcessor {
63  
64      public static ImageProcessorImpl getInstance() {
65          return _instance;
66      }
67  
68      public BufferedImage convertImageType(BufferedImage sourceImage, int type) {
69          BufferedImage targetImage = new BufferedImage(
70              sourceImage.getWidth(), sourceImage.getHeight(), type);
71  
72          Graphics2D graphics = targetImage.createGraphics();
73  
74          graphics.drawRenderedImage(sourceImage, null);
75  
76          graphics.dispose();
77  
78          return targetImage;
79      }
80  
81      public void encodeGIF(RenderedImage renderedImage, OutputStream os)
82          throws IOException {
83  
84          if (JavaProps.isJDK6()) {
85              ImageIO.write(renderedImage, "GIF", os);
86          }
87          else {
88              BufferedImage bufferedImage = getBufferedImage(renderedImage);
89  
90              if (!(bufferedImage.getColorModel() instanceof IndexColorModel)) {
91                  bufferedImage = convertImageType(
92                      bufferedImage, BufferedImage.TYPE_BYTE_INDEXED);
93              }
94  
95              Gif89Encoder encoder = new Gif89Encoder(bufferedImage);
96  
97              encoder.encode(os);
98          }
99      }
100 
101     public void encodeWBMP(RenderedImage renderedImage, OutputStream os)
102         throws IOException {
103 
104         BufferedImage bufferedImage = getBufferedImage(renderedImage);
105 
106         SampleModel sampleModel = bufferedImage.getSampleModel();
107 
108         int type = sampleModel.getDataType();
109 
110         if ((bufferedImage.getType() != BufferedImage.TYPE_BYTE_BINARY) ||
111             (type < DataBuffer.TYPE_BYTE) || (type > DataBuffer.TYPE_INT) ||
112             (sampleModel.getNumBands() != 1) ||
113             (sampleModel.getSampleSize(0) != 1)) {
114 
115             BufferedImage binaryImage = new BufferedImage(
116                 bufferedImage.getWidth(), bufferedImage.getHeight(),
117                 BufferedImage.TYPE_BYTE_BINARY);
118 
119             Graphics graphics = binaryImage.getGraphics();
120 
121             graphics.drawImage(bufferedImage, 0, 0, null);
122 
123             renderedImage = binaryImage;
124         }
125 
126         if (!ImageIO.write(renderedImage, "wbmp", os)) {
127 
128             // See http://www.jguru.com/faq/view.jsp?EID=127723
129 
130             os.write(0);
131             os.write(0);
132             os.write(_toMultiByte(bufferedImage.getWidth()));
133             os.write(_toMultiByte(bufferedImage.getHeight()));
134 
135             DataBuffer dataBuffer = bufferedImage.getData().getDataBuffer();
136 
137             int size = dataBuffer.getSize();
138 
139             for (int i = 0; i < size; i++) {
140                 os.write((byte)dataBuffer.getElem(i));
141             }
142         }
143     }
144 
145     public BufferedImage getBufferedImage(RenderedImage renderedImage) {
146         if (renderedImage instanceof BufferedImage) {
147             return (BufferedImage)renderedImage;
148         }
149         else {
150             RenderedImageAdapter adapter = new RenderedImageAdapter(
151                 renderedImage);
152 
153             return adapter.getAsBufferedImage();
154         }
155     }
156 
157     public ImageBag read(File file) throws IOException {
158         return read(_fileUtil.getBytes(file));
159     }
160 
161     public ImageBag read(byte[] bytes) {
162         RenderedImage renderedImage = null;
163         String type = TYPE_NOT_AVAILABLE;
164 
165         Enumeration<ImageCodec> enu = ImageCodec.getCodecs();
166 
167         while (enu.hasMoreElements()) {
168             ImageCodec codec = enu.nextElement();
169 
170             if (codec.isFormatRecognized(bytes)) {
171                 type = codec.getFormatName();
172 
173                 ImageDecoder decoder = ImageCodec.createImageDecoder(
174                     type, new ByteArrayInputStream(bytes), null);
175 
176                 try {
177                     renderedImage = decoder.decodeAsRenderedImage();
178                 }
179                 catch (IOException ioe) {
180                     if (_log.isDebugEnabled()) {
181                         _log.debug(type + ": " + ioe.getMessage());
182                     }
183                 }
184 
185                 break;
186             }
187         }
188 
189         if (type.equals("jpeg")) {
190             type = ImageProcessor.TYPE_JPEG;
191         }
192 
193         return new ImageBag(renderedImage, type);
194     }
195 
196     public RenderedImage scale(
197         RenderedImage renderedImage, int maxHeight, int maxWidth) {
198 
199         int imageHeight = renderedImage.getHeight();
200         int imageWidth = renderedImage.getWidth();
201 
202         if (maxHeight == 0) {
203             maxHeight = imageHeight;
204         }
205 
206         if (maxWidth == 0) {
207             maxWidth = imageWidth;
208         }
209 
210         if ((imageHeight <= maxHeight) && (imageWidth <= maxWidth)) {
211             return renderedImage;
212         }
213 
214         double factor = Math.min(
215             (double)maxHeight / imageHeight, (double)maxWidth / imageWidth);
216 
217         int scaledHeight = Math.max(1, (int)(factor * imageHeight));
218         int scaledWidth = Math.max(1, (int)(factor * imageWidth));
219 
220         BufferedImage bufferedImage = getBufferedImage(renderedImage);
221 
222         Image scaledImage = bufferedImage.getScaledInstance(
223             scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
224 
225         BufferedImage scaledBufferedImage = new BufferedImage(
226             scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
227 
228         scaledBufferedImage.getGraphics().drawImage(scaledImage, 0, 0, null);
229 
230         return scaledBufferedImage;
231     }
232 
233     private byte[] _toMultiByte(int intValue) {
234         int numBits = 32;
235         int mask = 0x80000000;
236 
237         while (mask != 0 && (intValue & mask) == 0) {
238             numBits--;
239             mask >>>= 1;
240         }
241 
242         int numBitsLeft = numBits;
243         byte[] multiBytes = new byte[(numBitsLeft + 6) / 7];
244 
245         int maxIndex = multiBytes.length - 1;
246 
247         for (int b = 0; b <= maxIndex; b++) {
248             multiBytes[b] = (byte)((intValue >>> ((maxIndex - b) * 7)) & 0x7f);
249 
250             if (b != maxIndex) {
251                 multiBytes[b] |= (byte)0x80;
252             }
253         }
254 
255         return multiBytes;
256     }
257 
258     private static Log _log = LogFactoryUtil.getLog(ImageProcessorImpl.class);
259 
260     private static ImageProcessorImpl _instance = new ImageProcessorImpl();
261 
262     private static FileImpl _fileUtil = FileImpl.getInstance();
263 
264 }