mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-27 00:00:02 -04:00
Adding the twelvemonkeys-imageio sub-project
This commit is contained in:
Executable
+112
@@ -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.twlevemonkeys.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
|
||||
}
|
||||
}
|
||||
Executable
+78
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+544
@@ -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_PREDICTON = 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;
|
||||
|
||||
}
|
||||
+67
@@ -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(ImageInputStream pInput) throws IOException {
|
||||
mNames = new ArrayList<String>();
|
||||
long left = mSize;
|
||||
while (left > 0) {
|
||||
String name = PSDUtil.readPascalStringByte(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();
|
||||
}
|
||||
}
|
||||
Executable
+61
@@ -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 {
|
||||
private short mChannelId;
|
||||
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();
|
||||
}
|
||||
}
|
||||
+69
@@ -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();
|
||||
}
|
||||
}
|
||||
Executable
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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(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 int[] toInterleavedRGB(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;
|
||||
}
|
||||
}
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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...
|
||||
//typedef _DisplayInfo
|
||||
//{
|
||||
// WORD ColorSpace;
|
||||
// WORD Color[4];
|
||||
// WORD Opacity; /* 0-100 */
|
||||
// BYTE Kind; /* 0=selected, 1=protected */
|
||||
// BYTE Padding; /* Always zero */
|
||||
//} DISPLAYINFO;
|
||||
|
||||
private int mColorSpace;
|
||||
private short[] mColors;
|
||||
private short mOpacity;
|
||||
private 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+327
@@ -0,0 +1,327 @@
|
||||
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.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>
|
||||
*/
|
||||
public 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
|
||||
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)
|
||||
private static class Directory {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Directory%s", mEntries);
|
||||
}
|
||||
}
|
||||
|
||||
// TIFF IFD Entry
|
||||
private 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+79
@@ -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 {
|
||||
private int mColorSpace;
|
||||
private int mColor1;
|
||||
private int mColor2;
|
||||
private int mColor3;
|
||||
private int mColor4;
|
||||
private int mOpacity;
|
||||
private int mKind;
|
||||
|
||||
PSDGlobalLayerMask(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();
|
||||
}
|
||||
}
|
||||
Executable
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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(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();
|
||||
if (version != 1) {
|
||||
if (version == 2) {
|
||||
throw new IIOException("Large Document Format (PSB) not supported yet.");
|
||||
}
|
||||
throw new IIOException("Unknown PSD version, expected 1 or 2: 0x" + Integer.toHexString(version));
|
||||
}
|
||||
|
||||
byte[] reserved = new byte[6];
|
||||
pInput.readFully(reserved);
|
||||
|
||||
mChannels = pInput.readShort();
|
||||
mHeight = pInput.readInt(); // Rows
|
||||
mWidth = pInput.readInt(); // Coloumns
|
||||
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();
|
||||
}
|
||||
}
|
||||
Executable
+575
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
* 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.ImageUtil;
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ImageReader for Adobe Photoshop Document format.
|
||||
*
|
||||
* @see <a href="http://www.fileformat.info/format/psd/egff.htm">Adobe Photoshop File Format Summary<a>
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDImageReader.java,v 1.0 Apr 29, 2008 4:45:52 PM haraldk Exp$
|
||||
*/
|
||||
// TODO: Implement AOI and subsampling
|
||||
// TODO: Implement meta data reading
|
||||
// TODO: Implement layer reading
|
||||
// TODO: Allow reading separate (or some?) layers
|
||||
// TODO: Consider Romain Guy's Java 2D implementation of PS filters for the blending modes in layers
|
||||
// http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/
|
||||
// See http://www.codeproject.com/KB/graphics/PSDParser.aspx
|
||||
// See http://www.adobeforums.com/webx?14@@.3bc381dc/0
|
||||
public class PSDImageReader extends ImageReaderBase {
|
||||
private PSDHeader mHeader;
|
||||
private PSDColorData mColorData;
|
||||
private List<PSDImageResource> mImageResources;
|
||||
private PSDGlobalLayerMask mGlobalLayerMask;
|
||||
private List<PSDLayerInfo> mLayerInfo;
|
||||
private ICC_ColorSpace mColorSpace;
|
||||
|
||||
protected PSDImageReader(final ImageReaderSpi pOriginatingProvider) {
|
||||
super(pOriginatingProvider);
|
||||
}
|
||||
|
||||
protected void resetMembers() {
|
||||
mHeader = null;
|
||||
mColorData = null;
|
||||
mImageResources = null;
|
||||
mColorSpace = null;
|
||||
}
|
||||
|
||||
public int getWidth(int pIndex) throws IOException {
|
||||
checkBounds(pIndex);
|
||||
readHeader();
|
||||
return mHeader.mWidth;
|
||||
}
|
||||
|
||||
public int getHeight(int pIndex) throws IOException {
|
||||
checkBounds(pIndex);
|
||||
readHeader();
|
||||
return mHeader.mHeight;
|
||||
}
|
||||
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int pIndex) throws IOException {
|
||||
checkBounds(pIndex);
|
||||
readHeader();
|
||||
|
||||
ColorSpace cs;
|
||||
List<ImageTypeSpecifier> types = new ArrayList<ImageTypeSpecifier>();
|
||||
|
||||
switch (mHeader.mMode) {
|
||||
case PSD.COLOR_MODE_INDEXED:
|
||||
if (mHeader.mChannels == 1 && mHeader.mBits == 8) {
|
||||
types.add(IndexedImageTypeSpecifier.createFromIndexColorModel(mColorData.getIndexColorModel()));
|
||||
}
|
||||
else {
|
||||
throw new IIOException("Unsupported channel count/bit depth for Indexed Color PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits");
|
||||
}
|
||||
break;
|
||||
case PSD.COLOR_MODE_DUOTONE:
|
||||
// NOTE: Duotone (whatever that is) should be treated as grayscale, so fall-through
|
||||
case PSD.COLOR_MODE_GRAYSCALE:
|
||||
if (mHeader.mChannels == 1 && mHeader.mBits == 8) {
|
||||
types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY));
|
||||
}
|
||||
else if (mHeader.mChannels == 1 && mHeader.mBits == 16) {
|
||||
types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY));
|
||||
}
|
||||
else {
|
||||
throw new IIOException("Unsupported channel count/bit depth for Gray Scale PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits");
|
||||
}
|
||||
break;
|
||||
case PSD.COLOR_MODE_RGB:
|
||||
cs = getEmbeddedColorSpace();
|
||||
if (cs == null) {
|
||||
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
||||
}
|
||||
|
||||
if (mHeader.mChannels == 3 && mHeader.mBits == 8) {
|
||||
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
|
||||
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {2, 1, 0}, DataBuffer.TYPE_BYTE, false, false));
|
||||
}
|
||||
else if (mHeader.mChannels >= 4 && mHeader.mBits == 8) {
|
||||
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
|
||||
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {3, 2, 1, 0}, DataBuffer.TYPE_BYTE, true, false));
|
||||
}
|
||||
else {
|
||||
throw new IIOException("Unsupported channel count/bit depth for RGB PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits");
|
||||
}
|
||||
break;
|
||||
case PSD.COLOR_MODE_CMYK:
|
||||
// TODO: We should convert these to their RGB equivalents while reading for the common-case,
|
||||
// as Java2D is extremely slow displaying custom images.
|
||||
// Converting to RGB is also correct behaviour, according to the docs.
|
||||
// The code below is, however, correct for raw type.
|
||||
cs = getEmbeddedColorSpace();
|
||||
if (cs == null) {
|
||||
cs = CMYKColorSpace.getInstance();
|
||||
}
|
||||
|
||||
if (mHeader.mChannels == 4 && mHeader.mBits == 8) {
|
||||
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
|
||||
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[]{3, 2, 1, 0}, DataBuffer.TYPE_BYTE, false, false));
|
||||
}
|
||||
else if (mHeader.mChannels == 5 && mHeader.mBits == 8) {
|
||||
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
|
||||
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[]{4, 3, 2, 1, 0}, DataBuffer.TYPE_BYTE, true, false));
|
||||
}
|
||||
else {
|
||||
throw new IIOException("Unsupported channel count/bit depth for CMYK PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IIOException("Unsupported PSD MODE: " + mHeader.mMode);
|
||||
}
|
||||
|
||||
return types.iterator();
|
||||
}
|
||||
|
||||
private ColorSpace getEmbeddedColorSpace() throws IOException {
|
||||
readImageResources(true);
|
||||
// TODO: Skip this, requires storing some stream offsets
|
||||
readLayerAndMaskInfo(false);
|
||||
|
||||
if (mColorSpace == null) {
|
||||
ICC_Profile profile = null;
|
||||
for (PSDImageResource resource : mImageResources) {
|
||||
if (resource instanceof ICCProfile) {
|
||||
profile = ((ICCProfile) resource).getProfile();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mColorSpace = profile == null ? null : new ICC_ColorSpace(profile);
|
||||
}
|
||||
|
||||
return mColorSpace;
|
||||
}
|
||||
|
||||
// TODO: Implement param handling
|
||||
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
|
||||
checkBounds(pIndex);
|
||||
|
||||
readHeader();
|
||||
|
||||
processImageStarted(pIndex);
|
||||
|
||||
readImageResources(false);
|
||||
readLayerAndMaskInfo(false);
|
||||
|
||||
BufferedImage image = getDestination(pParam, getImageTypes(pIndex), mHeader.mWidth, mHeader.mHeight);
|
||||
// TODO: Should do color convert op for CMYK -> RGB
|
||||
ColorModel cm = image.getColorModel();
|
||||
final boolean isCMYK = cm.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
|
||||
final int numColorComponents = cm.getColorSpace().getNumComponents();
|
||||
|
||||
WritableRaster raster = image.getRaster();
|
||||
if (!(raster.getDataBuffer() instanceof DataBufferByte)) {
|
||||
throw new IIOException("Unsupported raster type: " + raster);
|
||||
}
|
||||
byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
|
||||
|
||||
// TODO: Maybe a banded raster would be easier than interleaved?
|
||||
final int channels = raster.getNumBands();
|
||||
|
||||
// System.out.println("channels: " + channels);
|
||||
// System.out.println("numColorComponents: " + numColorComponents);
|
||||
// System.out.println("isCMYK: " + isCMYK);
|
||||
|
||||
short compression = mImageInput.readShort();
|
||||
|
||||
// TODO: Bitmap (depth = 1) and 16 bit (depth = 16) must be read differently, obviously...
|
||||
// This code works fine for images with channel depth = 8
|
||||
switch (compression) {
|
||||
case PSD.COMPRESSION_NONE:
|
||||
// TODO: This entire reading block is duplicated and should be replaced with the one for RLE!
|
||||
// System.out.println("Uncompressed");
|
||||
for (int c = 0; c < mHeader.mChannels; c++) {
|
||||
for (int y = 0; y < mHeader.mHeight; y++) {
|
||||
for (int x = 0; x < mHeader.mWidth; x++) {
|
||||
int offset = (x + y * mHeader.mWidth) * channels;
|
||||
|
||||
byte value = mImageInput.readByte();
|
||||
|
||||
// CMYK values are stored inverted, but alpha is not
|
||||
if (isCMYK && c < numColorComponents) {
|
||||
value = (byte) (255 - value & 0xff);
|
||||
}
|
||||
|
||||
// System.out.println("b: " + Integer.toHexString(b & 0xff));
|
||||
data[offset + (channels - 1 - c)] = value;
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
break;
|
||||
}
|
||||
processImageProgress((c * y * 100) / mHeader.mChannels * mHeader.mHeight);
|
||||
}
|
||||
if (abortRequested()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PSD.COMPRESSION_RLE:
|
||||
// System.out.println("PackBits compressed");
|
||||
// NOTE: Offsets will allow us to easily skip rows before AOI
|
||||
int[] offsets = new int[mHeader.mChannels * mHeader.mHeight];
|
||||
for (int i = 0; i < offsets.length; i++) {
|
||||
offsets[i] = mImageInput.readUnsignedShort();
|
||||
}
|
||||
|
||||
int x = 0, y = 0, c = 0;
|
||||
try {
|
||||
for (c = 0; c < channels; c++) {
|
||||
for (y = 0; y < mHeader.mHeight; y++) {
|
||||
int length = offsets[c * mHeader.mHeight + y];
|
||||
// System.out.println("channel: " + c + " line: " + y + " length: " + length);
|
||||
DataInputStream input = PSDUtil.createPackBitsStream(mImageInput, length);
|
||||
for (x = 0; x < mHeader.mWidth; x++) {
|
||||
int offset = (x + y * mHeader.mWidth) * channels;
|
||||
|
||||
byte value = input.readByte();
|
||||
|
||||
// if (c < numColorComponents) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// CMYK values are stored inverted, but alpha is not
|
||||
if (isCMYK && c < numColorComponents) {
|
||||
value = (byte) (255 - value & 0xff);
|
||||
}
|
||||
|
||||
// System.out.println("b: " + Integer.toHexString(b & 0xff));
|
||||
data[offset + (channels - 1 - c)] = value;
|
||||
}
|
||||
input.close();
|
||||
|
||||
if (abortRequested()) {
|
||||
break;
|
||||
}
|
||||
processImageProgress((c * y * 100) / mHeader.mChannels * mHeader.mHeight);
|
||||
}
|
||||
if (abortRequested()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println("c: " + c);
|
||||
System.err.println("y: " + y);
|
||||
System.err.println("x: " + x);
|
||||
throw e;
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("data.length: " + data.length);
|
||||
System.err.println("c: " + c);
|
||||
System.err.println("y: " + y);
|
||||
System.err.println("x: " + x);
|
||||
throw e;
|
||||
}
|
||||
break;
|
||||
case PSD.COMPRESSION_ZIP:
|
||||
// TODO: Could probably use the ZIPDecoder (DeflateDecoder) here..
|
||||
case PSD.COMPRESSION_ZIP_PREDICTON:
|
||||
// TODO: Need to find out if the normal java.util.zip can handle this...
|
||||
// Could be same as PNG prediction? Read up...
|
||||
throw new IIOException("ZIP compression not supported yet");
|
||||
default:
|
||||
throw new IIOException("Unknown compression type: " + compression);
|
||||
}
|
||||
|
||||
// Compose out the background of the semi-transparent pixels, as PS somehow has the background composed in
|
||||
decomposeAlpha(image);
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
}
|
||||
else {
|
||||
processImageComplete();
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private void decomposeAlpha(final BufferedImage pImage) throws IOException {
|
||||
ColorModel cm = pImage.getColorModel();
|
||||
|
||||
// TODO: What about CMYK + alpha?
|
||||
if (cm.hasAlpha() && cm.getColorSpace().getType() == ColorSpace.TYPE_RGB) {
|
||||
WritableRaster raster = pImage.getRaster();
|
||||
|
||||
// TODO: Probably faster to do this inline..
|
||||
// TODO: This is not so good, as it might break acceleration...
|
||||
byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
|
||||
|
||||
final int w = pImage.getWidth();
|
||||
final int channels = raster.getNumBands();
|
||||
for (int y = 0; y < pImage.getHeight(); y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
int offset = (x + y * w) * channels;
|
||||
|
||||
// TODO: Is the document background always white!?
|
||||
// ABGR format
|
||||
int alpha = data[offset] & 0xff;
|
||||
if (alpha != 0) {
|
||||
double normalizedAlpha = alpha / 255.0;
|
||||
for (int i = 1; i < channels; i++) {
|
||||
data[offset + i] = decompose(data[offset + i] & 0xff, normalizedAlpha);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 1; i < channels; i++) {
|
||||
data[offset + i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// System.out.println("PSDImageReader.coerceData: " + cm.getClass());
|
||||
// System.out.println("other.equals(cm): " + (other == cm));
|
||||
}
|
||||
|
||||
private static byte decompose(final int pColor, final double pAlpha) {
|
||||
// Adapted from Computer Graphics: Principles and Practice (Foley et al.), p. 837
|
||||
double color = pColor / 255.0;
|
||||
return (byte) ((color / pAlpha - ((1 - pAlpha) / pAlpha)) * 255);
|
||||
}
|
||||
|
||||
private void readHeader() throws IOException {
|
||||
assertInput();
|
||||
if (mHeader == null) {
|
||||
mHeader = new PSDHeader(mImageInput);
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
if (mHeader.mMode == PSD.COLOR_MODE_INDEXED) {
|
||||
mColorData = new PSDColorData(mImageInput);
|
||||
}
|
||||
else {
|
||||
// Skip color mode data for other modes
|
||||
long length = mImageInput.readUnsignedInt();
|
||||
mImageInput.skipBytes(length);
|
||||
}
|
||||
|
||||
// Don't need the header again
|
||||
mImageInput.flushBefore(mImageInput.getStreamPosition());
|
||||
}
|
||||
}
|
||||
|
||||
private void readImageResources(boolean pParseData) throws IOException {
|
||||
// TODO: Avoid unnecessary stream repositioning
|
||||
long pos = mImageInput.getFlushedPosition();
|
||||
mImageInput.seek(pos);
|
||||
|
||||
long length = mImageInput.readUnsignedInt();
|
||||
|
||||
if (pParseData && length > 0) {
|
||||
if (mImageResources == null) {
|
||||
mImageResources = new ArrayList<PSDImageResource>();
|
||||
long expectedEnd = mImageInput.getStreamPosition() + length;
|
||||
while (mImageInput.getStreamPosition() < expectedEnd) {
|
||||
PSDImageResource resource = PSDImageResource.read(mImageInput);
|
||||
mImageResources.add(resource);
|
||||
}
|
||||
|
||||
if (mImageInput.getStreamPosition() != expectedEnd) {
|
||||
throw new IIOException("Corrupt PSD document");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mImageInput.seek(pos + length + 4);
|
||||
}
|
||||
|
||||
private void readLayerAndMaskInfo(boolean pParseData) throws IOException {
|
||||
// TODO: Make sure we are positioned correctly
|
||||
long length = mImageInput.readUnsignedInt();
|
||||
if (pParseData && length > 0) {
|
||||
long pos = mImageInput.getStreamPosition();
|
||||
|
||||
long layerInfoLength = mImageInput.readUnsignedInt();
|
||||
|
||||
/*
|
||||
"Layer count. If it is a negative number, its absolute value is the number of
|
||||
layers and the first alpha channel contains the transparency data for the
|
||||
merged result."
|
||||
*/
|
||||
// TODO: Figure out what the last part of that sentence means in practice...
|
||||
int layers = mImageInput.readShort();
|
||||
// System.out.println("layers: " + layers);
|
||||
|
||||
PSDLayerInfo[] layerInfo = new PSDLayerInfo[Math.abs(layers)];
|
||||
for (int i = 0; i < layerInfo.length; i++) {
|
||||
layerInfo[i] = new PSDLayerInfo(mImageInput);
|
||||
// System.out.println("layerInfo[" + i + "]: " + layerInfo[i]);
|
||||
}
|
||||
mLayerInfo = Arrays.asList(layerInfo);
|
||||
|
||||
for (PSDLayerInfo info : layerInfo) {
|
||||
for (PSDChannelInfo channelInfo : info.mChannelInfo) {
|
||||
int compression = mImageInput.readUnsignedShort();
|
||||
// 0: None, 1: PackBits RLE, 2: Zip, 3: Zip w/prediction
|
||||
switch (compression) {
|
||||
case PSD.COMPRESSION_NONE:
|
||||
// System.out.println("Compression: None");
|
||||
break;
|
||||
case PSD.COMPRESSION_RLE:
|
||||
// System.out.println("Compression: PackBits RLE");
|
||||
break;
|
||||
case PSD.COMPRESSION_ZIP:
|
||||
// System.out.println("Compression: ZIP");
|
||||
break;
|
||||
case PSD.COMPRESSION_ZIP_PREDICTON:
|
||||
// System.out.println("Compression: ZIP with prediction");
|
||||
break;
|
||||
default:
|
||||
// TODO: Do we care, as we can just skip the data?
|
||||
// We could issue a warning to the warning listener
|
||||
throw new IIOException(String.format(
|
||||
"Unknown PSD compression: %d. Expected 0 (none), 1 (RLE), 2 (ZIP) or 3 (ZIP w/prediction).",
|
||||
compression
|
||||
));
|
||||
}
|
||||
|
||||
// TODO: If RLE, the the image data starts with the byte counts
|
||||
// for all the scan lines in the channel (LayerBottom*LayerTop), with
|
||||
// each count stored as a two*byte value.
|
||||
// if (compression == 1) {
|
||||
// mImageInput.skipBytes(channelInfo.mLength);
|
||||
// }
|
||||
|
||||
// TODO: Read channel image data (same format as composite image channel data)
|
||||
mImageInput.skipBytes(channelInfo.mLength - 2);
|
||||
// if (channelInfo.mLength % 2 != 0) {
|
||||
// mImageInput.readByte();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: We seem to have some alignment issues here...
|
||||
// I'm always reading two bytes off..
|
||||
|
||||
long read = mImageInput.getStreamPosition() - pos;
|
||||
// System.out.println("layerInfoLength: " + layerInfoLength);
|
||||
// System.out.println("layer info read: " + (read - 4)); // - 4 for the layerInfoLength field itself
|
||||
long diff = layerInfoLength - (read - 4);
|
||||
// System.out.println("diff: " + diff);
|
||||
mImageInput.skipBytes(diff);
|
||||
|
||||
// TODO: Global LayerMaskInfo (18 bytes or more..?)
|
||||
// 4 (length), 2 (colorSpace), 8 (4 * 2 byte color components), 2 (opacity %), 1 (kind), variable (pad)
|
||||
long layerMaskInfoLength = mImageInput.readUnsignedInt();
|
||||
// System.out.println("GlobalLayerMaskInfo length: " + layerMaskInfoLength);
|
||||
if (layerMaskInfoLength > 0) {
|
||||
mGlobalLayerMask = new PSDGlobalLayerMask(mImageInput);
|
||||
}
|
||||
|
||||
read = mImageInput.getStreamPosition() - pos;
|
||||
|
||||
long toSkip = length - read;
|
||||
// System.out.println("toSkip: " + toSkip);
|
||||
mImageInput.skipBytes(toSkip);
|
||||
}
|
||||
else {
|
||||
mImageInput.skipBytes(length);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] pArgs) throws IOException {
|
||||
PSDImageReader imageReader = new PSDImageReader(null);
|
||||
|
||||
File file = new File(pArgs[0]);
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(file);
|
||||
imageReader.setInput(stream);
|
||||
imageReader.readHeader();
|
||||
System.out.println("imageReader.mHeader: " + imageReader.mHeader);
|
||||
|
||||
imageReader.readImageResources(true);
|
||||
System.out.println("imageReader.mImageResources: " + imageReader.mImageResources);
|
||||
|
||||
imageReader.readLayerAndMaskInfo(true);
|
||||
System.out.println("imageReader.mLayerInfo: " + imageReader.mLayerInfo);
|
||||
System.out.println("imageReader.mGlobalLayerMask: " + imageReader.mGlobalLayerMask);
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
ImageReadParam param = new ImageReadParam();
|
||||
// param.setSourceRegion(new Rectangle(100, 100, 300, 200));
|
||||
BufferedImage image = imageReader.read(0, param);
|
||||
System.out.println("time: " + (System.currentTimeMillis() - start));
|
||||
System.out.println("image: " + image);
|
||||
|
||||
if (image.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
|
||||
try {
|
||||
ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null);
|
||||
image = op.filter(image, new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR_PRE));
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
image = ImageUtil.accelerate(image);
|
||||
}
|
||||
System.out.println("time: " + (System.currentTimeMillis() - start));
|
||||
System.out.println("image: " + image);
|
||||
}
|
||||
|
||||
showIt(image, file.getName());
|
||||
}
|
||||
|
||||
}
|
||||
twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReaderSpi.java
Executable
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.ImageReader;
|
||||
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 an PSDImageReaderSpi
|
||||
*/
|
||||
public PSDImageReaderSpi() {
|
||||
super(
|
||||
"TwelveMonkeys",
|
||||
"2.0",
|
||||
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, null, null, null, null,
|
||||
true, null, null, null, null
|
||||
);
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(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(Object pExtension) throws IOException {
|
||||
return new PSDImageReader(this);
|
||||
}
|
||||
|
||||
public String getDescription(Locale pLocale) {
|
||||
return "Adobe Photoshop Document (PSD) image reader";
|
||||
}
|
||||
}
|
||||
Executable
+168
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.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);
|
||||
|
||||
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());
|
||||
|
||||
builder.append(resourceTypeForId(mId));
|
||||
|
||||
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_EXIF_DATA_1:
|
||||
// case PSD.RES_EXIF_DATA_3:
|
||||
case PSD.RES_XMP_DATA:
|
||||
case PSD.RES_PRINT_FLAGS_INFORMATION:
|
||||
return "";
|
||||
default:
|
||||
try {
|
||||
for (Field field : PSD.class.getDeclaredFields()) {
|
||||
if (field.getName().startsWith("RES_") && field.getInt(null) == pId) {
|
||||
return "(" + field.getName().substring(4) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
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("Wrong image resource type, expected 8BIM: " + 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_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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerBlendMode.java
Executable
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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(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);
|
||||
builder.append(", flags: ").append(byteToBinary(mFlags));
|
||||
|
||||
// TODO: Maybe the flag bits have oposite order?
|
||||
builder.append(" (");
|
||||
if ((mFlags & 0x01) != 0) {
|
||||
builder.append("Transp. protected ");
|
||||
}
|
||||
else {
|
||||
builder.append("Transp. open");
|
||||
}
|
||||
if ((mFlags & 0x02) != 0) {
|
||||
builder.append(", Visible");
|
||||
}
|
||||
else {
|
||||
builder.append(", Hidden");
|
||||
}
|
||||
if ((mFlags & 0x04) != 0) {
|
||||
builder.append(", Obsolete bit");
|
||||
}
|
||||
if ((mFlags & 0x08) != 0) {
|
||||
builder.append(", Photoshop 5 data");
|
||||
}
|
||||
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");
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
Executable
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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 {
|
||||
private int mTop;
|
||||
private int mLeft;
|
||||
private int mBottom;
|
||||
private int mRight;
|
||||
|
||||
PSDChannelInfo[] mChannelInfo;
|
||||
private PSDLayerBlendMode mBlendMode;
|
||||
private PSDLayerMaskData mLayerMaskData;
|
||||
private PSDChannelSourceDestinationRange[] mRanges;
|
||||
private 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);
|
||||
}
|
||||
|
||||
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;
|
||||
// readPascalString has already read pad byte for word alignment
|
||||
if (layerNameSize % 2 != 0) {
|
||||
layerNameSize++;
|
||||
}
|
||||
// Skip two more pad bytes if needed
|
||||
if (layerNameSize % 4 != 0) {
|
||||
pInput.skipBytes(2);
|
||||
layerNameSize += 2;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
Executable
+136
@@ -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();
|
||||
}
|
||||
}
|
||||
Executable
+58
@@ -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.readUnsignedByte() != 0;
|
||||
mCropMasks = pInput.readUnsignedByte() != 0;
|
||||
mColorBars = pInput.readUnsignedByte() != 0;
|
||||
mRegistrationMarks = pInput.readUnsignedByte() != 0;
|
||||
mNegative = pInput.readUnsignedByte() != 0;
|
||||
mFlip = pInput.readUnsignedByte() != 0;
|
||||
mInterpolate = pInput.readUnsignedByte() != 0;
|
||||
mCaption = pInput.readUnsignedByte() != 0;
|
||||
|
||||
pInput.readUnsignedByte(); // Pad
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
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.readUnsignedByte() != 0;
|
||||
mField = pInput.readUnsignedByte();
|
||||
mBleedWidth = pInput.readUnsignedInt();
|
||||
mBleedScale = pInput.readUnsignedShort();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
Executable
+555
@@ -0,0 +1,555 @@
|
||||
package com.twelvemonkeys.imageio.plugins.psd;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Class PSDReader - Decodes a PhotoShop (.psd) file into one or more frames.
|
||||
* Supports uncompressed or RLE-compressed RGB files only. Each layer may be
|
||||
* retrieved as a full frame BufferedImage, or as a smaller image with an
|
||||
* offset if the layer does not occupy the full frame size. Transparency
|
||||
* in the original psd file is preserved in the returned BufferedImage's.
|
||||
* Does not support additional features in PS versions higher than 3.0.
|
||||
* Example:
|
||||
* <br><pre>
|
||||
* PSDReader r = new PSDReader();
|
||||
* r.readData("sample.psd");
|
||||
* int n = r.getFrameCount();
|
||||
* for (int i = 0; i < n; i++) {
|
||||
* BufferedImage image = r.getLayer(i);
|
||||
* Point offset = r.getLayerOffset(i);
|
||||
* // do something with image
|
||||
* }
|
||||
* </pre>
|
||||
* No copyright asserted on the source code of this class. May be used for
|
||||
* any purpose. Please forward any corrections to kweiner@fmsware.com.
|
||||
*
|
||||
* @author Kevin Weiner, FM Software.
|
||||
* @version 1.1 January 2004 [bug fix; add RLE support]
|
||||
*
|
||||
*/
|
||||
// SEE: http://www.fileformat.info/format/psd/egff.htm#ADOBEPHO-DMYID.4
|
||||
class PSDReader {
|
||||
|
||||
/**
|
||||
* File readData status: No errors.
|
||||
*/
|
||||
public static final int STATUS_OK = 0;
|
||||
|
||||
/**
|
||||
* File readData status: Error decoding file (may be partially decoded)
|
||||
*/
|
||||
public static final int STATUS_FORMAT_ERROR = 1;
|
||||
|
||||
/**
|
||||
* File readData status: Unable to open source.
|
||||
*/
|
||||
public static final int STATUS_OPEN_ERROR = 2;
|
||||
|
||||
/**
|
||||
* File readData status: Unsupported format
|
||||
*/
|
||||
public static final int STATUS_UNSUPPORTED = 3;
|
||||
|
||||
public static int ImageType = BufferedImage.TYPE_INT_ARGB;
|
||||
|
||||
protected BufferedInputStream input;
|
||||
protected int frameCount;
|
||||
protected BufferedImage[] frames;
|
||||
protected int status = 0;
|
||||
protected int nChan;
|
||||
protected int width;
|
||||
protected int height;
|
||||
protected int nLayers;
|
||||
protected int miscLen;
|
||||
protected boolean hasLayers;
|
||||
protected LayerInfo[] layers;
|
||||
protected short[] lineLengths;
|
||||
protected int lineIndex;
|
||||
protected boolean rleEncoded;
|
||||
|
||||
protected class LayerInfo {
|
||||
int x, y, w, h;
|
||||
int nChan;
|
||||
int[] chanID;
|
||||
int alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of layers readData from file.
|
||||
* @return frame count
|
||||
*/
|
||||
public int getFrameCount() {
|
||||
return frameCount;
|
||||
}
|
||||
|
||||
protected void setInput(InputStream stream) {
|
||||
// open input stream
|
||||
init();
|
||||
if (stream == null) {
|
||||
status = STATUS_OPEN_ERROR;
|
||||
} else {
|
||||
if (stream instanceof BufferedInputStream)
|
||||
input = (BufferedInputStream) stream;
|
||||
else
|
||||
input = new BufferedInputStream(stream);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setInput(String name) {
|
||||
// open input file
|
||||
init();
|
||||
try {
|
||||
name = name.trim();
|
||||
if (name.startsWith("file:")) {
|
||||
name = name.substring(5);
|
||||
while (name.startsWith("/"))
|
||||
name = name.substring(1);
|
||||
}
|
||||
if (name.indexOf("://") > 0) {
|
||||
URL url = new URL(name);
|
||||
input = new BufferedInputStream(url.openStream());
|
||||
} else {
|
||||
input = new BufferedInputStream(new FileInputStream(name));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
status = STATUS_OPEN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets display duration for specified frame. Always returns 0.
|
||||
*
|
||||
*/
|
||||
public int getDelay(int forFrame) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image contents of frame n. Note that this expands the image
|
||||
* to the full frame size (if the layer was smaller) and any subsequent
|
||||
* use of getLayer() will return the full image.
|
||||
*
|
||||
* @return BufferedImage representation of frame, or null if n is invalid.
|
||||
*/
|
||||
public BufferedImage getFrame(int n) {
|
||||
BufferedImage im = null;
|
||||
if ((n >= 0) && (n < nLayers)) {
|
||||
im = frames[n];
|
||||
LayerInfo info = layers[n];
|
||||
if ((info.w != width) || (info.h != height)) {
|
||||
BufferedImage temp =
|
||||
new BufferedImage(width, height, ImageType);
|
||||
Graphics2D gc = temp.createGraphics();
|
||||
gc.drawImage(im, info.x, info.y, null);
|
||||
gc.dispose();
|
||||
im = temp;
|
||||
frames[n] = im;
|
||||
}
|
||||
}
|
||||
return im;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets maximum image size. Individual layers may be smaller.
|
||||
*
|
||||
* @return maximum image dimensions
|
||||
*/
|
||||
public Dimension getFrameSize() {
|
||||
return new Dimension(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first (or only) image readData.
|
||||
*
|
||||
* @return BufferedImage containing first frame, or null if none.
|
||||
*/
|
||||
public BufferedImage getImage() {
|
||||
return getFrame(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image contents of layer n. May be smaller than full frame
|
||||
* size - use getFrameOffset() to obtain position of subimage within
|
||||
* main image area.
|
||||
*
|
||||
* @return BufferedImage representation of layer, or null if n is invalid.
|
||||
*/
|
||||
public BufferedImage getLayer(int n) {
|
||||
BufferedImage im = null;
|
||||
if ((n >= 0) && (n < nLayers)) {
|
||||
im = frames[n];
|
||||
}
|
||||
return im;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subimage offset of layer n if it is smaller than the
|
||||
* full frame size.
|
||||
*
|
||||
* @return Point indicating offset from upper left corner of frame.
|
||||
*/
|
||||
public Point getLayerOffset(int n) {
|
||||
Point p = null;
|
||||
if ((n >= 0) && (n < nLayers)) {
|
||||
int x = layers[n].x;
|
||||
int y = layers[n].y;
|
||||
p = new Point(x, y);
|
||||
}
|
||||
if (p == null) {
|
||||
p = new Point(0, 0);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads PhotoShop layers from stream.
|
||||
*
|
||||
* @param InputStream in PhotoShop format.
|
||||
* @return readData status code (0 = no errors)
|
||||
*/
|
||||
public int read(InputStream stream) {
|
||||
setInput(stream);
|
||||
process();
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads PhotoShop file from specified source (file or URL string)
|
||||
*
|
||||
* @param name String containing source
|
||||
* @return readData status code (0 = no errors)
|
||||
*/
|
||||
public int read(String name) {
|
||||
setInput(name);
|
||||
process();
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes input stream and discards contents of all frames.
|
||||
*
|
||||
*/
|
||||
public void reset() {
|
||||
init();
|
||||
}
|
||||
|
||||
protected void close() {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (Exception e) {}
|
||||
input = null;
|
||||
}
|
||||
}
|
||||
protected boolean err() {
|
||||
return status != STATUS_OK;
|
||||
}
|
||||
|
||||
protected byte[] fillBytes(int size, int value) {
|
||||
// create byte array filled with given value
|
||||
byte[] b = new byte[size];
|
||||
if (value != 0) {
|
||||
byte v = (byte) value;
|
||||
for (int i = 0; i < size; i++) {
|
||||
b[i] = v;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
close();
|
||||
frameCount = 0;
|
||||
frames = null;
|
||||
layers = null;
|
||||
hasLayers = true;
|
||||
status = STATUS_OK;
|
||||
}
|
||||
|
||||
protected void makeDummyLayer() {
|
||||
// creat dummy layer for non-layered image
|
||||
rleEncoded = readShort() == 1;
|
||||
hasLayers = false;
|
||||
nLayers = 1;
|
||||
layers = new LayerInfo[1];
|
||||
LayerInfo layer = new LayerInfo();
|
||||
layers[0] = layer;
|
||||
layer.h = height;
|
||||
layer.w = width;
|
||||
int nc = Math.min(nChan, 4);
|
||||
if (rleEncoded) {
|
||||
// get list of rle encoded line lengths for all channels
|
||||
readLineLengths(height * nc);
|
||||
}
|
||||
layer.nChan = nc;
|
||||
layer.chanID = new int[nc];
|
||||
for (int i = 0; i < nc; i++) {
|
||||
int id = i;
|
||||
if (i == 3) id = -1;
|
||||
layer.chanID[i] = id;
|
||||
}
|
||||
}
|
||||
|
||||
protected void readLineLengths(int nLines) {
|
||||
// readData list of rle encoded line lengths
|
||||
lineLengths = new short[nLines];
|
||||
for (int i = 0; i < nLines; i++) {
|
||||
lineLengths[i] = readShort();
|
||||
}
|
||||
lineIndex = 0;
|
||||
}
|
||||
|
||||
protected BufferedImage makeImage(int w, int h, byte[] r, byte[] g, byte[] b, byte[] a) {
|
||||
// create image from given plane data
|
||||
BufferedImage im = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
|
||||
int[] data = ((DataBufferInt) im.getRaster().getDataBuffer()).getData();
|
||||
int n = w * h;
|
||||
int j = 0;
|
||||
while (j < n) {
|
||||
try {
|
||||
int ac = a[j] & 0xff;
|
||||
int rc = r[j] & 0xff;
|
||||
int gc = g[j] & 0xff;
|
||||
int bc = b[j] & 0xff;
|
||||
data[j] = (((((ac << 8) | rc) << 8) | gc) << 8) | bc;
|
||||
} catch (Exception e) {}
|
||||
j++;
|
||||
}
|
||||
return im;
|
||||
}
|
||||
|
||||
protected void process() {
|
||||
// decode PSD file
|
||||
if (err()) return;
|
||||
readHeader();
|
||||
if (err()) return;
|
||||
readLayerInfo();
|
||||
if (err()) return;
|
||||
if (nLayers == 0) {
|
||||
makeDummyLayer();
|
||||
if (err()) return;
|
||||
}
|
||||
readLayers();
|
||||
}
|
||||
|
||||
protected int readByte() {
|
||||
// readData single byte from input
|
||||
int curByte = 0;
|
||||
try {
|
||||
curByte = input.read();
|
||||
} catch (IOException e) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
return curByte;
|
||||
}
|
||||
|
||||
protected int readBytes(byte[] bytes, int n) {
|
||||
// readData multiple bytes from input
|
||||
if (bytes == null) return 0;
|
||||
int r = 0;
|
||||
try {
|
||||
r = input.read(bytes, 0, n);
|
||||
} catch (IOException e) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
if (r < n) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
protected void readHeader() {
|
||||
// readData PSD header info
|
||||
String sig = readString(4);
|
||||
int ver = readShort();
|
||||
skipBytes(6);
|
||||
nChan = readShort();
|
||||
height = readInt();
|
||||
width = readInt();
|
||||
int depth = readShort();
|
||||
int mode = readShort();
|
||||
int cmLen = readInt();
|
||||
skipBytes(cmLen);
|
||||
int imResLen = readInt();
|
||||
skipBytes(imResLen);
|
||||
|
||||
// require 8-bit RGB data
|
||||
if ((!sig.equals("8BPS")) || (ver != 1)) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
} else if ((depth != 8) || (mode != 3)) {
|
||||
status = STATUS_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
protected int readInt() {
|
||||
// readData big-endian 32-bit integer
|
||||
return (((((readByte() << 8) | readByte()) << 8) | readByte()) << 8)
|
||||
| readByte();
|
||||
}
|
||||
|
||||
protected void readLayerInfo() {
|
||||
// readData layer header info
|
||||
miscLen = readInt();
|
||||
if (miscLen == 0) {
|
||||
return; // no layers, only base image
|
||||
}
|
||||
int layerInfoLen = readInt();
|
||||
nLayers = readShort();
|
||||
if (nLayers > 0) {
|
||||
layers = new LayerInfo[nLayers];
|
||||
}
|
||||
for (int i = 0; i < nLayers; i++) {
|
||||
LayerInfo info = new LayerInfo();
|
||||
layers[i] = info;
|
||||
info.y = readInt();
|
||||
info.x = readInt();
|
||||
info.h = readInt() - info.y;
|
||||
info.w = readInt() - info.x;
|
||||
info.nChan = readShort();
|
||||
info.chanID = new int[info.nChan];
|
||||
for (int j = 0; j < info.nChan; j++) {
|
||||
int id = readShort();
|
||||
int size = readInt();
|
||||
info.chanID[j] = id;
|
||||
}
|
||||
String s = readString(4);
|
||||
if (!s.equals("8BIM")) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
return;
|
||||
}
|
||||
skipBytes(4); // blend mode
|
||||
info.alpha = readByte();
|
||||
int clipping = readByte();
|
||||
int flags = readByte();
|
||||
readByte(); // filler
|
||||
int extraSize = readInt();
|
||||
skipBytes(extraSize);
|
||||
}
|
||||
}
|
||||
|
||||
protected void readLayers() {
|
||||
// readData and convert each layer to BufferedImage
|
||||
frameCount = nLayers;
|
||||
frames = new BufferedImage[nLayers];
|
||||
for (int i = 0; i < nLayers; i++) {
|
||||
LayerInfo info = layers[i];
|
||||
byte[] r = null, g = null, b = null, a = null;
|
||||
for (int j = 0; j < info.nChan; j++) {
|
||||
int id = info.chanID[j];
|
||||
switch (id) {
|
||||
case 0 : r = readPlane(info.w, info.h); break;
|
||||
case 1 : g = readPlane(info.w, info.h); break;
|
||||
case 2 : b = readPlane(info.w, info.h); break;
|
||||
case -1 : a = readPlane(info.w, info.h); break;
|
||||
default : readPlane(info.w, info.h);
|
||||
}
|
||||
if (err()) break;
|
||||
}
|
||||
if (err()) break;
|
||||
int n = info.w * info.h;
|
||||
if (r == null) r = fillBytes(n, 0);
|
||||
if (g == null) g = fillBytes(n, 0);
|
||||
if (b == null) b = fillBytes(n, 0);
|
||||
if (a == null) a = fillBytes(n, 255);
|
||||
|
||||
BufferedImage im = makeImage(info.w, info.h, r, g, b, a);
|
||||
frames[i] = im;
|
||||
}
|
||||
lineLengths = null;
|
||||
if ((miscLen > 0) && !err()) {
|
||||
int n = readInt(); // global layer mask info len
|
||||
skipBytes(n);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] readPlane(int w, int h) {
|
||||
// readData a single color plane
|
||||
byte[] b = null;
|
||||
int size = w * h;
|
||||
if (hasLayers) {
|
||||
// get RLE compression info for channel
|
||||
rleEncoded = readShort() == 1;
|
||||
if (rleEncoded) {
|
||||
// list of encoded line lengths
|
||||
readLineLengths(h);
|
||||
}
|
||||
}
|
||||
|
||||
if (rleEncoded) {
|
||||
b = readPlaneCompressed(w, h);
|
||||
} else {
|
||||
b = new byte[size];
|
||||
readBytes(b, size);
|
||||
}
|
||||
|
||||
return b;
|
||||
|
||||
}
|
||||
|
||||
protected byte[] readPlaneCompressed(int w, int h) {
|
||||
byte[] b = new byte[w * h];
|
||||
byte[] s = new byte[w * 2];
|
||||
int pos = 0;
|
||||
for (int i = 0; i < h; i++) {
|
||||
if (lineIndex >= lineLengths.length) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
return null;
|
||||
}
|
||||
int len = lineLengths[lineIndex++];
|
||||
readBytes(s, len);
|
||||
decodeRLE(s, 0, len, b, pos);
|
||||
pos += w;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
protected void decodeRLE(byte[] src, int sindex, int slen, byte[] dst, int dindex) {
|
||||
try {
|
||||
int max = sindex + slen;
|
||||
while (sindex < max) {
|
||||
byte b = src[sindex++];
|
||||
int n = (int) b;
|
||||
if (n < 0) {
|
||||
// dup next byte 1-n times
|
||||
n = 1 - n;
|
||||
b = src[sindex++];
|
||||
for (int i = 0; i < n; i++) {
|
||||
dst[dindex++] = b;
|
||||
}
|
||||
} else {
|
||||
// copy next n+1 bytes
|
||||
n = n + 1;
|
||||
System.arraycopy(src, sindex, dst, dindex, n);
|
||||
dindex += n;
|
||||
sindex += n;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
protected short readShort() {
|
||||
// readData big-endian 16-bit integer
|
||||
return (short) ((readByte() << 8) | readByte());
|
||||
}
|
||||
|
||||
protected String readString(int len) {
|
||||
// readData string of specified length
|
||||
String s = "";
|
||||
for (int i = 0; i < len; i++) {
|
||||
s = s + (char) readByte();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
protected void skipBytes(int n) {
|
||||
// skip over n input bytes
|
||||
for (int i = 0; i < n; i++) {
|
||||
readByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDResolutionInfo.java
Executable
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
private float mHRes;
|
||||
private short mHResUnit;
|
||||
private short mWidthUnit;
|
||||
private float mVRes;
|
||||
private short mVResUnit;
|
||||
private 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+86
@@ -0,0 +1,86 @@
|
||||
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;
|
||||
|
||||
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)
|
||||
int format = pInput.readInt();
|
||||
switch (format) {
|
||||
case 0:
|
||||
throw new IIOException("RAW RGB format thumbnail not supported yet");
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
throw new IIOException(String.format("Unsupported thumbnail format (%s) in PSD document", format));
|
||||
}
|
||||
|
||||
// This data isn't really useful, unless we're dealing with raw bytes
|
||||
int width = pInput.readInt();
|
||||
int height = pInput.readInt();
|
||||
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: Support BGR if id == RES_THUMBNAIL_PS4? Or is that already supported in the JPEG?
|
||||
mThumbnail = ImageIO.read(IIOUtil.createStreamAdapter(pInput, sizeCompressed));
|
||||
}
|
||||
|
||||
public BufferedImage getThumbnail() {
|
||||
return mThumbnail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = toStringBuilder();
|
||||
|
||||
builder.append(", ").append(mThumbnail);
|
||||
|
||||
builder.append("]");
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.InflateDecoder;
|
||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 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?
|
||||
static String readPascalString(ImageInputStream pInput) throws IOException {
|
||||
int length = pInput.readUnsignedByte();
|
||||
// int length = pInput.readUnsignedShort();
|
||||
byte[] bytes = new byte[length];
|
||||
pInput.readFully(bytes);
|
||||
if (length % 2 == 0) {
|
||||
pInput.readByte(); // Pad
|
||||
}
|
||||
return new String(bytes);
|
||||
}
|
||||
|
||||
static String readPascalStringByte(ImageInputStream pInput) throws IOException {
|
||||
int length = pInput.readUnsignedByte();
|
||||
byte[] bytes = new byte[length];
|
||||
pInput.readFully(bytes);
|
||||
return new String(bytes);
|
||||
}
|
||||
|
||||
static DataInputStream createPackBitsStream(final ImageInputStream pInput, int pLength) {
|
||||
return new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pInput, pLength), new PackBitsDecoder()));
|
||||
}
|
||||
|
||||
static DataInputStream createZipStream(final ImageInputStream pInput, int pLength) {
|
||||
return new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pInput, pLength), new InflateDecoder()));
|
||||
}
|
||||
|
||||
static DataInputStream createZipPredictorStream(final ImageInputStream pInput, int pLength) {
|
||||
throw new UnsupportedOperationException("Method createZipPredictonStream not implemented");
|
||||
}
|
||||
|
||||
public static float fixedPointToFloat(int pFP) {
|
||||
return ((pFP & 0xffff0000) >> 16) + (pFP & 0xffff) / (float) 0xffff;
|
||||
}
|
||||
}
|
||||
Executable
+60
@@ -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>
|
||||
*/
|
||||
public 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"));
|
||||
}
|
||||
}
|
||||
Executable
+122
@@ -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
|
||||
}
|
||||
}
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.psd.PSDImageReaderSpi
|
||||
Reference in New Issue
Block a user