/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.image;
import com.liferay.portal.kernel.image.ImageBag;
import com.liferay.portal.kernel.image.ImageToolUtil;
import com.liferay.portal.kernel.image.SpriteProcessor;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.security.pacl.DoPrivileged;
import com.liferay.portal.kernel.servlet.ServletContextUtil;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.JavaConstants;
import com.liferay.portal.kernel.util.PropertiesUtil;
import com.liferay.portal.kernel.util.SortedProperties;
import com.liferay.portal.kernel.util.URLUtil;
import com.liferay.portal.kernel.util.Validator;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Properties;
import javax.imageio.ImageIO;
import javax.media.jai.LookupTableJAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.RasterFactory;
import javax.media.jai.TiledImage;
import javax.media.jai.operator.LookupDescriptor;
import javax.media.jai.operator.MosaicDescriptor;
import javax.media.jai.operator.TranslateDescriptor;
import javax.servlet.ServletContext;
/**
* @author Brian Wing Shun Chan
*/
@DoPrivileged
public class SpriteProcessorImpl implements SpriteProcessor {
@Override
public Properties generate(
ServletContext servletContext, List<URL> imageURLs,
String spriteRootDirName, String spriteFileName,
String spritePropertiesFileName, String rootPath, int maxHeight,
int maxWidth, int maxSize)
throws IOException {
if (imageURLs.isEmpty()) {
return null;
}
Collections.sort(imageURLs, _urlPathComparator);
File spriteRootDir = null;
if (Validator.isNull(spriteRootDirName)) {
File tempDir = (File)servletContext.getAttribute(
JavaConstants.JAVAX_SERVLET_CONTEXT_TEMPDIR);
spriteRootDir = new File(tempDir, SpriteProcessor.PATH);
}
else {
spriteRootDir = new File(spriteRootDirName);
}
FileUtil.mkdirs(spriteRootDir);
File spritePropertiesFile = new File(
spriteRootDir, spritePropertiesFileName);
File spritePropertiesParentFile = spritePropertiesFile.getParentFile();
FileUtil.mkdirs(spritePropertiesParentFile);
boolean build = false;
long lastModified = 0;
if (spritePropertiesFile.exists()) {
lastModified = spritePropertiesFile.lastModified();
for (URL imageURL : imageURLs) {
if (URLUtil.getLastModifiedTime(imageURL) > lastModified) {
build = true;
break;
}
}
}
else {
build = true;
}
if (!build) {
String spritePropertiesString = FileUtil.read(spritePropertiesFile);
if (Validator.isNull(spritePropertiesString)) {
return null;
}
else {
return PropertiesUtil.load(spritePropertiesString);
}
}
List<RenderedImage> renderedImages = new ArrayList<>();
Properties spriteProperties = new SortedProperties();
float x = 0;
float y = 0;
URLConnection urlConnection = null;
for (URL imageURL : imageURLs) {
urlConnection = imageURL.openConnection();
if ((urlConnection != null) &&
(urlConnection.getContentLength() > maxSize)) {
continue;
}
try {
ImageBag imageBag = ImageToolUtil.read(
urlConnection.getInputStream());
RenderedImage renderedImage = imageBag.getRenderedImage();
int height = renderedImage.getHeight();
int width = renderedImage.getWidth();
if ((height <= maxHeight) && (width <= maxWidth)) {
renderedImage = convert(renderedImage);
renderedImage = TranslateDescriptor.create(
renderedImage, x, y, null, null);
renderedImages.add(renderedImage);
String key = ServletContextUtil.getResourcePath(imageURL);
int pos = key.indexOf(rootPath);
if (pos == 0) {
key = key.substring(rootPath.length());
}
String contextPath = servletContext.getContextPath();
key = contextPath.concat(key);
String value = (int)y + "," + height + "," + width;
spriteProperties.setProperty(key, value);
y += renderedImage.getHeight();
}
}
catch (Exception e) {
if (_log.isWarnEnabled()) {
_log.warn("Unable to process " + imageURL);
}
if (_log.isDebugEnabled()) {
_log.debug(e, e);
}
}
}
if (renderedImages.size() <= 1) {
renderedImages.clear();
spriteProperties.clear();
}
else {
// PNG
RenderedImage renderedImage = MosaicDescriptor.create(
renderedImages.toArray(
new RenderedImage[renderedImages.size()]),
MosaicDescriptor.MOSAIC_TYPE_OVERLAY, null, null, null, null,
null);
File spriteFile = new File(spriteRootDir, spriteFileName);
File spriteDir = spriteFile.getParentFile();
FileUtil.mkdirs(spriteDir);
ImageIO.write(renderedImage, "png", spriteFile);
if (lastModified > 0) {
spriteFile.setLastModified(lastModified);
}
}
FileUtil.write(
spritePropertiesFile, PropertiesUtil.toString(spriteProperties));
if (lastModified > 0) {
spritePropertiesFile.setLastModified(lastModified);
}
return spriteProperties;
}
protected RenderedImage convert(RenderedImage renderedImage)
throws Exception {
int height = renderedImage.getHeight();
int width = renderedImage.getWidth();
SampleModel sampleModel = renderedImage.getSampleModel();
ColorModel colorModel = renderedImage.getColorModel();
Raster raster = renderedImage.getData();
DataBuffer dataBuffer = raster.getDataBuffer();
if (colorModel instanceof IndexColorModel) {
IndexColorModel indexColorModel = (IndexColorModel)colorModel;
int mapSize = indexColorModel.getMapSize();
byte[][] data = new byte[4][mapSize];
indexColorModel.getReds(data[0]);
indexColorModel.getGreens(data[1]);
indexColorModel.getBlues(data[2]);
indexColorModel.getAlphas(data[3]);
LookupTableJAI lookupTableJAI = new LookupTableJAI(data);
renderedImage = LookupDescriptor.create(
renderedImage, lookupTableJAI, null);
}
else if (sampleModel.getNumBands() == 1) {
List<Byte> bytesList = new ArrayList<>(
height * width * _NUM_OF_BANDS);
for (int i = 0; i < dataBuffer.getSize(); i++) {
byte elem = (byte)dataBuffer.getElem(i);
if (elem == -1) {
bytesList.add((byte)0);
}
else {
bytesList.add((byte)255);
}
bytesList.add(elem);
bytesList.add(elem);
bytesList.add(elem);
}
byte[] data = ArrayUtil.toArray(
bytesList.toArray(new Byte[bytesList.size()]));
DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
renderedImage = createRenderedImage(
renderedImage, height, width, newDataBuffer);
}
else if (sampleModel.getNumBands() == 2) {
List<Byte> bytesList = new ArrayList<>(
height * width * _NUM_OF_BANDS);
List<Byte> tempBytesList = new ArrayList<>(_NUM_OF_BANDS);
for (int i = 0; i < dataBuffer.getSize(); i++) {
int mod = (i + 1) % 2;
int elemPos = i;
if (mod == 0) {
tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
}
tempBytesList.add((byte)dataBuffer.getElem(elemPos));
if (mod == 0) {
Collections.reverse(tempBytesList);
bytesList.addAll(tempBytesList);
tempBytesList.clear();
}
}
byte[] data = ArrayUtil.toArray(
bytesList.toArray(new Byte[bytesList.size()]));
DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
renderedImage = createRenderedImage(
renderedImage, height, width, newDataBuffer);
}
else if (colorModel.getTransparency() != Transparency.TRANSLUCENT) {
List<Byte> bytesList = new ArrayList<>(
height * width * _NUM_OF_BANDS);
List<Byte> tempBytesList = new ArrayList<>(_NUM_OF_BANDS);
for (int i = 0; i < dataBuffer.getSize(); i++) {
int mod = (i + 1) % 3;
int elemPos = i;
tempBytesList.add((byte)dataBuffer.getElem(elemPos));
if (mod == 0) {
tempBytesList.add((byte)255);
Collections.reverse(tempBytesList);
bytesList.addAll(tempBytesList);
tempBytesList.clear();
}
}
byte[] data = ArrayUtil.toArray(
bytesList.toArray(new Byte[bytesList.size()]));
DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
renderedImage = createRenderedImage(
renderedImage, height, width, newDataBuffer);
}
return renderedImage;
}
protected RenderedImage createRenderedImage(
RenderedImage renderedImage, int height, int width,
DataBuffer dataBuffer) {
SampleModel sampleModel =
RasterFactory.createPixelInterleavedSampleModel(
DataBuffer.TYPE_BYTE, width, height, _NUM_OF_BANDS);
ColorModel colorModel = PlanarImage.createColorModel(sampleModel);
TiledImage tiledImage = new TiledImage(
0, 0, width, height, 0, 0, sampleModel, colorModel);
Raster raster = RasterFactory.createWritableRaster(
sampleModel, dataBuffer, new Point(0, 0));
tiledImage.setData(raster);
/*javax.media.jai.JAI.create(
"filestore", tiledImage, "test.png", "PNG");
printImage(renderedImage);
printImage(tiledImage);*/
return tiledImage;
}
protected void printImage(RenderedImage renderedImage) {
SampleModel sampleModel = renderedImage.getSampleModel();
int height = renderedImage.getHeight();
int width = renderedImage.getWidth();
int numOfBands = sampleModel.getNumBands();
int[] pixels = new int[height * width * numOfBands];
Raster raster = renderedImage.getData();
raster.getPixels(0, 0, width, height, pixels);
int offset = 0;
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
offset = (h * width * numOfBands) + (w * numOfBands);
System.out.print("[" + w + ", " + h + "] = ");
for (int b = 0; b < numOfBands; b++) {
System.out.print(pixels[offset + b] + " ");
}
}
System.out.println();
}
}
private static final int _NUM_OF_BANDS = 4;
private static final Log _log = LogFactoryUtil.getLog(
SpriteProcessorImpl.class);
private static final Comparator<URL> _urlPathComparator =
new Comparator<URL>() {
@Override
public int compare(URL url1, URL url2) {
String path1 = url1.getPath();
String path2 = url2.getPath();
return path1.compareToIgnoreCase(path2);
}
};
}