It all works

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

View File

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

View File

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

View File

@@ -0,0 +1,259 @@
/*
* 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.thumbsdb;
import com.twelvemonkeys.io.LittleEndianDataInputStream;
import com.twelvemonkeys.io.ole2.CompoundDocument;
import com.twelvemonkeys.lang.StringUtil;
import java.io.InputStream;
import java.io.IOException;
import java.io.DataInput;
import java.util.Iterator;
import java.util.Date;
/**
* Represents a {@code Catalog} structure, typically found in a {@link com.twelvemonkeys.io.ole2.CompoundDocument}.
*
* @see <a href="http://www.petedavis.net/MySite/DynPageView.aspx?pageid=31">PeteDavis.NET</a>
*
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
* @author last modified by $Author: haku$
* @version $Id: Catalog.java,v 1.0 01.feb.2007 17:19:59 haku Exp$
*/
// TODO: Consider moving this one to io.ole2
public final class Catalog implements Iterable<Catalog.CatalogItem> {
private final CatalogHeader mHeader;
private final CatalogItem[] mItems;
Catalog(final CatalogHeader pHeader, final CatalogItem[] pItems) {
mHeader = pHeader;
mItems = pItems;
}
/**
* Reads the {@code Catalog} entry from the given input stream.
*
* @param pInput the input stream
* @return a new {@code Catalog}
*
* @throws java.io.IOException if an I/O exception occurs during read
*/
public static Catalog read(final InputStream pInput) throws IOException {
DataInput dataInput = new LittleEndianDataInputStream(pInput);
return read(dataInput);
}
/**
* Reads the {@code Catalog} entry from the given input stream.
* <p/>
* The data is assumed to be in little endian byte order.
*
* @param pDataInput the input stream
* @return a new {@code Catalog}
*
* @throws java.io.IOException if an I/O exception occurs during read
*/
public static Catalog read(final DataInput pDataInput) throws IOException {
CatalogHeader header = CatalogHeader.read(pDataInput);
CatalogItem[] items = new CatalogItem[header.getThumbnailCount()];
for (int i = 0; i < header.getThumbnailCount(); i++) {
CatalogItem item = CatalogItem.read(pDataInput);
//System.out.println("item: " + item);
items[item.getItemId() - 1] = item;
}
return new Catalog(header, items);
}
public final int getThumbnailCount() {
return mHeader.mThumbCount;
}
public final int getMaxThumbnailWidth() {
return mHeader.mThumbWidth;
}
public final int getMaxThumbnailHeight() {
return mHeader.mThumbHeight;
}
final CatalogItem getItem(final int pIndex) {
return mItems[pIndex];
}
final CatalogItem getItem(final String pName) {
return mItems[getIndex(pName)];
}
final int getItemId(final int pIndex) {
return mItems[pIndex].getItemId();
}
public final int getIndex(final String pName) {
for (int i = 0; i < mItems.length; i++) {
CatalogItem item = mItems[i];
if (item.getName().equals(pName)) {
return i;
}
}
return -1;
}
public final String getStreamName(final int pIndex) {
return StringUtil.reverse(String.valueOf(getItemId(pIndex)));
}
public final String getName(String pStreamName) {
return getName(Integer.parseInt(StringUtil.reverse(pStreamName)));
}
final String getName(int pItemId) {
return mItems[pItemId - 1].getName();
}
@Override
public String toString() {
return String.format("%s[%s]", getClass().getSimpleName(), mHeader);
}
public Iterator<CatalogItem> iterator() {
return new Iterator<CatalogItem>() {
int mCurrentIdx;
public boolean hasNext() {
return mCurrentIdx < mItems.length;
}
public CatalogItem next() {
return mItems[mCurrentIdx++];
}
public void remove() {
throw new UnsupportedOperationException("Remove not supported");
}
};
}
private static class CatalogHeader {
short mReserved1;
short mReserved2;
int mThumbCount;
int mThumbWidth;
int mThumbHeight;
CatalogHeader() {
}
public static CatalogHeader read(final DataInput pDataInput) throws IOException {
CatalogHeader header = new CatalogHeader();
header.mReserved1 = pDataInput.readShort();
header.mReserved2 = pDataInput.readShort();
header.mThumbCount = pDataInput.readInt();
header.mThumbWidth = pDataInput.readInt();
header.mThumbHeight = pDataInput.readInt();
return header;
}
public int getThumbnailCount() {
return mThumbCount;
}
public int getThumbHeight() {
return mThumbHeight;
}
public int getThumbWidth() {
return mThumbWidth;
}
@Override
public String toString() {
return String.format(
"%s: %s %s thumbs: %d maxWidth: %d maxHeight: %d",
getClass().getSimpleName(), mReserved1, mReserved2, mThumbCount, mThumbWidth, mThumbHeight
);
}
}
public static final class CatalogItem {
int mReserved1;
int mItemId; // Reversed stream name
String mFilename;
short mReserved2;
private long mLastModified;
private static CatalogItem read(final DataInput pDataInput) throws IOException {
CatalogItem item = new CatalogItem();
item.mReserved1 = pDataInput.readInt();
item.mItemId = pDataInput.readInt();
item.mLastModified = CompoundDocument.toJavaTimeInMillis(pDataInput.readLong());
char[] chars = new char[256];
char ch;
int len = 0;
while ((ch = pDataInput.readChar()) != 0) {
chars[len++] = ch;
}
String name = new String(chars, 0, len);
item.mFilename = StringUtil.getLastElement(name, "\\");
item.mReserved2 = pDataInput.readShort();
return item;
}
public String getName() {
return mFilename;
}
public int getItemId() {
return mItemId;
}
public long lastModified() {
return mLastModified;
}
@Override
public String toString() {
return String.format(
"%s: %d itemId: %d lastModified: %s fileName: %s %s",
getClass().getSimpleName(), mReserved1, mItemId, new Date(mLastModified), mFilename, mReserved2
);
}
}
}

