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