001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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.CharPool;
024    import com.liferay.portal.kernel.util.FileUtil;
025    import com.liferay.portal.kernel.util.PropertiesUtil;
026    import com.liferay.portal.kernel.util.SortedProperties;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.kernel.util.StringUtil;
029    import com.liferay.portal.kernel.util.Validator;
030    
031    import java.awt.Point;
032    import java.awt.Transparency;
033    import java.awt.image.ColorModel;
034    import java.awt.image.DataBuffer;
035    import java.awt.image.DataBufferByte;
036    import java.awt.image.IndexColorModel;
037    import java.awt.image.Raster;
038    import java.awt.image.RenderedImage;
039    import java.awt.image.SampleModel;
040    
041    import java.io.File;
042    import java.io.FileOutputStream;
043    import java.io.IOException;
044    
045    import java.util.ArrayList;
046    import java.util.Collections;
047    import java.util.List;
048    import java.util.Properties;
049    
050    import javax.imageio.ImageIO;
051    
052    import javax.media.jai.LookupTableJAI;
053    import javax.media.jai.PlanarImage;
054    import javax.media.jai.RasterFactory;
055    import javax.media.jai.TiledImage;
056    import javax.media.jai.operator.LookupDescriptor;
057    import javax.media.jai.operator.MosaicDescriptor;
058    import javax.media.jai.operator.TranslateDescriptor;
059    
060    import org.geotools.image.ImageWorker;
061    
062    /**
063     * @author Brian Wing Shun Chan
064     */
065    public class SpriteProcessorImpl implements SpriteProcessor {
066    
067            static {
068                    System.setProperty("com.sun.media.jai.disableMediaLib", "true");
069            }
070    
071            public Properties generate(
072                            List<File> images, String spriteFileName,
073                            String spritePropertiesFileName, String spritePropertiesRootPath,
074                            int maxHeight, int maxWidth, int maxSize)
075                    throws IOException {
076    
077                    if (images.size() < 1) {
078                            return null;
079                    }
080    
081                    if (spritePropertiesRootPath.endsWith(StringPool.SLASH) ||
082                            spritePropertiesRootPath.endsWith(StringPool.BACK_SLASH)) {
083    
084                            spritePropertiesRootPath = spritePropertiesRootPath.substring(
085                                    0, spritePropertiesRootPath.length() - 1);
086                    }
087    
088                    File dir = images.get(0).getParentFile();
089    
090                    File spritePropertiesFile = new File(
091                            dir.toString() + StringPool.SLASH + spritePropertiesFileName);
092    
093                    boolean build = false;
094    
095                    long lastModified = 0;
096    
097                    if (spritePropertiesFile.exists()) {
098                            lastModified = spritePropertiesFile.lastModified();
099    
100                            for (File image : images) {
101                                    if (image.lastModified() > lastModified) {
102                                            build = true;
103    
104                                            break;
105                                    }
106                            }
107                    }
108                    else {
109                            build = true;
110                    }
111    
112                    if (!build) {
113                            String spritePropertiesString = FileUtil.read(spritePropertiesFile);
114    
115                            if (Validator.isNull(spritePropertiesString)) {
116                                    return null;
117                            }
118                            else {
119                                    return PropertiesUtil.load(spritePropertiesString);
120                            }
121                    }
122    
123                    List<RenderedImage> renderedImages = new ArrayList<RenderedImage>();
124    
125                    Properties spriteProperties = new SortedProperties();
126    
127                    float x = 0;
128                    float y = 0;
129    
130                    for (File file : images) {
131                            if (file.length() > maxSize) {
132                                    continue;
133                            }
134    
135                            try {
136                                    ImageBag imageBag = ImageProcessorUtil.read(file);
137    
138                                    RenderedImage renderedImage = imageBag.getRenderedImage();
139    
140                                    int height = renderedImage.getHeight();
141                                    int width = renderedImage.getWidth();
142    
143                                    if ((height <= maxHeight) && (width <= maxWidth)) {
144                                            renderedImage = convert(renderedImage);
145    
146                                            renderedImage = TranslateDescriptor.create(
147                                                    renderedImage, x, y, null, null);
148    
149                                            renderedImages.add(renderedImage);
150    
151                                            String key = StringUtil.replace(
152                                                    file.toString(), CharPool.BACK_SLASH, CharPool.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    }