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 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 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 readByte(); 405 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 }