It all works

This commit is contained in:
Erlend Hamnaberg
2009-11-08 19:52:30 +01:00
parent b8faa6e36f
commit 0786949c1c
319 changed files with 0 additions and 0 deletions

25
imageio/imageio-psd/license.txt Executable file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2009, Harald Kuhr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name "TwelveMonkeys" nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,31 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-psd</artifactId>
<version>2.3-SNAPSHOT</version>
<name>TwelveMonkeys ImageIO PSD plugin</name>
<description>
ImageIO plugin for Adobe Photoshop Document (PSD).
</description>
<parent>
<artifactId>twelvemonkeys-imageio</artifactId>
<groupId>com.twelvemonkeys</groupId>
<version>2.3-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
<classifier>tests</classifier>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import java.awt.color.ColorSpace;
/**
* CMYKColorSpace
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: CMYKColorSpace.java,v 1.0 Apr 30, 2008 1:38:13 PM haraldk Exp$
*/
// TODO: Move to com.twelvemonkeys.image?
// TODO: Read a ICC CMYK profile from classpath resource (from ECI)? ISO coated?
final class CMYKColorSpace extends ColorSpace {
static final ColorSpace INSTANCE = new CMYKColorSpace();
final ColorSpace sRGB = getInstance(CS_sRGB);
CMYKColorSpace() {
super(ColorSpace.TYPE_CMYK, 4);
}
public static ColorSpace getInstance() {
return INSTANCE;
}
public float[] toRGB(float[] colorvalue) {
return new float[] {
(1 - colorvalue[0]) * (1 - colorvalue[3]),
(1 - colorvalue[1]) * (1 - colorvalue[3]),
(1 - colorvalue[2]) * (1 - colorvalue[3])
};
// TODO: Convert via CIEXYZ space using sRGB space, as suggested in docs
// return sRGB.fromCIEXYZ(toCIEXYZ(colorvalue));
}
public float[] fromRGB(float[] rgbvalue) {
// Compute CMY
float c = 1 - rgbvalue[0];
float m = 1 - rgbvalue[1];
float y = 1 - rgbvalue[2];
// Find K
float k = Math.min(c, Math.min(m, y));
// Convert to CMYK values
return new float[] {(c - k), (m - k), (y - k), k};
/*
http://www.velocityreviews.com/forums/t127265-rgb-to-cmyk.html
(Step 0: Normalize R,G, and B values to fit into range [0.0 ... 1.0], or
adapt the following matrix.)
Step 1: RGB to CMY
| C | | 1 | | R |
| M | = | 1 | - | G |
| Y | | 1 | | B |
Step 2: CMY to CMYK
| C' | | C | | min(C,M,Y) |
| M' | | M | | min(C,M,Y) |
| Y' | = | Y | - | min(C,M,Y) |
| K' | | min(C,M,Y) | | 0 |
Easier to calculate if K' is calculated first, because K' = min(C,M,Y):
| C' | | C | | K' |
| M' | | M | | K' |
| Y' | = | Y | - | K' |
| K' | | K'| | 0 |
*/
// return fromCIEXYZ(sRGB.toCIEXYZ(rgbvalue));
}
public float[] toCIEXYZ(float[] colorvalue) {
throw new UnsupportedOperationException("Method toCIEXYZ not implemented"); // TODO: Implement
}
public float[] fromCIEXYZ(float[] colorvalue) {
throw new UnsupportedOperationException("Method fromCIEXYZ not implemented"); // TODO: Implement
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.stream.ImageInputStream;
import java.awt.color.ICC_Profile;
import java.io.IOException;
import java.io.InputStream;
/**
* ICCProfile
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ICCProfile.java,v 1.0 May 20, 2008 6:24:10 PM haraldk Exp$
*/
class ICCProfile extends PSDImageResource {
private ICC_Profile mProfile;
ICCProfile(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
@Override
protected void readData(ImageInputStream pInput) throws IOException {
InputStream stream = IIOUtil.createStreamAdapter(pInput, mSize);
try {
mProfile = ICC_Profile.getInstance(stream);
}
finally {
// Make sure stream has correct position after read
stream.close();
}
}
public ICC_Profile getProfile() {
return mProfile;
}
@Override
public String toString() {
StringBuilder builder = toStringBuilder();
builder.append(", profile: ").append(mProfile);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,544 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
/**
* PSD format constants.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSD.java,v 1.0 Apr 29, 2008 4:47:47 PM haraldk Exp$
*
* @see <a href="http://www.fileformat.info/format/psd/egff.htm">http://www.fileformat.info/format/psd/egff.htm</a>
*/
interface PSD {
/** PSD 2+ Native format (.PSD) identifier "8BPS" */
int SIGNATURE_8BPS = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'S';
/** PSD 5+ Large Document Format (.PSB) identifier "8BPB" */
int SIGNATURE_8BPB = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'B';
/** PSD Resource type identifier "8BIM" */
int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
// Blending modes
/** Normal blending mode "norm"*/
int BLEND_NORM = ('n' << 24) + ('o' << 16) + ('r' << 8) + 'm';
/** Darken blending mode "dark" */
int BLEND_DARK = ('d' << 24) + ('a' << 16) + ('r' << 8) + 'k';
/** Lighten blending mode "lite" */
int BLEND_LITE = ('l' << 24) + ('i' << 16) + ('t' << 8) + 'e';
/** Hue blending mode "hue " */
int BLEND_HUE = ('h' << 24) + ('u' << 16) + ('e' << 8) + ' ';
/** Saturation blending mode "sat " */
int BLEND_SAT = ('s' << 24) + ('a' << 16) + ('t' << 8) + ' ';
/** Color blending mode "colr" */
int BLEND_COLR = ('c' << 24) + ('o' << 16) + ('l' << 8) + 'r';
/** Luminosity blending mode "lum " */
int BLEND_LUM = ('l' << 24) + ('u' << 16) + ('m' << 8) + ' ';
/** Multiply blending mode "mul " */
int BELND_MUL = ('m' << 24) + ('u' << 16) + ('l' << 8) + ' ';
/** Screen blending mode "scrn" */
int BLEND_SCRN = ('s' << 24) + ('c' << 16) + ('r' << 8) + 'n';
/** Dissolve blending mode "diss" */
int BLEND_DISS = ('d' << 24) + ('i' << 16) + ('s' << 8) + 's';
/** Overlay blending mode "over" */
int BLEND_OVER = ('o' << 24) + ('v' << 16) + ('e' << 8) + 'r';
/** Hard light blending mode "hLit" */
int BLEND_HLIT = ('h' << 24) + ('L' << 16) + ('i' << 8) + 't';
/** Soft light blending mode "sLit" */
int BLEND_SLIT = ('s' << 24) + ('L' << 16) + ('i' << 8) + 't';
/** Difference blending mode "diff" */
int BLEND_DIFF = ('d' << 24) + ('i' << 16) + ('f' << 8) + 'f';
// Compression modes
/** No compression */
int COMPRESSION_NONE = 0;
/** PacBits RLE compression */
int COMPRESSION_RLE = 1;
/** ZIP compression */
int COMPRESSION_ZIP = 2;
/** ZIP compression with prediction */
int COMPRESSION_ZIP_PREDICTION = 3;
// Color Modes
/** Bitmap (monochrome) */
short COLOR_MODE_MONOCHROME = 0;
/** Gray-scale */
short COLOR_MODE_GRAYSCALE = 1;
/** Indexed color (palette color) */
short COLOR_MODE_INDEXED = 2;
/** RGB color */
short COLOR_MODE_RGB = 3;
/** CMYK color */
short COLOR_MODE_CMYK = 4;
/** Multichannel color */
short COLOR_MODE_MULTICHANNEL = 7;
/** Duotone (halftone) */
short COLOR_MODE_DUOTONE = 8;
/** Lab color */
short COLOR_MODE_LAB = 9;
// TODO: Consider moving these constants to PSDImageResource
// ID values 03e8, 03eb, 03ff, and 0403 are considered obsolete. Values 03e8 and 03eb are associated with
// Photoshop v2.0. The data format for values 03f2, 03f4-03fa, 03fc, 03fd, 0405-0bb7 is intentionally not
// documented by Adobe, or the data is missing.
// Please refer to the Adobe Photoshop SDK for information on obtaining the IPTC-NAA record 2 structure definition.
// WORD[5]
/** Channels, rows, columns, depth, and mode (Obsolete?Photoshop 2.0 only). */
int RES_CHANNELS_ROWS_COLUMNS_DEPTH_MODE = 0x03e8;
/** Optional Macintosh print manager information. */
int RES_MAC_PRINT_MANAGER_INFO = 0x03e9;
/** Indexed color table (Obsolete?Photoshop 2.0 only). */
int RES_INDEXED_COLOR_TABLE = 0x03eb;
/** Resolution information
// ID value 03ed indicates that the data is in the form of a ResolutionInfo structure:
//
// typedef struct _ResolutionInfo
// {
// LONG hRes; // Fixed-point number: pixels per inch
// WORD hResUnit; // 1=pixels per inch, 2=pixels per centimeter
// WORD WidthUnit; // 1=in, 2=cm, 3=pt, 4=picas, 5=columns
// LONG vRes; // Fixed-point number: pixels per inch/
// WORD vResUnit; // 1=pixels per inch, 2=pixels per centimeter
// WORD HeightUnit; // 1=in, 2=cm, 3=pt, 4=picas, 5=columns
// } RESOLUTIONINFO;
*/
int RES_RESOLUTION_INFO = 0x3ed;
/** Alpha channel names (Pascal-format strings) */
int RES_ALPHA_CHANNEL_INFO = 0x3ee;
/** Display information for each channel
// ID value 03ef indicates that the data is stored as a DisplayInfo structure, which contains display information
// associated with each channel:
//
// typedef _DisplayInfo
// {
// WORD ColorSpace;
// WORD Color[4];
// WORD Opacity; // 0-100
// BYTE Kind; // 0=selected, 1=protected
// BYTE Padding; // Always zero
// } DISPLAYINFO;
//
*/
int RES_DISPLAY_INFO = 0x3ef;
// 03f0
// BYTE[]
/** Optional Pascal-format caption string */
int RES_CAPTION = 0x03f0;
// 03f1
// LONG, WORD
/** Fixed-point border width, border units */
int RES_BORDER_WIDTH = 0x03f1;
// 03f2
/** Background color */
// 2 byte Color space: 0 = RGB (unsigned 16 bit), 1 = HSB (unsigned 16 bit), 2 = CMYK (unsigned 16 bit),
// 3 = Pantone matching system (undocumented), 4 = Focoltone colour system (undocumented),
// 5 = Truematch color (undocumented), 6 = Toyo 88 colorfinder 1050 (undocumented),
// 7 = Lab (lighntess 0...10000, chrominance -12800..127000, 8 = Grayscale 0...10000,
// 10 = HKS colors
// 8 byte Color data: 6 first bytes used for RGB, HSB and Lab, all 8 for CMYK and only first two for grayscale
int RES_BACKGROUND_COLOR = 0x03f2;
// 03f3
// BYTE[8]
/**
* Print flags.
* ID value 03f3 indicates that the data is a series of eight flags, indicating the enabled state of labels,
* crop marks, color bars, registration marks, negative, flip, interpolate, and caption items in the
* Photoshop Page Setup dialog box.
*/
int RES_PRINT_FLAGS = 0x03f3;
// 03f4
/** Gray-scale and halftoning information */
int RES_GRAYSCALE_HALFTONE_INFO = 0x03f4;
// 03f5
/** Color halftoning information */
int RES_COLOR_HALFTONE_INFO = 0x03f5;
// 03f6
/** Duotone halftoning information */
int RES_DUOTONE_HALFTONE_INFO = 0x03f6;
// 03f7
/** Gray-scale and multichannel transfer function */
int RES_GRAYSCALE_MULTICHANNEL_TRANSFER_FUNCTION = 0x03f7;
// 03f8
/** Color transfer functions */
int RES_COLOR_TRANSFER_FUNCITON = 0x03f8;
// 03f9
/** Duotone transfer functions */
int RES_DUOTONE_TRANSFER_FUNCITON = 0x03f9;
// 03fa
/** Duotone image information */
int RES_DUOTONE_IMAGE_INFO = 0x03fa;
// 03fb
// BYTE[2]
/** Effective black and white value for dot range */
int RES_EFFECTIVE_BLACK_WHITE = 0x03fb;
// 03fc
/** Obsolete undocumented resource. */
int RES_03FC = 0x03fc;
// 03fd
/** EPS options */
int RES_EPS_OPTIONS = 0x03fd;
// 03fe
// WORD, BYTE
/** Quick Mask channel ID, flag for mask initially empty */
int RES_QUICK_MASK_CHANNEL_ID = 0x03fe;
// 03ff
/** Obsolete undocumented resource. */
int RES_03ff = 0x03ff;
// 0400
// WORD
/** Index of target layer (0=bottom)*/
int RES_INDEX_OF_TARGET_LAYER = 0x0400;
// 0401
/** Working path */
int RES_WORKING_PATH = 0x0401;
// 0402
// WORD[]
/** Layers group info, group ID for dragging groups */
int RES_LAYERS_GROUP_INFO = 0x0402;
//
// 0403
/** Obsolete undocumented resource. */
int RES_0403 = 0x0403;
// 0404
/** IPTC-NAA record */
int RES_IPTC_NAA = 0x0404;
// 0405
/** Image mode for raw-format files */
int RES_RAW_IMAGE_MODE = 0x0405;
// 0406
/** JPEG quality (Adobe internal) */
int RES_JPEG_QUALITY = 0x0406;
// 1032
/** (Photoshop 4.0) Grid and guides information */
int RES_GRID_AND_GUIDES_INFO = 0x0408;
// 1033
/**
* (Photoshop 4.0) Thumbnail resource for Photoshop 4.0 only. BGR layout. Obsolete.
* @see #RES_THUMBNAIL
*/
int RES_THUMBNAIL_PS4 = 0x0409;
// 1034
/**
* (Photoshop 4.0) Copyright flag
* Boolean indicating whether image is copyrighted. Can be set via
* Property suite or by user in File Info...
*/
int RES_COPYRIGHT_FLAG = 0x040A;
// 1035
/**
* (Photoshop 4.0) URL
* Handle of a text string with uniform resource locator. Can be set via
* Property suite or by user in File Info...
*/
int RES_URL = 0x040B;
// 1036
/** (Photoshop 5.0) Thumbnail resource (supersedes resource 1033) */
int RES_THUMBNAIL = 0x040C;
// 1037
/**
* (Photoshop 5.0) Global Angle
* 4 bytes that contain an integer between 0 and 359, which is the global
* lighting angle for effects layer. If not present, assumed to be 30.
*/
int RES_GLOBAL_ANGLE = 0x040D;
// 1038
/**
* (Photoshop 5.0) Color samplers resource
* See "Color samplers resource format" on page20.
*/
int RES_COLOR_SAMPLERS = 0x040E;
/**
* (Photoshop 5.0) ICC Profile
* The raw bytes of an ICC (International Color Consortium) format profile.
*/
int RES_ICC_PROFILE = 0x040f;
// 1040
/**
* (Photoshop 5.0) Watermark
* One byte.
*/
int RES_WATERMARK = 0x0410;
// 1041
/**
* (Photoshop 5.0) ICC Untagged Profile
* 1 byte that disables any assumed profile handling when opening the file.
* 1 = intentionally untagged.
*/
int RES_ICC_UNTAGGED_PROFILE = 0x0411;
// 1042
/**
* (Photoshop 5.0) Effects visible
* 1-byte global flag to show/hide all the effects layer. Only present when
* they are hidden.
*/
int RES_EFFECTS_VISIBLE = 0x0412;
// 1043
/**
* (Photoshop 5.0) Spot Halftone
* 4 bytes for version, 4 bytes for length, and the variable length data.
*/
int RES_SPOT_HALFTONE = 0x0413;
// 1044
/**
* (Photoshop 5.0) Document-specific IDs seed number
* 4 bytes: Base value, starting at which layer IDs will be generated (or a
* greater value if existing IDs already exceed it). Its purpose is to avoid the
* case where we add layers, flatten, save, open, and then add more layers
* that end up with the same IDs as the first set.
*/
int RES_DOC_ID_SEED = 0x0414;
// 1045
/**
* (Photoshop 5.0) Unicode Alpha Names
* Unicode string (4 bytes length followed by string).
*/
int RES_UNICODE_ALPHA_NAME = 0x0415;
// 1046
/**
* (Photoshop 6.0) Indexed Color Table Count
* 2 bytes for the number of colors in table that are actually defined
*/
int RES_INDEXED_COLOR_TABLE_COUNT = 0x0416;
//1047
/**
* (Photoshop 6.0) Transparency Index.
* 2 bytes for the index of transparent color, if any.
*/
int RES_TRANSPARENCY_INDEX = 0x0417;
//1049
/**
* (Photoshop 6.0) Global Altitude
* 4 byte entry for altitude
*/
int RES_GLOBAL_ALTITUDE = 0x0419;
//1050
/**
* (Photoshop 6.0) Slices
*/
int RES_SLICES = 0x041A;
//1051
/**
* (Photoshop 6.0) Workflow URL
* Unicode string
*/
int RES_WORKFLOW_URL = 0x041B;
// 1052
/**
* (Photoshop 6.0) Jump To XPEP
* 2 bytes major version, 2 bytes minor version, 4 bytes count. Following is
* repeated for count: 4 bytes block size, 4 bytes key, if key = 'jtDd', then
* next is a Boolean for the dirty flag; otherwise its a 4 byte entry for the
* mod date.
*/
int RES_JUMP_TO_XPEP = 0x041C;
// 1053
/**
* (Photoshop 6.0) Alpha Identifiers
* 4 bytes of length, followed by 4 bytes each for every alpha identifier.
*/
int RES_ALPHA_IDENTIFIERS = 0x041D;
// 1054
/**
* (Photoshop 6.0) URL List
* 4 byte count of URLs, followed by 4 byte long, 4 byte ID, and Unicode
* string for each count.
*/
int RES_URL_LIST = 0x041E;
// 1057
/**
* (Photoshop 6.0) Version Info
* 4 bytes version, 1 byte hasRealMergedData, Unicode string: writer
* name, Unicode string: reader name, 4 bytes file version.
*/
int RES_VERSION_INFO = 0x0421;
// 1058
/**
* (Photoshop 7.0) EXIF data 1
*
* @see <a href="http://www.pima.net/standards/it10/PIMA15740/exif.htm">EXIF standard</a>
*/
int RES_EXIF_DATA_1 = 0x0422;
//1059
/**
* (Photoshop 7.0) EXIF data 3
*
* @see <a href="http://www.pima.net/standards/it10/PIMA15740/exif.htm">EXIF standard</a>
*/
int RES_EXIF_DATA_3 = 0x0423;
//1060
/**
* (Photoshop 7.0) XMP metadata
* File info as XML description.
*
* @see <a href="http://Partners.adobe.com/asn/developer/xmp/main.html">XMP standard</a>
*/
int RES_XMP_DATA = 0x0424;
// 1061
/**
* (Photoshop 7.0) Caption digest
* 16 bytes: RSA Data Security, MD5 message-digest algorithm
*/
int RES_CAPTION_DIGEST = 0x0425;
// 1062
/**
* (Photoshop 7.0) Print scale
* 2 bytes style (0 = centered, 1 = size to fit, 2 = user defined). 4 bytes x
* location (floating point). 4 bytes y location (floating point). 4 bytes scale
* (floating point)
*/
int RES_PRINT_SCALE = 0x0426;
// 1064
/**
* (Photoshop CS) Pixel Aspect Ratio
* 4 bytes (version = 1), 8 bytes double, x / y of a pixel
* 0x0429 1065 (Photoshop CS) Layer Comps
* 4 bytes (descriptor version = 16), Descriptor (see ?Descriptor structure?
* on page57)
*/
int RES_PIXEL_ASPECT_RATIO = 0x0428;
// 1066
/**
* (Photoshop CS) Alternate Duotone Colors
* 2 bytes (version = 1), 2 bytes count, following is repeated for each count:
* [ Color: 2 bytes for space followed by 4 * 2 byte color component ],
* following this is another 2 byte count, usually 256, followed by Lab colors
* one byte each for L, a, b
* This resource is not read or used by Photoshop.
*/
int RES_ALTERNATE_DUOTONE_COLORS = 0x042A;
// 1067
/**
* (Photoshop CS) Alternate Spot Colors
* 2 bytes (version = 1), 2 bytes channel count, following is repeated for
* each count: 4 bytes channel ID, Color: 2 bytes for space followed by 4 * 2
* byte color component
* This resource is not read or used by Photoshop.
*/
int RES_ALTERNATE_SPOT_COLORS = 0x042B;
// 07d0-0bb6
/* Saved path information */
// 0bb7
/** Clipping path name */
int RES_CLIPPING_PATH_NAME = 0x0bb7;
// 2710
/** Print flags information
* ID value 2710 signals that the Data section contains a WORD-length version number (should be 1),
* a BYTE-length flag indicating crop marks, a BYTE-length field (should be 0), a LONG-length bleed width value, and a
* WORD indicating the bleed width scale.
*/
int RES_PRINT_FLAGS_INFORMATION = 0x2710;
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* PSDAlhpaChannelInfo
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDAlhpaChannelInfo.java,v 1.0 May 2, 2008 5:33:40 PM haraldk Exp$
*/
class PSDAlphaChannelInfo extends PSDImageResource {
List<String> mNames;
public PSDAlphaChannelInfo(short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
@Override
protected void readData(final ImageInputStream pInput) throws IOException {
mNames = new ArrayList<String>();
long left = mSize;
while (left > 0) {
String name = PSDUtil.readPascalString(pInput);
mNames.add(name);
left -= name.length() + 1;
}
}
@Override
public String toString() {
StringBuilder builder = toStringBuilder();
builder.append(", alpha channels: ").append(mNames).append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
/**
* PSDChannelInfo
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDChannelInfo.java,v 1.0 May 6, 2008 2:46:23 PM haraldk Exp$
*/
class PSDChannelInfo {
final short mChannelId;
final long mLength;
// typedef struct _CLI
// {
// WORD ChannelID; /* Channel Length Info field one */
// LONG LengthOfChannelData; /* Channel Length Info field two */
// } CLI;
public PSDChannelInfo(short pChannelId, long pLength) {
mChannelId = pChannelId;
mLength = pLength;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
builder.append("[");
builder.append("channelId: ").append(mChannelId);
builder.append(", length: ").append(mLength);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
/**
* PSDChannelSourceDestinationRange
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDChannelSourceDestinationRange.java,v 1.0 May 6, 2008 5:14:13 PM haraldk Exp$
*/
class PSDChannelSourceDestinationRange {
private String mChannel;
private short mSourceBlack;
private short mSourceWhite;
private short mDestBlack;
private short mDestWhite;
public PSDChannelSourceDestinationRange(ImageInputStream pInput, String pChannel) throws IOException {
mChannel = pChannel;
mSourceBlack = pInput.readShort();
mSourceWhite = pInput.readShort();
mDestBlack = pInput.readShort();
mDestWhite = pInput.readShort();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
builder.append("[(").append(mChannel);
builder.append("): sourceBlack: ").append(Integer.toHexString(mSourceBlack & 0xffff));
builder.append(", sourceWhite: ").append(Integer.toHexString(mSourceWhite & 0xffff));
builder.append(", destBlack: ").append(Integer.toHexString(mDestBlack & 0xffff));
builder.append(", destWhite: ").append(Integer.toHexString(mDestWhite & 0xffff));
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.image.InverseColorMapIndexColorModel;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.io.IOException;
/**
* PSDColorData
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDColorData.java,v 1.0 Apr 29, 2008 5:33:01 PM haraldk Exp$
*/
class PSDColorData {
final byte[] mColors;
private IndexColorModel mColorModel;
PSDColorData(final ImageInputStream pInput) throws IOException {
int length = pInput.readInt();
if (length == 0) {
throw new IIOException("No palette information in PSD");
}
else if (length % 3 != 0) {
throw new IIOException("Wrong palette information in PSD");
}
// NOTE: Spec says length may only be 768 bytes (256 RGB triplets)
mColors = new byte[length];
pInput.readFully(mColors);
// NOTE: Could be a padding byte here, if not even..
}
IndexColorModel getIndexColorModel() {
if (mColorModel == null) {
int[] rgb = toInterleavedRGB(mColors);
mColorModel = new InverseColorMapIndexColorModel(8, rgb.length, rgb, 0, false, -1, DataBuffer.TYPE_BYTE);
}
return mColorModel;
}
private static int[] toInterleavedRGB(final byte[] pColors) {
int[] rgb = new int[pColors.length / 3];
for (int i = 0; i < rgb.length; i++) {
// Pack the non-interleaved samples into interleaved form
int r = pColors[ i] & 0xff;
int g = pColors[ rgb.length + i] & 0xff;
int b = pColors[2 * rgb.length + i] & 0xff;
rgb[i] = (r << 16) | (g << 8) | b;
}
return rgb;
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.IIOException;
import java.io.IOException;
/**
* PSDResolutionInfo
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$
*/
class PSDDisplayInfo extends PSDImageResource {
// TODO: Size of this struct should be 14.. Does not compute... Something bogus here
// ColorSpace definitions:
// PSD_CS_RGB = 0, /* RGB */
// PSD_CS_HSB = 1, /* Hue, Saturation, Brightness */
// PSD_CS_CMYK = 2, /* CMYK */
// PSD_CS_PANTONE = 3, /* Pantone matching system (Lab)*/
// PSD_CS_FOCOLTONE = 4, /* Focoltone colour system (CMYK)*/
// PSD_CS_TRUMATCH = 5, /* Trumatch color (CMYK)*/
// PSD_CS_TOYO = 6, /* Toyo 88 colorfinder 1050 (Lab)*/
// PSD_CS_LAB = 7, /* L*a*b*/
// PSD_CS_GRAYSCALE = 8, /* Grey scale */
// PSD_CS_HKS = 10, /* HKS colors (CMYK)*/
// PSD_CS_DIC = 11, /* DIC color guide (Lab)*/
// PSD_CS_ANPA = 3000, /* Anpa color (Lab)*/
//typedef _DisplayInfo
//{
// WORD ColorSpace;
// WORD Color[4];
// WORD Opacity; /* 0-100 */
// BYTE Kind; /* 0=selected, 1=protected */
// BYTE Padding; /* Always zero */
//} DISPLAYINFO;
int mColorSpace;
short[] mColors;
short mOpacity;
byte mKind;
PSDDisplayInfo(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
@Override
protected void readData(ImageInputStream pInput) throws IOException {
if (mSize % 14 != 0) {
throw new IIOException("Display info length expected to be mod 14: " + mSize);
}
// long left = mSize;
// while (left > 0) {
mColorSpace = pInput.readShort();
// Color[4]...?
mColors = new short[4];
mColors[0] = pInput.readShort();
mColors[1] = pInput.readShort();
mColors[2] = pInput.readShort();
mColors[3] = pInput.readShort();
mOpacity = pInput.readShort();
mKind = pInput.readByte();
pInput.readByte(); // Pad
// left -= 14;
// }
pInput.skipBytes(mSize - 14);
}
@Override
public String toString() {
StringBuilder builder = toStringBuilder();
builder.append(", ColorSpace: ").append(mColorSpace);
builder.append(", Colors: {");
builder.append(mColors[0]);
builder.append(", ");
builder.append(mColors[1]);
builder.append(", ");
builder.append(mColors[2]);
builder.append(", ");
builder.append(mColors[3]);
builder.append("}, Opacity: ").append(mOpacity);
builder.append(", Kind: ").append(kind(mKind));
builder.append("]");
return builder.toString();
}
private String kind(final byte pKind) {
switch (pKind) {
case 0:
return "selected";
case 1:
return "protected";
default:
return "unknown kind: " + Integer.toHexString(pKind & 0xff);
}
}
}

View File

@@ -0,0 +1,342 @@
package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.lang.StringUtil;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* EXIF metadata.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: XMPData.java,v 1.0 Jul 28, 2009 5:50:34 PM haraldk Exp$
*
* @see <a href="http://en.wikipedia.org/wiki/Exchangeable_image_file_format">Wikipedia</a>
* @see <a href="http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html">Aware systems TIFF tag reference</a>
* @see <a href="http://partners.adobe.com/public/developer/tiff/index.html">Adobe TIFF developer resources</a>
*/
final class PSDEXIF1Data extends PSDImageResource {
// protected byte[] mData;
protected Directory mDirectory;
PSDEXIF1Data(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
@Override
protected void readData(final ImageInputStream pInput) throws IOException {
// This is in essence an embedded TIFF file.
// TODO: Extract TIFF parsing to more general purpose package
// TODO: Instead, read the byte data, store for later parsing (or store offset, and read on request)
MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(IIOUtil.createStreamAdapter(pInput, mSize));
byte[] bom = new byte[2];
stream.readFully(bom);
if (bom[0] == 'I' && bom[1] == 'I') {
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
}
else if (!(bom[0] == 'M' && bom[1] == 'M')) {
throw new IIOException(String.format("Invalid byte order marker '%s'", StringUtil.decode(bom, 0, bom.length, "ASCII")));
}
if (stream.readUnsignedShort() != 42) {
throw new IIOException("Wrong TIFF magic in EXIF data.");
}
long directoryOffset = stream.readUnsignedInt();
mDirectory = Directory.read(stream, directoryOffset);
}
@Override
public String toString() {
StringBuilder builder = toStringBuilder();
builder.append(", ").append(mDirectory);
builder.append("]");
return builder.toString();
}
// TIFF Image file directory (IFD)
static class Directory implements Iterable<Entry> {
private List<Entry> mEntries = new ArrayList<Entry>();
private Directory() {}
public static Directory read(final ImageInputStream pInput, final long pOffset) throws IOException {
Directory directory = new Directory();
pInput.seek(pOffset);
int entryCount = pInput.readUnsignedShort();
for (int i = 0; i < entryCount; i++) {
directory.mEntries.add(Entry.read(pInput));
}
long nextOffset = pInput.readUnsignedInt();
if (nextOffset != 0) {
Directory next = Directory.read(pInput, nextOffset);
directory.mEntries.addAll(next.mEntries);
}
return directory;
}
public Entry get(int pTag) {
for (Entry entry : mEntries) {
if (entry.mTag == pTag) {
return entry;
}
}
return null;
}
public Iterator<Entry> iterator() {
return mEntries.iterator();
}
@Override
public String toString() {
return String.format("Directory%s", mEntries);
}
}
// TIFF IFD Entry
static class Entry {
private static final int EXIF_IFD = 0x8769;
private final static String[] TYPE_NAMES = {
"BYTE", "ASCII", "SHORT", "LONG", "RATIONAL",
"SBYTE", "UNDEFINED", "SSHORT", "SLONG", "SRATIONAL", "FLOAT", "DOUBLE",
};
private final static int[] TYPE_LENGTHS = {
1, 1, 2, 4, 8,
1, 1, 2, 4, 8, 4, 8,
};
private int mTag;
/*
1 = BYTE 8-bit unsigned integer.
2 = ASCII 8-bit byte that contains a 7-bit ASCII code; the last byte
must be NUL (binary zero).
3 = SHORT 16-bit (2-byte) unsigned integer.
4 = LONG 32-bit (4-byte) unsigned integer.
5 = RATIONAL Two LONGs: the first represents the numerator of a
fraction; the second, the denominator.
TIFF 6.0 and above:
6 = SBYTE An 8-bit signed (twos-complement) integer.
7 = UNDEFINED An 8-bit byte that may contain anything, depending on
the definition of the field.
8 = SSHORT A 16-bit (2-byte) signed (twos-complement) integer.
9 = SLONG A 32-bit (4-byte) signed (twos-complement) integer.
10 = SRATIONAL Two SLONGs: the first represents the numerator of a
fraction, the second the denominator.
11 = FLOAT Single precision (4-byte) IEEE format.
12 = DOUBLE Double precision (8-byte) IEEE format.
*/
private short mType;
private int mCount;
private long mValueOffset;
private Object mValue;
private Entry() {}
public static Entry read(final ImageInputStream pInput) throws IOException {
Entry entry = new Entry();
entry.mTag = pInput.readUnsignedShort();
entry.mType = pInput.readShort();
entry.mCount = pInput.readInt(); // Number of values
// TODO: Handle other sub-IFDs
if (entry.mTag == EXIF_IFD) {
long offset = pInput.readUnsignedInt();
pInput.mark();
try {
entry.mValue = Directory.read(pInput, offset);
}
finally {
pInput.reset();
}
}
else {
int valueLength = entry.getValueLength();
if (valueLength > 0 && valueLength <= 4) {
entry.readValueInLine(pInput);
pInput.skipBytes(4 - valueLength);
}
else {
entry.mValueOffset = pInput.readUnsignedInt(); // This is the *value* iff the value size is <= 4 bytes
entry.readValue(pInput);
}
}
return entry;
}
private void readValue(final ImageInputStream pInput) throws IOException {
long pos = pInput.getStreamPosition();
try {
pInput.seek(mValueOffset);
readValueInLine(pInput);
}
finally {
pInput.seek(pos);
}
}
private void readValueInLine(ImageInputStream pInput) throws IOException {
mValue = readValueDirect(pInput, mType, mCount);
}
private static Object readValueDirect(final ImageInputStream pInput, final short pType, final int pCount) throws IOException {
switch (pType) {
case 2:
// TODO: This might be UTF-8 or ISO-8859-1, even though against the spec
byte[] ascii = new byte[pCount];
pInput.readFully(ascii);
return StringUtil.decode(ascii, 0, ascii.length, "ASCII");
case 1:
if (pCount == 1) {
return pInput.readUnsignedByte();
}
case 6:
if (pCount == 1) {
return pInput.readByte();
}
case 7:
byte[] bytes = new byte[pCount];
pInput.readFully(bytes);
return bytes;
case 3:
if (pCount == 1) {
return pInput.readUnsignedShort();
}
case 8:
if (pCount == 1) {
return pInput.readShort();
}
short[] shorts = new short[pCount];
pInput.readFully(shorts, 0, shorts.length);
return shorts;
case 4:
if (pCount == 1) {
return pInput.readUnsignedInt();
}
case 9:
if (pCount == 1) {
return pInput.readInt();
}
int[] ints = new int[pCount];
pInput.readFully(ints, 0, ints.length);
return ints;
case 11:
if (pCount == 1) {
return pInput.readFloat();
}
float[] floats = new float[pCount];
pInput.readFully(floats, 0, floats.length);
return floats;
case 12:
if (pCount == 1) {
return pInput.readDouble();
}
double[] doubles = new double[pCount];
pInput.readFully(doubles, 0, doubles.length);
return doubles;
// TODO: Consider using a Rational class
case 5:
if (pCount == 1) {
return pInput.readUnsignedInt() / (double) pInput.readUnsignedInt();
}
double[] rationals = new double[pCount];
for (int i = 0; i < rationals.length; i++) {
rationals[i] = pInput.readUnsignedInt() / (double) pInput.readUnsignedInt();
}
return rationals;
case 10:
if (pCount == 1) {
return pInput.readInt() / (double) pInput.readInt();
}
double[] srationals = new double[pCount];
for (int i = 0; i < srationals.length; i++) {
srationals[i] = pInput.readInt() / (double) pInput.readInt();
}
return srationals;
default:
throw new IIOException(String.format("Unknown EXIF type '%s'", pType));
}
}
private int getValueLength() {
if (mType > 0 && mType <= TYPE_LENGTHS.length) {
return TYPE_LENGTHS[mType - 1] * mCount;
}
return -1;
}
private String getTypeName() {
if (mType > 0 && mType <= TYPE_NAMES.length) {
return TYPE_NAMES[mType - 1];
}
return "Unknown type";
}
// TODO: Tag names!
@Override
public String toString() {
return String.format("0x%04x: %s (%s, %d)", mTag, getValueAsString(), getTypeName(), mCount);
}
public String getValueAsString() {
if (mValue instanceof String) {
return String.format("\"%s\"", mValue);
}
if (mValue != null && mValue.getClass().isArray()) {
Class<?> type = mValue.getClass().getComponentType();
if (byte.class == type) {
return Arrays.toString((byte[]) mValue);
}
if (short.class == type) {
return Arrays.toString((short[]) mValue);
}
if (int.class == type) {
return Arrays.toString((int[]) mValue);
}
if (float.class == type) {
return Arrays.toString((float[]) mValue);
}
if (double.class == type) {
return Arrays.toString((double[]) mValue);
}
}
return String.valueOf(mValue);
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
/**
* PSDGlobalLayerMask
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDGlobalLayerMask.java,v 1.0 May 8, 2008 5:33:48 PM haraldk Exp$
*/
class PSDGlobalLayerMask {
final int mColorSpace;
final int mColor1;
final int mColor2;
final int mColor3;
final int mColor4;
final int mOpacity;
final int mKind;
PSDGlobalLayerMask(final ImageInputStream pInput) throws IOException {
mColorSpace = pInput.readUnsignedShort();
mColor1 = pInput.readUnsignedShort();
mColor2 = pInput.readUnsignedShort();
mColor3 = pInput.readUnsignedShort();
mColor4 = pInput.readUnsignedShort();
mOpacity = pInput.readUnsignedShort();
mKind = pInput.readUnsignedByte();
pInput.readByte(); // Pad
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
builder.append("[");
builder.append("color space: 0x").append(Integer.toHexString(mColorSpace));
builder.append(", colors: [0x").append(Integer.toHexString(mColor1));
builder.append(", 0x").append(Integer.toHexString(mColor2));
builder.append(", 0x").append(Integer.toHexString(mColor3));
builder.append(", 0x").append(Integer.toHexString(mColor4));
builder.append("], opacity: ").append(mOpacity);
builder.append(", kind: ").append(mKind);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import org.w3c.dom.Node;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.IIOException;
import java.io.IOException;
/**
* PSDHeader
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDHeader.java,v 1.0 Apr 29, 2008 5:18:22 PM haraldk Exp$
*/
class PSDHeader {
// The header is 26 bytes in length and is structured as follows:
//
// typedef struct _PSD_HEADER
// {
// BYTE Signature[4]; /* File ID "8BPS" */
// WORD Version; /* Version number, always 1 */
// BYTE Reserved[6]; /* Reserved, must be zeroed */
// WORD Channels; /* Number of color channels (1-24) including alpha
// channels */
// LONG Rows; /* Height of image in pixels (1-30000) */
// LONG Columns; /* Width of image in pixels (1-30000) */
// WORD Depth; /* Number of bits per channel (1, 8, and 16) */
// WORD Mode; /* Color mode */
// } PSD_HEADER;
final short mChannels;
final int mWidth;
final int mHeight;
final short mBits;
final short mMode;
PSDHeader(final ImageInputStream pInput) throws IOException {
int signature = pInput.readInt();
if (signature != PSD.SIGNATURE_8BPS) {
throw new IIOException("Not a PSD document, expected signature \"8BPS\": \"" + PSDUtil.intToStr(signature) + "\" (0x" + Integer.toHexString(signature) + ")");
}
int version = pInput.readUnsignedShort();
switch (version) {
case 1:
break;
case 2:
throw new IIOException("Photoshop Large Document Format (PSB) not supported yet.");
default:
throw new IIOException(String.format("Unknown PSD version, expected 1 or 2: 0x%08x", version));
}
byte[] reserved = new byte[6];
pInput.readFully(reserved);
mChannels = pInput.readShort();
mHeight = pInput.readInt(); // Rows
mWidth = pInput.readInt(); // Columns
mBits = pInput.readShort();
mMode = pInput.readShort();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
builder.append("[Channels: ");
builder.append(mChannels);
builder.append(", width: ");
builder.append(mWidth);
builder.append(", height: ");
builder.append(mHeight);
builder.append(", depth: ");
builder.append(mBits);
builder.append(", mode: ");
builder.append(mMode);
switch (mMode) {
case PSD.COLOR_MODE_MONOCHROME:
builder.append(" (Monochrome)");
break;
case PSD.COLOR_MODE_GRAYSCALE:
builder.append(" (Grayscale)");
break;
case PSD.COLOR_MODE_INDEXED:
builder.append(" (Indexed)");
break;
case PSD.COLOR_MODE_RGB:
builder.append(" (RGB)");
break;
case PSD.COLOR_MODE_CMYK:
builder.append(" (CMYK)");
break;
case PSD.COLOR_MODE_MULTICHANNEL:
builder.append(" (Multi channel)");
break;
case PSD.COLOR_MODE_DUOTONE:
builder.append(" (Duotone)");
break;
case PSD.COLOR_MODE_LAB:
builder.append(" (Lab color)");
break;
default:
builder.append(" (Unkown mode)");
}
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
/**
* PSDImageReaderSpi
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDImageReaderSpi.java,v 1.0 Apr 29, 2008 4:49:03 PM haraldk Exp$
*/
public class PSDImageReaderSpi extends ImageReaderSpi {
/**
* Creates a {@code PSDImageReaderSpi}.
*/
public PSDImageReaderSpi() {
this(IIOUtil.getProviderInfo(PSDImageReaderSpi.class));
}
private PSDImageReaderSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(),
pProviderInfo.getVersion(),
new String[]{"psd", "PSD"},
new String[]{"psd"},
new String[]{
"application/vnd.adobe.photoshop", // This one seems official, used in XMP
"image/x-psd", "application/x-photoshop", "image/x-photoshop"
},
"com.twelvemkonkeys.imageio.plugins.psd.PSDImageReader",
STANDARD_INPUT_TYPE,
// new String[]{"com.twelvemkonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
null,
true, // supports standard stream metadata
null, null, // native stream format name and class
null, null, // extra stream formats
true, // supports standard image metadata
PSDMetadata.NATIVE_METADATA_FORMAT_NAME, PSDMetadata.NATIVE_METADATA_FORMAT_CLASS_NAME,
null, null // extra image metadata formats
);
}
public boolean canDecodeInput(final Object pSource) throws IOException {
if (!(pSource instanceof ImageInputStream)) {
return false;
}
ImageInputStream stream = (ImageInputStream) pSource;
stream.mark();
try {
return stream.readInt() == PSD.SIGNATURE_8BPS;
// TODO: Test more of the header, see PSDImageReader#readHeader
}
finally {
stream.reset();
}
}
public ImageReader createReaderInstance(final Object pExtension) throws IOException {
return new PSDImageReader(this);
}
public String getDescription(final Locale pLocale) {
return "Adobe Photoshop Document (PSD) image reader";
}
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.lang.StringUtil;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.IIOException;
import java.io.IOException;
import java.lang.reflect.Field;
/**
* PSDImageResource
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDImageResource.java,v 1.0 Apr 29, 2008 5:49:06 PM haraldk Exp$
*/
class PSDImageResource {
final short mId;
final String mName;
final long mSize;
PSDImageResource(final short pId, final ImageInputStream pInput) throws IOException {
mId = pId;
mName = PSDUtil.readPascalString(pInput);
// Skip pad
int nameSize = mName.length() + 1;
if (nameSize % 2 != 0) {
pInput.readByte();
}
mSize = pInput.readUnsignedInt();
readData(pInput);
// Data is even-padded
if (mSize % 2 != 0) {
pInput.read();
}
}
/**
* This default implementation simply skips the data.
*
* @param pInput the input
* @throws IOException if an I/O exception occurs
*/
protected void readData(final ImageInputStream pInput) throws IOException {
// TODO: This design is ugly, as subclasses readData is invoked BEFORE their respective constructor...
pInput.skipBytes(mSize);
}
@Override
public String toString() {
StringBuilder builder = toStringBuilder();
builder.append(", data length: ");
builder.append(mSize);
builder.append("]");
return builder.toString();
}
protected StringBuilder toStringBuilder() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
String fakeType = resourceTypeForId(mId);
if (fakeType != null) {
builder.append("(").append(fakeType).append(")");
}
builder.append("[ID: 0x");
builder.append(Integer.toHexString(mId));
if (mName != null && mName.trim().length() != 0) {
builder.append(", name: \"");
builder.append(mName);
builder.append("\"");
}
return builder;
}
static String resourceTypeForId(final short pId) {
switch (pId) {
case PSD.RES_RESOLUTION_INFO:
case PSD.RES_ALPHA_CHANNEL_INFO:
case PSD.RES_DISPLAY_INFO:
case PSD.RES_PRINT_FLAGS:
case PSD.RES_THUMBNAIL_PS4:
case PSD.RES_THUMBNAIL:
case PSD.RES_ICC_PROFILE:
case PSD.RES_VERSION_INFO:
case PSD.RES_EXIF_DATA_1:
// case PSD.RES_EXIF_DATA_3:
case PSD.RES_XMP_DATA:
case PSD.RES_PRINT_FLAGS_INFORMATION:
return null;
default:
try {
for (Field field : PSD.class.getDeclaredFields()) {
if (field.getName().startsWith("RES_") && field.getInt(null) == pId) {
String name = field.getName().substring(4);
return StringUtil.lispToCamel(name.replace("_", "-").toLowerCase(), true);
}
}
}
catch (IllegalAccessException ignore) {
}
return "unknown resource";
}
}
public static PSDImageResource read(final ImageInputStream pInput) throws IOException {
int type = pInput.readInt();
if (type != PSD.RESOURCE_TYPE) {
throw new IIOException(String.format("Wrong image resource type, expected '8BIM': '%s'", PSDUtil.intToStr(type)));
}
// TODO: Process more of the resource stuff, most important are IPTC, EXIF and XMP data,
// version info, and thumbnail for thumbnail-support.
short id = pInput.readShort();
switch (id) {
case PSD.RES_RESOLUTION_INFO:
return new PSDResolutionInfo(id, pInput);
case PSD.RES_ALPHA_CHANNEL_INFO:
return new PSDAlphaChannelInfo(id, pInput);
case PSD.RES_DISPLAY_INFO:
return new PSDDisplayInfo(id, pInput);
case PSD.RES_PRINT_FLAGS:
return new PSDPrintFlags(id, pInput);
case PSD.RES_THUMBNAIL_PS4:
case PSD.RES_THUMBNAIL:
return new PSDThumbnail(id, pInput);
case PSD.RES_ICC_PROFILE:
return new ICCProfile(id, pInput);
case PSD.RES_VERSION_INFO:
return new PSDVersionInfo(id, pInput);
case PSD.RES_EXIF_DATA_1:
return new PSDEXIF1Data(id, pInput);
case PSD.RES_XMP_DATA:
return new PSDXMPData(id, pInput);
case PSD.RES_PRINT_FLAGS_INFORMATION:
return new PSDPrintFlagsInformation(id, pInput);
default:
if (id >= 0x07d0 && id <= 0x0bb6) {
// TODO: Parse saved path information
return new PSDImageResource(id, pInput);
}
else {
return new PSDImageResource(id, pInput);
}
}
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.IIOException;
import java.io.IOException;
/**
* PSDLayerBlendMode
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDLayerBlendMode.java,v 1.0 May 8, 2008 4:34:35 PM haraldk Exp$
*/
class PSDLayerBlendMode {
final int mBlendMode;
final int mOpacity; // 0-255
final int mClipping; // 0: base, 1: non-base
final int mFlags;
public PSDLayerBlendMode(final ImageInputStream pInput) throws IOException {
int blendModeSig = pInput.readInt();
if (blendModeSig != PSD.RESOURCE_TYPE) { // TODO: Is this really just a resource?
throw new IIOException("Illegal PSD Blend Mode signature, expected 8BIM: " + PSDUtil.intToStr(blendModeSig));
}
mBlendMode = pInput.readInt();
mOpacity = pInput.readUnsignedByte();
mClipping = pInput.readUnsignedByte();
mFlags = pInput.readUnsignedByte();
pInput.readByte(); // Pad
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
builder.append("[");
builder.append("mode: \"").append(PSDUtil.intToStr(mBlendMode));
builder.append("\", opacity: ").append(mOpacity);
builder.append(", clipping: ").append(mClipping);
switch (mClipping) {
case 0:
builder.append(" (base)");
break;
case 1:
builder.append(" (non-base)");
break;
default:
builder.append(" (unknown)");
break;
}
builder.append(", flags: ").append(byteToBinary(mFlags));
/*
bit 0 = transparency protected; bit 1 = visible; bit 2 = obsolete;
bit 3 = 1 for Photoshop 5.0 and later, tells if bit 4 has useful information;
bit 4 = pixel data irrelevant to appearance of document
*/
builder.append(" (");
if ((mFlags & 0x01) != 0) {
builder.append("Transp. protected, ");
}
if ((mFlags & 0x02) != 0) {
builder.append("Hidden, ");
}
if ((mFlags & 0x04) != 0) {
builder.append("Obsolete bit, ");
}
if ((mFlags & 0x08) != 0) {
builder.append("PS 5.0 data present, "); // "tells if next bit has useful information"...
}
if ((mFlags & 0x10) != 0) {
builder.append("Pixel data irrelevant, ");
}
if ((mFlags & 0x20) != 0) {
builder.append("Unknown bit 5, ");
}
if ((mFlags & 0x40) != 0) {
builder.append("Unknown bit 6, ");
}
if ((mFlags & 0x80) != 0) {
builder.append("Unknown bit 7, ");
}
// Stupidity...
if (mFlags != 0) {
builder.delete(builder.length() - 2, builder.length());
}
builder.append(")");
builder.append("]");
return builder.toString();
}
private static String byteToBinary(final int pFlags) {
String flagStr = Integer.toBinaryString(pFlags);
flagStr = "00000000".substring(flagStr.length()) + flagStr;
return flagStr;
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.IIOException;
import java.io.IOException;
import java.util.Arrays;
/**
* PSDLayerInfo
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDLayerInfo.java,v 1.0 Apr 29, 2008 6:01:12 PM haraldk Exp$
*/
class PSDLayerInfo {
final int mTop;
final int mLeft;
final int mBottom;
final int mRight;
final PSDChannelInfo[] mChannelInfo;
final PSDLayerBlendMode mBlendMode;
final PSDLayerMaskData mLayerMaskData;
final PSDChannelSourceDestinationRange[] mRanges;
final String mLayerName;
PSDLayerInfo(ImageInputStream pInput) throws IOException {
mTop = pInput.readInt();
mLeft = pInput.readInt();
mBottom = pInput.readInt();
mRight = pInput.readInt();
int channels = pInput.readUnsignedShort();
mChannelInfo = new PSDChannelInfo[channels];
for (int i = 0; i < channels; i++) {
short channelId = pInput.readShort();
long length = pInput.readUnsignedInt();
mChannelInfo[i] = new PSDChannelInfo(channelId, length);
}
mBlendMode = new PSDLayerBlendMode(pInput);
// Lenght of layer mask data
long extraDataSize = pInput.readUnsignedInt();
// TODO: Allow skipping the rest here?
// pInput.skipBytes(extraDataSize);
// Layer mask/adjustment layer data
int layerMaskDataSize = pInput.readInt(); // May be 0, 20 or 36 bytes...
if (layerMaskDataSize != 0) {
mLayerMaskData = new PSDLayerMaskData(pInput, layerMaskDataSize);
}
else {
mLayerMaskData = null;
}
int layerBlendingDataSize = pInput.readInt();
if (layerBlendingDataSize % 8 != 0) {
throw new IIOException("Illegal PSD Layer Blending Data size: " + layerBlendingDataSize + ", expected multiple of 8");
}
mRanges = new PSDChannelSourceDestinationRange[layerBlendingDataSize / 8];
for (int i = 0; i < mRanges.length; i++) {
mRanges[i] = new PSDChannelSourceDestinationRange(pInput, (i == 0 ? "Gray" : "Channel " + (i - 1)));
}
mLayerName = PSDUtil.readPascalString(pInput);
int layerNameSize = mLayerName.length() + 1;
// Skip pad bytes for long word alignment
if (layerNameSize % 4 != 0) {
int skip = layerNameSize % 4;
pInput.skipBytes(skip);
layerNameSize += skip;
}
// TODO: There's some data skipped here...
// Adjustment layer info etc...
pInput.skipBytes(extraDataSize - layerMaskDataSize - 4 - layerBlendingDataSize - 4 - layerNameSize);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
builder.append("[");
builder.append("top: ").append(mTop);
builder.append(", left: ").append(mLeft);
builder.append(", bottom: ").append(mBottom);
builder.append(", right: ").append(mRight);
builder.append(", channels: ").append(Arrays.toString(mChannelInfo));
builder.append(", blend mode: ").append(mBlendMode);
if (mLayerMaskData != null) {
builder.append(", layer mask data: ").append(mLayerMaskData);
}
builder.append(", ranges: ").append(Arrays.toString(mRanges));
builder.append(", layer name: \"").append(mLayerName).append("\"");
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
/**
* PSDLayerMaskData
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDLayerMaskData.java,v 1.0 May 6, 2008 5:15:05 PM haraldk Exp$
*/
class PSDLayerMaskData {
private int mTop;
private int mLeft;
private int mBottom;
private int mRight;
private int mDefaultColor;
private int mFlags;
private boolean mLarge;
private int mRealFlags;
private int mRealUserBackground;
private int mRealTop;
private int mRealLeft;
private int mRealBottom;
private int mRealRight;
PSDLayerMaskData(ImageInputStream pInput, int pSize) throws IOException {
if (pSize != 20 && pSize != 36) {
throw new IIOException("Illegal PSD Layer Mask data size: " + pSize + " (expeced 20 or 36)");
}
mTop = pInput.readInt();
mLeft = pInput.readInt();
mBottom = pInput.readInt();
mRight = pInput.readInt();
mDefaultColor = pInput.readUnsignedByte();
mFlags = pInput.readUnsignedByte();
if (pSize == 20) {
pInput.readShort(); // Pad
}
else {
// TODO: What to make out of this?
mLarge = true;
mRealFlags = pInput.readUnsignedByte();
mRealUserBackground = pInput.readUnsignedByte();
mRealTop = pInput.readInt();
mRealLeft = pInput.readInt();
mRealBottom = pInput.readInt();
mRealRight = pInput.readInt();
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
builder.append("[");
builder.append("top: ").append(mTop);
builder.append(", left: ").append(mLeft);
builder.append(", bottom: ").append(mBottom);
builder.append(", right: ").append(mRight);
builder.append(", default color: ").append(mDefaultColor);
builder.append(", flags: ").append(Integer.toBinaryString(mFlags));
// TODO: Maybe the flag bits have oposite order?
builder.append(" (");
if ((mFlags & 0x01) != 0) {
builder.append("Pos. rel. to layer");
}
else {
builder.append("Pos. abs.");
}
if ((mFlags & 0x02) != 0) {
builder.append(", Mask disabled");
}
else {
builder.append(", Mask enabled");
}
if ((mFlags & 0x04) != 0) {
builder.append(", Invert mask");
}
if ((mFlags & 0x08) != 0) {
builder.append(", Unknown bit 3");
}
if ((mFlags & 0x10) != 0) {
builder.append(", Unknown bit 4");
}
if ((mFlags & 0x20) != 0) {
builder.append(", Unknown bit 5");
}
if ((mFlags & 0x40) != 0) {
builder.append(", Unknown bit 6");
}
if ((mFlags & 0x80) != 0) {
builder.append(", Unknown bit 7");
}
builder.append(")");
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,611 @@
package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.util.FilterIterator;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.awt.image.IndexColorModel;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* PSDMetadata
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDMetadata.java,v 1.0 Nov 4, 2009 5:28:12 PM haraldk Exp$
*/
public final class PSDMetadata extends IIOMetadata implements Cloneable {
// TODO: Decide on image/stream metadata...
static final String NATIVE_METADATA_FORMAT_NAME = "com_twelvemonkeys_imageio_psd_image_1.0";
static final String NATIVE_METADATA_FORMAT_CLASS_NAME = "com.twelvemonkeys.imageio.plugins.psd.PSDMetadataFormat";
PSDHeader mHeader;
PSDColorData mColorData;
int mCompression = -1;
List<PSDImageResource> mImageResources;
PSDGlobalLayerMask mGlobalLayerMask;
List<PSDLayerInfo> mLayerInfo;
static final String[] COLOR_MODES = {
"MONOCHROME", "GRAYSCALE", "INDEXED", "RGB", "CMYK", null, null, "MULTICHANNEL", "DUOTONE", "LAB"
};
static final String[] DISPLAY_INFO_CS = {
"RGB", "HSB", "CMYK", "PANTONE", "FOCOLTONE", "TRUMATCH", "TOYO", "LAB", "GRAYSCALE", null, "HKS", "DIC",
null, // ... (until index 2999),
"ANPA"
};
static final String[] DISPLAY_INFO_KINDS = {"selected", "protected"};
protected PSDMetadata() {
// TODO: Allow XMP, EXIF and IPTC as extra formats?
super(true, NATIVE_METADATA_FORMAT_NAME, NATIVE_METADATA_FORMAT_CLASS_NAME, null, null);
}
@Override
public boolean isReadOnly() {
// TODO: Extract to abstract metadata impl class?
return true;
}
@Override
public Node getAsTree(final String pFormatName) {
validateFormatName(pFormatName);
if (pFormatName.equals(nativeMetadataFormatName)) {
return getNativeTree();
}
else if (pFormatName.equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
return getStandardTree();
}
throw new AssertionError("Unreachable");
}
@Override
public void mergeTree(final String pFormatName, final Node pRoot) throws IIOInvalidTreeException {
// TODO: Extract to abstract metadata impl class?
assertMutable();
validateFormatName(pFormatName);
if (!pRoot.getNodeName().equals(nativeMetadataFormatName)) {
throw new IIOInvalidTreeException("Root must be " + nativeMetadataFormatName, pRoot);
}
Node node = pRoot.getFirstChild();
while (node != null) {
// TODO: Merge values from node into this
// Move to the next sibling
node = node.getNextSibling();
}
}
@Override
public void reset() {
// TODO: Extract to abstract metadata impl class?
assertMutable();
throw new UnsupportedOperationException("Method reset not implemented"); // TODO: Implement
}
// TODO: Extract to abstract metadata impl class?
private void assertMutable() {
if (isReadOnly()) {
throw new IllegalStateException("Metadata is read-only");
}
}
// TODO: Extract to abstract metadata impl class?
private void validateFormatName(final String pFormatName) {
String[] metadataFormatNames = getMetadataFormatNames();
if (metadataFormatNames != null) {
for (String metadataFormatName : metadataFormatNames) {
if (metadataFormatName.equals(pFormatName)) {
return; // Found, we're ok!
}
}
}
throw new IllegalArgumentException(
String.format("Bad format name: \"%s\". Expected one of %s", pFormatName, Arrays.toString(metadataFormatNames))
);
}
@Override
public Object clone() {
// TODO: Make it a deep clone
try {
return super.clone();
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
/// Native format support
private Node getNativeTree() {
IIOMetadataNode root = new IIOMetadataNode(NATIVE_METADATA_FORMAT_NAME);
root.appendChild(createHeaderNode());
if (mHeader.mMode == PSD.COLOR_MODE_INDEXED) {
root.appendChild(createPaletteNode());
}
if (mImageResources != null && !mImageResources.isEmpty()) {
root.appendChild(createImageResourcesNode());
}
return root;
}
private Node createHeaderNode() {
IIOMetadataNode header = new IIOMetadataNode("PSDHeader");
header.setAttribute("version", "1");
header.setAttribute("channels", Integer.toString(mHeader.mChannels));
header.setAttribute("height", Integer.toString(mHeader.mHeight));
header.setAttribute("width", Integer.toString(mHeader.mWidth));
header.setAttribute("bits", Integer.toString(mHeader.mBits));
header.setAttribute("mode", COLOR_MODES[mHeader.mMode]);
return header;
}
private Node createImageResourcesNode() {
IIOMetadataNode resource = new IIOMetadataNode("ImageResources");
IIOMetadataNode node;
for (PSDImageResource imageResource : mImageResources) {
// TODO: Always add name (if set) and id (as resourceId) to all nodes?
// Resource Id is useful for people with access to the PSD spec..
if (imageResource instanceof PSDAlphaChannelInfo) {
PSDAlphaChannelInfo alphaChannelInfo = (PSDAlphaChannelInfo) imageResource;
node = new IIOMetadataNode("AlphaChannelInfo");
for (String name : alphaChannelInfo.mNames) {
IIOMetadataNode nameNode = new IIOMetadataNode("Name");
nameNode.setAttribute("value", name);
node.appendChild(nameNode);
}
resource.appendChild(node);
}
else if (imageResource instanceof PSDDisplayInfo) {
PSDDisplayInfo displayInfo = (PSDDisplayInfo) imageResource;
node = new IIOMetadataNode("DisplayInfo");
node.setAttribute("colorSpace", DISPLAY_INFO_CS[displayInfo.mColorSpace]);
StringBuilder builder = new StringBuilder();
for (short color : displayInfo.mColors) {
if (builder.length() > 0) {
builder.append(" ");
}
builder.append(Integer.toString(color));
}
node.setAttribute("colors", builder.toString());
node.setAttribute("opacity", Integer.toString(displayInfo.mOpacity));
node.setAttribute("kind", DISPLAY_INFO_KINDS[displayInfo.mKind]);
resource.appendChild(node);
}
else if (imageResource instanceof PSDXMPData) {
// TODO: Revise/rethink this...
PSDXMPData xmp = (PSDXMPData) imageResource;
node = new IIOMetadataNode("XMPData");
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(xmp.getData()));
// Set the entire XMP document as user data
node.setUserObject(document);
}
catch (Exception e) {
e.printStackTrace();
}
resource.appendChild(node);
}
else {
// Generic resource..
node = new IIOMetadataNode(PSDImageResource.resourceTypeForId(imageResource.mId));
resource.appendChild(node);
}
// TODO: More resources
node.setAttribute("resourceId", Integer.toHexString(imageResource.mId));
}
return resource;
}
/// Standard format support
@Override
protected IIOMetadataNode getStandardChromaNode() {
IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("ColorSpaceType");
String cs;
switch (mHeader.mMode) {
case PSD.COLOR_MODE_MONOCHROME:
case PSD.COLOR_MODE_GRAYSCALE:
case PSD.COLOR_MODE_DUOTONE: // Rationale: Spec says treat as gray...
cs = "GRAY";
break;
case PSD.COLOR_MODE_RGB:
case PSD.COLOR_MODE_INDEXED:
cs = "RGB";
break;
case PSD.COLOR_MODE_CMYK:
cs = "CMYK";
break;
case PSD.COLOR_MODE_MULTICHANNEL:
cs = getMultiChannelCS(mHeader.mChannels);
break;
case PSD.COLOR_MODE_LAB:
cs = "Lab";
break;
default:
throw new AssertionError("Unreachable");
}
node.setAttribute("name", cs);
chroma_node.appendChild(node);
// TODO: Channels might be 5 for RGB + A + Mask... Probably not correct
node = new IIOMetadataNode("NumChannels");
node.setAttribute("value", Integer.toString(mHeader.mChannels));
chroma_node.appendChild(node);
// TODO: Check if this is correct with bitmap (monchrome)
node = new IIOMetadataNode("BlackIsZero");
node.setAttribute("value", "true");
chroma_node.appendChild(node);
if (mHeader.mMode == PSD.COLOR_MODE_INDEXED) {
node = createPaletteNode();
chroma_node.appendChild(node);
}
// TODO: Hardcode background color to white?
// if (bKGD_present) {
// if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
// node = new IIOMetadataNode("BackgroundIndex");
// node.setAttribute("value", Integer.toString(bKGD_index));
// } else {
// node = new IIOMetadataNode("BackgroundColor");
// int r, g, b;
//
// if (bKGD_colorType == PNGImageReader.PNG_COLOR_GRAY) {
// r = g = b = bKGD_gray;
// } else {
// r = bKGD_red;
// g = bKGD_green;
// b = bKGD_blue;
// }
// node.setAttribute("red", Integer.toString(r));
// node.setAttribute("green", Integer.toString(g));
// node.setAttribute("blue", Integer.toString(b));
// }
// chroma_node.appendChild(node);
// }
return chroma_node;
}
private IIOMetadataNode createPaletteNode() {
IIOMetadataNode node = new IIOMetadataNode("Palette");
IndexColorModel cm = mColorData.getIndexColorModel();
for (int i = 0; i < cm.getMapSize(); i++) {
IIOMetadataNode entry = new IIOMetadataNode("PaletteEntry");
entry.setAttribute("index", Integer.toString(i));
entry.setAttribute("red", Integer.toString(cm.getRed(i)));
entry.setAttribute("green", Integer.toString(cm.getGreen(i)));
entry.setAttribute("blue", Integer.toString(cm.getBlue(i)));
node.appendChild(entry);
}
return node;
}
private String getMultiChannelCS(short pChannels) {
if (pChannels < 16) {
return Integer.toHexString(pChannels) + "CLR";
}
throw new UnsupportedOperationException("Standard meta data format does not support more than 15 channels");
}
@Override
protected IIOMetadataNode getStandardCompressionNode() {
IIOMetadataNode compression_node = new IIOMetadataNode("Compression");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("CompressionTypeName");
String compression;
switch (mCompression) {
case PSD.COMPRESSION_NONE:
compression = "none";
break;
case PSD.COMPRESSION_RLE:
compression = "packbits";
break;
case PSD.COMPRESSION_ZIP:
case PSD.COMPRESSION_ZIP_PREDICTION:
compression = "zip";
break;
default:
throw new AssertionError("Unreachable");
}
node.setAttribute("value", compression);
compression_node.appendChild(node);
node = new IIOMetadataNode("Lossless");
node.setAttribute("value", "true");
compression_node.appendChild(node);
return compression_node;
}
@Override
protected IIOMetadataNode getStandardDataNode() {
IIOMetadataNode data_node = new IIOMetadataNode("Data");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("PlanarConfiguration");
node.setAttribute("value", "PlaneInterleaved"); // TODO: Check with spec
data_node.appendChild(node);
node = new IIOMetadataNode("SampleFormat");
node.setAttribute("value", mHeader.mMode == PSD.COLOR_MODE_INDEXED ? "Index" : "UnsignedIntegral");
data_node.appendChild(node);
String bitDepth = Integer.toString(mHeader.mBits); // bits per plane
// TODO: Channels might be 5 for RGB + A + Mask...
String[] bps = new String[mHeader.mChannels];
Arrays.fill(bps, bitDepth);
node = new IIOMetadataNode("BitsPerSample");
node.setAttribute("value", StringUtil.toCSVString(bps, " "));
data_node.appendChild(node);
// TODO: SampleMSB? Or is network (aka Motorola/big endian) byte order assumed?
return data_node;
}
@Override
protected IIOMetadataNode getStandardDimensionNode() {
IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("PixelAspectRatio");
// TODO: This is not incorrect wrt resolution info
float ratio = 1f;
node.setAttribute("value", Float.toString(ratio));
dimension_node.appendChild(node);
node = new IIOMetadataNode("ImageOrientation");
node.setAttribute("value", "Normal");
dimension_node.appendChild(node);
Iterator<PSDResolutionInfo> resolutionInfos = getResources(PSDResolutionInfo.class);
if (!resolutionInfos.hasNext()) {
PSDResolutionInfo resolutionInfo = resolutionInfos.next();
node = new IIOMetadataNode("HorizontalPixelSize");
node.setAttribute("value", Float.toString(asMM(resolutionInfo.mHResUnit, resolutionInfo.mHRes)));
dimension_node.appendChild(node);
node = new IIOMetadataNode("VerticalPixelSize");
node.setAttribute("value", Float.toString(asMM(resolutionInfo.mVResUnit, resolutionInfo.mVRes)));
dimension_node.appendChild(node);
}
// TODO:
/*
<!ELEMENT "HorizontalPixelOffset" EMPTY>
<!-- The horizonal position, in pixels, where the image should be
rendered onto a raster display -->
<!ATTLIST "HorizontalPixelOffset" "value" #CDATA #REQUIRED>
<!-- Data type: Integer -->
<!ELEMENT "VerticalPixelOffset" EMPTY>
<!-- The vertical position, in pixels, where the image should be
rendered onto a raster display -->
<!ATTLIST "VerticalPixelOffset" "value" #CDATA #REQUIRED>
<!-- Data type: Integer -->
<!ELEMENT "HorizontalScreenSize" EMPTY>
<!-- The width, in pixels, of the raster display into which the
image should be rendered -->
<!ATTLIST "HorizontalScreenSize" "value" #CDATA #REQUIRED>
<!-- Data type: Integer -->
<!ELEMENT "VerticalScreenSize" EMPTY>
<!-- The height, in pixels, of the raster display into which the
image should be rendered -->
<!ATTLIST "VerticalScreenSize" "value" #CDATA #REQUIRED>
<!-- Data type: Integer -->
*/
return dimension_node;
}
private static float asMM(final short pUnit, final float pResolution) {
// Unit: 1 -> pixels per inch, 2 -> pixels pr cm
return (pUnit == 1 ? 25.4f : 10) / pResolution;
}
@Override
protected IIOMetadataNode getStandardDocumentNode() {
IIOMetadataNode document_node = new IIOMetadataNode("Document");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("FormatVersion");
node.setAttribute("value", "1"); // PSD format version is always 1
document_node.appendChild(node);
// Get EXIF data if present
Iterator<PSDEXIF1Data> exif = getResources(PSDEXIF1Data.class);
if (exif.hasNext()) {
PSDEXIF1Data data = exif.next();
// Get the EXIF DateTime (aka ModifyDate) tag if present
PSDEXIF1Data.Entry dateTime = data.mDirectory.get(0x0132); // TODO: Constant
if (dateTime != null) {
node = new IIOMetadataNode("ImageModificationTime");
// Format: "YYYY:MM:DD hh:mm:ss" (with quotes! :-P)
String value = dateTime.getValueAsString();
node.setAttribute("year", value.substring(1, 5));
node.setAttribute("month", value.substring(6, 8));
node.setAttribute("day", value.substring(9, 11));
node.setAttribute("hour", value.substring(12, 14));
node.setAttribute("minute", value.substring(15, 17));
node.setAttribute("second", value.substring(18, 20));
document_node.appendChild(node);
}
}
return document_node;
}
@Override
protected IIOMetadataNode getStandardTextNode() {
// TODO: CaptionDigest?, EXIF, XMP
Iterator<PSDImageResource> textResources = getResources(PSDEXIF1Data.class, PSDXMPData.class);
while (textResources.hasNext()) {
PSDImageResource textResource = textResources.next();
}
// int numEntries = tEXt_keyword.size() +
// iTXt_keyword.size() + zTXt_keyword.size();
// if (numEntries == 0) {
// return null;
// }
//
// IIOMetadataNode text_node = new IIOMetadataNode("Text");
// IIOMetadataNode node = null; // scratch node
//
// for (int i = 0; i < tEXt_keyword.size(); i++) {
// node = new IIOMetadataNode("TextEntry");
// node.setAttribute("keyword", (String)tEXt_keyword.get(i));
// node.setAttribute("value", (String)tEXt_text.get(i));
// node.setAttribute("encoding", "ISO-8859-1");
// node.setAttribute("compression", "none");
//
// text_node.appendChild(node);
// }
//
// for (int i = 0; i < iTXt_keyword.size(); i++) {
// node = new IIOMetadataNode("TextEntry");
// node.setAttribute("keyword", iTXt_keyword.get(i));
// node.setAttribute("value", iTXt_text.get(i));
// node.setAttribute("language",
// iTXt_languageTag.get(i));
// if (iTXt_compressionFlag.get(i)) {
// node.setAttribute("compression", "deflate");
// } else {
// node.setAttribute("compression", "none");
// }
//
// text_node.appendChild(node);
// }
//
// for (int i = 0; i < zTXt_keyword.size(); i++) {
// node = new IIOMetadataNode("TextEntry");
// node.setAttribute("keyword", (String)zTXt_keyword.get(i));
// node.setAttribute("value", (String)zTXt_text.get(i));
// node.setAttribute("compression", "deflate");
//
// text_node.appendChild(node);
// }
//
// return text_node;
return null;
}
@Override
protected IIOMetadataNode getStandardTileNode() {
return super.getStandardTileNode();
}
@Override
protected IIOMetadataNode getStandardTransparencyNode() {
IIOMetadataNode transparency_node = new IIOMetadataNode("Transparency");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("Alpha");
node.setAttribute("value", hasAlpha() ? "nonpremultipled" : "none"); // TODO: Check spec
transparency_node.appendChild(node);
return transparency_node;
}
private boolean hasAlpha() {
return mHeader.mMode == PSD.COLOR_MODE_RGB && mHeader.mChannels >= 4 ||
mHeader.mMode == PSD.COLOR_MODE_CMYK & mHeader.mChannels >= 5;
}
<T extends PSDImageResource> Iterator<T> getResources(final Class<T> pResourceType) {
// NOTE: The cast here is wrong, strictly speaking, but it does not matter...
@SuppressWarnings({"unchecked"})
Iterator<T> iterator = (Iterator<T>) mImageResources.iterator();
return new FilterIterator<T>(iterator, new FilterIterator.Filter<T>() {
public boolean accept(final T pElement) {
return pResourceType.isInstance(pElement);
}
});
}
Iterator<PSDImageResource> getResources(final Class<? extends PSDImageResource>... pResourceTypes) {
Iterator<PSDImageResource> iterator = mImageResources.iterator();
return new FilterIterator<PSDImageResource>(iterator, new FilterIterator.Filter<PSDImageResource>() {
public boolean accept(final PSDImageResource pElement) {
for (Class<?> type : pResourceTypes) {
if (type.isInstance(pElement)) {
return true;
}
}
return false;
}
});
}
}

View File

@@ -0,0 +1,164 @@
package com.twelvemonkeys.imageio.plugins.psd;
import org.w3c.dom.Document;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import java.util.Arrays;
/**
* PSDMetadataFormat
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDMetadataFormat.java,v 1.0 Nov 4, 2009 5:27:53 PM haraldk Exp$
*/
public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
private final static PSDMetadataFormat sInstance = new PSDMetadataFormat();
/**
* Private constructor.
* <p/>
* The {@link javax.imageio.metadata.IIOMetadata} class will instantiate this class
* by reflection, invoking the static {@code getInstance()} method.
*
* @see javax.imageio.metadata.IIOMetadata#getMetadataFormat
* @see #getInstance()
*/
private PSDMetadataFormat() {
// Defines the root element
super(PSDMetadata.NATIVE_METADATA_FORMAT_NAME, CHILD_POLICY_SOME);
// root -> PSDHeader
// TODO: How do I specify that the header is required?
addElement("PSDHeader", PSDMetadata.NATIVE_METADATA_FORMAT_NAME, CHILD_POLICY_EMPTY);
// TODO: Do the first two make sense?
// addAttribute("PSDHeader", "signature", DATATYPE_STRING, false, "8BPS", Arrays.asList("8BPS"));
addAttribute("PSDHeader", "version", DATATYPE_INTEGER, false, "1", Arrays.asList("1"));
addAttribute("PSDHeader", "channels", DATATYPE_INTEGER, true, null, "1", "24", true, true);
// rows?
addAttribute("PSDHeader", "height", DATATYPE_INTEGER, true, null, "1", "30000", true, true);
// columns?
addAttribute("PSDHeader", "width", DATATYPE_INTEGER, true, null, "1", "30000", true, true);
addAttribute("PSDHeader", "bits", DATATYPE_INTEGER, true, null, Arrays.asList("1", "8", "16"));
// TODO: Consider using more readable names?!
addAttribute("PSDHeader", "mode", DATATYPE_INTEGER, true, null, Arrays.asList(PSDMetadata.COLOR_MODES));
/*
Contains the required data to define the color mode.
For indexed color images, the count will be equal to 768, and the mode data
will contain the color table for the image, in non-interleaved order.
For duotone images, the mode data will contain the duotone specification,
the format of which is not documented. Non-Photoshop readers can treat
the duotone image as a grayscale image, and keep the duotone specification
around as a black box for use when saving the file.
*/
// root -> Palette
// Color map for indexed, optional
// NOTE: Palette, PaletteEntry naming taken from the standard format, native PSD naming is ColorModeData
// NOTE: PSD stores these as 256 Red, 256 Green, 256 Blue.. Should we do the same in the meta data?
addElement("Palette", PSDMetadata.NATIVE_METADATA_FORMAT_NAME, 256, 256); // 768 = 256 * 3
addElement("PaletteEntry", "Palette", CHILD_POLICY_EMPTY);
addAttribute("PaletteEntry", "index", DATATYPE_INTEGER, true, null, "0", "255", true, true);
addAttribute("PaletteEntry", "red", DATATYPE_INTEGER, true, null, "0", "255", true, true);
addAttribute("PaletteEntry", "green", DATATYPE_INTEGER, true, null, "0", "255", true, true);
addAttribute("PaletteEntry", "blue", DATATYPE_INTEGER, true, null, "0", "255", true, true);
// No alpha allowed in indexed color PSD
// TODO: Duotone spec, optional (use same element as palette?)
// Or use object or raw bytes..
// root -> ImageResources
// Image resources, optional
addElement("ImageResources", PSDMetadata.NATIVE_METADATA_FORMAT_NAME, CHILD_POLICY_SEQUENCE); // SOME?
// root -> ImageResources -> ImageResource
// Generic resource
addElement("ImageResource", "ImageResources", CHILD_POLICY_ALL);
// TODO: Allow arbitrary values to be added as a generic resource...
// root -> ImageResources -> AlphaChannelInfo
addElement("AlphaChannelInfo", "ImageResources", 0, Integer.MAX_VALUE); // The format probably does not support that many layers..
addElement("Name", "AlphaChannelInfo", CHILD_POLICY_EMPTY);
addAttribute("Name", "value", DATATYPE_STRING, true, 0, Integer.MAX_VALUE);
// root -> ImageResources -> DisplayInfo
addElement("DisplayInfo", "ImageResources", CHILD_POLICY_EMPTY);
// TODO: Consider using human readable strings
// TODO: Limit values (0-8, 10, 11, 3000)
addAttribute("DisplayInfo", "colorSpace", DATATYPE_INTEGER, true, null);
addAttribute("DisplayInfo", "colors", DATATYPE_INTEGER, true, 4, 4);
addAttribute("DisplayInfo", "opacity", DATATYPE_INTEGER, true, null, "0", "100", true, true);
// TODO: Consider using human readable strings
addAttribute("DisplayInfo", "kind", DATATYPE_INTEGER, true, null, Arrays.asList(PSDMetadata.DISPLAY_INFO_KINDS));
// root -> ImageResources -> EXIF1Data
addElement("EXIF1Data", "ImageResources", CHILD_POLICY_ALL);
// TODO: Incorporate EXIF / TIFF metadata here somehow... (or treat as opaque bytes?)
// root -> ImageResources -> PrintFlags
addElement("PrintFlags", "ImageResources", CHILD_POLICY_EMPTY);
addBooleanAttribute("PrintFlags", "labels", false, false);
addBooleanAttribute("PrintFlags", "cropMasks", false, false);
addBooleanAttribute("PrintFlags", "colorBars", false, false);
addBooleanAttribute("PrintFlags", "registrationMarks", false, false);
addBooleanAttribute("PrintFlags", "negative", false, false);
addBooleanAttribute("PrintFlags", "flip", false, false);
addBooleanAttribute("PrintFlags", "interpolate", false, false);
addBooleanAttribute("PrintFlags", "caption", false, false);
// root -> ImageResources -> PrintFlagsInformation
addElement("PrintFlagsInformation", "ImageResources", CHILD_POLICY_EMPTY);
addAttribute("PrintFlagsInformation", "version", DATATYPE_INTEGER, true, null);
addBooleanAttribute("PrintFlagsInformation", "cropMarks", false, false);
addAttribute("PrintFlagsInformation", "field", DATATYPE_INTEGER, true, null);
addAttribute("PrintFlagsInformation", "bleedWidth", DATATYPE_INTEGER, true, null, "0", String.valueOf(Long.MAX_VALUE), true, true); // TODO: LONG??!
addAttribute("PrintFlagsInformation", "bleedScale", DATATYPE_INTEGER, true, null, "0", String.valueOf(Integer.MAX_VALUE), true, true);
// root -> ImageResources -> ResolutionInfo
addElement("ResolutionInfo", "ImageResources", CHILD_POLICY_EMPTY);
addAttribute("ResolutionInfo", "hRes", DATATYPE_FLOAT, true, null);
// TODO: Or use string and more friendly names? "pixels/inch"/"pixels/cm" and "inch"/"cm"/"pt"/"pica"/"column"
addAttribute("ResolutionInfo", "hResUnit", DATATYPE_INTEGER, true, null, Arrays.asList("1", "2"));
addAttribute("ResolutionInfo", "widthUnit", DATATYPE_INTEGER, true, null, Arrays.asList("1", "2", "3", "4", "5"));
addAttribute("ResolutionInfo", "vRes", DATATYPE_FLOAT, true, null);
// TODO: Or use more friendly names?
addAttribute("ResolutionInfo", "vResUnit", DATATYPE_INTEGER, true, null, Arrays.asList("1", "2"));
addAttribute("ResolutionInfo", "heightUnit", DATATYPE_INTEGER, true, null, Arrays.asList("1", "2", "3", "4", "5"));
// ??? addElement("Thumbnail", "ImageResources", CHILD_POLICY_CHOICE);
// root -> ImageResources -> XMPData
addElement("XMPData", "ImageResources", CHILD_POLICY_CHOICE);
// TODO: Incorporate XMP metadata here somehow (or treat as opaque bytes?)
addObjectValue("XMPData", Document.class, true, null);
// TODO: Layers
//addElement("ChannelSourceDestinationRange", "LayerSomething", CHILD_POLICY_CHOICE);
// TODO: Global layer mask info
}
@Override
public boolean canNodeAppear(final String pElementName, final ImageTypeSpecifier pImageType) {
// TODO: PSDColorData and PaletteEntry only for indexed color model
throw new UnsupportedOperationException("Method canNodeAppear not implemented"); // TODO: Implement
}
/**
* Returns the shared instance of the {@code PSDMetadataFormat}.
*
* @return the shared instance.
* @see javax.imageio.metadata.IIOMetadata#getMetadataFormat
*/
public static PSDMetadataFormat getInstance() {
return sInstance;
}
}

View File

@@ -0,0 +1,58 @@
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
/**
* PSDPrintFlagsInfo
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDPrintFlagsInfo.java,v 1.0 Jul 28, 2009 5:16:27 PM haraldk Exp$
*/
final class PSDPrintFlags extends PSDImageResource {
private boolean mLabels;
private boolean mCropMasks;
private boolean mColorBars;
private boolean mRegistrationMarks;
private boolean mNegative;
private boolean mFlip;
private boolean mInterpolate;
private boolean mCaption;
PSDPrintFlags(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
@Override
protected void readData(final ImageInputStream pInput) throws IOException {
mLabels = pInput.readBoolean();
mCropMasks = pInput.readBoolean();
mColorBars = pInput.readBoolean();
mRegistrationMarks = pInput.readBoolean();
mNegative = pInput.readBoolean();
mFlip = pInput.readBoolean();
mInterpolate = pInput.readBoolean();
mCaption = pInput.readBoolean();
pInput.skipBytes(mSize - 8);
}
@Override
public String toString() {
StringBuilder builder = toStringBuilder();
builder.append(", labels: ").append(mLabels);
builder.append(", crop masks: ").append(mCropMasks);
builder.append(", color bars: ").append(mColorBars);
builder.append(", registration marks: ").append(mRegistrationMarks);
builder.append(", negative: ").append(mNegative);
builder.append(", flip: ").append(mFlip);
builder.append(", interpolate: ").append(mInterpolate);
builder.append(", caption: ").append(mCaption);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,49 @@
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
/**
* PSDPrintFlagsInfo
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDPrintFlagsInfo.java,v 1.0 Jul 28, 2009 5:16:27 PM haraldk Exp$
*/
final class PSDPrintFlagsInformation extends PSDImageResource {
private int mVersion;
private boolean mCropMasks;
private int mField;
private long mBleedWidth;
private int mBleedScale;
PSDPrintFlagsInformation(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
@Override
protected void readData(final ImageInputStream pInput) throws IOException {
mVersion = pInput.readUnsignedShort();
mCropMasks = pInput.readBoolean();
mField = pInput.readUnsignedByte();
mBleedWidth = pInput.readUnsignedInt();
mBleedScale = pInput.readUnsignedShort();
pInput.skipBytes(mSize - 10);
}
@Override
public String toString() {
StringBuilder builder = toStringBuilder();
builder.append(", version: ").append(mVersion);
builder.append(", crop masks: ").append(mCropMasks);
builder.append(", field: ").append(mField);
builder.append(", bleed width: ").append(mBleedWidth);
builder.append(", bleed scale: ").append(mBleedScale);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.IIOException;
import java.io.IOException;
/**
* PSDResolutionInfo
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$
*/
class PSDResolutionInfo extends PSDImageResource {
// typedef struct _ResolutionInfo
// {
// LONG hRes; /* Fixed-point number: pixels per inch */
// WORD hResUnit; /* 1=pixels per inch, 2=pixels per centimeter */
// WORD WidthUnit; /* 1=in, 2=cm, 3=pt, 4=picas, 5=columns */
// LONG vRes; /* Fixed-point number: pixels per inch */
// WORD vResUnit; /* 1=pixels per inch, 2=pixels per centimeter */
// WORD HeightUnit; /* 1=in, 2=cm, 3=pt, 4=picas, 5=columns */
// } RESOLUTIONINFO;
float mHRes;
short mHResUnit;
short mWidthUnit;
float mVRes;
short mVResUnit;
short mHeightUnit;
PSDResolutionInfo(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
@Override
protected void readData(ImageInputStream pInput) throws IOException {
if (mSize != 16) {
throw new IIOException("Resolution info length expected to be 16: " + mSize);
}
mHRes = PSDUtil.fixedPointToFloat(pInput.readInt());
mHResUnit = pInput.readShort();
mWidthUnit = pInput.readShort();
mVRes = PSDUtil.fixedPointToFloat(pInput.readInt());
mVResUnit = pInput.readShort();
mHeightUnit = pInput.readShort();
}
@Override
public String toString() {
StringBuilder builder = toStringBuilder();
builder.append(", hRes: ").append(mHRes);
builder.append(" ");
builder.append(resUnit(mHResUnit));
builder.append(", width unit: ");
builder.append(dimUnit(mWidthUnit));
builder.append(", vRes: ").append(mVRes);
builder.append(" ");
builder.append(resUnit(mVResUnit));
builder.append(", height unit: ");
builder.append(dimUnit(mHeightUnit));
builder.append("]");
return builder.toString();
}
private String resUnit(final short pResUnit) {
switch (pResUnit) {
case 1:
return "pixels/inch";
case 2:
return "pixels/cm";
default:
return "unknown unit " + pResUnit;
}
}
private String dimUnit(final short pUnit) {
switch (pUnit) {
case 1:
return "in";
case 2:
return "cm";
case 3:
return "pt";
case 4:
return "pica";
case 5:
return "column";
default:
return "unknown unit " + pUnit;
}
}
}

View File

@@ -0,0 +1,100 @@
package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
import java.io.IOException;
/**
* PSDThumbnail
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDThumbnail.java,v 1.0 Jul 29, 2009 4:41:06 PM haraldk Exp$
*/
class PSDThumbnail extends PSDImageResource {
private BufferedImage mThumbnail;
private int mWidth;
private int mHeight;
public PSDThumbnail(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
/*
Thumbnail header, size 28
4 Format. 1 = kJpegRGB . Also supports kRawRGB (0).
4 Width of thumbnail in pixels.
4 Height of thumbnail in pixels.
4 Widthbytes: Padded row bytes = (width * bits per pixel + 31) / 32 * 4.
4 Total size = widthbytes * height * planes
4 Size after compression. Used for consistency check.
2 Bits per pixel. = 24
2 Number of planes. = 1
*/
@Override
protected void readData(final ImageInputStream pInput) throws IOException {
// TODO: Support for RAW RGB (format == 0): Extract RAW reader from PICT RAW QuickTime decompressor
int format = pInput.readInt();
switch (format) {
case 0:
// RAW RGB
throw new IIOException("RAW RGB format thumbnail not supported yet");
case 1:
// JPEG
break;
default:
throw new IIOException(String.format("Unsupported thumbnail format (%s) in PSD document", format));
}
mWidth = pInput.readInt();
mHeight = pInput.readInt();
// This data isn't really useful, unless we're dealing with raw bytes
int widthBytes = pInput.readInt();
int totalSize = pInput.readInt();
// Consistency check
int sizeCompressed = pInput.readInt();
if (sizeCompressed != (mSize - 28)) {
throw new IIOException("Corrupt thumbnail in PSD document");
}
// According to the spec, only 24 bits and 1 plane is supported
int bits = pInput.readUnsignedShort();
int planes = pInput.readUnsignedShort();
if (bits != 24 && planes != 1) {
// TODO: Warning/Exception
}
// TODO: Defer decoding until getThumbnail?
// TODO: Support BGR if id == RES_THUMBNAIL_PS4? Or is that already supported in the JPEG?
mThumbnail = ImageIO.read(IIOUtil.createStreamAdapter(pInput, sizeCompressed));
}
public final int getWidth() {
return mWidth;
}
public final int getHeight() {
return mHeight;
}
public final BufferedImage getThumbnail() {
return mThumbnail;
}
@Override
public String toString() {
StringBuilder builder = toStringBuilder();
builder.append(", ").append(mThumbnail);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.io.enc.PackBitsDecoder;
import com.twelvemonkeys.lang.StringUtil;
import javax.imageio.stream.ImageInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.zip.ZipInputStream;
/**
* PSDUtil
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDUtil.java,v 1.0 Apr 29, 2008 5:05:00 PM haraldk Exp$
*/
final class PSDUtil {
// TODO: Duplicated code from IFF plugin, move to some common util?
static String intToStr(int pChunkId) {
return new String(
new byte[]{
(byte) ((pChunkId & 0xff000000) >> 24),
(byte) ((pChunkId & 0x00ff0000) >> 16),
(byte) ((pChunkId & 0x0000ff00) >> 8),
(byte) ((pChunkId & 0x000000ff))
}
);
}
// TODO: Proably also useful for PICT reader, move to some common util?
// TODO: Is this REALLY different from the previous method? Maybe the pad should not be read..
static String readPascalString(final DataInput pInput) throws IOException {
int length = pInput.readUnsignedByte();
byte[] bytes = new byte[length];
pInput.readFully(bytes);
return StringUtil.decode(bytes, 0, bytes.length, "ASCII");
}
static String readUTF16String(final DataInput pInput) throws IOException {
int length = pInput.readInt();
byte[] bytes = new byte[length * 2];
pInput.readFully(bytes);
return StringUtil.decode(bytes, 0, bytes.length, "UTF-16");
}
static DataInputStream createPackBitsStream(final ImageInputStream pInput, long pLength) {
return new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pInput, pLength), new PackBitsDecoder()));
}
static DataInputStream createZipStream(final ImageInputStream pInput, long pLength) {
//return new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pInput, pLength), new InflateDecoder()));
return new DataInputStream(new ZipInputStream(IIOUtil.createStreamAdapter(pInput, pLength)));
}
static DataInputStream createZipPredictorStream(final ImageInputStream pInput, long pLength) {
throw new UnsupportedOperationException("Method createZipPredictonStream not implemented");
}
public static float fixedPointToFloat(int pFP) {
return ((pFP & 0xffff0000) >> 16) + (pFP & 0xffff) / (float) 0xffff;
}
}

View File

@@ -0,0 +1,57 @@
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
/**
* PSDVersionInfo
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDVersionInfo.java,v 1.0 Nov 6, 2009 1:02:19 PM haraldk Exp$
*/
final class PSDVersionInfo extends PSDImageResource {
int mVersion;
boolean mHasRealMergedData;
String mWriter;
String mReader;
int mFileVersion;
PSDVersionInfo(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
@Override
protected void readData(final ImageInputStream pInput) throws IOException {
/*
4 bytes version
1 byte hasRealMergedData
Unicode string: writer name
Unicode string: reader name
4 bytes file version.
*/
mVersion = pInput.readInt();
mHasRealMergedData = pInput.readBoolean();
mWriter = PSDUtil.readUTF16String(pInput);
mReader = PSDUtil.readUTF16String(pInput);
mFileVersion = pInput.readInt();
}
@Override
public String toString() {
StringBuilder builder = toStringBuilder();
builder.append(", version: ").append(mVersion);
builder.append(", hasRealMergedData: ").append(mHasRealMergedData);
builder.append(", writer: ").append(mWriter);
builder.append(", reader: ").append(mReader);
builder.append(", file version: ").append(mFileVersion);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,60 @@
package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.lang.StringUtil;
import javax.imageio.stream.ImageInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
/**
* XMP metadata.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: XMPData.java,v 1.0 Jul 28, 2009 5:50:34 PM haraldk Exp$
*
* @see <a href="http://www.adobe.com/products/xmp/">Adobe Extensible Metadata Platform (XMP)</a>
* @see <a href="http://www.adobe.com/devnet/xmp/">Adobe XMP Developer Center</a>
*/
final class PSDXMPData extends PSDImageResource {
protected byte[] mData;
PSDXMPData(final short pId, final ImageInputStream pInput) throws IOException {
super(pId, pInput);
}
@Override
protected void readData(final ImageInputStream pInput) throws IOException {
mData = new byte[(int) mSize]; // TODO: Fix potential overflow, or document why that can't happen (read spec)
pInput.readFully(mData);
}
@Override
public String toString() {
StringBuilder builder = toStringBuilder();
int length = Math.min(256, mData.length);
String data = StringUtil.decode(mData, 0, length, "UTF-8").replace('\n', ' ').replaceAll("\\s+", " ");
builder.append(", data: \"").append(data);
if (length < mData.length) {
builder.append("...");
}
builder.append("\"]");
return builder.toString();
}
/**
* Returns a character stream containing the XMP metadata (XML).
*
* @return the XMP metadata.
*/
public Reader getData() {
return new InputStreamReader(new ByteArrayInputStream(mData), Charset.forName("UTF-8"));
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.psd;
import java.awt.color.ColorSpace;
/**
* YCbCrColorSpace
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: YCbCrColorSpace.java,v 1.0 Jun 28, 2008 3:30:50 PM haraldk Exp$
*/
// TODO: Move to com.twlevemonkeys.image?
// TODO: Read an ICC YCbCr profile from classpath resource? Is there such a thing?
final class YCbCrColorSpace extends ColorSpace {
static final ColorSpace INSTANCE = new CMYKColorSpace();
final ColorSpace sRGB = getInstance(CS_sRGB);
YCbCrColorSpace() {
super(ColorSpace.TYPE_YCbCr, 3);
}
public static ColorSpace getInstance() {
return INSTANCE;
}
// http://www.w3.org/Graphics/JPEG/jfif.txt
/*
Conversion to and from RGB
Y, Cb, and Cr are converted from R, G, and B as defined in CCIR Recommendation 601
but are normalized so as to occupy the full 256 levels of a 8-bit binary encoding. More
precisely:
Y = 256 * E'y
Cb = 256 * [ E'Cb ] + 128
Cr = 256 * [ E'Cr ] + 128
where the E'y, E'Cb and E'Cb are defined as in CCIR 601. Since values of E'y have a
range of 0 to 1.0 and those for E'Cb and E'Cr have a range of -0.5 to +0.5, Y, Cb, and Cr
must be clamped to 255 when they are maximum value.
RGB to YCbCr Conversion
YCbCr (256 levels) can be computed directly from 8-bit RGB as follows:
Y = 0.299 R + 0.587 G + 0.114 B
Cb = - 0.1687 R - 0.3313 G + 0.5 B + 128
Cr = 0.5 R - 0.4187 G - 0.0813 B + 128
NOTE - Not all image file formats store image samples in the order R0, G0,
B0, ... Rn, Gn, Bn. Be sure to verify the sample order before converting an
RGB file to JFIF.
YCbCr to RGB Conversion
RGB can be computed directly from YCbCr (256 levels) as follows:
R = Y + 1.402 (Cr-128)
G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
B = Y + 1.772 (Cb-128)
*/
public float[] toRGB(float[] colorvalue) {
// R = Y + 1.402 (Cr-128)
// G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
// B = Y + 1.772 (Cb-128)
return new float[] {
colorvalue[0] + 1.402f * (colorvalue[2] - .5f),
colorvalue[0] - 0.34414f * (colorvalue[1] - .5f) - 0.71414f * (colorvalue[2] - .5f),
colorvalue[0] + 1.772f * (colorvalue[1] - .5f),
};
// TODO: Convert via CIEXYZ space using sRGB space, as suggested in docs
// return sRGB.fromCIEXYZ(toCIEXYZ(colorvalue));
}
public float[] fromRGB(float[] rgbvalue) {
// Y = 0.299 R + 0.587 G + 0.114 B
// Cb = - 0.1687 R - 0.3313 G + 0.5 B + 128
// Cr = 0.5 R - 0.4187 G - 0.0813 B + 128
return new float[] {
0.299f * rgbvalue[0] + 0.587f * rgbvalue[1] + 0.114f * rgbvalue[2],
-0.1687f * rgbvalue[0] - 0.3313f * rgbvalue[1] + 0.5f * rgbvalue[2] + .5f,
0.5f * rgbvalue[0] - 0.4187f * rgbvalue[1] - 0.0813f * rgbvalue[2] + .5f
};
}
public float[] toCIEXYZ(float[] colorvalue) {
throw new UnsupportedOperationException("Method toCIEXYZ not implemented"); // TODO: Implement
}
public float[] fromCIEXYZ(float[] colorvalue) {
throw new UnsupportedOperationException("Method fromCIEXYZ not implemented"); // TODO: Implement
}
}

View File

@@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.psd.PSDImageReaderSpi

View File

@@ -0,0 +1,215 @@
package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.ImageReader;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.io.IOException;
/**
* PSDImageReaderTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/
public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImageReader> {
static ImageReaderSpi sProvider = new PSDImageReaderSpi();
protected List<TestData> getTestData() {
return Arrays.asList(
// 5 channel, RGB
new TestData(getClassLoaderResource("/psd/photoshopping.psd"), new Dimension(300, 225)),
// 1 channel, gray, 8 bit samples
new TestData(getClassLoaderResource("/psd/buttons.psd"), new Dimension(20, 20)),
// 5 channel, CMYK
new TestData(getClassLoaderResource("/psd/escenic-liquid-logo.psd"), new Dimension(595, 420)),
// 3 channel RGB, "no composite layer"
new TestData(getClassLoaderResource("/psd/jugware-icon.psd"), new Dimension(128, 128)),
// 3 channel RGB, old data, no layer info/mask
new TestData(getClassLoaderResource("/psd/MARBLES.PSD"), new Dimension(1419, 1001)),
// 1 channel, indexed color
new TestData(getClassLoaderResource("/psd/coral_fish.psd"), new Dimension(800, 800)),
// 1 channel, bitmap, 1 bit samples
new TestData(getClassLoaderResource("/psd/test_bitmap.psd"), new Dimension(710, 512)),
// 1 channel, gray, 16 bit samples
new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)),
// 4 channel, CMYK, 16 bit samples
new TestData(getClassLoaderResource("/psd/cmyk_16bits.psd"), new Dimension(1000, 275))
// TODO: Need uncompressed PSD
// TODO: Need more recent ZIP compressed PSD files from CS2/CS3+
);
}
protected ImageReaderSpi createProvider() {
return sProvider;
}
@Override
protected PSDImageReader createReader() {
return new PSDImageReader(sProvider);
}
protected Class<PSDImageReader> getReaderClass() {
return PSDImageReader.class;
}
protected List<String> getFormatNames() {
return Arrays.asList("psd");
}
protected List<String> getSuffixes() {
return Arrays.asList("psd");
}
protected List<String> getMIMETypes() {
return Arrays.asList("image/x-psd");
}
public void testSupportsThumbnail() {
PSDImageReader imageReader = createReader();
assertTrue(imageReader.readerSupportsThumbnails());
}
public void testThumbnailReading() throws IOException {
PSDImageReader imageReader = createReader();
imageReader.setInput(getTestData().get(0).getInputStream());
assertEquals(1, imageReader.getNumThumbnails(0));
BufferedImage thumbnail = imageReader.readThumbnail(0, 0);
assertNotNull(thumbnail);
assertEquals(128, thumbnail.getWidth());
assertEquals(96, thumbnail.getHeight());
}
public void testThumbnailReadingNoInput() throws IOException {
PSDImageReader imageReader = createReader();
try {
imageReader.getNumThumbnails(0);
fail("Expected IllegalStateException");
}
catch (IllegalStateException expected) {
assertTrue(expected.getMessage().toLowerCase().contains("input"));
}
try {
imageReader.getThumbnailWidth(0, 0);
fail("Expected IllegalStateException");
}
catch (IllegalStateException expected) {
assertTrue(expected.getMessage().toLowerCase().contains("input"));
}
try {
imageReader.getThumbnailHeight(0, 0);
fail("Expected IllegalStateException");
}
catch (IllegalStateException expected) {
assertTrue(expected.getMessage().toLowerCase().contains("input"));
}
try {
imageReader.readThumbnail(0, 0);
fail("Expected IllegalStateException");
}
catch (IllegalStateException expected) {
assertTrue(expected.getMessage().toLowerCase().contains("input"));
}
}
public void testThumbnailReadingOutOfBounds() throws IOException {
PSDImageReader imageReader = createReader();
imageReader.setInput(getTestData().get(0).getInputStream());
try {
imageReader.getNumThumbnails(2);
fail("Expected IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException expected) {
assertTrue(expected.getMessage(), expected.getMessage().toLowerCase().contains("index"));
}
try {
imageReader.getThumbnailWidth(-1, 0);
fail("Expected IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException expected) {
assertTrue(expected.getMessage(), expected.getMessage().toLowerCase().contains("index"));
}
try {
imageReader.getThumbnailHeight(0, -2);
fail("Expected IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException expected) {
// Sloppy...
assertTrue(expected.getMessage(), expected.getMessage().toLowerCase().contains("-2"));
}
try {
imageReader.readThumbnail(99, 42);
fail("Expected IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException expected) {
assertTrue(expected.getMessage(), expected.getMessage().toLowerCase().contains("index"));
}
}
public void testThumbnailDimensions() throws IOException {
PSDImageReader imageReader = createReader();
imageReader.setInput(getTestData().get(0).getInputStream());
assertEquals(1, imageReader.getNumThumbnails(0));
assertEquals(128, imageReader.getThumbnailWidth(0, 0));
assertEquals(96, imageReader.getThumbnailHeight(0, 0));
}
public void testThumbnailReadListeners() throws IOException {
PSDImageReader imageReader = createReader();
imageReader.setInput(getTestData().get(0).getInputStream());
final List<Object> sequnce = new ArrayList<Object>();
imageReader.addIIOReadProgressListener(new ProgressListenerBase() {
private float mLastPercentageDone = 0;
@Override
public void thumbnailStarted(final ImageReader pSource, final int pImageIndex, final int pThumbnailIndex) {
sequnce.add("started");
}
@Override
public void thumbnailComplete(final ImageReader pSource) {
sequnce.add("complete");
}
@Override
public void thumbnailProgress(final ImageReader pSource, final float pPercentageDone) {
// Optional
assertTrue("Listener invoked out of sequence", sequnce.size() == 1);
assertTrue(pPercentageDone >= mLastPercentageDone);
}
});
BufferedImage thumbnail = imageReader.readThumbnail(0, 0);
assertNotNull(thumbnail);
assertEquals("Listeners not invoked", 2, sequnce.size());
assertEquals("started", sequnce.get(0));
assertEquals("complete", sequnce.get(1));
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

5
imageio/imageio-psd/todo.txt Executable file
View File

@@ -0,0 +1,5 @@
Implement source subsampling and region of interest
Separate package for the resources (seems to be a lot)?
Possibility to read only some resources? readResources(int[] resourceKeys)?
- Probably faster when we only need the color space
PSDImageWriter