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