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