001
014
015 package com.liferay.portal.image;
016
017 import com.liferay.portal.kernel.image.ImageBag;
018 import com.liferay.portal.kernel.image.ImageToolUtil;
019 import com.liferay.portal.kernel.image.SpriteProcessor;
020 import com.liferay.portal.kernel.log.Log;
021 import com.liferay.portal.kernel.log.LogFactoryUtil;
022 import com.liferay.portal.kernel.security.pacl.DoPrivileged;
023 import com.liferay.portal.kernel.servlet.ServletContextUtil;
024 import com.liferay.portal.kernel.util.ArrayUtil;
025 import com.liferay.portal.kernel.util.FileUtil;
026 import com.liferay.portal.kernel.util.JavaConstants;
027 import com.liferay.portal.kernel.util.PropertiesUtil;
028 import com.liferay.portal.kernel.util.SortedProperties;
029 import com.liferay.portal.kernel.util.URLUtil;
030 import com.liferay.portal.kernel.util.Validator;
031 import com.liferay.util.PropertyComparator;
032
033 import java.awt.Point;
034 import java.awt.Transparency;
035 import java.awt.image.ColorModel;
036 import java.awt.image.DataBuffer;
037 import java.awt.image.DataBufferByte;
038 import java.awt.image.IndexColorModel;
039 import java.awt.image.Raster;
040 import java.awt.image.RenderedImage;
041 import java.awt.image.SampleModel;
042
043 import java.io.File;
044 import java.io.IOException;
045
046 import java.net.URL;
047 import java.net.URLConnection;
048
049 import java.util.ArrayList;
050 import java.util.Collections;
051 import java.util.List;
052 import java.util.Properties;
053
054 import javax.imageio.ImageIO;
055
056 import javax.media.jai.LookupTableJAI;
057 import javax.media.jai.PlanarImage;
058 import javax.media.jai.RasterFactory;
059 import javax.media.jai.TiledImage;
060 import javax.media.jai.operator.LookupDescriptor;
061 import javax.media.jai.operator.MosaicDescriptor;
062 import javax.media.jai.operator.TranslateDescriptor;
063
064 import javax.servlet.ServletContext;
065
066
069 @DoPrivileged
070 public class SpriteProcessorImpl implements SpriteProcessor {
071
072 @Override
073 public Properties generate(
074 ServletContext servletContext, List<URL> imageURLs,
075 String spriteRootDirName, String spriteFileName,
076 String spritePropertiesFileName, String rootPath, int maxHeight,
077 int maxWidth, int maxSize)
078 throws IOException {
079
080 if (imageURLs.size() < 1) {
081 return null;
082 }
083
084 Collections.sort(imageURLs, new PropertyComparator("path"));
085
086 File spriteRootDir = null;
087
088 if (Validator.isNull(spriteRootDirName)) {
089 File tempDir = (File)servletContext.getAttribute(
090 JavaConstants.JAVAX_SERVLET_CONTEXT_TEMPDIR);
091
092 spriteRootDir = new File(tempDir, SpriteProcessor.PATH);
093 }
094 else {
095 spriteRootDir = new File(spriteRootDirName);
096 }
097
098 FileUtil.mkdirs(spriteRootDir);
099
100 File spritePropertiesFile = new File(
101 spriteRootDir, spritePropertiesFileName);
102
103 File spritePropertiesParentFile = spritePropertiesFile.getParentFile();
104
105 FileUtil.mkdirs(spritePropertiesParentFile);
106
107 boolean build = false;
108
109 long lastModified = 0;
110
111 if (spritePropertiesFile.exists()) {
112 lastModified = spritePropertiesFile.lastModified();
113
114 for (URL imageURL : imageURLs) {
115 if (URLUtil.getLastModifiedTime(imageURL) > lastModified) {
116 build = true;
117
118 break;
119 }
120 }
121 }
122 else {
123 build = true;
124 }
125
126 if (!build) {
127 String spritePropertiesString = FileUtil.read(spritePropertiesFile);
128
129 if (Validator.isNull(spritePropertiesString)) {
130 return null;
131 }
132 else {
133 return PropertiesUtil.load(spritePropertiesString);
134 }
135 }
136
137 List<RenderedImage> renderedImages = new ArrayList<>();
138
139 Properties spriteProperties = new SortedProperties();
140
141 float x = 0;
142 float y = 0;
143
144 URLConnection urlConnection = null;
145
146 for (URL imageURL : imageURLs) {
147 urlConnection = imageURL.openConnection();
148
149 if ((urlConnection != null) &&
150 (urlConnection.getContentLength() > maxSize)) {
151
152 continue;
153 }
154
155 try {
156 ImageBag imageBag = ImageToolUtil.read(
157 urlConnection.getInputStream());
158
159 RenderedImage renderedImage = imageBag.getRenderedImage();
160
161 int height = renderedImage.getHeight();
162 int width = renderedImage.getWidth();
163
164 if ((height <= maxHeight) && (width <= maxWidth)) {
165 renderedImage = convert(renderedImage);
166
167 renderedImage = TranslateDescriptor.create(
168 renderedImage, x, y, null, null);
169
170 renderedImages.add(renderedImage);
171
172 String key = ServletContextUtil.getResourcePath(imageURL);
173
174 int pos = key.indexOf(rootPath);
175
176 if (pos == 0) {
177 key = key.substring(rootPath.length());
178 }
179
180 String contextPath = servletContext.getContextPath();
181
182 key = contextPath.concat(key);
183
184 String value = (int)y + "," + height + "," + width;
185
186 spriteProperties.setProperty(key, value);
187
188 y += renderedImage.getHeight();
189 }
190 }
191 catch (Exception e) {
192 if (_log.isWarnEnabled()) {
193 _log.warn("Unable to process " + imageURL);
194 }
195
196 if (_log.isDebugEnabled()) {
197 _log.debug(e, e);
198 }
199 }
200 }
201
202 if (renderedImages.size() <= 1) {
203 renderedImages.clear();
204 spriteProperties.clear();
205 }
206 else {
207
208
209
210 RenderedImage renderedImage = MosaicDescriptor.create(
211 renderedImages.toArray(
212 new RenderedImage[renderedImages.size()]),
213 MosaicDescriptor.MOSAIC_TYPE_OVERLAY, null, null, null, null,
214 null);
215
216 File spriteFile = new File(spriteRootDir, spriteFileName);
217
218 File spriteDir = spriteFile.getParentFile();
219
220 FileUtil.mkdirs(spriteDir);
221
222 ImageIO.write(renderedImage, "png", spriteFile);
223
224 if (lastModified > 0) {
225 spriteFile.setLastModified(lastModified);
226 }
227 }
228
229 FileUtil.write(
230 spritePropertiesFile, PropertiesUtil.toString(spriteProperties));
231
232 if (lastModified > 0) {
233 spritePropertiesFile.setLastModified(lastModified);
234 }
235
236 return spriteProperties;
237 }
238
239 protected RenderedImage convert(RenderedImage renderedImage)
240 throws Exception {
241
242 int height = renderedImage.getHeight();
243 int width = renderedImage.getWidth();
244
245 SampleModel sampleModel = renderedImage.getSampleModel();
246 ColorModel colorModel = renderedImage.getColorModel();
247
248 Raster raster = renderedImage.getData();
249
250 DataBuffer dataBuffer = raster.getDataBuffer();
251
252 if (colorModel instanceof IndexColorModel) {
253 IndexColorModel indexColorModel = (IndexColorModel)colorModel;
254
255 int mapSize = indexColorModel.getMapSize();
256
257 byte[][] data = new byte[4][mapSize];
258
259 indexColorModel.getReds(data[0]);
260 indexColorModel.getGreens(data[1]);
261 indexColorModel.getBlues(data[2]);
262 indexColorModel.getAlphas(data[3]);
263
264 LookupTableJAI lookupTableJAI = new LookupTableJAI(data);
265
266 renderedImage = LookupDescriptor.create(
267 renderedImage, lookupTableJAI, null);
268 }
269 else if (sampleModel.getNumBands() == 2) {
270 List<Byte> bytesList = new ArrayList<>(
271 height * width * _NUM_OF_BANDS);
272
273 List<Byte> tempBytesList = new ArrayList<>(_NUM_OF_BANDS);
274
275 for (int i = 0; i < dataBuffer.getSize(); i++) {
276 int mod = (i + 1) % 2;
277
278 int elemPos = i;
279
280 if (mod == 0) {
281 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
282 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
283 }
284
285 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
286
287 if (mod == 0) {
288 Collections.reverse(tempBytesList);
289
290 bytesList.addAll(tempBytesList);
291
292 tempBytesList.clear();
293 }
294 }
295
296 byte[] data = ArrayUtil.toArray(
297 bytesList.toArray(new Byte[bytesList.size()]));
298
299 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
300
301 renderedImage = createRenderedImage(
302 renderedImage, height, width, newDataBuffer);
303 }
304 else if (colorModel.getTransparency() != Transparency.TRANSLUCENT) {
305 List<Byte> bytesList = new ArrayList<>(
306 height * width * _NUM_OF_BANDS);
307
308 List<Byte> tempBytesList = new ArrayList<>(_NUM_OF_BANDS);
309
310 for (int i = 0; i < dataBuffer.getSize(); i++) {
311 int mod = (i + 1) % 3;
312
313 int elemPos = i;
314
315 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
316
317 if (mod == 0) {
318 tempBytesList.add((byte)255);
319
320 Collections.reverse(tempBytesList);
321
322 bytesList.addAll(tempBytesList);
323
324 tempBytesList.clear();
325 }
326 }
327
328 byte[] data = ArrayUtil.toArray(
329 bytesList.toArray(new Byte[bytesList.size()]));
330
331 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
332
333 renderedImage = createRenderedImage(
334 renderedImage, height, width, newDataBuffer);
335 }
336
337 return renderedImage;
338 }
339
340 protected RenderedImage createRenderedImage(
341 RenderedImage renderedImage, int height, int width,
342 DataBuffer dataBuffer) {
343
344 SampleModel sampleModel =
345 RasterFactory.createPixelInterleavedSampleModel(
346 DataBuffer.TYPE_BYTE, width, height, _NUM_OF_BANDS);
347 ColorModel colorModel = PlanarImage.createColorModel(sampleModel);
348
349 TiledImage tiledImage = new TiledImage(
350 0, 0, width, height, 0, 0, sampleModel, colorModel);
351
352 Raster raster = RasterFactory.createWritableRaster(
353 sampleModel, dataBuffer, new Point(0, 0));
354
355 tiledImage.setData(raster);
356
357
362
363 return tiledImage;
364 }
365
366 protected void printImage(RenderedImage renderedImage) {
367 SampleModel sampleModel = renderedImage.getSampleModel();
368
369 int height = renderedImage.getHeight();
370 int width = renderedImage.getWidth();
371 int numOfBands = sampleModel.getNumBands();
372
373 int[] pixels = new int[height * width * numOfBands];
374
375 Raster raster = renderedImage.getData();
376
377 raster.getPixels(0, 0, width, height, pixels);
378
379 int offset = 0;
380
381 for (int h = 0; h < height; h++) {
382 for (int w = 0; w < width; w++) {
383 offset = (h * width * numOfBands) + (w * numOfBands);
384
385 System.out.print("[" + w + ", " + h + "] = ");
386
387 for (int b = 0; b < numOfBands; b++) {
388 System.out.print(pixels[offset + b] + " ");
389 }
390 }
391
392 System.out.println();
393 }
394 }
395
396 private static final int _NUM_OF_BANDS = 4;
397
398 private static final Log _log = LogFactoryUtil.getLog(
399 SpriteProcessorImpl.class);
400
401 }