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.io.unsync.UnsyncByteArrayInputStream;
28  import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
29  import com.liferay.portal.kernel.log.Log;
30  import com.liferay.portal.kernel.log.LogFactoryUtil;
31  import com.liferay.portal.kernel.util.JavaProps;
32  import com.liferay.portal.util.FileImpl;
33  
34  import com.sun.media.jai.codec.ImageCodec;
35  import com.sun.media.jai.codec.ImageDecoder;
36  import com.sun.media.jai.codec.ImageEncoder;
37  
38  import java.awt.Graphics2D;
39  import java.awt.Graphics;
40  import java.awt.Image;
41  import java.awt.image.BufferedImage;
42  import java.awt.image.DataBuffer;
43  import java.awt.image.IndexColorModel;
44  import java.awt.image.RenderedImage;
45  import java.awt.image.SampleModel;
46  
47  import java.io.File;
48  import java.io.IOException;
49  import java.io.OutputStream;
50  
51  import java.util.Enumeration;
52  
53  import javax.imageio.ImageIO;
54  
55  import javax.media.jai.RenderedImageAdapter;
56  
57  import net.jmge.gif.Gif89Encoder;
58  
59  /**
60   * <a href="ImageProcessorImpl.java.html"><b><i>View Source</i></b></a>
61   *
62   * @author Brian Wing Shun Chan
63   */
64  public class ImageProcessorImpl implements ImageProcessor {
65  
66      public static ImageProcessorImpl getInstance() {
67          return _instance;
68      }
69  
70      public BufferedImage convertImageType(BufferedImage sourceImage, int type) {
71          BufferedImage targetImage = new BufferedImage(
72              sourceImage.getWidth(), sourceImage.getHeight(), type);
73  
74          Graphics2D graphics = targetImage.createGraphics();
75  
76          graphics.drawRenderedImage(sourceImage, null);
77  
78          graphics.dispose();
79  
80          return targetImage;
81      }
82  
83      public void encodeGIF(RenderedImage renderedImage, OutputStream os)
84          throws IOException {
85  
86          if (JavaProps.isJDK6()) {
87              ImageIO.write(renderedImage, TYPE_GIF, os);
88          }
89          else {
90              BufferedImage bufferedImage = getBufferedImage(renderedImage);
91  
92              if (!(bufferedImage.getColorModel() instanceof IndexColorModel)) {
93                  bufferedImage = convertImageType(
94                      bufferedImage, BufferedImage.TYPE_BYTE_INDEXED);
95              }
96  
97              Gif89Encoder encoder = new Gif89Encoder(bufferedImage);
98  
99              encoder.encode(os);
100         }
101     }
102 
103     public void encodeWBMP(RenderedImage renderedImage, OutputStream os)
104         throws IOException {
105 
106         BufferedImage bufferedImage = getBufferedImage(renderedImage);
107 
108         SampleModel sampleModel = bufferedImage.getSampleModel();
109 
110         int type = sampleModel.getDataType();
111 
112         if ((bufferedImage.getType() != BufferedImage.TYPE_BYTE_BINARY) ||
113             (type < DataBuffer.TYPE_BYTE) || (type > DataBuffer.TYPE_INT) ||
114             (sampleModel.getNumBands() != 1) ||
115             (sampleModel.getSampleSize(0) != 1)) {
116 
117             BufferedImage binaryImage = new BufferedImage(
118                 bufferedImage.getWidth(), bufferedImage.getHeight(),
119                 BufferedImage.TYPE_BYTE_BINARY);
120 
121             Graphics graphics = binaryImage.getGraphics();
122 
123             graphics.drawImage(bufferedImage, 0, 0, null);
124 
125             renderedImage = binaryImage;
126         }
127 
128         if (!ImageIO.write(renderedImage, "wbmp", os)) {
129 
130             // See http://www.jguru.com/faq/view.jsp?EID=127723
131 
132             os.write(0);
133             os.write(0);
134             os.write(_toMultiByte(bufferedImage.getWidth()));
135             os.write(_toMultiByte(bufferedImage.getHeight()));
136 
137             DataBuffer dataBuffer = bufferedImage.getData().getDataBuffer();
138 
139             int size = dataBuffer.getSize();
140 
141             for (int i = 0; i < size; i++) {
142                 os.write((byte)dataBuffer.getElem(i));
143             }
144         }
145     }
146 
147     public BufferedImage getBufferedImage(RenderedImage renderedImage) {
148         if (renderedImage instanceof BufferedImage) {
149             return (BufferedImage)renderedImage;
150         }
151         else {
152             RenderedImageAdapter adapter = new RenderedImageAdapter(
153                 renderedImage);
154 
155             return adapter.getAsBufferedImage();
156         }
157     }
158 
159     public byte[] getBytes(RenderedImage renderedImage, String contentType)
160         throws IOException {
161 
162         UnsyncByteArrayOutputStream baos = new UnsyncByteArrayOutputStream();
163 
164         if (contentType.indexOf(TYPE_BMP) != -1) {
165             ImageEncoder encoder = ImageCodec.createImageEncoder(
166                 TYPE_BMP, baos, null);
167 
168             encoder.encode(renderedImage);
169         }
170         else if (contentType.indexOf(TYPE_GIF) != -1) {
171             encodeGIF(renderedImage, baos);
172         }
173         else if ((contentType.indexOf(TYPE_JPEG) != -1) ||
174                  (contentType.indexOf("jpeg") != -1)) {
175 
176             ImageIO.write(renderedImage, "jpeg", baos);
177         }
178         else if (contentType.indexOf(TYPE_PNG) != -1) {
179             ImageIO.write(renderedImage, TYPE_PNG, baos);
180         }
181         else if (contentType.indexOf("tif") != -1) {
182             ImageEncoder encoder = ImageCodec.createImageEncoder(
183                 TYPE_TIFF, baos, null);
184 
185             encoder.encode(renderedImage);
186         }
187 
188         return baos.toByteArray();
189     }
190 
191     public ImageBag read(File file) throws IOException {
192         return read(_fileUtil.getBytes(file));
193     }
194 
195     public ImageBag read(byte[] bytes) {
196         RenderedImage renderedImage = null;
197         String type = TYPE_NOT_AVAILABLE;
198 
199         Enumeration<ImageCodec> enu = ImageCodec.getCodecs();
200 
201         while (enu.hasMoreElements()) {
202             ImageCodec codec = enu.nextElement();
203 
204             if (codec.isFormatRecognized(bytes)) {
205                 type = codec.getFormatName();
206 
207                 ImageDecoder decoder = ImageCodec.createImageDecoder(
208                     type, new UnsyncByteArrayInputStream(bytes), null);
209 
210                 try {
211                     renderedImage = decoder.decodeAsRenderedImage();
212                 }
213                 catch (IOException ioe) {
214                     if (_log.isDebugEnabled()) {
215                         _log.debug(type + ": " + ioe.getMessage());
216                     }
217                 }
218 
219                 break;
220             }
221         }
222 
223         if (type.equals("jpeg")) {
224             type = TYPE_JPEG;
225         }
226 
227         return new ImageBag(renderedImage, type);
228     }
229 
230     public RenderedImage scale(
231         RenderedImage renderedImage, int maxHeight, int maxWidth) {
232 
233         int imageHeight = renderedImage.getHeight();
234         int imageWidth = renderedImage.getWidth();
235 
236         if (maxHeight == 0) {
237             maxHeight = imageHeight;
238         }
239 
240         if (maxWidth == 0) {
241             maxWidth = imageWidth;
242         }
243 
244         if ((imageHeight <= maxHeight) && (imageWidth <= maxWidth)) {
245             return renderedImage;
246         }
247 
248         double factor = Math.min(
249             (double)maxHeight / imageHeight, (double)maxWidth / imageWidth);
250 
251         int scaledHeight = Math.max(1, (int)(factor * imageHeight));
252         int scaledWidth = Math.max(1, (int)(factor * imageWidth));
253 
254         BufferedImage bufferedImage = getBufferedImage(renderedImage);
255 
256         Image scaledImage = bufferedImage.getScaledInstance(
257             scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
258 
259         BufferedImage scaledBufferedImage = new BufferedImage(
260             scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
261 
262         scaledBufferedImage.getGraphics().drawImage(scaledImage, 0, 0, null);
263 
264         return scaledBufferedImage;
265     }
266 
267     private byte[] _toMultiByte(int intValue) {
268         int numBits = 32;
269         int mask = 0x80000000;
270 
271         while (mask != 0 && (intValue & mask) == 0) {
272             numBits--;
273             mask >>>= 1;
274         }
275 
276         int numBitsLeft = numBits;
277         byte[] multiBytes = new byte[(numBitsLeft + 6) / 7];
278 
279         int maxIndex = multiBytes.length - 1;
280 
281         for (int b = 0; b <= maxIndex; b++) {
282             multiBytes[b] = (byte)((intValue >>> ((maxIndex - b) * 7)) & 0x7f);
283 
284             if (b != maxIndex) {
285                 multiBytes[b] |= (byte)0x80;
286             }
287         }
288 
289         return multiBytes;
290     }
291 
292     private static Log _log = LogFactoryUtil.getLog(ImageProcessorImpl.class);
293 
294     private static ImageProcessorImpl _instance = new ImageProcessorImpl();
295 
296     private static FileImpl _fileUtil = FileImpl.getInstance();
297 
298 }