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