Skip to main content

RE: [JAI-IMAGEIO] Getting filename to codec?

2 replies [Last post]
Anonymous

This seems to be a fairly common problem, I guess I'll chime in here
with my experiences. I call it the "associated files" problem, because
it deals with the need to find other files that are associated with a
given input file. I just completed a design that accommodates this
situation last week, so it's fresh in my mind.

We originally used a specialized ImageReadParam to pass the File
(or the filename) into the reader. But it seems that for certain
cases, the ImageReadParam isn't available early enough in the
decoding process, or perhaps not at the right times.

Our second implementation was to create our own custom implementation
of ImageInputStream that contained the file (much like the solution
talked about below). This works fine, although we didn't use the
ImageInputStreamSpi but rather just created the custom stream
directly.

Then we encountered problems when we wanted our readers to be able
to read from, say, an HTTP stream or some other non-file stream.
The issue of locating "associated resources" (the other file or
files, with different extensions or whatever) becomes more tricky
in these cases. We needed to abstract this so we could open
images and other resources from any source.

We considered using a custom ImageInputStreamSpi, but these are
keyed off the java class of the input object (from which you are
trying to generate an ImageInputStream). In our case, for example,
we might want a different implementation depending on whether
we're creating an http URL or some other kind of URL. So there is
no way, using the ImageInputStreamSpi, to differentiate between
one kind of URL and another (since the Spi mechanism uses the java
class to choose a Spi).

What I came up with relies a little bit on our architecture that
was already in place, but its principle could be adapted to any
application. Essentially, we have an abstraction of the
ImageReader called an ImageSource, which allows you to get
image attributes and metadata, as well as RenderedImages. Then
there is an ImageSourceFactory that constructs these based on
an identifier -- in most cases a filename, but possibly also
a URL or other token.

The ImageSourceFactory creates a stream and uses it to perform
a lookup from ImageIO.getImageReaders(). The stream itself is
created using a special interface called a StreamFactory (surprise).
StreamFactory implementations are plugin objects that use a SPI
mechanism (much like the ImageReaderSpis). A resource name
(filename, URL string etc) is passed to a lookup object that
identifies the appropriate StreamFactory for the resource.
The resource name and the StreamFactory are bundled up in a
wrapper object we call a SourceID (which is actually built in
a different place).

The StreamFactory is used by the ImageSourceFactory to produce
an ImageInputStream for the ImageReader lookup. Once an
ImageReader is located, we check to see if it is "one of ours",
i.e. an ImageReader implemented in-house that requires this
ability to locate associated files. If so, we have a custom
API (just a one-method interface) that allows passing the
custom object to the reader BEFORE it is used in the app.

In pseudocode it's something like this:

public ImageSource createImageSource(String resource)
{
// use custom SPI mechanism to find a StreamFactory
StreamFactory factory = lookupStreamFactory(resource);
SourceID sid = new SourceID(resource, factory);

ImageInputStream stream = factory.createImageInputStream(resource);
ImageReader reader = getImageReaderFromImageIO(stream);
if(reader instanceof SourceIDImageReader)
{
// this gives our custom readers access to the filename and stream
factory
((SourceIDImageReader)reader).setSourceID(sid);
}

return new ImageIoImageSource(reader);
}

The only thing that is NOT handled by this code is the case where
your ImageReaderSpi needs to know about associated files BEFORE it can
decide whether a given image is supported by your ImageReader. As you
can see, we're not embedding the filename/resource name directly IN
the ImageInputStream. In our case, the SourceID encapsulates that. But
you certainly could do something much like the above, where the
StreamFactory puts the resource name directly in the ImageInputStream
that it creates. Then you could get access to it on the canDecodeInput()
calls to your ImageReaderSpi.

I hope this description helps somebody or provides food for thought.

Mike

