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() == 1) {
270 List<Byte> bytesList = new ArrayList<>(
271 height * width * _NUM_OF_BANDS);
272
273 for (int i = 0; i < dataBuffer.getSize(); i++) {
274 byte elem = (byte)dataBuffer.getElem(i);
275
276 if (elem == -1) {
277 bytesList.add((byte)0);
278 }
279 else {
280 bytesList.add((byte)255);
281 }
282
283 bytesList.add(elem);
284 bytesList.add(elem);
285 bytesList.add(elem);
286 }
287
288 byte[] data = ArrayUtil.toArray(
289 bytesList.toArray(new Byte[bytesList.size()]));
290
291 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
292
293 renderedImage = createRenderedImage(
294 renderedImage, height, width, newDataBuffer);
295 }
296 else if (sampleModel.getNumBands() == 2) {
297 List<Byte> bytesList = new ArrayList<>(
298 height * width * _NUM_OF_BANDS);
299
300 List<Byte> tempBytesList = new ArrayList<>(_NUM_OF_BANDS);
301
302 for (int i = 0; i < dataBuffer.getSize(); i++) {
303 int mod = (i + 1) % 2;
304
305 int elemPos = i;
306
307 if (mod == 0) {
308 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
309 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
310 }
311
312 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
313
314 if (mod == 0) {
315 Collections.reverse(tempBytesList);
316
317 bytesList.addAll(tempBytesList);
318
319 tempBytesList.clear();
320 }
321 }
322
323 byte[] data = ArrayUtil.toArray(
324 bytesList.toArray(new Byte[bytesList.size()]));
325
326 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
327
328 renderedImage = createRenderedImage(
329 renderedImage, height, width, newDataBuffer);
330 }
331 else if (colorModel.getTransparency() != Transparency.TRANSLUCENT) {
332 List<Byte> bytesList = new ArrayList<>(
333 height * width * _NUM_OF_BANDS);
334
335 List<Byte> tempBytesList = new ArrayList<>(_NUM_OF_BANDS);
336
337 for (int i = 0; i < dataBuffer.getSize(); i++) {
338 int mod = (i + 1) % 3;
339
340 int elemPos = i;
341
342 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
343
344 if (mod == 0) {
345 tempBytesList.add((byte)255);
346
347 Collections.reverse(tempBytesList);
348
349 bytesList.addAll(tempBytesList);
350
351 tempBytesList.clear();
352 }
353 }
354
355 byte[] data = ArrayUtil.toArray(
356 bytesList.toArray(new Byte[bytesList.size()]));
357
358 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
359
360 renderedImage = createRenderedImage(
361 renderedImage, height, width, newDataBuffer);
362 }
363
364 return renderedImage;
365 }
366
367 protected RenderedImage createRenderedImage(
368 RenderedImage renderedImage, int height, int width,
369 DataBuffer dataBuffer) {
370
371 SampleModel sampleModel =
372 RasterFactory.createPixelInterleavedSampleModel(
373 DataBuffer.TYPE_BYTE, width, height, _NUM_OF_BANDS);
374 ColorModel colorModel = PlanarImage.createColorModel(sampleModel);
375
376 TiledImage tiledImage = new TiledImage(
377 0, 0, width, height, 0, 0, sampleModel, colorModel);
378
379 Raster raster = RasterFactory.createWritableRaster(
380 sampleModel, dataBuffer, new Point(0, 0));
381
382 tiledImage.setData(raster);
383
384
389
390 return tiledImage;
391 }
392
393 protected void printImage(RenderedImage renderedImage) {
394 SampleModel sampleModel = renderedImage.getSampleModel();
395
396 int height = renderedImage.getHeight();
397 int width = renderedImage.getWidth();
398 int numOfBands = sampleModel.getNumBands();
399
400 int[] pixels = new int[height * width * numOfBands];
401
402 Raster raster = renderedImage.getData();
403
404 raster.getPixels(0, 0, width, height, pixels);
405
406 int offset = 0;
407
408 for (int h = 0; h < height; h++) {
409 for (int w = 0; w < width; w++) {
410 offset = (h * width * numOfBands) + (w * numOfBands);
411
412 System.out.print("[" + w + ", " + h + "] = ");
413
414 for (int b = 0; b < numOfBands; b++) {
415 System.out.print(pixels[offset + b] + " ");
416 }
417 }
418
419 System.out.println();
420 }
421 }
422
423 private static final int _NUM_OF_BANDS = 4;
424
425 private static final Log _log = LogFactoryUtil.getLog(
426 SpriteProcessorImpl.class);
427
428 }