001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
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    /**
062     * @author Brian Wing Shun Chan
063     */
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                            // PNG
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                            // GIF
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                    /*javax.media.jai.JAI.create(
353                            "filestore", tiledImage, "test.png", "PNG");
354    
355                    printImage(renderedImage);
356                    printImage(tiledImage);}*/
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    }