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