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