> -----Original Message-----
> From: Fabrizio Giudici [mailto:Fabrizio.Giudici@tidalwave.it]
> Sent: Thursday, June 01, 2006 3:26 AM
> To: interest@jai-imageio.dev.java.net
> Subject: Re: [JAI-IMAGEIO] Getting filename to codec?
>
>
>
> On Jun 1, 2006, at 6:28 AM, jai-imageio@javadesktop.org wrote:
>
> > I'm implementing an ImageIO codec which is based on
> > an image library that, unfortunately, requires a file
> > name in order to open the image as opposed to supporting
> using already
> > opened files or streams.
> >
> > I see that in writing the codec I can specify different
> > sorts of input objects (ImageInputStream, for example)
> > so it seems the codec can support opening the image
> > based on a string filename. What I don't see is how
> > to get JAI to send a filename across.
> >
> > When I do a JAI.create( "ImageRead", filename ) it
> > seems that the ImageRead operation has already opened
> > the file and turned it into an ImageInputStream by the
> > time it gets to my codec. Is there a trick to getting
> > the data across? Or do I need to write my own JAI
> > operation?
>
> I had a similar problem with a codec I wrote for the Canon
> .CRW format, where unfortunately a single image is split into
> two files with two
> different
> extensions, and matching should be done by file name.
>
> I registered a custom ImageInputStreamSpi, which is a factory that
> can control the
> way an ImageInputStream are created. In this way I instantiate a
> custom ImageInputStream
> which "remembers" the original file:
>
> public class FileImageInputStream2 extends FileImageInputStream
> {
> private File file;
>
> public FileImageInputStream2 (File file) throws
> FileNotFoundException, IOException
> {
> super(file);
> this.file = file;
> }
>
> public File getFile ()
> {
> return file;
> }
> }
>
> In this way, an instance of FileImageInputStream2 is passed to your
> codec and you can invoke
> the getFile() method. There's something to improve, but it's
> actually
> working.
>
> The complete code is available for download at jrawio.dev.java.net,
> look at the package
> it.tidalwave.imageio.io.
>
> --
> Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
> Tidalwave s.a.s. - "We make Java work. Everywhere."
> http://www.tidalwave.it/blog - Fabrizio.Giudici@tidalwave.it
> mobile: +39 348.150.6941 - fax: +39 027.005.105.36
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
> For additional commands, e-mail:
> interest-help@jai-imageio.dev.java.net
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
For additional commands, e-mail: interest-help@jai-imageio.dev.java.net

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Robert Engels

I think your solution is a bit more complex than it needs to be, and uses a
strangely named abstraction.

All you need to do is implement a custom object (MultipleImageInput ?) that
encapsulates both files. It can encapsulate it using a stream for each,
byte[] for each, file for each, url for each, etc. If only one of the
"files" is provided, in most cases you can find the other object, in other
cases (e.g. the first is a byte[]) you will NEVER be able to find the
second.

Then create an ImageReaderSpi. There is a method canDecodeInput(input), and
only your ImageReaderSpi will be able to do so, and thus it will be used.
You can then create an ImageInputStream for each object in the
MultipleImageInput if that makes things easier in your decoder.

It is impossible to work with a standard ImageInputStream that was created
by the default ImageInputStreamSpis since the ImageInputStream does not have
any meta information on where the stream is located.

You could however also implement the ImageInputStreamSpi and for certain
input types (e.g. file and URL) probably construct an ImageInputStream where
the first object is at offset 0 in the stream, and the second object is at
offset (length of first object). This has problems in that you would need to
make sure your Spi was always asked first since you are using standard input
objects.

You will also have problems with some of the static read() methods in
ImageIO though, since those create the stream from the source (e.g.
url.getInputStream()) before asking any SPIs if they can read a URL. But it
will work for others (e.g. ImageIO.read(File) ). You may be able to
instanceof on the Stream and some casting, but this is going to fail in the
arbitrary case.

