1
22
23 package com.liferay.portal.image;
24
25 import com.liferay.portal.kernel.image.ImageProcessorUtil;
26 import com.liferay.portal.kernel.image.SpriteProcessor;
27 import com.liferay.portal.kernel.log.Log;
28 import com.liferay.portal.kernel.log.LogFactoryUtil;
29 import com.liferay.portal.kernel.util.ArrayUtil;
30 import com.liferay.portal.kernel.util.FileUtil;
31 import com.liferay.portal.kernel.util.PropertiesUtil;
32 import com.liferay.portal.kernel.util.SortedProperties;
33 import com.liferay.portal.kernel.util.StringPool;
34 import com.liferay.portal.kernel.util.StringUtil;
35 import com.liferay.portal.kernel.util.Validator;
36
37 import java.awt.Point;
38 import java.awt.Transparency;
39 import java.awt.image.ColorModel;
40 import java.awt.image.DataBuffer;
41 import java.awt.image.DataBufferByte;
42 import java.awt.image.IndexColorModel;
43 import java.awt.image.Raster;
44 import java.awt.image.RenderedImage;
45 import java.awt.image.SampleModel;
46
47 import java.io.File;
48 import java.io.FileInputStream;
49 import java.io.FileOutputStream;
50 import java.io.IOException;
51
52 import java.util.ArrayList;
53 import java.util.Collections;
54 import java.util.List;
55 import java.util.Properties;
56
57 import javax.imageio.ImageIO;
58
59 import javax.media.jai.JAI;
60 import javax.media.jai.LookupTableJAI;
61 import javax.media.jai.PlanarImage;
62 import javax.media.jai.RasterFactory;
63 import javax.media.jai.TiledImage;
64 import javax.media.jai.operator.LookupDescriptor;
65 import javax.media.jai.operator.MosaicDescriptor;
66 import javax.media.jai.operator.TranslateDescriptor;
67
68 import org.geotools.image.ImageWorker;
69
70
76 public class SpriteProcessorImpl implements SpriteProcessor {
77
78 static {
79 System.setProperty("com.sun.media.jai.disableMediaLib", "true");
80 }
81
82 public Properties generate(
83 List<File> images, String spriteFileName,
84 String spritePropertiesFileName, String spritePropertiesRootPath,
85 int maxHeight, int maxWidth, int maxSize)
86 throws IOException {
87
88 if (images.size() < 1) {
89 return null;
90 }
91
92 if (spritePropertiesRootPath.endsWith(StringPool.SLASH) ||
93 spritePropertiesRootPath.endsWith(StringPool.BACK_SLASH)) {
94
95 spritePropertiesRootPath = spritePropertiesRootPath.substring(
96 0, spritePropertiesRootPath.length() - 1);
97 }
98
99 File dir = images.get(0).getParentFile();
100
101 File spritePropertiesFile = new File(
102 dir.toString() + StringPool.SLASH + spritePropertiesFileName);
103
104 boolean build = false;
105
106 long lastModified = 0;
107
108 if (spritePropertiesFile.exists()) {
109 lastModified = spritePropertiesFile.lastModified();
110
111 for (File image : images) {
112 if (image.lastModified() > lastModified) {
113 build = true;
114
115 break;
116 }
117 }
118 }
119 else {
120 build = true;
121 }
122
123 if (!build) {
124 String spritePropertiesString = FileUtil.read(spritePropertiesFile);
125
126 if (Validator.isNull(spritePropertiesString)) {
127 return null;
128 }
129 else {
130 return PropertiesUtil.load(spritePropertiesString);
131 }
132 }
133
134 List<RenderedImage> renderedImages = new ArrayList<RenderedImage>();
135
136 Properties spriteProperties = new SortedProperties();
137
138 float x = 0;
139 float y = 0;
140
141 for (File file : images) {
142 String fileName = file.getName();
143
144 if (file.length() > maxSize) {
145 continue;
146 }
147
148 FileInputStream fis = new FileInputStream(file);
149
150 try {
151 RenderedImage renderedImage = ImageIO.read(fis);
152
153 int height = renderedImage.getHeight();
154 int width = renderedImage.getWidth();
155
156 if ((height <= maxHeight) && (width <= maxWidth)) {
157 renderedImage = convert(renderedImage);
158
159 renderedImage = TranslateDescriptor.create(
160 renderedImage, x, y, null, null);
161
162 renderedImages.add(renderedImage);
163
164 String key = StringUtil.replace(
165 file.toString(), StringPool.BACK_SLASH,
166 StringPool.SLASH);
167
168 key = key.substring(
169 spritePropertiesRootPath.toString().length());
170
171 String value = (int)y + "," + height + "," + width;
172
173 spriteProperties.setProperty(key, value);
174
175 y += renderedImage.getHeight();
176 }
177 }
178 catch (Exception e) {
179 if (_log.isWarnEnabled()) {
180 _log.warn("Unable to process " + file);
181 }
182
183 if (_log.isDebugEnabled()) {
184 _log.debug(e, e);
185 }
186 }
187 finally {
188 fis.close();
189 }
190 }
191
192 if (renderedImages.size() <= 1) {
193 renderedImages.clear();
194 spriteProperties.clear();
195 }
196 else {
197
198
200 RenderedImage renderedImage = MosaicDescriptor.create(
201 (RenderedImage[])renderedImages.toArray(
202 new RenderedImage[renderedImages.size()]),
203 MosaicDescriptor.MOSAIC_TYPE_OVERLAY, null, null, null, null,
204 null);
205
206 File spriteFile = new File(
207 dir.toString() + StringPool.SLASH + spriteFileName);
208
209 ImageIO.write(renderedImage, "png", spriteFile);
210
211 if (lastModified > 0) {
212 spriteFile.setLastModified(lastModified);
213 }
214
215 ImageWorker imageWorker = new ImageWorker(renderedImage);
216
217 imageWorker.forceIndexColorModelForGIF(true);
218
219
221 renderedImage = imageWorker.getPlanarImage();
222
223 spriteFile = new File(
224 dir.toString() + StringPool.SLASH +
225 StringUtil.replace(spriteFileName, ".png", ".gif"));
226
227 FileOutputStream fos = new FileOutputStream(spriteFile);
228
229 try {
230 ImageProcessorUtil.encodeGIF(renderedImage, fos);
231 }
232 finally {
233 fos.close();
234 }
235
236 if (lastModified > 0) {
237 spriteFile.setLastModified(lastModified);
238 }
239 }
240
241 FileUtil.write(
242 spritePropertiesFile, PropertiesUtil.toString(spriteProperties));
243
244 if (lastModified > 0) {
245 spritePropertiesFile.setLastModified(lastModified);
246 }
247
248 return spriteProperties;
249 }
250
251 protected RenderedImage convert(RenderedImage renderedImage)
252 throws Exception {
253
254 int height = renderedImage.getHeight();
255 int width = renderedImage.getWidth();
256
257 SampleModel sampleModel = renderedImage.getSampleModel();
258 ColorModel colorModel = renderedImage.getColorModel();
259
260 Raster raster = renderedImage.getData();
261
262 DataBuffer dataBuffer = raster.getDataBuffer();
263
264 if (colorModel instanceof IndexColorModel) {
265 IndexColorModel indexColorModel = (IndexColorModel)colorModel;
266
267 int mapSize = indexColorModel.getMapSize();
268
269 byte[][] data = new byte[4][mapSize];
270
271 indexColorModel.getReds(data[0]);
272 indexColorModel.getGreens(data[1]);
273 indexColorModel.getBlues(data[2]);
274 indexColorModel.getAlphas(data[3]);
275
276 LookupTableJAI lookupTableJAI = new LookupTableJAI(data);
277
278 renderedImage = LookupDescriptor.create(
279 renderedImage, lookupTableJAI, null);
280 }
281 else if (sampleModel.getNumBands() == 2) {
282 List<Byte> bytesList = new ArrayList<Byte>(
283 height * width * _NUM_OF_BANDS);
284
285 List<Byte> tempBytesList = new ArrayList<Byte>(_NUM_OF_BANDS);
286
287 for (int i = 0; i < dataBuffer.getSize(); i++) {
288 int mod = (i + 1) % 2;
289
290 int elemPos = i;
291
292 if (mod == 0) {
293 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
294 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
295 }
296
297 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
298
299 if (mod == 0) {
300 Collections.reverse(tempBytesList);
301
302 bytesList.addAll(tempBytesList);
303
304 tempBytesList.clear();
305 }
306 }
307
308 byte[] data = ArrayUtil.toArray(
309 bytesList.toArray(new Byte[bytesList.size()]));
310
311 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
312
313 renderedImage = createRenderedImage(
314 renderedImage, height, width, newDataBuffer);
315 }
316 else if (colorModel.getTransparency() != Transparency.TRANSLUCENT) {
317 List<Byte> bytesList = new ArrayList<Byte>(
318 height * width * _NUM_OF_BANDS);
319
320 List<Byte> tempBytesList = new ArrayList<Byte>(_NUM_OF_BANDS);
321
322 for (int i = 0; i < dataBuffer.getSize(); i++) {
323 int mod = (i + 1) % 3;
324
325 int elemPos = i;
326
327 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
328
329 if (mod == 0) {
330 tempBytesList.add((byte)255);
331
332 Collections.reverse(tempBytesList);
333
334 bytesList.addAll(tempBytesList);
335
336 tempBytesList.clear();
337 }
338 }
339
340 byte[] data = ArrayUtil.toArray(
341 bytesList.toArray(new Byte[bytesList.size()]));
342
343 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
344
345 renderedImage = createRenderedImage(
346 renderedImage, height, width, newDataBuffer);
347 }
348
349 return renderedImage;
350 }
351
352 protected RenderedImage createRenderedImage(
353 RenderedImage renderedImage, int height, int width,
354 DataBuffer dataBuffer) {
355
356 SampleModel sampleModel =
357 RasterFactory.createPixelInterleavedSampleModel(
358 DataBuffer.TYPE_BYTE, width, height, _NUM_OF_BANDS);
359 ColorModel colorModel = PlanarImage.createColorModel(sampleModel);
360
361 TiledImage tiledImage = new TiledImage(
362 0, 0, width, height, 0, 0, sampleModel, colorModel);
363
364 Raster raster = RasterFactory.createWritableRaster(
365 sampleModel, dataBuffer, new Point(0, 0));
366
367 tiledImage.setData(raster);
368
369 if (false) {
370 JAI.create("filestore", tiledImage, "test.png", "PNG");
371
372 printImage(renderedImage);
373 printImage(tiledImage);
374 }
375
376 return tiledImage;
377 }
378
379 protected void printImage(RenderedImage renderedImage) {
380 SampleModel sampleModel = renderedImage.getSampleModel();
381
382 int height = renderedImage.getHeight();
383 int width = renderedImage.getWidth();
384 int numOfBands = sampleModel.getNumBands();
385
386 int[] pixels = new int[height * width * numOfBands];
387
388 Raster raster = renderedImage.getData();
389
390 raster.getPixels(0, 0, width, height, pixels);
391
392 int offset = 0;
393
394 for (int h = 0; h < height; h++) {
395 for (int w = 0; w < width; w++) {
396 offset = (h * width * numOfBands) + (w * numOfBands);
397
398 System.out.print("[" + w + ", " + h + "] = ");
399
400 for (int b = 0; b < numOfBands; b++) {
401 System.out.print(pixels[offset + b] + " ");
402 }
403 }
404
405 System.out.println();
406 }
407 }
408
409 private static final int _NUM_OF_BANDS = 4;
410
411 private static Log _log = LogFactoryUtil.getLog(SpriteProcessorImpl.class);
412
413 }