View File

@@ -0,0 +1,425 @@
/*
* 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.thumbsdb;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.io.ole2.CompoundDocument;
import com.twelvemonkeys.io.ole2.Entry;
import com.twelvemonkeys.lang.StringUtil;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.Iterator;
import java.util.SortedSet;
/**
* ThumbsDBImageReader
*
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
* @author last modified by $Author: haku$
* @version $Id: ThumbsDBImageReader.java,v 1.0 22.jan.2007 18:49:38 haku Exp$
* @see com.twelvemonkeys.io.ole2.CompoundDocument
* @see <a href="http://en.wikipedia.org/wiki/Thumbs.db>Wikipedia: Thumbs.db</a>
*/
public class ThumbsDBImageReader extends ImageReaderBase {
private static final int THUMBNAIL_OFFSET = 12;
private Entry mRoot;
private Catalog mCatalog;
private BufferedImage[] mThumbnails;
private final ImageReader mReader;
private int mCurrentImage = -1;
private boolean mLoadEagerly;
public ThumbsDBImageReader() {
this(new ThumbsDBImageReaderSpi());
}
protected ThumbsDBImageReader(final ThumbsDBImageReaderSpi pProvider) {
super(pProvider);
mReader = createJPEGReader(pProvider);
initReaderListeners();
}
protected void resetMembers() {
mRoot = null;
mCatalog = null;
mThumbnails = null;
}
private static ImageReader createJPEGReader(final ThumbsDBImageReaderSpi pProvider) {
return pProvider.createJPEGReader();
}
public void dispose() {
mReader.dispose();
super.dispose();
}
public boolean isLoadEagerly() {
return mLoadEagerly;
}
/**
* Instructs the reader wether it should read and cache alle thumbnails
* in sequence, during the first read operation.
* <p/>
* This is useful mainly if you need to read all the thumbnails, and you
* need them in random order, as it requires less repositioning in the
* underlying stream.
*
* @param pLoadEagerly {@code true} if the reader should read all thumbs on first read
*/
public void setLoadEagerly(final boolean pLoadEagerly) {
mLoadEagerly = pLoadEagerly;
}
/**
* Reads the image data from the given input stream, and returns it as a
* {@code BufferedImage}.
*
* @param pIndex the index of the image to read
* @param pParam additional parameters used while decoding, may be
* {@code null}, in which case defaults will be used
* @return a {@code BufferedImage}
* @throws IndexOutOfBoundsException if {@code pIndex} is out of bounds
* @throws IllegalStateException if the input source has not been set
* @throws java.io.IOException if an error occurs during reading
*/
@Override
public BufferedImage read(final int pIndex, final ImageReadParam pParam) throws IOException {
init();
checkBounds(pIndex);
// Quick look-up
BufferedImage image = null;
if (pIndex < mThumbnails.length) {
image = mThumbnails[pIndex];
}
if (image == null) {
// Read the image, it's a JFIF stream, inside the OLE 2 CompoundDocument
init(pIndex);
image = mReader.read(0, pParam);
mReader.reset();
if (pParam == null) {
mThumbnails[pIndex] = image; // TODO: Caching is not kosher, as images are mutable!!
}
}
else {
// Keep progress listeners happy
processImageStarted(pIndex);
processImageProgress(100);
processImageComplete();
}
// Fake destination support
if (pParam != null && (pParam.getDestination() != null && pParam.getDestination() != image ||
pParam.getDestinationType() != null && pParam.getDestinationType().getBufferedImageType() != image.getType())) {
BufferedImage destination = getDestination(pParam, getImageTypes(pIndex), getWidth(pIndex), getHeight(pIndex));
Graphics2D g = destination.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.drawImage(image, 0, 0, null);
}
finally {
g.dispose();
}
image = destination;
}
return image;
}
/**
* Reads the image data from the given input stream, and returns it as a
* {@code BufferedImage}.
*
* @param pName the name of the image to read
* @param pParam additional parameters used while decoding, may be
* {@code null}, in which case defaults will be used
* @return a {@code BufferedImage}
* @throws java.io.FileNotFoundException if the given file name is not found in the
* "Catalog" entry of the {@code CompoundDocument}
* @throws IllegalStateException if the input source has not been set
* @throws java.io.IOException if an error occurs during reading
*/
public BufferedImage read(final String pName, final ImageReadParam pParam) throws IOException {
initCatalog();
int index = mCatalog.getIndex(pName);
if (index < 0) {
throw new FileNotFoundException("Name not found in \"Catalog\" entry: " + pName);
}
return read(index, pParam);
}
public void abort() {
super.abort();
mReader.abort();
}
@Override
public void setInput(Object pInput, boolean pSeekForwardOnly, boolean pIgnoreMetadata) {
super.setInput(pInput, pSeekForwardOnly, pIgnoreMetadata);
if (mImageInput != null) {
mImageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
}
}
private void init(final int pIndex) throws IOException {
if (mCurrentImage == -1 || pIndex != mCurrentImage || mReader.getInput() == null) {
init();
checkBounds(pIndex);
mCurrentImage = pIndex;
initReader(pIndex);
}
}
private void initReader(final int pIndex) throws IOException {
String name = mCatalog.getStreamName(pIndex);
Entry entry = mRoot.getChildEntry(name);
// TODO: It might be possible to speed this up, with less wrapping...
// Use in-memory input stream for max speed, images are small
ImageInputStream input = new MemoryCacheImageInputStream(entry.getInputStream());
input.skipBytes(THUMBNAIL_OFFSET);
mReader.setInput(input);
}
private void initReaderListeners() {
mReader.addIIOReadProgressListener(new ProgressListenerBase() {
@Override
public void imageComplete(ImageReader pSource) {
processImageComplete();
}
@Override
public void imageStarted(ImageReader pSource, int pImageIndex) {
processImageStarted(mCurrentImage);
}
@Override
public void imageProgress(ImageReader pSource, float pPercentageDone) {
processImageProgress(pPercentageDone);
}
@Override
public void readAborted(ImageReader pSource) {
processReadAborted();
}
});
// TODO: Update listeners
// TODO: Warning listeners
}
private void init() throws IOException {
assertInput();
if (mRoot == null) {
mRoot = new CompoundDocument(mImageInput).getRootEntry();
SortedSet children = mRoot.getChildEntries();
mThumbnails = new BufferedImage[children.size() - 1];
initCatalog();
// NOTE: This is usually slower, unless you need all images
// TODO: Use as many threads as there are CPU cores? :-)
if (mLoadEagerly) {
for (int i = 0; i < mThumbnails.length; i++) {
initReader(i);
ImageReader reader = mReader;
// TODO: If stream was detached, we could probably create a
// new reader, then fire this off in a separate thread...
mThumbnails[i] = reader.read(0, null);
}
}
}
}
private void initCatalog() throws IOException {
if (mCatalog == null) {
Entry catalog = mRoot.getChildEntry("Catalog");
if (catalog.length() <= 16L) {
// TODO: Throw exception? Return empty catalog?
}
mCatalog = Catalog.read(catalog.getInputStream());
}
}
public int getNumImages(boolean pAllowSearch) throws IOException {
if (pAllowSearch) {
init();
}
return mCatalog != null ? mCatalog.getThumbnailCount() : super.getNumImages(false);
}
public int getWidth(int pIndex) throws IOException {
init(pIndex);
BufferedImage image = mThumbnails[pIndex];
return image != null ? image.getWidth() : mReader.getWidth(0);
}
public int getHeight(int pIndex) throws IOException {
init(pIndex);
BufferedImage image = mThumbnails[pIndex];
return image != null ? image.getHeight() : mReader.getHeight(0);
}
public Iterator<ImageTypeSpecifier> getImageTypes(int pIndex) throws IOException {
init(pIndex);
initReader(pIndex);
return mReader.getImageTypes(0);
}
public boolean isPresent(final String pFileName) {
try {
init();
}
catch (IOException e) {
resetMembers();
return false;
}
// TODO: Rethink this...
// Seems to be up to Windows and the installed programs what formats
// are supported...
// Some thumbs are just icons, and it might be better to use ImageIO to create thumbs for these... :-/
// At least this seems fine for now
String extension = FileUtil.getExtension(pFileName);
if (StringUtil.isEmpty(extension)) {
return false;
}
extension = extension.toLowerCase();
return !"psd".equals(extension) && !"svg".equals(extension) && mCatalog != null && mCatalog.getIndex(pFileName) != -1;
}
/// Test code below
public static void main(String[] pArgs) throws IOException {
ThumbsDBImageReader reader = new ThumbsDBImageReader();
reader.setInput(ImageIO.createImageInputStream(new File(pArgs[0])));
// reader.setLoadEagerly(true);
if (pArgs.length > 1) {
long start = System.currentTimeMillis();
reader.init();
for (Catalog.CatalogItem item : reader.mCatalog) {
reader.read(item.getName(), null);
}
long end = System.currentTimeMillis();
System.out.println("Time: " + (end - start) + " ms");
}
else {
JFrame frame = createWindow(pArgs[0]);
JPanel panel = new JPanel();
panel.setBackground(Color.WHITE);
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
long start = System.currentTimeMillis();
reader.init();
for (Catalog.CatalogItem item : reader.mCatalog) {
addImage(panel, reader, reader.mCatalog.getIndex(item.getName()), item.getName());
}
long end = System.currentTimeMillis();
System.out.println("Time: " + (end - start) + " ms");
frame.getContentPane().add(new JScrollPane(panel));
frame.pack();
frame.setVisible(true);
}
}
private static JFrame createWindow(final String pTitle) {
JFrame frame = new JFrame(pTitle);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e) {
System.exit(0);
}
});
return frame;
}
private static void addImage(Container pParent, ImageReader pReader, int pImageNo, String pName) throws IOException {
final JLabel label = new JLabel();
final BufferedImage image = pReader.read(pImageNo);
label.setIcon(new Icon() {
private static final int SIZE = 110;
public void paintIcon(Component c, Graphics g, int x, int y) {
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.DARK_GRAY);
g.fillRoundRect(x, y, SIZE, SIZE, 10, 10);
g.drawImage(image, (SIZE - image.getWidth()) / 2 + x, (SIZE - image.getHeight()) / 2 + y, null);
}
public int getIconWidth() {
return SIZE;
}
public int getIconHeight() {
return SIZE;
}
});
label.setText("" + image.getWidth() + "x" + image.getHeight() + ": " + pName);
label.setToolTipText(image.toString());
pParent.add(label);
}
}

