blob: f102d41f4085e4a132d439453b252db46489f062 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.intellij.images.util.imageio;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.ArrayUtil;
import org.apache.sanselan.ImageFormat;
import org.apache.sanselan.ImageInfo;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.Sanselan;
import org.apache.sanselan.common.byteSources.ByteSource;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public class SanselanImageReaderSpi extends ImageReaderSpi {
private ThreadLocal<ImageFormat> myFormat = new ThreadLocal<ImageFormat>();
private final List<ImageFormat> myFormats;
public SanselanImageReaderSpi() {
super();
vendorName = "JetBrains, s.r.o.";
version = "1.0";
// todo standard GIF/BMP formats can be optionally skipped as well
// JPEG is skipped due to Exception: Sanselan cannot read or write JPEG images. (JpegImageParser.java:92)
// tiff reader seems to be broken
// PNG reader has bugs with well-compressed PNG images, use standard one instead
myFormats = new ArrayList<ImageFormat>(Arrays.asList(ImageFormat.getAllFormats()));
myFormats.removeAll(Arrays.asList(ImageFormat.IMAGE_FORMAT_UNKNOWN,
ImageFormat.IMAGE_FORMAT_JPEG,
ImageFormat.IMAGE_FORMAT_TIFF,
ImageFormat.IMAGE_FORMAT_PNG));
names = new String[myFormats.size() * 2];
suffixes = new String[myFormats.size()];
MIMETypes = new String[myFormats.size()];
pluginClassName = MyImageReader.class.getName();
inputTypes = new Class[] {ImageInputStream.class};
for (int i = 0, allFormatsLength = myFormats.size(); i < allFormatsLength; i++) {
final ImageFormat format = myFormats.get(i);
names[2 * i] = format.extension.toLowerCase();
names[2 * i + 1] = format.extension.toUpperCase();
suffixes[i] = names[2 * i];
MIMETypes[i] = "image/" + names[2 * i];
}
}
public String getDescription(Locale locale) {
return "Apache Sanselan project based image reader";
}
public boolean canDecodeInput(Object input) throws IOException {
if (!(input instanceof ImageInputStream)) {
return false;
}
final ImageInputStream stream = (ImageInputStream)input;
try {
final ImageFormat imageFormat = Sanselan.guessFormat(new MyByteSource(stream));
if (myFormats.contains(imageFormat)) {
myFormat.set(imageFormat);
return true;
}
return false;
}
catch (ImageReadException e) {
throw new IOException(e);
}
}
public ImageReader createReaderInstance(Object extension) {
return new MyImageReader(this, myFormat.get());
}
private static class MyByteSource extends ByteSource {
private final ImageInputStream myStream;
public MyByteSource(final ImageInputStream stream) {
super(stream.toString());
myStream = stream;
}
@Override
public InputStream getInputStream() throws IOException {
myStream.seek(0);
return new InputStream() {
@Override
public int read() throws IOException {
return myStream.read();
}
@Override
public int read(final byte[] b, final int off, final int len) throws IOException {
return myStream.read(b, off, len);
}
};
}
@Override
public byte[] getBlock(final int start, final int length) throws IOException {
myStream.seek(start);
final byte[] bytes = new byte[length];
final int read = myStream.read(bytes);
return ArrayUtil.realloc(bytes, read);
}
@Override
public byte[] getAll() throws IOException {
return FileUtil.loadBytes(getInputStream());
}
@Override
public long getLength() throws IOException {
return myStream.length();
}
@Override
public String getDescription() {
return myStream.toString();
}
}
private static class MyImageReader extends ImageReader {
private byte[] myBytes;
private ImageInfo myInfo;
private BufferedImage[] myImages;
private final ImageFormat myDefaultFormat;
private MyImageReader(final SanselanImageReaderSpi provider, final ImageFormat imageFormat) {
super(provider);
myDefaultFormat = imageFormat == null? ImageFormat.IMAGE_FORMAT_UNKNOWN : imageFormat;
}
@Override
public void dispose() {
myBytes = null;
myInfo = null;
myImages = null;
}
@Override
public void setInput(final Object input, final boolean seekForwardOnly, final boolean ignoreMetadata) {
super.setInput(input, seekForwardOnly, ignoreMetadata);
myBytes = null;
myInfo = null;
myImages = null;
}
private ImageInfo getInfo() throws IOException {
if (myInfo == null) {
try {
myInfo = Sanselan.getImageInfo(getBytes());
}
catch (ImageReadException e) {
throw new IOException(e);
}
}
return myInfo;
}
private byte[] getBytes() throws IOException {
if (myBytes == null) {
final ImageInputStream stream = (ImageInputStream)input;
myBytes = new MyByteSource(stream).getAll();
}
return myBytes;
}
private BufferedImage[] getImages() throws IOException {
if (myImages == null) {
try {
final ArrayList<BufferedImage> images = Sanselan.getAllBufferedImages(getBytes());
myImages = images.toArray(new BufferedImage[images.size()]);
}
catch (ImageReadException e) {
throw new IOException(e);
}
}
return myImages;
}
@Override
public int getNumImages(final boolean allowSearch) throws IOException {
return getInfo().getNumberOfImages();
}
@Override
public int getWidth(final int imageIndex) throws IOException {
return getInfo().getWidth();
}
@Override
public int getHeight(final int imageIndex) throws IOException {
return getInfo().getHeight();
}
@Override
public Iterator<ImageTypeSpecifier> getImageTypes(final int imageIndex) throws IOException {
return Collections.singletonList(ImageTypeSpecifier.createFromRenderedImage(getImages()[imageIndex])).iterator();
}
@Override
public IIOMetadata getStreamMetadata() throws IOException {
return null;
}
@Override
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
return null;
}
@Override
public BufferedImage read(final int imageIndex, final ImageReadParam param) throws IOException {
return getImages()[imageIndex];
}
@Override
public String getFormatName() throws IOException {
// return default if called before setInput
return input == null? myDefaultFormat.name : getInfo().getFormat().name;
}
}
}