001
014
015 package com.liferay.portal.image;
016
017 import com.liferay.portal.kernel.image.ImageBag;
018 import com.liferay.portal.kernel.image.ImageProcessorUtil;
019 import com.liferay.portal.kernel.image.SpriteProcessor;
020 import com.liferay.portal.kernel.log.Log;
021 import com.liferay.portal.kernel.log.LogFactoryUtil;
022 import com.liferay.portal.kernel.util.ArrayUtil;
023 import com.liferay.portal.kernel.util.FileUtil;
024 import com.liferay.portal.kernel.util.PropertiesUtil;
025 import com.liferay.portal.kernel.util.SortedProperties;
026 import com.liferay.portal.kernel.util.StringPool;
027 import com.liferay.portal.kernel.util.StringUtil;
028 import com.liferay.portal.kernel.util.Validator;
029
030 import java.awt.Point;
031 import java.awt.Transparency;
032 import java.awt.image.ColorModel;
033 import java.awt.image.DataBuffer;
034 import java.awt.image.DataBufferByte;
035 import java.awt.image.IndexColorModel;
036 import java.awt.image.Raster;
037 import java.awt.image.RenderedImage;
038 import java.awt.image.SampleModel;
039
040 import java.io.File;
041 import java.io.FileOutputStream;
042 import java.io.IOException;
043
044 import java.util.ArrayList;
045 import java.util.Collections;
046 import java.util.List;
047 import java.util.Properties;
048
049 import javax.imageio.ImageIO;
050
051 import javax.media.jai.LookupTableJAI;
052 import javax.media.jai.PlanarImage;
053 import javax.media.jai.RasterFactory;
054 import javax.media.jai.TiledImage;
055 import javax.media.jai.operator.LookupDescriptor;
056 import javax.media.jai.operator.MosaicDescriptor;
057 import javax.media.jai.operator.TranslateDescriptor;
058
059 import org.geotools.image.ImageWorker;
060
061
064 public class SpriteProcessorImpl implements SpriteProcessor {
065
066 static {
067 System.setProperty("com.sun.media.jai.disableMediaLib", "true");
068 }
069
070 public Properties generate(
071 List<File> images, String spriteFileName,
072 String spritePropertiesFileName, String spritePropertiesRootPath,
073 int maxHeight, int maxWidth, int maxSize)
074 throws IOException {
075
076 if (images.size() < 1) {
077 return null;
078 }
079
080 if (spritePropertiesRootPath.endsWith(StringPool.SLASH) ||
081 spritePropertiesRootPath.endsWith(StringPool.BACK_SLASH)) {
082
083 spritePropertiesRootPath = spritePropertiesRootPath.substring(
084 0, spritePropertiesRootPath.length() - 1);
085 }
086
087 File dir = images.get(0).getParentFile();
088
089 File spritePropertiesFile = new File(
090 dir.toString() + StringPool.SLASH + spritePropertiesFileName);
091
092 boolean build = false;
093
094 long lastModified = 0;
095
096 if (spritePropertiesFile.exists()) {
097 lastModified = spritePropertiesFile.lastModified();
098
099 for (File image : images) {
100 if (image.lastModified() > lastModified) {
101 build = true;
102
103 break;
104 }
105 }
106 }
107 else {
108 build = true;
109 }
110
111 if (!build) {
112 String spritePropertiesString = FileUtil.read(spritePropertiesFile);
113
114 if (Validator.isNull(spritePropertiesString)) {
115 return null;
116 }
117 else {
118 return PropertiesUtil.load(spritePropertiesString);
119 }
120 }
121
122 List<RenderedImage> renderedImages = new ArrayList<RenderedImage>();
123
124 Properties spriteProperties = new SortedProperties();
125
126 float x = 0;
127 float y = 0;
128
129 for (File file : images) {
130 if (file.length() > maxSize) {
131 continue;
132 }
133
134 try {
135 ImageBag imageBag = ImageProcessorUtil.read(file);
136
137 RenderedImage renderedImage = imageBag.getRenderedImage();
138
139 int height = renderedImage.getHeight();
140 int width = renderedImage.getWidth();
141
142 if ((height <= maxHeight) && (width <= maxWidth)) {
143 renderedImage = convert(renderedImage);
144
145 renderedImage = TranslateDescriptor.create(
146 renderedImage, x, y, null, null);
147
148 renderedImages.add(renderedImage);
149
150 String key = StringUtil.replace(
151 file.toString(), StringPool.BACK_SLASH,
152 StringPool.SLASH);
153
154 key = key.substring(
155 spritePropertiesRootPath.toString().length());
156
157 String value = (int)y + "," + height + "," + width;
158
159 spriteProperties.setProperty(key, value);
160
161 y += renderedImage.getHeight();
162 }
163 }
164 catch (Exception e) {
165 if (_log.isWarnEnabled()) {
166 _log.warn("Unable to process " + file);
167 }
168
169 if (_log.isDebugEnabled()) {
170 _log.debug(e, e);
171 }
172 }
173 }
174
175 if (renderedImages.size() <= 1) {
176 renderedImages.clear();
177 spriteProperties.clear();
178 }
179 else {
180
181
182
183 RenderedImage renderedImage = MosaicDescriptor.create(
184 renderedImages.toArray(
185 new RenderedImage[renderedImages.size()]),
186 MosaicDescriptor.MOSAIC_TYPE_OVERLAY, null, null, null, null,
187 null);
188
189 File spriteFile = new File(
190 dir.toString() + StringPool.SLASH + spriteFileName);
191
192 ImageIO.write(renderedImage, "png", spriteFile);
193
194 if (lastModified > 0) {
195 spriteFile.setLastModified(lastModified);
196 }
197
198 ImageWorker imageWorker = new ImageWorker(renderedImage);
199
200 imageWorker.forceIndexColorModelForGIF(true);
201
202
203
204 renderedImage = imageWorker.getPlanarImage();
205
206 spriteFile = new File(
207 dir.toString() + StringPool.SLASH +
208 StringUtil.replace(spriteFileName, ".png", ".gif"));
209
210 FileOutputStream fos = new FileOutputStream(spriteFile);
211
212 try {
213 ImageProcessorUtil.encodeGIF(renderedImage, fos);
214 }
215 finally {
216 fos.close();
217 }
218
219 if (lastModified > 0) {
220 spriteFile.setLastModified(lastModified);
221 }
222 }
223
224 FileUtil.write(
225 spritePropertiesFile, PropertiesUtil.toString(spriteProperties));
226
227 if (lastModified > 0) {
228 spritePropertiesFile.setLastModified(lastModified);
229 }
230
231 return spriteProperties;
232 }
233
234 protected RenderedImage convert(RenderedImage renderedImage)
235 throws Exception {
236
237 int height = renderedImage.getHeight();
238 int width = renderedImage.getWidth();
239
240 SampleModel sampleModel = renderedImage.getSampleModel();
241 ColorModel colorModel = renderedImage.getColorModel();
242
243 Raster raster = renderedImage.getData();
244
245 DataBuffer dataBuffer = raster.getDataBuffer();
246
247 if (colorModel instanceof IndexColorModel) {
248 IndexColorModel indexColorModel = (IndexColorModel)colorModel;
249
250 int mapSize = indexColorModel.getMapSize();
251
252 byte[][] data = new byte[4][mapSize];
253
254 indexColorModel.getReds(data[0]);
255 indexColorModel.getGreens(data[1]);
256 indexColorModel.getBlues(data[2]);
257 indexColorModel.getAlphas(data[3]);
258
259 LookupTableJAI lookupTableJAI = new LookupTableJAI(data);
260
261 renderedImage = LookupDescriptor.create(
262 renderedImage, lookupTableJAI, null);
263 }
264 else if (sampleModel.getNumBands() == 2) {
265 List<Byte> bytesList = new ArrayList<Byte>(
266 height * width * _NUM_OF_BANDS);
267
268 List<Byte> tempBytesList = new ArrayList<Byte>(_NUM_OF_BANDS);
269
270 for (int i = 0; i < dataBuffer.getSize(); i++) {
271 int mod = (i + 1) % 2;
272
273 int elemPos = i;
274
275 if (mod == 0) {
276 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
277 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
278 }
279
280 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
281
282 if (mod == 0) {
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 renderedImage, height, width, newDataBuffer);
298 }
299 else if (colorModel.getTransparency() != Transparency.TRANSLUCENT) {
300 List<Byte> bytesList = new ArrayList<Byte>(
301 height * width * _NUM_OF_BANDS);
302
303 List<Byte> tempBytesList = new ArrayList<Byte>(_NUM_OF_BANDS);
304
305 for (int i = 0; i < dataBuffer.getSize(); i++) {
306 int mod = (i + 1) % 3;
307
308 int elemPos = i;
309
310 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
311
312 if (mod == 0) {
313 tempBytesList.add((byte)255);
314
315 Collections.reverse(tempBytesList);
316
317 bytesList.addAll(tempBytesList);
318
319 tempBytesList.clear();
320 }
321 }
322
323 byte[] data = ArrayUtil.toArray(
324 bytesList.toArray(new Byte[bytesList.size()]));
325
326 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
327
328 renderedImage = createRenderedImage(
329 renderedImage, height, width, newDataBuffer);
330 }
331
332 return renderedImage;
333 }
334
335 protected RenderedImage createRenderedImage(
336 RenderedImage renderedImage, int height, int width,
337 DataBuffer dataBuffer) {
338
339 SampleModel sampleModel =
340 RasterFactory.createPixelInterleavedSampleModel(
341 DataBuffer.TYPE_BYTE, width, height, _NUM_OF_BANDS);
342 ColorModel colorModel = PlanarImage.createColorModel(sampleModel);
343
344 TiledImage tiledImage = new TiledImage(
345 0, 0, width, height, 0, 0, sampleModel, colorModel);
346
347 Raster raster = RasterFactory.createWritableRaster(
348 sampleModel, dataBuffer, new Point(0, 0));
349
350 tiledImage.setData(raster);
351
352
357
358 return tiledImage;
359 }
360
361 protected void printImage(RenderedImage renderedImage) {
362 SampleModel sampleModel = renderedImage.getSampleModel();
363
364 int height = renderedImage.getHeight();
365 int width = renderedImage.getWidth();
366 int numOfBands = sampleModel.getNumBands();
367
368 int[] pixels = new int[height * width * numOfBands];
369
370 Raster raster = renderedImage.getData();
371
372 raster.getPixels(0, 0, width, height, pixels);
373
374 int offset = 0;
375
376 for (int h = 0; h < height; h++) {
377 for (int w = 0; w < width; w++) {
378 offset = (h * width * numOfBands) + (w * numOfBands);
379
380 System.out.print("[" + w + ", " + h + "] = ");
381
382 for (int b = 0; b < numOfBands; b++) {
383 System.out.print(pixels[offset + b] + " ");
384 }
385 }
386
387 System.out.println();
388 }
389 }
390
391 private static final int _NUM_OF_BANDS = 4;
392
393 private static Log _log = LogFactoryUtil.getLog(SpriteProcessorImpl.class);
394
395 }