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