001
014
015 package com.liferay.portal.image;
016
017 import com.liferay.portal.kernel.image.ImageBag;
018 import com.liferay.portal.kernel.image.ImageMagick;
019 import com.liferay.portal.kernel.image.ImageTool;
020 import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
021 import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
022 import com.liferay.portal.kernel.log.Log;
023 import com.liferay.portal.kernel.log.LogFactoryUtil;
024 import com.liferay.portal.kernel.util.JavaDetector;
025 import com.liferay.portal.util.FileImpl;
026
027 import com.sun.media.jai.codec.ImageCodec;
028 import com.sun.media.jai.codec.ImageDecoder;
029 import com.sun.media.jai.codec.ImageEncoder;
030
031 import java.awt.Graphics2D;
032 import java.awt.Graphics;
033 import java.awt.Image;
034 import java.awt.image.BufferedImage;
035 import java.awt.image.DataBuffer;
036 import java.awt.image.IndexColorModel;
037 import java.awt.image.RenderedImage;
038 import java.awt.image.SampleModel;
039 import java.awt.image.WritableRaster;
040
041 import java.io.File;
042 import java.io.IOException;
043 import java.io.InputStream;
044 import java.io.OutputStream;
045
046 import java.util.Enumeration;
047 import java.util.concurrent.ExecutionException;
048 import java.util.concurrent.Future;
049 import java.util.concurrent.TimeUnit;
050 import java.util.concurrent.TimeoutException;
051
052 import javax.imageio.ImageIO;
053
054 import javax.media.jai.RenderedImageAdapter;
055
056 import net.jmge.gif.Gif89Encoder;
057
058 import org.im4java.core.IMOperation;
059
060
064 public class ImageToolImpl implements ImageTool {
065
066 public static ImageTool getInstance() {
067 return _instance;
068 }
069
070 public Future<RenderedImage> convertCMYKtoRGB(byte[] bytes, String type) {
071 ImageMagick imageMagick = getImageMagick();
072
073 if (!imageMagick.isEnabled()) {
074 return null;
075 }
076
077 File inputFile = _fileUtil.createTempFile(type);
078 File outputFile = _fileUtil.createTempFile(type);
079
080 try {
081 _fileUtil.write(inputFile, bytes);
082
083 IMOperation imOperation = new IMOperation();
084
085 imOperation.addRawArgs("-format", "%[colorspace]");
086 imOperation.addImage(inputFile.getPath());
087
088 String[] output = imageMagick.identify(imOperation.getCmdArgs());
089
090 if ((output.length == 1) && output[0].equalsIgnoreCase("CMYK")) {
091 if (_log.isInfoEnabled()) {
092 _log.info("The image is in the CMYK colorspace");
093 }
094
095 imOperation = new IMOperation();
096
097 imOperation.addRawArgs("-colorspace", "RGB");
098 imOperation.addImage(inputFile.getPath());
099 imOperation.addImage(outputFile.getPath());
100
101 Future<?> future = imageMagick.convert(
102 imOperation.getCmdArgs());
103
104 return new RenderedImageFuture(future, outputFile, type);
105 }
106 }
107 catch (Exception e) {
108 if (_log.isErrorEnabled()) {
109 _log.error(e, e);
110 }
111 }
112 finally {
113 _fileUtil.delete(inputFile);
114 _fileUtil.delete(outputFile);
115 }
116
117 return null;
118 }
119
120 public BufferedImage convertImageType(BufferedImage sourceImage, int type) {
121 BufferedImage targetImage = new BufferedImage(
122 sourceImage.getWidth(), sourceImage.getHeight(), type);
123
124 Graphics2D graphics = targetImage.createGraphics();
125
126 graphics.drawRenderedImage(sourceImage, null);
127
128 graphics.dispose();
129
130 return targetImage;
131 }
132
133 public void encodeGIF(RenderedImage renderedImage, OutputStream os)
134 throws IOException {
135
136 if (JavaDetector.isJDK6()) {
137 ImageIO.write(renderedImage, TYPE_GIF, os);
138 }
139 else {
140 BufferedImage bufferedImage = getBufferedImage(renderedImage);
141
142 if (!(bufferedImage.getColorModel() instanceof IndexColorModel)) {
143 bufferedImage = convertImageType(
144 bufferedImage, BufferedImage.TYPE_BYTE_INDEXED);
145 }
146
147 Gif89Encoder encoder = new Gif89Encoder(bufferedImage);
148
149 encoder.encode(os);
150 }
151 }
152
153 public void encodeWBMP(RenderedImage renderedImage, OutputStream os)
154 throws IOException {
155
156 BufferedImage bufferedImage = getBufferedImage(renderedImage);
157
158 SampleModel sampleModel = bufferedImage.getSampleModel();
159
160 int type = sampleModel.getDataType();
161
162 if ((bufferedImage.getType() != BufferedImage.TYPE_BYTE_BINARY) ||
163 (type < DataBuffer.TYPE_BYTE) || (type > DataBuffer.TYPE_INT) ||
164 (sampleModel.getNumBands() != 1) ||
165 (sampleModel.getSampleSize(0) != 1)) {
166
167 BufferedImage binaryImage = new BufferedImage(
168 bufferedImage.getWidth(), bufferedImage.getHeight(),
169 BufferedImage.TYPE_BYTE_BINARY);
170
171 Graphics graphics = binaryImage.getGraphics();
172
173 graphics.drawImage(bufferedImage, 0, 0, null);
174
175 renderedImage = binaryImage;
176 }
177
178 if (!ImageIO.write(renderedImage, "wbmp", os)) {
179
180
181
182 os.write(0);
183 os.write(0);
184 os.write(toMultiByte(bufferedImage.getWidth()));
185 os.write(toMultiByte(bufferedImage.getHeight()));
186
187 DataBuffer dataBuffer = bufferedImage.getData().getDataBuffer();
188
189 int size = dataBuffer.getSize();
190
191 for (int i = 0; i < size; i++) {
192 os.write((byte)dataBuffer.getElem(i));
193 }
194 }
195 }
196
197 public BufferedImage getBufferedImage(RenderedImage renderedImage) {
198 if (renderedImage instanceof BufferedImage) {
199 return (BufferedImage)renderedImage;
200 }
201 else {
202 RenderedImageAdapter adapter = new RenderedImageAdapter(
203 renderedImage);
204
205 return adapter.getAsBufferedImage();
206 }
207 }
208
209 public byte[] getBytes(RenderedImage renderedImage, String contentType)
210 throws IOException {
211
212 UnsyncByteArrayOutputStream baos = new UnsyncByteArrayOutputStream();
213
214 write(renderedImage, contentType, baos);
215
216 return baos.toByteArray();
217 }
218
219 public ImageBag read(byte[] bytes) {
220 RenderedImage renderedImage = null;
221 String type = TYPE_NOT_AVAILABLE;
222
223 Enumeration<ImageCodec> enu = ImageCodec.getCodecs();
224
225 while (enu.hasMoreElements()) {
226 ImageCodec codec = enu.nextElement();
227
228 if (codec.isFormatRecognized(bytes)) {
229 type = codec.getFormatName();
230
231 renderedImage = read(bytes, type);
232
233 break;
234 }
235 }
236
237 if (type.equals("jpeg")) {
238 type = TYPE_JPEG;
239 }
240
241 return new ImageBag(renderedImage, type);
242 }
243
244 public ImageBag read(File file) throws IOException {
245 return read(_fileUtil.getBytes(file));
246 }
247
248 public ImageBag read(InputStream inputStream) throws IOException {
249 return read(_fileUtil.getBytes(inputStream));
250 }
251
252 public RenderedImage scale(RenderedImage renderedImage, int width) {
253 if (width <= 0) {
254 return renderedImage;
255 }
256
257 int imageHeight = renderedImage.getHeight();
258 int imageWidth = renderedImage.getWidth();
259
260 double factor = (double) width / imageWidth;
261
262 int scaledHeight = (int)(factor * imageHeight);
263 int scaledWidth = width;
264
265 BufferedImage bufferedImage = getBufferedImage(renderedImage);
266
267 int type = bufferedImage.getType();
268
269 if (type == 0) {
270 type = BufferedImage.TYPE_INT_ARGB;
271 }
272
273 BufferedImage scaledBufferedImage = new BufferedImage(
274 scaledWidth, scaledHeight, type);
275
276 Graphics graphics = scaledBufferedImage.getGraphics();
277
278 Image scaledImage = bufferedImage.getScaledInstance(
279 scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
280
281 graphics.drawImage(scaledImage, 0, 0, null);
282
283 return scaledBufferedImage;
284 }
285
286 public RenderedImage scale(
287 RenderedImage renderedImage, int maxHeight, int maxWidth) {
288
289 int imageHeight = renderedImage.getHeight();
290 int imageWidth = renderedImage.getWidth();
291
292 if (maxHeight == 0) {
293 maxHeight = imageHeight;
294 }
295
296 if (maxWidth == 0) {
297 maxWidth = imageWidth;
298 }
299
300 if ((imageHeight <= maxHeight) && (imageWidth <= maxWidth)) {
301 return renderedImage;
302 }
303
304 double factor = Math.min(
305 (double)maxHeight / imageHeight, (double)maxWidth / imageWidth);
306
307 int scaledHeight = Math.max(1, (int)(factor * imageHeight));
308 int scaledWidth = Math.max(1, (int)(factor * imageWidth));
309
310 BufferedImage bufferedImage = getBufferedImage(renderedImage);
311
312 int type = bufferedImage.getType();
313
314 if (type == 0) {
315 type = BufferedImage.TYPE_INT_ARGB;
316 }
317
318 BufferedImage scaledBufferedImage = null;
319
320 if ((type == BufferedImage.TYPE_BYTE_BINARY) ||
321 (type == BufferedImage.TYPE_BYTE_INDEXED)) {
322
323 IndexColorModel indexColorModel =
324 (IndexColorModel)bufferedImage.getColorModel();
325
326 BufferedImage tempBufferedImage = new BufferedImage(
327 1, 1, type, indexColorModel);
328
329 int bits = indexColorModel.getPixelSize();
330 int size = indexColorModel.getMapSize();
331
332 byte[] reds = new byte[size];
333
334 indexColorModel.getReds(reds);
335
336 byte[] greens = new byte[size];
337
338 indexColorModel.getGreens(greens);
339
340 byte[] blues = new byte[size];
341
342 indexColorModel.getBlues(blues);
343
344 WritableRaster writableRaster = tempBufferedImage.getRaster();
345
346 int pixel = writableRaster.getSample(0, 0, 0);
347
348 IndexColorModel scaledIndexColorModel = new IndexColorModel(
349 bits, size, reds, greens, blues, pixel);
350
351 scaledBufferedImage = new BufferedImage(
352 scaledWidth, scaledHeight, type, scaledIndexColorModel);
353 }
354 else {
355 scaledBufferedImage = new BufferedImage(
356 scaledWidth, scaledHeight, type);
357 }
358
359 Graphics graphics = scaledBufferedImage.getGraphics();
360
361 Image scaledImage = bufferedImage.getScaledInstance(
362 scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
363
364 graphics.drawImage(scaledImage, 0, 0, null);
365
366 return scaledBufferedImage;
367 }
368
369 public void write(
370 RenderedImage renderedImage, String contentType, OutputStream os)
371 throws IOException {
372
373 if (contentType.contains(TYPE_BMP)) {
374 ImageEncoder imageEncoder = ImageCodec.createImageEncoder(
375 TYPE_BMP, os, null);
376
377 imageEncoder.encode(renderedImage);
378 }
379 else if (contentType.contains(TYPE_GIF)) {
380 encodeGIF(renderedImage, os);
381 }
382 else if (contentType.contains(TYPE_JPEG) ||
383 contentType.contains("jpeg")) {
384
385 ImageIO.write(renderedImage, "jpeg", os);
386 }
387 else if (contentType.contains(TYPE_PNG)) {
388 ImageIO.write(renderedImage, TYPE_PNG, os);
389 }
390 else if (contentType.contains(TYPE_TIFF) ||
391 contentType.contains("tif")) {
392
393 ImageEncoder imageEncoder = ImageCodec.createImageEncoder(
394 TYPE_TIFF, os, null);
395
396 imageEncoder.encode(renderedImage);
397 }
398 }
399
400 protected ImageMagick getImageMagick() {
401 if (_imageMagick == null) {
402 _imageMagick = ImageMagickImpl.getInstance();
403
404 _imageMagick.reset();
405 }
406
407 return _imageMagick;
408 }
409
410 protected RenderedImage read(byte[] bytes, String type) {
411 RenderedImage renderedImage = null;
412
413 try {
414 if (type.equals(TYPE_JPEG)) {
415 type = "jpeg";
416 }
417
418 ImageDecoder decoder = ImageCodec.createImageDecoder(
419 type, new UnsyncByteArrayInputStream(bytes), null);
420
421 renderedImage = decoder.decodeAsRenderedImage();
422 }
423 catch (IOException ioe) {
424 if (_log.isDebugEnabled()) {
425 _log.debug(type + ": " + ioe.getMessage());
426 }
427 }
428
429 return renderedImage;
430 }
431
432 protected byte[] toMultiByte(int intValue) {
433 int numBits = 32;
434 int mask = 0x80000000;
435
436 while ((mask != 0) && ((intValue & mask) == 0)) {
437 numBits--;
438 mask >>>= 1;
439 }
440
441 int numBitsLeft = numBits;
442 byte[] multiBytes = new byte[(numBitsLeft + 6) / 7];
443
444 int maxIndex = multiBytes.length - 1;
445
446 for (int b = 0; b <= maxIndex; b++) {
447 multiBytes[b] = (byte)((intValue >>> ((maxIndex - b) * 7)) & 0x7f);
448
449 if (b != maxIndex) {
450 multiBytes[b] |= (byte)0x80;
451 }
452 }
453
454 return multiBytes;
455 }
456
457 private static Log _log = LogFactoryUtil.getLog(ImageToolImpl.class);
458
459 private static ImageTool _instance = new ImageToolImpl();
460
461 private static FileImpl _fileUtil = FileImpl.getInstance();
462 private static ImageMagick _imageMagick;
463
464 private class RenderedImageFuture implements Future<RenderedImage> {
465
466 public RenderedImageFuture(
467 Future<?> future, File outputFile, String type) {
468
469 _future = future;
470 _outputFile = outputFile;
471 _type = type;
472 }
473
474 public boolean cancel(boolean mayInterruptIfRunning) {
475 if (_future.isCancelled() || _future.isDone()) {
476 return false;
477 }
478
479 _future.cancel(true);
480
481 return true;
482 }
483
484 public RenderedImage get()
485 throws ExecutionException, InterruptedException {
486
487 _future.get();
488
489 byte[] bytes = new byte[0];
490
491 try {
492 bytes = _fileUtil.getBytes(_outputFile);
493 }
494 catch (IOException e) {
495 throw new ExecutionException(e);
496 }
497
498 return read(bytes, _type);
499 }
500
501 public RenderedImage get(long timeout, TimeUnit timeUnit)
502 throws ExecutionException, InterruptedException, TimeoutException {
503
504 _future.get(timeout, timeUnit);
505
506 byte[] bytes = new byte[0];
507
508 try {
509 bytes = _fileUtil.getBytes(_outputFile);
510 }
511 catch (IOException ioe) {
512 throw new ExecutionException(ioe);
513 }
514
515 return read(bytes, _type);
516 }
517
518 public boolean isCancelled() {
519 return _future.isCancelled();
520 }
521
522 public boolean isDone() {
523 return _future.isDone();
524 }
525
526 private final Future<?> _future;
527 private final File _outputFile;
528 private final String _type;
529
530 }
531
532 }