001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.img;
020
021import java.awt.Dimension;
022import java.awt.Graphics2D;
023import java.awt.Point;
024import java.awt.image.BufferedImage;
025import java.awt.image.DataBufferInt;
026import java.io.BufferedInputStream;
027import java.io.FileInputStream;
028import java.io.IOException;
029import java.io.InputStream;
030import java.net.URL;
031public class PSDReader {
032
033    /**
034     * File read status: No errors.
035     */
036       public static final int STATUS_OK = 0;
037
038    /**
039     * File read status: Error decoding file (may be partially decoded)
040     */
041    public static final int STATUS_FORMAT_ERROR = 1;
042
043    /**
044     * File read status: Unable to open source.
045     */
046    public static final int STATUS_OPEN_ERROR = 2;
047
048    /**
049     * File read status: Unsupported format
050     */
051    public static final int STATUS_UNSUPPORTED = 3;
052
053    public static int ImageType = BufferedImage.TYPE_INT_ARGB;
054
055    protected BufferedInputStream input;
056    protected int frameCount;
057    protected BufferedImage[] frames;
058    protected int status = 0;
059    protected int nChan;
060    protected int width;
061    protected int height;
062    protected int nLayers;
063    protected int miscLen;
064    protected boolean hasLayers;
065    protected LayerInfo[] layers;
066    protected short[] lineLengths;
067    protected int lineIndex;
068    protected boolean rleEncoded;
069    
070    protected class LayerInfo {
071        int x, y, w, h;
072        int nChan;
073        int[] chanID;
074        int alpha;
075    }
076
077    /**
078     * Gets the number of layers read from file.
079     * @return frame count
080     */
081    public int getFrameCount() {
082        return frameCount;
083    }
084    
085    protected void setInput(InputStream stream) {
086        // open input stream
087        init();
088        if (stream == null) {
089            status = STATUS_OPEN_ERROR;
090        } else {
091            if (stream instanceof BufferedInputStream)
092                input = (BufferedInputStream) stream;
093            else
094                input = new BufferedInputStream(stream);
095        }
096    }
097    
098    protected void setInput(String name) {
099        // open input file
100        init();
101        try {
102            name = name.trim();
103            if (name.startsWith("file:")) {
104                name = name.substring(5);
105                while (name.startsWith("/"))
106                    name = name.substring(1);
107            }
108            if (name.indexOf("://") > 0) {
109                URL url = new URL(name);
110                input = new BufferedInputStream(url.openStream());
111            } else {
112                input = new BufferedInputStream(new FileInputStream(name));
113            }
114        } catch (IOException e) {
115            status = STATUS_OPEN_ERROR;
116        }
117    }
118    
119    /**
120     * Gets display duration for specified frame.  Always returns 0.
121     *
122     */
123    public int getDelay(int forFrame) {
124        return 0;
125    }
126    
127    /**
128     * Gets the image contents of frame n.  Note that this expands the image
129     * to the full frame size (if the layer was smaller) and any subsequent
130     * use of getLayer() will return the full image.
131     *
132     * @return BufferedImage representation of frame, or null if n is invalid.
133     */
134    public BufferedImage getFrame(int n) {
135        BufferedImage im = null;
136        if ((n >= 0) && (n < nLayers)) {
137            im = frames[n];
138            LayerInfo info = layers[n];
139            if ((info.w != width) || (info.h != height)) {
140                BufferedImage temp =
141                    new BufferedImage(width, height, ImageType);
142                Graphics2D gc = temp.createGraphics();
143                gc.drawImage(im, info.x, info.y, null);
144                gc.dispose();
145                im = temp;
146                frames[n] = im;
147            }
148        }
149        return im;
150    }
151    
152    /**
153     * Gets maximum image size.  Individual layers may be smaller.
154     *
155     * @return maximum image dimensions
156     */
157    public Dimension getFrameSize() {
158        return new Dimension(width, height);
159    }
160    
161    /**
162     * Gets the first (or only) image read.
163     *
164     * @return BufferedImage containing first frame, or null if none.
165     */
166    public BufferedImage getImage() {
167        return getFrame(0);
168    }
169    
170    /**
171     * Gets the image contents of layer n.  May be smaller than full frame
172     * size - use getFrameOffset() to obtain position of subimage within
173     * main image area.
174     *
175     * @return BufferedImage representation of layer, or null if n is invalid.
176     */
177    public BufferedImage getLayer(int n) {
178        BufferedImage im = null;
179        if ((n >= 0) && (n < nLayers)) {
180            im = frames[n];
181        }
182        return im;
183    }
184    
185    /**
186     * Gets the subimage offset of layer n if it is smaller than the
187     * full frame size.
188     *
189     * @return Point indicating offset from upper left corner of frame.
190     */
191    public Point getLayerOffset(int n) {
192        Point p = null;
193        if ((n >= 0) && (n < nLayers)) {
194            int x = layers[n].x;
195            int y = layers[n].y;
196            p = new Point(x, y);
197        }
198        if (p == null) {
199            p = new Point(0, 0);
200        }
201        return p;
202    }
203    
204    /**
205     * Reads PhotoShop layers from stream.
206     *
207     * @param InputStream in PhotoShop format.
208     * @return read status code (0 = no errors)
209     */
210    public int read(InputStream stream) {
211        setInput(stream);
212        process();
213        return status;
214    }
215    
216    /**
217     * Reads PhotoShop file from specified source (file or URL string)
218     *
219     * @param name String containing source
220     * @return read status code (0 = no errors)
221     */
222    public int read(String name) {
223        setInput(name);
224        process();
225        return status;
226    }
227    
228    /**
229     * Closes input stream and discards contents of all frames.
230     *
231     */
232    public void reset() {
233        init();
234    }
235    
236    protected void close() {
237        if (input != null) {
238            try {
239                input.close();
240            } catch (Exception e) {}
241            input = null;
242        }
243    }
244    protected boolean err() {
245        return status != STATUS_OK;
246    }
247    
248    protected byte[] fillBytes(int size, int value) {
249        // create byte array filled with given value
250        byte[] b = new byte[size];
251        if (value != 0) {
252            byte v = (byte) value;
253            for (int i = 0; i < size; i++) {
254                b[i] = v;
255            }
256        }
257        return b;
258    }
259    
260    protected void init() {
261        close();
262        frameCount = 0;
263        frames = null;
264        layers = null;
265        hasLayers = true;
266        status = STATUS_OK;
267    }
268    
269    protected void makeDummyLayer() {
270        // creat dummy layer for non-layered image
271        rleEncoded = readShort() == 1;
272        hasLayers = false;
273        nLayers = 1;
274        layers = new LayerInfo[1];
275        LayerInfo layer = new LayerInfo();
276        layers[0] = layer;
277        layer.h = height;
278        layer.w = width;
279        int nc = Math.min(nChan, 4);
280        if (rleEncoded) {
281            // get list of rle encoded line lengths for all channels
282            readLineLengths(height * nc);
283        }
284        layer.nChan = nc;
285        layer.chanID = new int[nc];
286        for (int i = 0; i < nc; i++) {
287            int id = i;
288            if (i == 3) id = -1;
289            layer.chanID[i] = id;
290        }
291    }
292
293    protected void readLineLengths(int nLines) {
294        // read list of rle encoded line lengths
295        lineLengths = new short[nLines];
296        for (int i = 0; i < nLines; i++) {
297            lineLengths[i] = readShort();
298        }
299        lineIndex = 0;
300    }
301
302    protected BufferedImage makeImage(int w, int h, byte[] r, byte[] g, byte[] b, byte[] a) {
303        // create image from given plane data
304        BufferedImage im = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
305        int[] data = ((DataBufferInt) im.getRaster().getDataBuffer()).getData();
306        int n = w * h;
307        int j = 0;
308        while (j < n) {
309            try {
310                int ac = a[j] & 0xff;
311                int rc = r[j] & 0xff;
312                int gc = g[j] & 0xff;
313                int bc = b[j] & 0xff;
314                data[j] = (((((ac << 8) | rc) << 8) | gc) << 8) | bc;
315            } catch (Exception e) {}
316            j++;
317        }
318        return im;
319    }
320    
321    protected void process() {
322        // decode PSD file
323        if (err()) return;
324        readHeader();
325        if (err()) return;
326        readLayerInfo();
327        if (err()) return;
328        if (nLayers == 0) {
329            makeDummyLayer();
330            if (err()) return;
331        }
332        readLayers();
333    }
334    
335    protected int readByte() {
336        // read single byte from input
337        int curByte = 0;
338        try {
339            curByte = input.read();
340        } catch (IOException e) {
341            status = STATUS_FORMAT_ERROR;
342        }
343        return curByte;
344    }
345
346    protected int readBytes(byte[] bytes, int n) {
347        // read multiple bytes from input
348        if (bytes == null) return 0;
349        int r = 0;
350        try {
351            r = input.read(bytes, 0, n);
352        } catch (IOException e) {
353            status = STATUS_FORMAT_ERROR;
354        }
355        if (r < n) {
356            status = STATUS_FORMAT_ERROR;
357        }
358        return r;
359    }
360    
361    protected void readHeader() {
362        // read PSD header info
363        String sig = readString(4);
364        int ver = readShort();
365        skipBytes(6);
366        nChan = readShort();
367        height = readInt();
368        width = readInt();
369        int depth = readShort();
370        int mode = readShort();
371        int cmLen = readInt();
372        skipBytes(cmLen);
373        int imResLen = readInt();
374        skipBytes(imResLen);
375
376        // require 8-bit RGB data
377        if ((!sig.equals("8BPS")) || (ver != 1)) {
378            status = STATUS_FORMAT_ERROR;
379        } else if ((depth != 8) || (mode != 3)) {
380            status = STATUS_UNSUPPORTED;
381        }
382    }
383    
384    protected int readInt() {
385        // read big-endian 32-bit integer
386        return (((((readByte() << 8) | readByte()) << 8) | readByte()) << 8)
387            | readByte();
388    }
389    
390    protected void readLayerInfo() {
391        // read layer header info
392        miscLen = readInt();
393        if (miscLen == 0) {
394            return; // no layers, only base image
395        }
396        readInt();
397        nLayers = readShort();
398        if (nLayers > 0) {
399            layers = new LayerInfo[nLayers];
400        }
401        for (int i = 0; i < nLayers; i++) {
402            LayerInfo info = new LayerInfo();
403            layers[i] = info;
404            info.y = readInt();
405            info.x = readInt();
406            info.h = readInt() - info.y;
407            info.w = readInt() - info.x;
408            info.nChan = readShort();
409            info.chanID = new int[info.nChan];
410            for (int j = 0; j < info.nChan; j++) {
411                int id = readShort();
412                readInt();
413                info.chanID[j] = id;
414            }
415            String s = readString(4);
416            if (!s.equals("8BIM")) {
417                status = STATUS_FORMAT_ERROR;
418                return;
419            }
420            skipBytes(4); // blend mode
421            info.alpha = readByte();
422            readByte();
423            readByte();
424            readByte(); // filler
425            int extraSize = readInt();
426            skipBytes(extraSize);
427        }
428    }
429    
430    protected void readLayers() {
431        // read and convert each layer to BufferedImage
432        frameCount = nLayers;
433        frames = new BufferedImage[nLayers];
434        for (int i = 0; i < nLayers; i++) {
435            LayerInfo info = layers[i];
436            byte[] r = null, g = null, b = null, a = null;
437            for (int j = 0; j < info.nChan; j++) {
438                int id = info.chanID[j];
439                switch (id) {
440                    case  0 : r = readPlane(info.w, info.h); break;
441                    case  1 : g = readPlane(info.w, info.h); break;
442                    case  2 : b = readPlane(info.w, info.h); break;
443                    case -1 : a = readPlane(info.w, info.h); break;
444                    default : readPlane(info.w, info.h);
445                }
446                if (err()) break;
447            }
448            if (err()) break;
449            int n = info.w * info.h;
450            if (r == null) r = fillBytes(n, 0);
451            if (g == null) g = fillBytes(n, 0);
452            if (b == null) b = fillBytes(n, 0);
453            if (a == null) a = fillBytes(n, 255);
454
455            BufferedImage im = makeImage(info.w, info.h, r, g, b, a);
456            frames[i] = im;
457        }
458        lineLengths = null;
459        if ((miscLen > 0) && !err()) {
460            int n = readInt(); // global layer mask info len
461            skipBytes(n);
462        }
463    }
464    
465    protected byte[] readPlane(int w, int h) {
466        // read a single color plane
467        byte[] b = null;
468        int size = w * h;
469        if (hasLayers) {
470            // get RLE compression info for channel
471            rleEncoded = readShort() == 1;
472            if (rleEncoded) {
473                // list of encoded line lengths
474                readLineLengths(h);
475            }
476        }
477
478        if (rleEncoded) {
479            b = readPlaneCompressed(w, h);
480        } else {
481            b = new byte[size];
482            readBytes(b, size);
483        }
484
485        return b;
486
487    }
488
489    protected byte[] readPlaneCompressed(int w, int h) {
490        byte[] b = new byte[w * h];
491        byte[] s = new byte[w * 2];
492        int pos = 0;
493        for (int i = 0; i < h; i++) {
494            if (lineIndex >= lineLengths.length) {
495                status = STATUS_FORMAT_ERROR;
496                return null;
497            }
498            int len = lineLengths[lineIndex++];
499            readBytes(s, len);
500            decodeRLE(s, 0, len, b, pos);
501            pos += w;
502        }
503        return b;
504    }
505
506    protected void decodeRLE(byte[] src, int sindex, int slen, byte[] dst, int dindex) {
507        try {
508            int max = sindex + slen;
509            while (sindex < max) {
510                byte b = src[sindex++];
511                int n = b;
512                if (n < 0) {
513                    // dup next byte 1-n times
514                    n = 1 - n;
515                    b = src[sindex++];
516                    for (int i = 0; i < n; i++) {
517                        dst[dindex++] = b;
518                    }
519                } else {
520                    // copy next n+1 bytes
521                    n = n + 1;
522                    System.arraycopy(src, sindex, dst, dindex, n);
523                    dindex += n;
524                    sindex += n;
525                }
526            }
527        } catch (Exception e) {
528            status = STATUS_FORMAT_ERROR;
529        }
530    }
531    
532    protected short readShort() {
533        // read big-endian 16-bit integer
534        return (short) ((readByte() << 8) | readByte());
535    }
536    
537    protected String readString(int len) {
538        // read string of specified length
539        String s = "";
540        for (int i = 0; i < len; i++) {
541            s = s + (char) readByte();
542        }
543        return s;
544    }
545    
546    protected void skipBytes(int n) {
547        // skip over n input bytes
548        for (int i = 0; i < n; i++) {
549            readByte();
550        }
551    }
552}