1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17   * SOFTWARE.
18   */
19  
20  package com.liferay.portal.image;
21  
22  import com.liferay.portal.kernel.image.ImageBag;
23  import com.liferay.portal.kernel.image.ImageProcessor;
24  import com.liferay.portal.kernel.log.Log;
25  import com.liferay.portal.kernel.log.LogFactoryUtil;
26  import com.liferay.portal.kernel.util.JavaProps;
27  import com.liferay.portal.util.FileImpl;
28  
29  import com.sun.media.jai.codec.ImageCodec;
30  import com.sun.media.jai.codec.ImageDecoder;
31  
32  import java.awt.Graphics2D;
33  import java.awt.Graphics;
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  /**
55   * <a href="ImageProcessorImpl.java.html"><b><i>View Source</i></b></a>
56   *
57   * @author Brian Wing Shun Chan
58   *
59   */
60  public class ImageProcessorImpl implements ImageProcessor {
61  
62      public static ImageProcessorImpl getInstance() {
63          return _instance;
64      }
65  
66      public BufferedImage convertImageType(BufferedImage sourceImage, int type) {
67          BufferedImage targetImage = new BufferedImage(
68              sourceImage.getWidth(), sourceImage.getHeight(), type);
69  
70          Graphics2D graphics = targetImage.createGraphics();
71  
72          graphics.drawRenderedImage(sourceImage, null);
73  
74          graphics.dispose();
75  
76          return targetImage;
77      }
78  
79      public void encodeGIF(RenderedImage renderedImage, OutputStream os)
80          throws IOException {
81  
82          if (JavaProps.isJDK6()) {
83              ImageIO.write(renderedImage, "GIF", os);
84          }
85          else {
86              BufferedImage bufferedImage = getBufferedImage(renderedImage);
87  
88              if (!(bufferedImage.getColorModel() instanceof IndexColorModel)) {
89                  bufferedImage = convertImageType(
90                      bufferedImage, BufferedImage.TYPE_BYTE_INDEXED);
91              }
92  
93              Gif89Encoder encoder = new Gif89Encoder(bufferedImage);
94  
95              encoder.encode(os);
96          }
97      }
98  
99      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     public BufferedImage getBufferedImage(RenderedImage renderedImage) {
144         if (renderedImage instanceof BufferedImage) {
145             return (BufferedImage)renderedImage;
146         }
147         else {
148             RenderedImageAdapter adapter = new RenderedImageAdapter(
149                 renderedImage);
150 
151             return adapter.getAsBufferedImage();
152         }
153     }
154 
155     public ImageBag read(File file) throws IOException {
156         return read(_fileUtil.getBytes(file));
157     }
158 
159     public ImageBag read(byte[] bytes) {
160         RenderedImage renderedImage = null;
161         String type = TYPE_NOT_AVAILABLE;
162 
163         Enumeration<ImageCodec> enu = ImageCodec.getCodecs();
164 
165         while (enu.hasMoreElements()) {
166             ImageCodec codec = enu.nextElement();
167 
168             if (codec.isFormatRecognized(bytes)) {
169                 type = codec.getFormatName();
170 
171                 ImageDecoder decoder = ImageCodec.createImageDecoder(
172                     type, new ByteArrayInputStream(bytes), null);
173 
174                 try {
175                     renderedImage = decoder.decodeAsRenderedImage();
176                 }
177                 catch (IOException ioe) {
178                     if (_log.isDebugEnabled()) {
179                         _log.debug(type + ": " + ioe.getMessage());
180                     }
181                 }
182 
183                 break;
184             }
185         }
186 
187         if (type.equals("jpeg")) {
188             type = ImageProcessor.TYPE_JPEG;
189         }
190 
191         return new ImageBag(renderedImage, type);
192     }
193 
194     public RenderedImage scale(
195         RenderedImage renderedImage, int maxHeight, int maxWidth) {
196 
197         int imageHeight = renderedImage.getHeight();
198         int imageWidth = renderedImage.getWidth();
199 
200         if (maxHeight == 0) {
201             maxHeight = imageHeight;
202         }
203 
204         if (maxWidth == 0) {
205             maxWidth = imageWidth;
206         }
207 
208         if ((imageHeight <= maxHeight) && (imageWidth <= maxWidth)) {
209             return renderedImage;
210         }
211 
212         double factor = Math.min(
213             (double)maxHeight / imageHeight, (double)maxWidth / imageWidth);
214 
215         int scaledHeight = Math.max(1, (int)(factor * imageHeight));
216         int scaledWidth = Math.max(1, (int)(factor * imageWidth));
217 
218         BufferedImage bufferedImage = getBufferedImage(renderedImage);
219 
220         Image scaledImage = bufferedImage.getScaledInstance(
221             scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
222 
223         BufferedImage scaledBufferedImage = new BufferedImage(
224             scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
225 
226         scaledBufferedImage.getGraphics().drawImage(scaledImage, 0, 0, null);
227 
228         return scaledBufferedImage;
229     }
230 
231     private byte[] _toMultiByte(int intValue) {
232         int numBits = 32;
233         int mask = 0x80000000;
234 
235         while (mask != 0 && (intValue & mask) == 0) {
236             numBits--;
237             mask >>>= 1;
238         }
239 
240         int numBitsLeft = numBits;
241         byte[] multiBytes = new byte[(numBitsLeft + 6) / 7];
242 
243         int maxIndex = multiBytes.length - 1;
244 
245         for (int b = 0; b <= maxIndex; b++) {
246             multiBytes[b] = (byte)((intValue >>> ((maxIndex - b) * 7)) & 0x7f);
247 
248             if (b != maxIndex) {
249                 multiBytes[b] |= (byte)0x80;
250             }
251         }
252 
253         return multiBytes;
254     }
255 
256     private static Log _log = LogFactoryUtil.getLog(ImageProcessorImpl.class);
257 
258     private static ImageProcessorImpl _instance = new ImageProcessorImpl();
259 
260     private static FileImpl _fileUtil = FileImpl.getInstance();
261 
262 }