mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 04:25:29 -04:00
#483: Rollback
This commit is contained in:
parent
eb6a3bde39
commit
00cd9471dd
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2021, 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 of the copyright holder 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 HOLDER 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.ImageWriteParam;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PSDImageWriteParam
|
|
||||||
*/
|
|
||||||
public final class PSDImageWriteParam extends ImageWriteParam {
|
|
||||||
|
|
||||||
PSDImageWriteParam() {
|
|
||||||
this(Locale.getDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
PSDImageWriteParam(final Locale locale) {
|
|
||||||
super(locale);
|
|
||||||
|
|
||||||
compressionTypes = new String[] {
|
|
||||||
"None",
|
|
||||||
"PackBits",
|
|
||||||
// Two ZIP compression types are defined in spec, never seen in the wild...
|
|
||||||
// "ZIP",
|
|
||||||
// "ZIP+Predictor",
|
|
||||||
};
|
|
||||||
compressionType = compressionTypes[1];
|
|
||||||
canWriteCompressed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int getCompressionType(final ImageWriteParam param) {
|
|
||||||
if (param == null || param.getCompressionMode() != MODE_EXPLICIT || param.getCompressionType() == null || param.getCompressionType().equals("None")) {
|
|
||||||
return PSD.COMPRESSION_NONE;
|
|
||||||
}
|
|
||||||
else if (param.getCompressionType().equals("PackBits")) {
|
|
||||||
return PSD.COMPRESSION_RLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalArgumentException(String.format("Unsupported compression type: %s", param.getCompressionType()));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,400 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2021, 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 of the copyright holder 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 HOLDER 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.ImageWriterBase;
|
|
||||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry;
|
|
||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
|
||||||
import com.twelvemonkeys.imageio.util.RasterUtils;
|
|
||||||
import com.twelvemonkeys.io.enc.EncoderStream;
|
|
||||||
import com.twelvemonkeys.io.enc.PackBitsEncoder;
|
|
||||||
|
|
||||||
import javax.imageio.*;
|
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
|
||||||
import javax.imageio.spi.ImageWriterSpi;
|
|
||||||
import java.awt.color.ColorSpace;
|
|
||||||
import java.awt.color.ICC_ColorSpace;
|
|
||||||
import java.awt.color.ICC_Profile;
|
|
||||||
import java.awt.image.*;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimal ImageWriter for Adobe Photoshop Document (PSD) format.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
|
||||||
* @author last modified by $Author: haraldk$
|
|
||||||
* @version $Id: PSDImageWriter.java,v 1.0 Apr 29, 2008 4:45:52 PM haraldk Exp$
|
|
||||||
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/">Adobe Photoshop File Formats Specification<a>
|
|
||||||
* @see <a href="http://www.fileformat.info/format/psd/egff.htm">Adobe Photoshop File Format Summary<a>
|
|
||||||
*/
|
|
||||||
public final class PSDImageWriter extends ImageWriterBase {
|
|
||||||
|
|
||||||
PSDImageWriter(ImageWriterSpi provider) {
|
|
||||||
super(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) {
|
|
||||||
// TODO: Implement
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) {
|
|
||||||
// TODO: Implement
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ImageWriteParam getDefaultWriteParam() {
|
|
||||||
return new PSDImageWriteParam(getLocale());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(IIOMetadata streamMetadata, IIOImage iioImage, ImageWriteParam param) throws IOException {
|
|
||||||
assertOutput();
|
|
||||||
imageOutput.setByteOrder(ByteOrder.BIG_ENDIAN);
|
|
||||||
|
|
||||||
RenderedImage image = iioImage.getRenderedImage();
|
|
||||||
SampleModel sampleModel = image.getSampleModel();
|
|
||||||
|
|
||||||
int colorComponents = image.getColorModel().getColorSpace().getNumComponents();
|
|
||||||
int channels = sampleModel.getNumBands();
|
|
||||||
int width = image.getWidth();
|
|
||||||
int height = image.getHeight();
|
|
||||||
|
|
||||||
int bits = getBitsPerSample(sampleModel);
|
|
||||||
int mode = getColorMode(image.getColorModel());
|
|
||||||
// TODO: Allow stream metadata or param to force PSD/PSB (version 1/2)?
|
|
||||||
boolean largeFormat = width > PSDHeader.PSD_MAX_SIZE || height > PSDHeader.PSD_MAX_SIZE;
|
|
||||||
|
|
||||||
new PSDHeader(channels, width, height, bits, mode, largeFormat).write(imageOutput);
|
|
||||||
writeColorModeData(image, mode);
|
|
||||||
writeImageResources(image, mode);
|
|
||||||
|
|
||||||
// Length of the layer and mask information section. (**PSB** length is 8 bytes.)
|
|
||||||
// TODO: Write an empty dummy layer here, if there's alpha? See below... Or see if Photoshop handles alpha if no layers at all...
|
|
||||||
if (largeFormat) {
|
|
||||||
imageOutput.writeLong(0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
imageOutput.writeInt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
processImageStarted(0);
|
|
||||||
|
|
||||||
// Image Data Section (composite layer only).
|
|
||||||
// The last section of a Photoshop file contains the image pixel data.
|
|
||||||
// Image data is stored in planar order: first all the red data, then all the green data, etc.
|
|
||||||
// Each plane is stored in scan-line order, with no pad bytes,
|
|
||||||
final int compression = PSDImageWriteParam.getCompressionType(param);
|
|
||||||
imageOutput.writeShort(compression);
|
|
||||||
|
|
||||||
long byteCountPos = imageOutput.getStreamPosition();
|
|
||||||
// PSB (large format) byte counts are actually 32 bit offsets, not 16 bit as described in spec
|
|
||||||
int[] byteCounts = new int[compression == PSD.COMPRESSION_RLE ? height * channels : 0];
|
|
||||||
imageOutput.skipBytes(byteCounts.length * (largeFormat ? 4 : 2));
|
|
||||||
|
|
||||||
// TODO: Loop over tiles?
|
|
||||||
Raster tile = sampleModel.getTransferType() == DataBuffer.TYPE_INT && sampleModel instanceof SinglePixelPackedSampleModel
|
|
||||||
? RasterUtils.asByteRaster(image.getTile(0, 0))
|
|
||||||
: image.getTile(0, 0);
|
|
||||||
|
|
||||||
for (int channel = 0; channel < channels; channel++) {
|
|
||||||
// TODO: Alpha issues:
|
|
||||||
// 1. Alpha channel is written (but not read, because there are no layers, and alpha is considered present only if layer count is negative)
|
|
||||||
// - Can we write a small hidden layer, just to have -1 layers?
|
|
||||||
// 2. Alpha needs to be premultiplied against white background (to avoid inverse halo)
|
|
||||||
Raster channelRaster = tile.createChild(0, 0, width, height, 0, 0, new int[] {channel});
|
|
||||||
|
|
||||||
switch (bits) {
|
|
||||||
case 1:
|
|
||||||
// TODO: Figure out why we can't write multi-pixel packed 1 bit samples as bytes...
|
|
||||||
case 8:
|
|
||||||
write8BitChannel(channel, colorComponents, mode, compression, channelRaster, byteCounts);
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
write16BitChannel(channel, colorComponents, mode, compression, channelRaster, byteCounts);
|
|
||||||
break;
|
|
||||||
case 32:
|
|
||||||
write32BitChannel(channel, colorComponents, mode, compression, channelRaster, byteCounts);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new AssertionError(); // Should be guarded against already
|
|
||||||
}
|
|
||||||
|
|
||||||
processImageProgress(channel * 100f / channels);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateByteCounts(byteCountPos, byteCounts, largeFormat);
|
|
||||||
|
|
||||||
processImageComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateByteCounts(long byteCountPos, int[] byteCounts, boolean largeFormat) throws IOException {
|
|
||||||
if (byteCounts.length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update byte counts for RLE
|
|
||||||
long pos = imageOutput.getStreamPosition();
|
|
||||||
|
|
||||||
imageOutput.seek(byteCountPos);
|
|
||||||
if (largeFormat) {
|
|
||||||
imageOutput.writeInts(byteCounts, 0, byteCounts.length);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (int byteCount : byteCounts) {
|
|
||||||
imageOutput.writeShort(byteCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
imageOutput.seek(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeColorModeData(RenderedImage image, int mode) throws IOException {
|
|
||||||
if (mode == PSD.COLOR_MODE_INDEXED) {
|
|
||||||
IndexColorModel icm = (IndexColorModel) image.getColorModel();
|
|
||||||
|
|
||||||
// Indexed color images: length is 768; color data contains the color table for the image, in non-interleaved order.
|
|
||||||
imageOutput.writeInt(768);
|
|
||||||
byte[] colors = new byte[256];
|
|
||||||
|
|
||||||
icm.getReds(colors);
|
|
||||||
imageOutput.write(colors);
|
|
||||||
icm.getGreens(colors);
|
|
||||||
imageOutput.write(colors);
|
|
||||||
icm.getBlues(colors);
|
|
||||||
imageOutput.write(colors);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
imageOutput.writeInt(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeImageResources(RenderedImage image, int mode) throws IOException {
|
|
||||||
// Length of image resource section. The length may be zero
|
|
||||||
imageOutput.writeInt(0);
|
|
||||||
long startImageResources = imageOutput.getStreamPosition();
|
|
||||||
|
|
||||||
// Write ICC color profile if not "native" sRGB or gray (or bitmap/indexed)
|
|
||||||
if (mode != PSD.COLOR_MODE_BITMAP && mode != PSD.COLOR_MODE_INDEXED) {
|
|
||||||
ColorSpace colorSpace = image.getColorModel().getColorSpace();
|
|
||||||
if (!colorSpace.isCS_sRGB() && colorSpace instanceof ICC_ColorSpace) {
|
|
||||||
ICC_Profile profile = ((ICC_ColorSpace) colorSpace).getProfile();
|
|
||||||
ICCProfile.writeData(imageOutput, profile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write creator software (Exif)
|
|
||||||
Entry software = new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys ImageIO PSD writer " + originatingProvider.getVersion());
|
|
||||||
PSDEXIF1Data.writeData(imageOutput, Collections.singleton(software));
|
|
||||||
|
|
||||||
long endImageResources = imageOutput.getStreamPosition();
|
|
||||||
|
|
||||||
// Update image resources length
|
|
||||||
imageOutput.seek(startImageResources - 4);
|
|
||||||
imageOutput.writeInt((int) (endImageResources - startImageResources));
|
|
||||||
imageOutput.seek(endImageResources);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void write8BitChannel(int channel, int colorComponents, int colorMode, int compression, Raster raster, int[] byteCounts) throws IOException {
|
|
||||||
int width = raster.getWidth();
|
|
||||||
int height = raster.getHeight();
|
|
||||||
|
|
||||||
byte[] rowBytes = null;
|
|
||||||
|
|
||||||
for (int y = 0; y < height; y++) {
|
|
||||||
rowBytes = (byte[]) raster.getDataElements(0, y, width, 1, rowBytes);
|
|
||||||
|
|
||||||
// Photoshop likes to store CMYK values inverted (but not the alpha value)
|
|
||||||
if (colorMode == PSD.COLOR_MODE_CMYK && channel < colorComponents) {
|
|
||||||
for (int i = 0; i < rowBytes.length; i++) {
|
|
||||||
rowBytes[i] = (byte) (0xff - rowBytes[i] & 0xff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compression == PSD.COMPRESSION_NONE) {
|
|
||||||
imageOutput.write(rowBytes);
|
|
||||||
}
|
|
||||||
else if (compression == PSD.COMPRESSION_RLE) {
|
|
||||||
long startPos = imageOutput.getStreamPosition();
|
|
||||||
|
|
||||||
// The RLE compressed data follows, with each scan line compressed separately
|
|
||||||
try (OutputStream stream = new EncoderStream(IIOUtil.createStreamAdapter(imageOutput), new PackBitsEncoder())) {
|
|
||||||
stream.write(rowBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
long endPos = imageOutput.getStreamPosition();
|
|
||||||
byteCounts[y + channel * height] = (int) (endPos - startPos);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IIOException("PSD with ZIP compression not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void write16BitChannel(int channel, int colorComponents, int colorMode, int compression, Raster raster, int[] byteCounts) throws IOException {
|
|
||||||
int width = raster.getWidth();
|
|
||||||
int height = raster.getHeight();
|
|
||||||
|
|
||||||
short[] row = null;
|
|
||||||
|
|
||||||
for (int y = 0; y < height; y++) {
|
|
||||||
row = (short[]) raster.getDataElements(0, y, width, 1, row);
|
|
||||||
|
|
||||||
// Photoshop likes to store CMYK values inverted (but not the alpha value)
|
|
||||||
if (colorMode == PSD.COLOR_MODE_CMYK && channel < colorComponents) {
|
|
||||||
for (int i = 0; i < row.length; i++) {
|
|
||||||
row[i] = (short) (0xffff - row[i] & 0xffff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compression == PSD.COMPRESSION_NONE) {
|
|
||||||
imageOutput.writeShorts(row, 0, row.length);
|
|
||||||
}
|
|
||||||
else if (compression == PSD.COMPRESSION_RLE) {
|
|
||||||
long startPos = imageOutput.getStreamPosition();
|
|
||||||
|
|
||||||
// The RLE compressed data follows, with each scan line compressed separately
|
|
||||||
try (DataOutputStream stream = new DataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(imageOutput), new PackBitsEncoder()))) {
|
|
||||||
for (short sample : row) {
|
|
||||||
stream.writeShort(sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long endPos = imageOutput.getStreamPosition();
|
|
||||||
byteCounts[y + channel * height] = (int) (endPos - startPos);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IIOException("PSD with ZIP compression not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void write32BitChannel(int channel, int colorComponents, int colorMode, int compression, Raster raster, int[] byteCounts) throws IOException {
|
|
||||||
int width = raster.getWidth();
|
|
||||||
int height = raster.getHeight();
|
|
||||||
|
|
||||||
int[] row = null;
|
|
||||||
|
|
||||||
for (int y = 0; y < height; y++) {
|
|
||||||
row = (int[]) raster.getDataElements(0, y, width, 1, row);
|
|
||||||
|
|
||||||
// Photoshop likes to store CMYK values inverted (but not the alpha value)
|
|
||||||
if (colorMode == PSD.COLOR_MODE_CMYK && channel < colorComponents) {
|
|
||||||
for (int i = 0; i < row.length; i++) {
|
|
||||||
row[i] = 0xffffffff - row[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compression == PSD.COMPRESSION_NONE) {
|
|
||||||
imageOutput.writeInts(row, 0, row.length);
|
|
||||||
}
|
|
||||||
else if (compression == PSD.COMPRESSION_RLE) {
|
|
||||||
long startPos = imageOutput.getStreamPosition();
|
|
||||||
|
|
||||||
// The RLE compressed data follows, with each scan line compressed separately
|
|
||||||
try (DataOutputStream stream = new DataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(imageOutput), new PackBitsEncoder()))) {
|
|
||||||
for (int sample : row) {
|
|
||||||
stream.writeInt(sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long endPos = imageOutput.getStreamPosition();
|
|
||||||
byteCounts[y + channel * height] = (int) (endPos - startPos);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IIOException("PSD with ZIP compression not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int getColorMode(ColorModel colorModel) {
|
|
||||||
if (colorModel instanceof IndexColorModel) {
|
|
||||||
if (colorModel.getPixelSize() == 1) {
|
|
||||||
return PSD.COLOR_MODE_BITMAP;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return PSD.COLOR_MODE_INDEXED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int csType = colorModel.getColorSpace().getType();
|
|
||||||
switch (csType) {
|
|
||||||
case ColorSpace.TYPE_GRAY:
|
|
||||||
if (colorModel.getPixelSize() == 1) {
|
|
||||||
return PSD.COLOR_MODE_BITMAP;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return PSD.COLOR_MODE_GRAYSCALE;
|
|
||||||
}
|
|
||||||
case ColorSpace.TYPE_RGB:
|
|
||||||
return PSD.COLOR_MODE_RGB;
|
|
||||||
case ColorSpace.TYPE_CMYK:
|
|
||||||
return PSD.COLOR_MODE_CMYK;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unsupported color space type for PSD: " + csType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int getBitsPerSample(SampleModel sampleModel) {
|
|
||||||
int bits = sampleModel.getSampleSize(0);
|
|
||||||
|
|
||||||
for (int i = 1; i < sampleModel.getNumBands(); i++) {
|
|
||||||
if (bits != sampleModel.getSampleSize(i)) {
|
|
||||||
throw new IllegalArgumentException("All samples must be of equal size for PSD: " + bits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (bits) {
|
|
||||||
case 1:
|
|
||||||
case 8:
|
|
||||||
case 16:
|
|
||||||
case 32:
|
|
||||||
return (short) bits;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unsupported sample size for PSD (expected 1, 8, 16 or 32): " + bits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
|
||||||
BufferedImage image = ImageIO.read(new File(args[0]));
|
|
||||||
ImageIO.write(image, "PSD", new File("test.psd"));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2021, 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 of the copyright holder 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 HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.psd;
|
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
|
|
||||||
|
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
|
||||||
import javax.imageio.ImageWriter;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import static com.twelvemonkeys.imageio.plugins.psd.PSDImageWriter.getBitsPerSample;
|
|
||||||
import static com.twelvemonkeys.imageio.plugins.psd.PSDImageWriter.getColorMode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PSDImageWriterSpi
|
|
||||||
*/
|
|
||||||
public final class PSDImageWriterSpi extends ImageWriterSpiBase {
|
|
||||||
|
|
||||||
public PSDImageWriterSpi() {
|
|
||||||
super(new PSDProviderInfo());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canEncodeImage(ImageTypeSpecifier type) {
|
|
||||||
// PSD supports:
|
|
||||||
// - 1, 8, 16 or 32 bit/sample
|
|
||||||
// - Number of samples <= 56
|
|
||||||
// - RGB, CMYK, Gray, Indexed color
|
|
||||||
try {
|
|
||||||
getBitsPerSample(type.getSampleModel());
|
|
||||||
getColorMode(type.getColorModel());
|
|
||||||
}
|
|
||||||
catch (IllegalArgumentException ignore) {
|
|
||||||
// We can't write this type
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return type.getNumBands() <= 56; // Can't be negative
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ImageWriter createWriterInstance(Object extension) {
|
|
||||||
return new PSDImageWriter(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription(final Locale pLocale) {
|
|
||||||
return "Adobe Photoshop Document (PSD) image writer";
|
|
||||||
}
|
|
||||||
}
|
|
@ -54,8 +54,8 @@ final class PSDProviderInfo extends ReaderWriterProviderInfo {
|
|||||||
},
|
},
|
||||||
"com.twelvemonkeys.imageio.plugins.psd.PSDImageReader",
|
"com.twelvemonkeys.imageio.plugins.psd.PSDImageReader",
|
||||||
new String[] {"com.twelvemonkeys.imageio.plugins.psd.PSDImageReaderSpi"},
|
new String[] {"com.twelvemonkeys.imageio.plugins.psd.PSDImageReaderSpi"},
|
||||||
"com.twelvemonkeys.imageio.plugins.psd.PSDImageWriter",
|
null,
|
||||||
new String[] {"com.twelvemonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
|
null,
|
||||||
false, null, null, null, null,
|
false, null, null, null, null,
|
||||||
true, PSDMetadata.NATIVE_METADATA_FORMAT_NAME, PSDMetadata.NATIVE_METADATA_FORMAT_CLASS_NAME, null, null
|
true, PSDMetadata.NATIVE_METADATA_FORMAT_NAME, PSDMetadata.NATIVE_METADATA_FORMAT_CLASS_NAME, null, null
|
||||||
);
|
);
|
||||||
|
@ -1 +0,0 @@
|
|||||||
com.twelvemonkeys.imageio.plugins.psd.PSDImageWriterSpi
|
|
@ -1,37 +0,0 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.psd;
|
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
|
||||||
|
|
||||||
import javax.imageio.spi.ImageWriterSpi;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.awt.image.RenderedImage;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PSDImageWriterTest.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
|
||||||
* @author last modified by $Author: haraldk$
|
|
||||||
* @version $Id: PSDImageWriterTest.java,v 1.0 05/05/2021 haraldk Exp$
|
|
||||||
*/
|
|
||||||
public class PSDImageWriterTest extends ImageWriterAbstractTest<PSDImageWriter> {
|
|
||||||
@Override
|
|
||||||
protected ImageWriterSpi createProvider() {
|
|
||||||
return new PSDImageWriterSpi();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<? extends RenderedImage> getTestData() {
|
|
||||||
return Arrays.asList(
|
|
||||||
new BufferedImage(300, 200, BufferedImage.TYPE_INT_RGB),
|
|
||||||
new BufferedImage(301, 199, BufferedImage.TYPE_INT_ARGB),
|
|
||||||
new BufferedImage(299, 201, BufferedImage.TYPE_3BYTE_BGR),
|
|
||||||
new BufferedImage(160, 90, BufferedImage.TYPE_4BYTE_ABGR),
|
|
||||||
new BufferedImage(90, 160, BufferedImage.TYPE_BYTE_GRAY),
|
|
||||||
new BufferedImage(30, 20, BufferedImage.TYPE_USHORT_GRAY),
|
|
||||||
new BufferedImage(30, 20, BufferedImage.TYPE_BYTE_BINARY),
|
|
||||||
new BufferedImage(30, 20, BufferedImage.TYPE_BYTE_INDEXED)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user