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