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.ContextPathUtil;
026 import com.liferay.portal.kernel.util.FileUtil;
027 import com.liferay.portal.kernel.util.JavaConstants;
028 import com.liferay.portal.kernel.util.PropertiesUtil;
029 import com.liferay.portal.kernel.util.SortedProperties;
030 import com.liferay.portal.kernel.util.Validator;
031 import com.liferay.util.PropertyComparator;
032
033 import java.awt.Point;
034 import java.awt.Transparency;
035 import java.awt.image.ColorModel;
036 import java.awt.image.DataBuffer;
037 import java.awt.image.DataBufferByte;
038 import java.awt.image.IndexColorModel;
039 import java.awt.image.Raster;
040 import java.awt.image.RenderedImage;
041 import java.awt.image.SampleModel;
042
043 import java.io.File;
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
069 @DoPrivileged
070 public class SpriteProcessorImpl implements SpriteProcessor {
071
072 @Override
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 FileUtil.mkdirs(spriteRootDir);
099
100 File spritePropertiesFile = new File(
101 spriteRootDir, spritePropertiesFileName);
102
103 File spritePropertiesParentFile = spritePropertiesFile.getParentFile();
104
105 FileUtil.mkdirs(spritePropertiesParentFile);
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 = ServletContextUtil.getResourcePath(imageURL);
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 File spriteDir = spriteFile.getParentFile();
226
227 FileUtil.mkdirs(spriteDir);
228
229 ImageIO.write(renderedImage, "png", spriteFile);
230
231 if (lastModified > 0) {
232 spriteFile.setLastModified(lastModified);
233 }
234 }
235
236 FileUtil.write(
237 spritePropertiesFile, PropertiesUtil.toString(spriteProperties));
238
239 if (lastModified > 0) {
240 spritePropertiesFile.setLastModified(lastModified);
241 }
242
243 return spriteProperties;
244 }
245
246 protected RenderedImage convert(RenderedImage renderedImage)
247 throws Exception {
248
249 int height = renderedImage.getHeight();
250 int width = renderedImage.getWidth();
251
252 SampleModel sampleModel = renderedImage.getSampleModel();
253 ColorModel colorModel = renderedImage.getColorModel();
254
255 Raster raster = renderedImage.getData();
256
257 DataBuffer dataBuffer = raster.getDataBuffer();
258
259 if (colorModel instanceof IndexColorModel) {
260 IndexColorModel indexColorModel = (IndexColorModel)colorModel;
261
262 int mapSize = indexColorModel.getMapSize();
263
264 byte[][] data = new byte[4][mapSize];
265
266 indexColorModel.getReds(data[0]);
267 indexColorModel.getGreens(data[1]);
268 indexColorModel.getBlues(data[2]);
269 indexColorModel.getAlphas(data[3]);
270
271 LookupTableJAI lookupTableJAI = new LookupTableJAI(data);
272
273 renderedImage = LookupDescriptor.create(
274 renderedImage, lookupTableJAI, null);
275 }
276 else if (sampleModel.getNumBands() == 2) {
277 List<Byte> bytesList = new ArrayList<Byte>(
278 height * width * _NUM_OF_BANDS);
279
280 List<Byte> tempBytesList = new ArrayList<Byte>(_NUM_OF_BANDS);
281
282 for (int i = 0; i < dataBuffer.getSize(); i++) {
283 int mod = (i + 1) % 2;
284
285 int elemPos = i;
286
287 if (mod == 0) {
288 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
289 tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
290 }
291
292 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
293
294 if (mod == 0) {
295 Collections.reverse(tempBytesList);
296
297 bytesList.addAll(tempBytesList);
298
299 tempBytesList.clear();
300 }
301 }
302
303 byte[] data = ArrayUtil.toArray(
304 bytesList.toArray(new Byte[bytesList.size()]));
305
306 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
307
308 renderedImage = createRenderedImage(
309 renderedImage, height, width, newDataBuffer);
310 }
311 else if (colorModel.getTransparency() != Transparency.TRANSLUCENT) {
312 List<Byte> bytesList = new ArrayList<Byte>(
313 height * width * _NUM_OF_BANDS);
314
315 List<Byte> tempBytesList = new ArrayList<Byte>(_NUM_OF_BANDS);
316
317 for (int i = 0; i < dataBuffer.getSize(); i++) {
318 int mod = (i + 1) % 3;
319
320 int elemPos = i;
321
322 tempBytesList.add((byte)dataBuffer.getElem(elemPos));
323
324 if (mod == 0) {
325 tempBytesList.add((byte)255);
326
327 Collections.reverse(tempBytesList);
328
329 bytesList.addAll(tempBytesList);
330
331 tempBytesList.clear();
332 }
333 }
334
335 byte[] data = ArrayUtil.toArray(
336 bytesList.toArray(new Byte[bytesList.size()]));
337
338 DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
339
340 renderedImage = createRenderedImage(
341 renderedImage, height, width, newDataBuffer);
342 }
343
344 return renderedImage;
345 }
346
347 protected RenderedImage createRenderedImage(
348 RenderedImage renderedImage, int height, int width,
349 DataBuffer dataBuffer) {
350
351 SampleModel sampleModel =
352 RasterFactory.createPixelInterleavedSampleModel(
353 DataBuffer.TYPE_BYTE, width, height, _NUM_OF_BANDS);
354 ColorModel colorModel = PlanarImage.createColorModel(sampleModel);
355
356 TiledImage tiledImage = new TiledImage(
357 0, 0, width, height, 0, 0, sampleModel, colorModel);
358
359 Raster raster = RasterFactory.createWritableRaster(
360 sampleModel, dataBuffer, new Point(0, 0));
361
362 tiledImage.setData(raster);
363
364
369
370 return tiledImage;
371 }
372
373 protected void printImage(RenderedImage renderedImage) {
374 SampleModel sampleModel = renderedImage.getSampleModel();
375
376 int height = renderedImage.getHeight();
377 int width = renderedImage.getWidth();
378 int numOfBands = sampleModel.getNumBands();
379
380 int[] pixels = new int[height * width * numOfBands];
381
382 Raster raster = renderedImage.getData();
383
384 raster.getPixels(0, 0, width, height, pixels);
385
386 int offset = 0;
387
388 for (int h = 0; h < height; h++) {
389 for (int w = 0; w < width; w++) {
390 offset = (h * width * numOfBands) + (w * numOfBands);
391
392 System.out.print("[" + w + ", " + h + "] = ");
393
394 for (int b = 0; b < numOfBands; b++) {
395 System.out.print(pixels[offset + b] + " ");
396 }
397 }
398
399 System.out.println();
400 }
401 }
402
403 private static final int _NUM_OF_BANDS = 4;
404
405 private static Log _log = LogFactoryUtil.getLog(SpriteProcessorImpl.class);
406
407 }