001    /**
002     * Copyright (c) 2000-present 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.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.FileUtil;
026    import com.liferay.portal.kernel.util.JavaConstants;
027    import com.liferay.portal.kernel.util.PropertiesUtil;
028    import com.liferay.portal.kernel.util.SortedProperties;
029    import com.liferay.portal.kernel.util.URLUtil;
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    /**
067     * @author Brian Wing Shun Chan
068     */
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                            for (URL imageURL : imageURLs) {
115                                    if (URLUtil.getLastModifiedTime(imageURL) > lastModified) {
116                                            build = true;
117    
118                                            break;
119                                    }
120                            }
121                    }
122                    else {
123                            build = true;
124                    }
125    
126                    if (!build) {
127                            String spritePropertiesString = FileUtil.read(spritePropertiesFile);
128    
129                            if (Validator.isNull(spritePropertiesString)) {
130                                    return null;
131                            }
132                            else {
133                                    return PropertiesUtil.load(spritePropertiesString);
134                            }
135                    }
136    
137                    List<RenderedImage> renderedImages = new ArrayList<>();
138    
139                    Properties spriteProperties = new SortedProperties();
140    
141                    float x = 0;
142                    float y = 0;
143    
144                    URLConnection urlConnection = null;
145    
146                    for (URL imageURL : imageURLs) {
147                            urlConnection = imageURL.openConnection();
148    
149                            if ((urlConnection != null) &&
150                                    (urlConnection.getContentLength() > maxSize)) {
151    
152                                    continue;
153                            }
154    
155                            try {
156                                    ImageBag imageBag = ImageToolUtil.read(
157                                            urlConnection.getInputStream());
158    
159                                    RenderedImage renderedImage = imageBag.getRenderedImage();
160    
161                                    int height = renderedImage.getHeight();
162                                    int width = renderedImage.getWidth();
163    
164                                    if ((height <= maxHeight) && (width <= maxWidth)) {
165                                            renderedImage = convert(renderedImage);
166    
167                                            renderedImage = TranslateDescriptor.create(
168                                                    renderedImage, x, y, null, null);
169    
170                                            renderedImages.add(renderedImage);
171    
172                                            String key = ServletContextUtil.getResourcePath(imageURL);
173    
174                                            int pos = key.indexOf(rootPath);
175    
176                                            if (pos == 0) {
177                                                    key = key.substring(rootPath.length());
178                                            }
179    
180                                            String contextPath = servletContext.getContextPath();
181    
182                                            key = contextPath.concat(key);
183    
184                                            String value = (int)y + "," + height + "," + width;
185    
186                                            spriteProperties.setProperty(key, value);
187    
188                                            y += renderedImage.getHeight();
189                                    }
190                            }
191                            catch (Exception e) {
192                                    if (_log.isWarnEnabled()) {
193                                            _log.warn("Unable to process " + imageURL);
194                                    }
195    
196                                    if (_log.isDebugEnabled()) {
197                                            _log.debug(e, e);
198                                    }
199                            }
200                    }
201    
202                    if (renderedImages.size() <= 1) {
203                            renderedImages.clear();
204                            spriteProperties.clear();
205                    }
206                    else {
207    
208                            // PNG
209    
210                            RenderedImage renderedImage = MosaicDescriptor.create(
211                                    renderedImages.toArray(
212                                            new RenderedImage[renderedImages.size()]),
213                                    MosaicDescriptor.MOSAIC_TYPE_OVERLAY, null, null, null, null,
214                                    null);
215    
216                            File spriteFile = new File(spriteRootDir, spriteFileName);
217    
218                            File spriteDir = spriteFile.getParentFile();
219    
220                            FileUtil.mkdirs(spriteDir);
221    
222                            ImageIO.write(renderedImage, "png", spriteFile);
223    
224                            if (lastModified > 0) {
225                                    spriteFile.setLastModified(lastModified);
226                            }
227                    }
228    
229                    FileUtil.write(
230                            spritePropertiesFile, PropertiesUtil.toString(spriteProperties));
231    
232                    if (lastModified > 0) {
233                            spritePropertiesFile.setLastModified(lastModified);
234                    }
235    
236                    return spriteProperties;
237            }
238    
239            protected RenderedImage convert(RenderedImage renderedImage)
240                    throws Exception {
241    
242                    int height = renderedImage.getHeight();
243                    int width = renderedImage.getWidth();
244    
245                    SampleModel sampleModel = renderedImage.getSampleModel();
246                    ColorModel colorModel = renderedImage.getColorModel();
247    
248                    Raster raster = renderedImage.getData();
249    
250                    DataBuffer dataBuffer = raster.getDataBuffer();
251    
252                    if (colorModel instanceof IndexColorModel) {
253                            IndexColorModel indexColorModel = (IndexColorModel)colorModel;
254    
255                            int mapSize = indexColorModel.getMapSize();
256    
257                            byte[][] data = new byte[4][mapSize];
258    
259                            indexColorModel.getReds(data[0]);
260                            indexColorModel.getGreens(data[1]);
261                            indexColorModel.getBlues(data[2]);
262                            indexColorModel.getAlphas(data[3]);
263    
264                            LookupTableJAI lookupTableJAI = new LookupTableJAI(data);
265    
266                            renderedImage = LookupDescriptor.create(
267                                    renderedImage, lookupTableJAI, null);
268                    }
269                    else if (sampleModel.getNumBands() == 2) {
270                            List<Byte> bytesList = new ArrayList<>(
271                                    height * width * _NUM_OF_BANDS);
272    
273                            List<Byte> tempBytesList = new ArrayList<>(_NUM_OF_BANDS);
274    
275                            for (int i = 0; i < dataBuffer.getSize(); i++) {
276                                    int mod = (i + 1) % 2;
277    
278                                    int elemPos = i;
279    
280                                    if (mod == 0) {
281                                            tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
282                                            tempBytesList.add((byte)dataBuffer.getElem(elemPos - 1));
283                                    }
284    
285                                    tempBytesList.add((byte)dataBuffer.getElem(elemPos));
286    
287                                    if (mod == 0) {
288                                            Collections.reverse(tempBytesList);
289    
290                                            bytesList.addAll(tempBytesList);
291    
292                                            tempBytesList.clear();
293                                    }
294                            }
295    
296                            byte[] data = ArrayUtil.toArray(
297                                    bytesList.toArray(new Byte[bytesList.size()]));
298    
299                            DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
300    
301                            renderedImage = createRenderedImage(
302                                    renderedImage, height, width, newDataBuffer);
303                    }
304                    else if (colorModel.getTransparency() != Transparency.TRANSLUCENT) {
305                            List<Byte> bytesList = new ArrayList<>(
306                                    height * width * _NUM_OF_BANDS);
307    
308                            List<Byte> tempBytesList = new ArrayList<>(_NUM_OF_BANDS);
309    
310                            for (int i = 0; i < dataBuffer.getSize(); i++) {
311                                    int mod = (i + 1) % 3;
312    
313                                    int elemPos = i;
314    
315                                    tempBytesList.add((byte)dataBuffer.getElem(elemPos));
316    
317                                    if (mod == 0) {
318                                            tempBytesList.add((byte)255);
319    
320                                            Collections.reverse(tempBytesList);
321    
322                                            bytesList.addAll(tempBytesList);
323    
324                                            tempBytesList.clear();
325                                    }
326                            }
327    
328                            byte[] data = ArrayUtil.toArray(
329                                    bytesList.toArray(new Byte[bytesList.size()]));
330    
331                            DataBuffer newDataBuffer = new DataBufferByte(data, data.length);
332    
333                            renderedImage = createRenderedImage(
334                                    renderedImage, height, width, newDataBuffer);
335                    }
336    
337                    return renderedImage;
338            }
339    
340            protected RenderedImage createRenderedImage(
341                    RenderedImage renderedImage, int height, int width,
342                    DataBuffer dataBuffer) {
343    
344                    SampleModel sampleModel =
345                            RasterFactory.createPixelInterleavedSampleModel(
346                                    DataBuffer.TYPE_BYTE, width, height, _NUM_OF_BANDS);
347                    ColorModel colorModel = PlanarImage.createColorModel(sampleModel);
348    
349                    TiledImage tiledImage = new TiledImage(
350                            0, 0, width, height, 0, 0, sampleModel, colorModel);
351    
352                    Raster raster = RasterFactory.createWritableRaster(
353                            sampleModel, dataBuffer, new Point(0, 0));
354    
355                    tiledImage.setData(raster);
356    
357                    /*javax.media.jai.JAI.create(
358                            "filestore", tiledImage, "test.png", "PNG");
359    
360                    printImage(renderedImage);
361                    printImage(tiledImage);*/
362    
363                    return tiledImage;
364            }
365    
366            protected void printImage(RenderedImage renderedImage) {
367                    SampleModel sampleModel = renderedImage.getSampleModel();
368    
369                    int height = renderedImage.getHeight();
370                    int width = renderedImage.getWidth();
371                    int numOfBands = sampleModel.getNumBands();
372    
373                    int[] pixels = new int[height * width * numOfBands];
374    
375                    Raster raster = renderedImage.getData();
376    
377                    raster.getPixels(0, 0, width, height, pixels);
378    
379                    int offset = 0;
380    
381                    for (int h = 0; h < height; h++) {
382                            for (int w = 0; w < width; w++) {
383                                    offset = (h * width * numOfBands) + (w * numOfBands);
384    
385                                    System.out.print("[" + w + ", " + h + "] = ");
386    
387                                    for (int b = 0; b < numOfBands; b++) {
388                                            System.out.print(pixels[offset + b] + " ");
389                                    }
390                            }
391    
392                            System.out.println();
393                    }
394            }
395    
396            private static final int _NUM_OF_BANDS = 4;
397    
398            private static final Log _log = LogFactoryUtil.getLog(
399                    SpriteProcessorImpl.class);
400    
401    }