Java/2D Graphics GUI/Psd

Материал из Java эксперт
Версия от 09:55, 1 июня 2010; Admin (обсуждение | вклад) (1 версия)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Decodes a PhotoShop (.psd) file into one or more frames

<source lang="java"> import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Point; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; /**

* Class PSDReader - Decodes a PhotoShop (.psd) file into one or more frames.
* Supports uncompressed or RLE-compressed RGB files only. Each layer may be
* retrieved as a full frame BufferedImage, or as a smaller image with an offset
* if the layer does not occupy the full frame size. Transparency in the
* original psd file is preserved in the returned BufferedImage"s. Does not
* support additional features in PS versions higher than 3.0. Example: 
*
*
 * PSDReader r = new PSDReader();
 * r.read("sample.psd");
 * int n = r.getFrameCount();
 * for (int i = 0; i < n; i++) {
 *   BufferedImage image = r.getLayer(i);
 *   Point offset = r.getLayerOffset(i);
 *   // do something with image
 * }
 * 
* 
* No copyright asserted on the source code of this class. May be used for any
* purpose. Please forward any corrections to kweiner@fmsware.ru.
* 
* @author Kevin Weiner, FM Software.
* @version 1.1 January 2004 [bug fix; add RLE support]
* 
*/

public class PSDReader {