In general, this seems like a bad image format design. All related files
should be appended into a single file with an index structure at the start.
Far easier to work with and less chance for corruption.

-----Original Message-----
From: Nidel, Mike [mailto:mike.nidel@lmco.com]
Sent: Friday, June 02, 2006 8:51 AM
To: interest@jai-imageio.dev.java.net
Subject: RE: [JAI-IMAGEIO] Getting filename to codec?

This seems to be a fairly common problem, I guess I'll chime in here with my
experiences. I call it the "associated files" problem, because it deals with
the need to find other files that are associated with a given input file. I
just completed a design that accommodates this situation last week, so it's
fresh in my mind.

We originally used a specialized ImageReadParam to pass the File (or the
filename) into the reader. But it seems that for certain cases, the
ImageReadParam isn't available early enough in the decoding process, or
perhaps not at the right times.

Our second implementation was to create our own custom implementation of
ImageInputStream that contained the file (much like the solution talked
about below). This works fine, although we didn't use the
ImageInputStreamSpi but rather just created the custom stream directly.

Then we encountered problems when we wanted our readers to be able to read
from, say, an HTTP stream or some other non-file stream.
The issue of locating "associated resources" (the other file or files, with
different extensions or whatever) becomes more tricky in these cases. We
needed to abstract this so we could open images and other resources from any
source.

We considered using a custom ImageInputStreamSpi, but these are keyed off
the java class of the input object (from which you are trying to generate an
ImageInputStream). In our case, for example, we might want a different
implementation depending on whether we're creating an http URL or some other
kind of URL. So there is no way, using the ImageInputStreamSpi, to
differentiate between one kind of URL and another (since the Spi mechanism
uses the java class to choose a Spi).

What I came up with relies a little bit on our architecture that was already
in place, but its principle could be adapted to any application.
Essentially, we have an abstraction of the ImageReader called an
ImageSource, which allows you to get image attributes and metadata, as well
as RenderedImages. Then there is an ImageSourceFactory that constructs these
based on an identifier -- in most cases a filename, but possibly also a URL
or other token.

The ImageSourceFactory creates a stream and uses it to perform a lookup from
ImageIO.getImageReaders(). The stream itself is created using a special
interface called a StreamFactory (surprise).
StreamFactory implementations are plugin objects that use a SPI mechanism
(much like the ImageReaderSpis). A resource name (filename, URL string etc)
is passed to a lookup object that identifies the appropriate StreamFactory
for the resource.
The resource name and the StreamFactory are bundled up in a wrapper object
we call a SourceID (which is actually built in a different place).

The StreamFactory is used by the ImageSourceFactory to produce an
ImageInputStream for the ImageReader lookup. Once an ImageReader is located,
we check to see if it is "one of ours", i.e. an ImageReader implemented
in-house that requires this ability to locate associated files. If so, we
have a custom API (just a one-method interface) that allows passing the
custom object to the reader BEFORE it is used in the app.

In pseudocode it's something like this:

public ImageSource createImageSource(String resource) { // use custom SPI
mechanism to find a StreamFactory StreamFactory factory =
lookupStreamFactory(resource); SourceID sid = new SourceID(resource,
factory);

ImageInputStream stream = factory.createImageInputStream(resource);
ImageReader reader = getImageReaderFromImageIO(stream);
if(reader instanceof SourceIDImageReader) {
// this gives our custom readers access to the filename and stream factory
((SourceIDImageReader)reader).setSourceID(sid);
}

return new ImageIoImageSource(reader);
}

The only thing that is NOT handled by this code is the case where your
ImageReaderSpi needs to know about associated files BEFORE it can decide
whether a given image is supported by your ImageReader. As you can see,
we're not embedding the filename/resource name directly IN the
ImageInputStream. In our case, the SourceID encapsulates that. But you
certainly could do something much like the above, where the StreamFactory
puts the resource name directly in the ImageInputStream that it creates.
Then you could get access to it on the canDecodeInput() calls to your
ImageReaderSpi.

I hope this description helps somebody or provides food for thought.

Mike

> -----Original Message-----
> From: Fabrizio Giudici [mailto:Fabrizio.Giudici@tidalwave.it]
> Sent: Thursday, June 01, 2006 3:26 AM
> To: interest@jai-imageio.dev.java.net
> Subject: Re: [JAI-IMAGEIO] Getting filename to codec?
>
>
>
> On Jun 1, 2006, at 6:28 AM, jai-imageio@javadesktop.org wrote:
>
> > I'm implementing an ImageIO codec which is based on an image library
> > that, unfortunately, requires a file name in order to open the image
> > as opposed to supporting
> using already
> > opened files or streams.
> >
> > I see that in writing the codec I can specify different sorts of
> > input objects (ImageInputStream, for example) so it seems the codec
> > can support opening the image based on a string filename. What I
> > don't see is how to get JAI to send a filename across.
> >
> > When I do a JAI.create( "ImageRead", filename ) it seems that the
> > ImageRead operation has already opened the file and turned it into
> > an ImageInputStream by the time it gets to my codec. Is there a
> > trick to getting the data across? Or do I need to write my own JAI
> > operation?
>
> I had a similar problem with a codec I wrote for the Canon .CRW
> format, where unfortunately a single image is split into two files
> with two different extensions, and matching should be done by file
> name.
>
> I registered a custom ImageInputStreamSpi, which is a factory that can
> control the way an ImageInputStream are created. In this way I
> instantiate a custom ImageInputStream which "remembers" the original
> file:
>
> public class FileImageInputStream2 extends FileImageInputStream
> {
> private File file;
>
> public FileImageInputStream2 (File file) throws
> FileNotFoundException, IOException
> {
> super(file);
> this.file = file;
> }
>
> public File getFile ()
> {
> return file;
> }
> }
>
> In this way, an instance of FileImageInputStream2 is passed to your
> codec and you can invoke the getFile() method. There's something to
> improve, but it's actually working.
>
> The complete code is available for download at jrawio.dev.java.net,
> look at the package it.tidalwave.imageio.io.
>
> --
> Fabrizio Giudici, Ph.D. - Java Architect, Project Manager Tidalwave
> s.a.s. - "We make Java work. Everywhere."
> http://www.tidalwave.it/blog - Fabrizio.Giudici@tidalwave.it
> mobile: +39 348.150.6941 - fax: +39 027.005.105.36
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
> For additional commands, e-mail:
> interest-help@jai-imageio.dev.java.net
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
For additional commands, e-mail: interest-help@jai-imageio.dev.java.net

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
For additional commands, e-mail: interest-help@jai-imageio.dev.java.net

Fabrizio Giudici

I'm reading with interest this thread to find out if I can improve my
design in jrawio.

But I'd point out that - unless, and it's probable - I'm missing
something, the alternate solutions provided require for dealing with
custom code in the application, creating a wrapper around ImageIO,
while my "FileImageInputStream2" approach lies entirely within the
implementation of the Image I/O Spi.

Sometimes specialized code could be ok, others not (e.g. when
implementing a custom image reader that should be completely
decoupled from the image format details).

Furthermore FileImageInputStream2 could be easily extended to deal
with URLs.The 'read from memory' case is always a problem, with any
implementation, as memory is not aware of any resource name - this
case would always require custom code at application level.

Finally, concatenating the two streams sometimes could be not an
option, if the size of the former stream is unknown.

--
Fabrizio Giudici, Ph.D. - Java Architect, Project Manager
Tidalwave s.a.s. - "We make Java work. Everywhere."
http://www.tidalwave.it/blog - Fabrizio.Giudici@tidalwave.it
mobile: +39 348.150.6941 - fax: +39 027.005.105.36

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
For additional commands, e-mail: interest-help@jai-imageio.dev.java.net