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.security.pacl.DoPrivileged;
023 import com.liferay.portal.kernel.servlet.ServletContextUtil;
024 import com.liferay.portal.kernel.util.ArrayUtil;
025 import com.liferay.portal.kernel.util.CharPool;
026 import com.liferay.portal.kernel.util.ContextPathUtil;
027 import com.liferay.portal.kernel.util.FileUtil;
028 import com.liferay.portal.kernel.util.PropertiesUtil;
029 import com.liferay.portal.kernel.util.SortedProperties;
030 import com.liferay.portal.kernel.util.StringPool;
031 import com.liferay.portal.kernel.util.StringUtil;
032 import com.liferay.portal.kernel.util.Validator;
033 import com.liferay.portal.util.PropsValues;
034
035 import java.awt.Point;
036 import java.awt.Transparency;
037 import java.awt.image.ColorModel;
038 import java.awt.image.DataBuffer;
039 import java.awt.image.DataBufferByte;
040 import java.awt.image.IndexColorModel;
041 import java.awt.image.Raster;
042 import java.awt.image.RenderedImage;
043 import java.awt.image.SampleModel;
044
045 import java.io.File;
046 import java.io.FileOutputStream;
047 import java.io.IOException;
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 @DoPrivileged
072 public class SpriteProcessorImpl implements SpriteProcessor {
073
074 @Override
075 public Properties generate(
076 ServletContext servletContext, List<File> imageFiles,
077 String spriteFileName, String spritePropertiesFileName,
078 String spritePropertiesRootPath, int maxHeight, int maxWidth,
079 int maxSize)
080 throws IOException {
081
082 if (imageFiles.size() < 1) {
083 return null;
084 }
085
086 if (spritePropertiesRootPath.endsWith(StringPool.BACK_SLASH) ||
087 spritePropertiesRootPath.endsWith(StringPool.SLASH)) {
088
089 spritePropertiesRootPath = spritePropertiesRootPath.substring(
090 0, spritePropertiesRootPath.length() - 1);
091 }
092
093 Collections.sort(imageFiles);
094
095 String spriteRootDirName = getSpriteRootDirName(
096 servletContext, imageFiles);
097
098 File spritePropertiesFile = new File(
099 spriteRootDirName + 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 imageFile : imageFiles) {
109 if (imageFile.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 imageFile : imageFiles) {
139 if (imageFile.length() > maxSize) {
140 continue;
141 }
142
143 try {
144 ImageBag imageBag = ImageToolUtil.read(imageFile);
145
146 RenderedImage renderedImage = imageBag.getRenderedImage();
147
148 int height = renderedImage.getHeight();
149 int width = renderedImage.getWidth();
150
151 if ((height <= maxHeight) && (width <= maxWidth)) {
152 renderedImage = convert(renderedImage);
153
154 renderedImage = TranslateDescriptor.create(
155 renderedImage, x, y, null, null);
156
157 renderedImages.add(renderedImage);
158
159 String key = StringUtil.replace(
160 imageFile.toString(), CharPool.BACK_SLASH,
161 CharPool.SLASH);
162
163 key = key.substring(
164 spritePropertiesRootPath.toString().length());
165
166 String value = (int)y + "," + height + "," + width;
167
168 spriteProperties.setProperty(key, value);
169
170 y += renderedImage.getHeight();
171 }
172 }
173 catch (Exception e) {
174 if (_log.isWarnEnabled()) {
175 _log.warn("Unable to process " + imageFile);
176 }
177
178 if (_log.isDebugEnabled()) {
179 _log.debug(e, e);
180 }
181 }
182 }
183
184 if (renderedImages.size() <= 1) {
185 renderedImages.clear();
186 spriteProperties.clear();
187 }
188 else {
189
190
191
192 RenderedImage renderedImage = MosaicDescriptor.create(
193 renderedImages.toArray(
194 new RenderedImage[renderedImages.size()]),
195 MosaicDescriptor.MOSAIC_TYPE_OVERLAY, null, null, null, null,
196 null);
197
198 File spriteFile = new File(
199 spriteRootDirName + StringPool.SLASH + spriteFileName);
200
201 spriteFile.mkdirs();
202
203 ImageIO.write(renderedImage, "png", spriteFile);
204
205 if (lastModified > 0) {
206 spriteFile.setLastModified(lastModified);
207 }
208
209 ImageWorker imageWorker = new ImageWorker(renderedImage);
210
211 imageWorker.forceIndexColorModelForGIF(true);
212
213
214
215 renderedImage = imageWorker.getPlanarImage();
216
217 spriteFile = new File(
218 spriteRootDirName + StringPool.SLASH +
219 StringUtil.replace(spriteFileName, ".png", ".gif"));
220
221 FileOutputStream fos = new FileOutputStream(spriteFile);
222
223 try {
224 ImageToolUtil.encodeGIF(renderedImage, fos);
225 }
226 finally {
227 fos.close();
228 }
229
230 if (lastModified > 0) {
231 spriteFile.setLastModified(lastModified);
232 }
233 }
234
235 FileUtil.write(
236 spritePropertiesFile, PropertiesUtil.toString(spriteProperties));
237
238 if (lastModified > 0) {
239 spritePropertiesFile.setLastModified(lastModified);
240 }
241
242 return spriteProperties;
243 }
244
245 protected RenderedImage convert(RenderedImage renderedImage)
246 throws Exception {
247
248 int height = renderedImage.getHeight();
249 int width = renderedImage.getWidth();
250
251 SampleModel sampleModel = renderedImage.getSampleModel();
252 ColorModel colorModel = renderedImage.getColorModel();
253
254 Raster raster = renderedImage.getData();
255
256 DataBuffer dataBuffer = raster.getDataBuffer();
257
258 if (colorModel instanceof IndexColorModel) {
259 IndexColorModel indexColorModel = (IndexColorModel)colorModel;
260
261 int mapSize = indexColorModel.getMapSize();
262
263 byte[][] data = new byte[4][mapSize];
264
265 indexColorModel.getReds(data[0]);
266 indexColorModel.getGreens(data[1]);
267 indexColorModel.getBlues(data[2]);
268 indexColorModel.getAlphas(data[3]);
269
270 LookupTableJAI lookupTableJAI = new LookupTableJAI(data);
271
272 renderedImage = LookupDescriptor.create(
273 renderedImage, lookupTableJAI, null);
274 }
275 else if (sampleModel.getNumBands() == 2) {
276 List<Byte> bytesList = new ArrayList<Byte>(
277 height * width * _NUM_OF_BANDS);
278
279 List<Byte> tempBytesList = new ArrayList<Byte>(_NUM_OF_BANDS);
280
281 for (int i = 0; i < dataBuffer.getSize(); i++) {
282 int mod = (i + 1) % 2;
283
284 int elemPos = i;
285
286 if (mod == 0) {
287 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
288 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
289 }
290
291 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
292
293 if (mod == 0) {
294 Collections.reverse(tempBytesList);
295
296 bytesList.addAll(tempBytesList);
297
298 tempBytesList.clear();
299 }
300 }
301
302 byte[] data = ArrayUtil.toArray(
303 bytesList.toArray(new Byte[bytesList.size()]));
304
305 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
306
307 renderedImage = createRenderedImage(
308 renderedImage, height, width, newDataBuffer);
309 }
310 else if (colorModel.getTransparency() != Transparency.TRANSLUCENT) {
311 List<Byte> bytesList = new ArrayList<Byte>(
312 height * width * _NUM_OF_BANDS);
313
314 List<Byte> tempBytesList = new ArrayList<Byte>(_NUM_OF_BANDS);
315
316 for (int i = 0; i < dataBuffer.getSize(); i++) {
317 int mod = (i + 1) % 3;
318
319 int elemPos = i;
320
321 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
322
323 if (mod == 0) {
324 tempBytesList.add((byte)255);
325
326 Collections.reverse(tempBytesList);
327
328 bytesList.addAll(tempBytesList);
329
330 tempBytesList.clear();
331 }
332 }
333
334 byte[] data = ArrayUtil.toArray(
335 bytesList.toArray(new Byte[bytesList.size()]));
336
337 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
338
339 renderedImage = createRenderedImage(
340 renderedImage, height, width, newDataBuffer);
341 }
342
343 return renderedImage;
344 }
345
346 protected RenderedImage createRenderedImage(
347 RenderedImage renderedImage, int height, int width,
348 DataBuffer dataBuffer) {
349
350 SampleModel sampleModel =
351 RasterFactory.createPixelInterleavedSampleModel(
352 DataBuffer.TYPE_BYTE, width, height, _NUM_OF_BANDS);
353 ColorModel colorModel = PlanarImage.createColorModel(sampleModel);
354
355 TiledImage tiledImage = new TiledImage(
356 0, 0, width, height, 0, 0, sampleModel, colorModel);
357
358 Raster raster = RasterFactory.createWritableRaster(
359 sampleModel, dataBuffer, new Point(0, 0));
360
361 tiledImage.setData(raster);
362
363
368
369 return tiledImage;
370 }
371
372 protected String getSpriteRootDirName(
373 ServletContext servletContext, List<File> imageFiles) {
374
375 String spriteRootDirName = PropsValues.SPRITE_ROOT_DIR;
376
377 File imageFile = imageFiles.get(0);
378
379 File imageDir = imageFile.getParentFile();
380
381 String imageDirName = imageDir.toString();
382
383 if (Validator.isNull(spriteRootDirName)) {
384 return imageDirName;
385 }
386
387 if (!spriteRootDirName.endsWith(StringPool.BACK_SLASH) &&
388 !spriteRootDirName.endsWith(StringPool.SLASH)) {
389
390 spriteRootDirName += StringPool.SLASH;
391 }
392
393 String portalProxyPath = PropsValues.PORTAL_PROXY_PATH;
394
395 if (Validator.isNotNull(portalProxyPath)) {
396 spriteRootDirName += portalProxyPath + StringPool.SLASH;
397 }
398
399 String portalContextPath = PropsValues.PORTAL_CTX;
400
401 if (Validator.isNotNull(portalContextPath) &&
402 !portalContextPath.equals(StringPool.SLASH)) {
403
404 spriteRootDirName += portalContextPath + StringPool.SLASH;
405 }
406
407 String portletContextPath = ContextPathUtil.getContextPath(
408 servletContext);
409
410 if (Validator.isNotNull(portletContextPath)) {
411 spriteRootDirName += portletContextPath + StringPool.SLASH;
412 }
413
414 String rootRealPath = ServletContextUtil.getRealPath(
415 servletContext, StringPool.SLASH);
416
417 spriteRootDirName = StringUtil.replace(
418 spriteRootDirName + imageDirName.substring(rootRealPath.length()),
419 CharPool.BACK_SLASH, CharPool.SLASH);
420
421 if (spriteRootDirName.endsWith(StringPool.BACK_SLASH) ||
422 spriteRootDirName.endsWith(StringPool.SLASH)) {
423
424 spriteRootDirName = spriteRootDirName.substring(
425 0, spriteRootDirName.length() - 1);
426 }
427
428 return spriteRootDirName;
429 }
430
431 protected void printImage(RenderedImage renderedImage) {
432 SampleModel sampleModel = renderedImage.getSampleModel();
433
434 int height = renderedImage.getHeight();
435 int width = renderedImage.getWidth();
436 int numOfBands = sampleModel.getNumBands();
437
438 int[] pixels = new int[height * width * numOfBands];
439
440 Raster raster = renderedImage.getData();
441
442 raster.getPixels(0, 0, width, height, pixels);
443
444 int offset = 0;
445
446 for (int h = 0; h < height; h++) {
447 for (int w = 0; w < width; w++) {
448 offset = (h * width * numOfBands) + (w * numOfBands);
449
450 System.out.print("[" + w + ", " + h + "] = ");
451
452 for (int b = 0; b < numOfBands; b++) {
453 System.out.print(pixels[offset + b] + " ");
454 }
455 }
456
457 System.out.println();
458 }
459 }
460
461 private static final int _NUM_OF_BANDS = 4;
462
463 private static Log _log = LogFactoryUtil.getLog(SpriteProcessorImpl.class);
464
465 }