1
14
15 package com.liferay.portal.image;
16
17 import com.liferay.portal.kernel.image.ImageProcessorUtil;
18 import com.liferay.portal.kernel.image.SpriteProcessor;
19 import com.liferay.portal.kernel.log.Log;
20 import com.liferay.portal.kernel.log.LogFactoryUtil;
21 import com.liferay.portal.kernel.util.ArrayUtil;
22 import com.liferay.portal.kernel.util.FileUtil;
23 import com.liferay.portal.kernel.util.PropertiesUtil;
24 import com.liferay.portal.kernel.util.SortedProperties;
25 import com.liferay.portal.kernel.util.StringPool;
26 import com.liferay.portal.kernel.util.StringUtil;
27 import com.liferay.portal.kernel.util.Validator;
28
29 import java.awt.Point;
30 import java.awt.Transparency;
31 import java.awt.image.ColorModel;
32 import java.awt.image.DataBuffer;
33 import java.awt.image.DataBufferByte;
34 import java.awt.image.IndexColorModel;
35 import java.awt.image.Raster;
36 import java.awt.image.RenderedImage;
37 import java.awt.image.SampleModel;
38
39 import java.io.File;
40 import java.io.FileInputStream;
41 import java.io.FileOutputStream;
42 import java.io.IOException;
43
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.List;
47 import java.util.Properties;
48
49 import javax.imageio.ImageIO;
50
51 import javax.media.jai.JAI;
52 import javax.media.jai.LookupTableJAI;
53 import javax.media.jai.PlanarImage;
54 import javax.media.jai.RasterFactory;
55 import javax.media.jai.TiledImage;
56 import javax.media.jai.operator.LookupDescriptor;
57 import javax.media.jai.operator.MosaicDescriptor;
58 import javax.media.jai.operator.TranslateDescriptor;
59
60 import org.geotools.image.ImageWorker;
61
62
67 public class SpriteProcessorImpl implements SpriteProcessor {
68
69 static {
70 System.setProperty("com.sun.media.jai.disableMediaLib", "true");
71 }
72
73 public Properties generate(
74 List<File> images, String spriteFileName,
75 String spritePropertiesFileName, String spritePropertiesRootPath,
76 int maxHeight, int maxWidth, int maxSize)
77 throws IOException {
78
79 if (images.size() < 1) {
80 return null;
81 }
82
83 if (spritePropertiesRootPath.endsWith(StringPool.SLASH) ||
84 spritePropertiesRootPath.endsWith(StringPool.BACK_SLASH)) {
85
86 spritePropertiesRootPath = spritePropertiesRootPath.substring(
87 0, spritePropertiesRootPath.length() - 1);
88 }
89
90 File dir = images.get(0).getParentFile();
91
92 File spritePropertiesFile = new File(
93 dir.toString() + StringPool.SLASH + spritePropertiesFileName);
94
95 boolean build = false;
96
97 long lastModified = 0;
98
99 if (spritePropertiesFile.exists()) {
100 lastModified = spritePropertiesFile.lastModified();
101
102 for (File image : images) {
103 if (image.lastModified() > lastModified) {
104 build = true;
105
106 break;
107 }
108 }
109 }
110 else {
111 build = true;
112 }
113
114 if (!build) {
115 String spritePropertiesString = FileUtil.read(spritePropertiesFile);
116
117 if (Validator.isNull(spritePropertiesString)) {
118 return null;
119 }
120 else {
121 return PropertiesUtil.load(spritePropertiesString);
122 }
123 }
124
125 List<RenderedImage> renderedImages = new ArrayList<RenderedImage>();
126
127 Properties spriteProperties = new SortedProperties();
128
129 float x = 0;
130 float y = 0;
131
132 for (File file : images) {
133 String fileName = file.getName();
134
135 if (file.length() > maxSize) {
136 continue;
137 }
138
139 FileInputStream fis = new FileInputStream(file);
140
141 try {
142 RenderedImage renderedImage = ImageIO.read(fis);
143
144 int height = renderedImage.getHeight();
145 int width = renderedImage.getWidth();
146
147 if ((height <= maxHeight) && (width <= maxWidth)) {
148 renderedImage = convert(renderedImage);
149
150 renderedImage = TranslateDescriptor.create(
151 renderedImage, x, y, null, null);
152
153 renderedImages.add(renderedImage);
154
155 String key = StringUtil.replace(
156 file.toString(), StringPool.BACK_SLASH,
157 StringPool.SLASH);
158
159 key = key.substring(
160 spritePropertiesRootPath.toString().length());
161
162 String value = (int)y + "," + height + "," + width;
163
164 spriteProperties.setProperty(key, value);
165
166 y += renderedImage.getHeight();
167 }
168 }
169 catch (Exception e) {
170 if (_log.isWarnEnabled()) {
171 _log.warn("Unable to process " + file);
172 }
173
174 if (_log.isDebugEnabled()) {
175 _log.debug(e, e);
176 }
177 }
178 finally {
179 fis.close();
180 }
181 }
182
183 if (renderedImages.size() <= 1) {
184 renderedImages.clear();
185 spriteProperties.clear();
186 }
187 else {
188
189
191 RenderedImage renderedImage = MosaicDescriptor.create(
192 (RenderedImage[])renderedImages.toArray(
193 new RenderedImage[renderedImages.size()]),
194 MosaicDescriptor.MOSAIC_TYPE_OVERLAY, null, null, null, null,
195 null);
196
197 File spriteFile = new File(
198 dir.toString() + StringPool.SLASH + spriteFileName);
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
212 renderedImage = imageWorker.getPlanarImage();
213
214 spriteFile = new File(
215 dir.toString() + StringPool.SLASH +
216 StringUtil.replace(spriteFileName, ".png", ".gif"));
217
218 FileOutputStream fos = new FileOutputStream(spriteFile);
219
220 try {
221 ImageProcessorUtil.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 if (false) {
361 JAI.create("filestore", tiledImage, "test.png", "PNG");
362
363 printImage(renderedImage);
364 printImage(tiledImage);
365 }
366
367 return tiledImage;
368 }
369
370 protected void printImage(RenderedImage renderedImage) {
371 SampleModel sampleModel = renderedImage.getSampleModel();
372
373 int height = renderedImage.getHeight();
374 int width = renderedImage.getWidth();
375 int numOfBands = sampleModel.getNumBands();
376
377 int[] pixels = new int[height * width * numOfBands];
378
379 Raster raster = renderedImage.getData();
380
381 raster.getPixels(0, 0, width, height, pixels);
382
383 int offset = 0;
384
385 for (int h = 0; h < height; h++) {
386 for (int w = 0; w < width; w++) {
387 offset = (h * width * numOfBands) + (w * numOfBands);
388
389 System.out.print("[" + w + ", " + h + "] = ");
390
391 for (int b = 0; b < numOfBands; b++) {
392 System.out.print(pixels[offset + b] + " ");
393 }
394 }
395
396 System.out.println();
397 }
398 }
399
400 private static final int _NUM_OF_BANDS = 4;
401
402 private static Log _log = LogFactoryUtil.getLog(SpriteProcessorImpl.class);
403
404 }