View File

@@ -0,0 +1,179 @@
/*
* 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.thumbsdb;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.io.ole2.CompoundDocument;
import javax.imageio.ImageReader;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.Locale;
/**
* ThumbsDBImageReaderSpi
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ThumbsDBImageReaderSpi.java,v 1.0 28.feb.2006 19:21:05 haku Exp$
*/
public class ThumbsDBImageReaderSpi extends ImageReaderSpi {
private ImageReaderSpi mJPEGProvider;
/**
* Creates a {@code ThumbsDBImageReaderSpi}.
*/
public ThumbsDBImageReaderSpi() {
this(IIOUtil.getProviderInfo(ThumbsDBImageReaderSpi.class));
}
private ThumbsDBImageReaderSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(),
pProviderInfo.getVersion(),
new String[]{"thumbs", "THUMBS", "Thumbs DB"},
new String[]{"db"},
new String[]{"image/x-thumbs-db", "application/octet-stream"}, // TODO: Check IANA et al...
ThumbsDBImageReader.class.getName(),
STANDARD_INPUT_TYPE,
null,
true, null, null, null, null,
true, null, null, null, null
);
}
public boolean canDecodeInput(Object source) throws IOException {
return source instanceof ImageInputStream && canDecode((ImageInputStream) source);
}
public boolean canDecode(ImageInputStream pInput) throws IOException {
maybeInitJPEGProvider();
// If this is a OLE 2 CompoundDocument, we could try...
// TODO: How do we know it's thumbs.db format (structure), without reading quite a lot?
return mJPEGProvider != null && CompoundDocument.canRead(pInput);
}
public ImageReader createReaderInstance(Object extension) throws IOException {
return new ThumbsDBImageReader(this);
}
private void maybeInitJPEGProvider() {
// NOTE: Can't do this from constructor, as ImageIO itself is not initialized yet,
// and the lookup below will produce a NPE..
// TODO: A better approach...
// - Could have a list with known working JPEG decoders?
// - System property?
// - Class path lookup of properties file with reader?
// This way we could deregister immediately
if (mJPEGProvider == null) {
ImageReaderSpi provider = null;
try {
Iterator<ImageReaderSpi> providers = getJPEGProviders();
while (providers.hasNext()) {
provider = providers.next();
// Prefer the one we know
if ("Sun Microsystems, Inc.".equals(provider.getVendorName())) {
break;
}
}
}
catch (Exception ignore) {
// It's pretty safe to assume there's always a JPEG reader out there
// In any case, we deregister the provider if there isn't one
IIORegistry.getDefaultInstance().deregisterServiceProvider(this, ImageReaderSpi.class);
}
mJPEGProvider = provider;
}
}
private Iterator<ImageReaderSpi> getJPEGProviders() {
return IIORegistry.getDefaultInstance().getServiceProviders(
ImageReaderSpi.class,
new ServiceRegistry.Filter() {
public boolean filter(Object provider) {
if (provider instanceof ImageReaderSpi) {
ImageReaderSpi spi = (ImageReaderSpi) provider;
for (String format : spi.getFormatNames()) {
if ("JPEG".equals(format)) {
return true;
}
}
}
return false;
}
}, true
);
}
/**
* Returns a new {@code ImageReader} that can read JPEG images.
*
* @return a new {@code ImageReader} that can read JPEG images.
* @throws IllegalStateException if no JPEG provider was found
* @throws Error if the reader can't be instantiated
*/
ImageReader createJPEGReader() {
maybeInitJPEGProvider();
if (mJPEGProvider == null) {
throw new IllegalStateException("No suitable JPEG reader provider found");
}
try {
return mJPEGProvider.createReaderInstance();
}
catch (IOException e) {
// NOTE: The default Sun version never throws IOException here
throw new Error("Could not create JPEG reader: " + e.getMessage(), e);
}
}
public String getDescription(Locale locale) {
return "Microsoft Windows Thumbs DB (Thumbs.db) image reader";
}
// @Override
// public void onRegistration(ServiceRegistry registry, Class<?> category) {
// System.out.println("ThumbsDBImageReaderSpi.onRegistration");
// maybeInitJPEGProvider();
// if (mJPEGProvider == null) {
// System.out.println("Deregistering");
// registry.deregisterServiceProvider(this, ImageReaderSpi.class);
// }
// }
}

