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