 /**
  * File read status: No errors.
  */
 public static final int STATUS_OK = 0;
 /**
  * File read status: Error decoding file (may be partially decoded)
  */
 public static final int STATUS_FORMAT_ERROR = 1;
 /**
  * File read status: Unable to open source.
  */
 public static final int STATUS_OPEN_ERROR = 2;
 /**
  * File read status: Unsupported format
  */
 public static final int STATUS_UNSUPPORTED = 3;
 public static int ImageType = BufferedImage.TYPE_INT_ARGB;
 protected BufferedInputStream input;
 protected int frameCount;
 protected BufferedImage[] frames;
 protected int status = 0;
 protected int nChan;
 protected int width;
 protected int height;
 protected int nLayers;
 protected int miscLen;
 protected boolean hasLayers;
 protected LayerInfo[] layers;
 protected short[] lineLengths;
 protected int lineIndex;
 protected boolean rleEncoded;
 protected class LayerInfo {
   int x, y, w, h;
   int nChan;
   int[] chanID;
   int alpha;
 }
 /**
  * Gets the number of layers read from file.
  * 
  * @return frame count
  */
 public int getFrameCount() {
   return frameCount;
 }
 protected void setInput(InputStream stream) {
   // open input stream
   init();
   if (stream == null) {
     status = STATUS_OPEN_ERROR;
   } else {
     if (stream instanceof BufferedInputStream)
       input = (BufferedInputStream) stream;
     else
       input = new BufferedInputStream(stream);
   }
 }
 protected void setInput(String name) {
   // open input file
   init();
   try {
     name = name.trim();
     if (name.startsWith("file:")) {
       name = name.substring(5);
       while (name.startsWith("/"))
         name = name.substring(1);
     }
     if (name.indexOf("://") > 0) {
       URL url = new URL(name);
       input = new BufferedInputStream(url.openStream());
     } else {
       input = new BufferedInputStream(new FileInputStream(name));
     }
   } catch (IOException e) {
     status = STATUS_OPEN_ERROR;
   }
 }
 /**
  * Gets display duration for specified frame. Always returns 0.
  * 
  */
 public int getDelay(int forFrame) {
   return 0;
 }
 /**
  * Gets the image contents of frame n. Note that this expands the image to the
  * full frame size (if the layer was smaller) and any subsequent use of
  * getLayer() will return the full image.
  * 
  * @return BufferedImage representation of frame, or null if n is invalid.
  */
 public BufferedImage getFrame(int n) {
   BufferedImage im = null;
   if ((n >= 0) && (n < nLayers)) {
     im = frames[n];
     LayerInfo info = layers[n];
     if ((info.w != width) || (info.h != height)) {
       BufferedImage temp = new BufferedImage(width, height, ImageType);
       Graphics2D gc = temp.createGraphics();
       gc.drawImage(im, info.x, info.y, null);
       gc.dispose();
       im = temp;
       frames[n] = im;
     }
   }
   return im;
 }
 /**
  * Gets maximum image size. Individual layers may be smaller.
  * 
  * @return maximum image dimensions
  */
 public Dimension getFrameSize() {
   return new Dimension(width, height);
 }
 /**
  * Gets the first (or only) image read.
  * 
  * @return BufferedImage containing first frame, or null if none.
  */
 public BufferedImage getImage() {
   return getFrame(0);
 }
 /**
  * Gets the image contents of layer n. May be smaller than full frame size -
  * use getFrameOffset() to obtain position of subimage within main image area.
  * 
  * @return BufferedImage representation of layer, or null if n is invalid.
  */
 public BufferedImage getLayer(int n) {
   BufferedImage im = null;
   if ((n >= 0) && (n < nLayers)) {
     im = frames[n];
   }
   return im;
 }
 /**
  * Gets the subimage offset of layer n if it is smaller than the full frame
  * size.
  * 
  * @return Point indicating offset from upper left corner of frame.
  */
 public Point getLayerOffset(int n) {
   Point p = null;
   if ((n >= 0) && (n < nLayers)) {
     int x = layers[n].x;
     int y = layers[n].y;
     p = new Point(x, y);
   }
   if (p == null) {
     p = new Point(0, 0);
   }
   return p;
 }
 /**
  * Reads PhotoShop layers from stream.
  * 
  * @param InputStream
  *          in PhotoShop format.
  * @return read status code (0 = no errors)
  */
 public int read(InputStream stream) {
   setInput(stream);
   process();
   return status;
 }
 /**
  * Reads PhotoShop file from specified source (file or URL string)
  * 
  * @param name
  *          String containing source
  * @return read status code (0 = no errors)
  */
 public int read(String name) {
   setInput(name);
   process();
   return status;
 }
 /**
  * Closes input stream and discards contents of all frames.
  * 
  */
 public void reset() {
   init();
 }
 protected void close() {
   if (input != null) {
     try {
       input.close();
     } catch (Exception e) {
     }
     input = null;
   }
 }
 protected boolean err() {
   return status != STATUS_OK;
 }
 protected byte[] fillBytes(int size, int value) {
   // create byte array filled with given value
   byte[] b = new byte[size];
   if (value != 0) {
     byte v = (byte) value;
     for (int i = 0; i < size; i++) {
       b[i] = v;
     }
   }
   return b;
 }
 protected void init() {
   close();
   frameCount = 0;
   frames = null;
   layers = null;
   hasLayers = true;
   status = STATUS_OK;
 }
 protected void makeDummyLayer() {
   // creat dummy layer for non-layered image
   rleEncoded = readShort() == 1;
   hasLayers = false;
   nLayers = 1;
   layers = new LayerInfo[1];
   LayerInfo layer = new LayerInfo();
   layers[0] = layer;
   layer.h = height;
   layer.w = width;
   int nc = Math.min(nChan, 4);
   if (rleEncoded) {
     // get list of rle encoded line lengths for all channels
     readLineLengths(height * nc);
   }
   layer.nChan = nc;
   layer.chanID = new int[nc];
   for (int i = 0; i < nc; i++) {
     int id = i;
     if (i == 3)
       id = -1;
     layer.chanID[i] = id;
   }
 }
 protected void readLineLengths(int nLines) {
   // read list of rle encoded line lengths
   lineLengths = new short[nLines];
   for (int i = 0; i < nLines; i++) {
     lineLengths[i] = readShort();
   }
   lineIndex = 0;
 }
 protected BufferedImage makeImage(int w, int h, byte[] r, byte[] g, byte[] b, byte[] a) {
   // create image from given plane data
   BufferedImage im = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
   int[] data = ((DataBufferInt) im.getRaster().getDataBuffer()).getData();
   int n = w * h;
   int j = 0;
   while (j < n) {
     try {
       int ac = a[j] & 0xff;
       int rc = r[j] & 0xff;
       int gc = g[j] & 0xff;
       int bc = b[j] & 0xff;
       data[j] = (((((ac << 8) | rc) << 8) | gc) << 8) | bc;
     } catch (Exception e) {
     }
     j++;
   }
   return im;
 }
 protected void process() {
   // decode PSD file
   if (err())
     return;
   readHeader();
   if (err())
     return;
   readLayerInfo();
   if (err())
     return;
   if (nLayers == 0) {
     makeDummyLayer();
     if (err())
       return;
   }
   readLayers();
 }
 protected int readByte() {
   // read single byte from input
   int curByte = 0;
   try {
     curByte = input.read();
   } catch (IOException e) {
     status = STATUS_FORMAT_ERROR;
   }
   return curByte;
 }
 protected int readBytes(byte[] bytes, int n) {
   // read multiple bytes from input
   if (bytes == null)
     return 0;
   int r = 0;
   try {
     r = input.read(bytes, 0, n);
   } catch (IOException e) {
     status = STATUS_FORMAT_ERROR;
   }
   if (r < n) {
     status = STATUS_FORMAT_ERROR;
   }
   return r;
 }
 protected void readHeader() {
   // read PSD header info
   String sig = readString(4);
   int ver = readShort();
   skipBytes(6);
   nChan = readShort();
   height = readInt();
   width = readInt();
   int depth = readShort();
   int mode = readShort();
   int cmLen = readInt();
   skipBytes(cmLen);
   int imResLen = readInt();
   skipBytes(imResLen);
   // require 8-bit RGB data
   if ((!sig.equals("8BPS")) || (ver != 1)) {
     status = STATUS_FORMAT_ERROR;
   } else if ((depth != 8) || (mode != 3)) {
     status = STATUS_UNSUPPORTED;
   }
 }
 protected int readInt() {
   // read big-endian 32-bit integer
   return (((((readByte() << 8) | readByte()) << 8) | readByte()) << 8) | readByte();
 }
 protected void readLayerInfo() {
   // read layer header info
   miscLen = readInt();
   if (miscLen == 0) {
     return; // no layers, only base image
   }
   int layerInfoLen = readInt();
   nLayers = readShort();
   if (nLayers > 0) {
     layers = new LayerInfo[nLayers];
   }
   for (int i = 0; i < nLayers; i++) {
     LayerInfo info = new LayerInfo();
     layers[i] = info;
     info.y = readInt();
     info.x = readInt();
     info.h = readInt() - info.y;
     info.w = readInt() - info.x;
     info.nChan = readShort();
     info.chanID = new int[info.nChan];
     for (int j = 0; j < info.nChan; j++) {
       int id = readShort();
       int size = readInt();
       info.chanID[j] = id;
     }
     String s = readString(4);
     if (!s.equals("8BIM")) {
       status = STATUS_FORMAT_ERROR;
       return;
     }
     skipBytes(4); // blend mode
     info.alpha = readByte();
     int clipping = readByte();
     int flags = readByte();
     readByte(); // filler
     int extraSize = readInt();
     skipBytes(extraSize);
   }
 }
 protected void readLayers() {
   // read and convert each layer to BufferedImage
   frameCount = nLayers;
   frames = new BufferedImage[nLayers];
   for (int i = 0; i < nLayers; i++) {
     LayerInfo info = layers[i];
     byte[] r = null, g = null, b = null, a = null;
     for (int j = 0; j < info.nChan; j++) {
       int id = info.chanID[j];
       switch (id) {
       case 0:
         r = readPlane(info.w, info.h);
         break;
       case 1:
         g = readPlane(info.w, info.h);
         break;
       case 2:
         b = readPlane(info.w, info.h);
         break;
       case -1:
         a = readPlane(info.w, info.h);
         break;
       default:
         readPlane(info.w, info.h);
       }
       if (err())
         break;
     }
     if (err())
       break;
     int n = info.w * info.h;
     if (r == null)
       r = fillBytes(n, 0);
     if (g == null)
       g = fillBytes(n, 0);
     if (b == null)
       b = fillBytes(n, 0);
     if (a == null)
       a = fillBytes(n, 255);
     BufferedImage im = makeImage(info.w, info.h, r, g, b, a);
     frames[i] = im;
   }
   lineLengths = null;
   if ((miscLen > 0) && !err()) {
     int n = readInt(); // global layer mask info len
     skipBytes(n);
   }
 }
 protected byte[] readPlane(int w, int h) {
   // read a single color plane
   byte[] b = null;
   int size = w * h;
   if (hasLayers) {
     // get RLE compression info for channel
     rleEncoded = readShort() == 1;
     if (rleEncoded) {
       // list of encoded line lengths
       readLineLengths(h);
     }
   }
   if (rleEncoded) {
     b = readPlaneCompressed(w, h);
   } else {
     b = new byte[size];
     readBytes(b, size);
   }
   return b;
 }
 protected byte[] readPlaneCompressed(int w, int h) {
   byte[] b = new byte[w * h];
   byte[] s = new byte[w * 2];
   int pos = 0;
   for (int i = 0; i < h; i++) {
     if (lineIndex >= lineLengths.length) {
       status = STATUS_FORMAT_ERROR;
       return null;
     }
     int len = lineLengths[lineIndex++];
     readBytes(s, len);
     decodeRLE(s, 0, len, b, pos);
     pos += w;
   }
   return b;
 }
 protected void decodeRLE(byte[] src, int sindex, int slen, byte[] dst, int dindex) {
   try {
     int max = sindex + slen;
     while (sindex < max) {
       byte b = src[sindex++];
       int n = (int) b;
       if (n < 0) {
         // dup next byte 1-n times
         n = 1 - n;
         b = src[sindex++];
         for (int i = 0; i < n; i++) {
           dst[dindex++] = b;
         }
       } else {
         // copy next n+1 bytes
         n = n + 1;
         System.arraycopy(src, sindex, dst, dindex, n);
         dindex += n;
         sindex += n;
       }
     }
   } catch (Exception e) {
     status = STATUS_FORMAT_ERROR;
   }
 }
 protected short readShort() {
   // read big-endian 16-bit integer
   return (short) ((readByte() << 8) | readByte());
 }
 protected String readString(int len) {
   // read string of specified length
   String s = "";
   for (int i = 0; i < len; i++) {
     s = s + (char) readByte();
   }
   return s;
 }
 protected void skipBytes(int n) {
   // skip over n input bytes
   for (int i = 0; i < n; i++) {
     readByte();
   }
 }

}

      </source>