View File

@@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.thumbsdb.ThumbsDBImageReaderSpi

View File

@@ -0,0 +1,139 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.thumbsdb;
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import com.twelvemonkeys.io.ole2.CompoundDocument;
import com.twelvemonkeys.io.ole2.Entry;
import com.twelvemonkeys.lang.SystemUtil;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.awt.*;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.List;
/**
* ICOImageReaderTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ICOImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/
public class ThumbsDBImageReaderTestCase extends ImageReaderAbstractTestCase<ThumbsDBImageReader> {
private static final boolean IS_JAVA_6 = SystemUtil.isClassAvailable("java.util.Deque");
private ThumbsDBImageReaderSpi mProvider = new ThumbsDBImageReaderSpi();
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(
getClassLoaderResource("/thumbsdb/Thumbs.db"),
new Dimension(96, 96), new Dimension(96, 96), new Dimension(16, 16),
new Dimension(96, 45), new Dimension(63, 96), new Dimension(96, 96),
new Dimension(96, 64), new Dimension(96, 96), new Dimension(96, 77)
),
new TestData(
getClassLoaderResource("/thumbsdb/Thumbs-camera.db"),
new Dimension(96, 96),
new Dimension(64, 96),
new Dimension(96, 96),
new Dimension(96, 64), new Dimension(64, 96),
new Dimension(96, 64), new Dimension(96, 64), new Dimension(96, 64),
new Dimension(64, 96), new Dimension(64, 96), new Dimension(64, 96),
new Dimension(64, 96), new Dimension(64, 96), new Dimension(64, 96),
new Dimension(96, 64),
new Dimension(64, 96),
new Dimension(96, 64), new Dimension(96, 64), new Dimension(96, 64),
new Dimension(64, 96), new Dimension(64, 96),
new Dimension(64, 96), new Dimension(64, 96), new Dimension(64, 96)
)
);
}
protected ImageReaderSpi createProvider() {
return mProvider;
}
@Override
protected ThumbsDBImageReader createReader() {
return new ThumbsDBImageReader(mProvider);
}
protected Class<ThumbsDBImageReader> getReaderClass() {
return ThumbsDBImageReader.class;
}
protected List<String> getFormatNames() {
return Arrays.asList("thumbs");
}
protected List<String> getSuffixes() {
return Arrays.asList("db");
}
protected List<String> getMIMETypes() {
return Arrays.asList("image/x-thumbs-db");
}
public void testArrayIndexOutOfBoundsBufferedReadBug() throws IOException {
ImageInputStream input = new BufferedImageInputStream(new MemoryCacheImageInputStream(getClass().getResourceAsStream("/thumbsdb/Thumbs-camera.db")));
input.setByteOrder(ByteOrder.LITTLE_ENDIAN);
Entry root = new CompoundDocument(input).getRootEntry();
Entry child = root.getChildEntry("Catalog");
child.getInputStream();
}
@Override
public void testSetDestination() throws IOException {
// Known bug in Sun JPEGImageReader before Java 6
if (IS_JAVA_6) {
super.testSetDestination();
}
else {
System.err.println("WARNING: Test skipped due to known bug in Java 1.5, please test again with Java 6 or later");
}
}
@Override
public void testSetDestinationType() throws IOException {
// Known bug in Sun JPEGImageReader before Java 6
if (IS_JAVA_6) {
super.testSetDestinationType();
}
else {
System.err.println("WARNING: Test skipped due to known bug in Java 1.5, please test again with Java 6 or later");
}
}
}

View File

@@ -0,0 +1,14 @@
- Listeners
- Expose one of the methods below:
- String getName(int pIndex)
- String[] getNames()
- Catalog getCatalog()
- Make it multi-threaded? Or at least support multi-threaded reading
- There seems to be different versions of the Thumbs.db:
- http://vinetto.sourceforge.net/docs.html
- We probably want to support all of these
DONE: