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