Skip to main content

[JAI-IMAGEIO] Wirting image metadata to control animated gif delays

11 replies [Last post]
Anonymous

Hi, I'm trying to write an animated gif with variable frame delays (they may
differ for each frame). I understand I have to manually specify the image
metadata to the ImageWriter, but I can't seem to find any straightforward
guidelines on how to do so.

Basically I need to specify the "Delay Time" field of the the "Graphic
Control Extension" block before each image in the GIF file output.

I am using the prepareWriteSequence, writeToSequence and endWriteSequence
methods to create the animated GIF. This currently works but sets the delay
on each frame to 0. I would assume I need to change the ImageWriteParam
argument of writeToSequence every time I call it, but I'm not sure how what
I should be doing on the ImageWriteParam.

Any advice or pointers to info would be greatly appreciated!

Thanks!

Marc Boscher
Communications Digicharm Inc.

---------------------------------------------------------------------
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.
Marc Boscher

I have been playing with the netscape application extension for animated gif
looping and have hit a few problems, potentially pointing to a bug:

- First I tried adding the ApplicationExtensions to the stream metadata,
within the logical screen descriptor element (as suggested by the specs at
http://www.let.rug.nl/~kleiweg/gif/netscape.html). This is performed by the
method setGifToLoopIndefinitely_old below. Unfortunately, the
ApplicationsExtensions element is stripped from the tree when calling
setFromTree. For some reason the gif plugin is dropping it. So no looping
there.

- Second I tried Brian's approach of setting the ApplicationExtensions
directly in the image metadata after the GraphicControlExtension (which does
not seem to follow the specs above). I have had some successes, but also the
following problems getting it to work with "Windows Picture and Fax Viewer"
(called WPFV below).

1) By adding the ApplicationExtension to the Metadata of each image frame,
WPFV becomes only able to display the first frame; the animation is never
started. Everything, including looping works correctly in IE and Firefox.

2) By updating the code to add the ApplicationExtension to the Metadata of
ONLY the first image frame, WPFV can play the animation again, but does not
loop it correctly (plays once only). Everything, including looping works
correctly in IE and Firefox.

3) If using the ImageWriter com.sun.imageio.plugins.gif.GIFImageWriter
instead of com.sun.media.imageioimpl.plugins.gif.GIFImageWriter, the output
is no longer viewable by WPFV: get a "drawing failed". Everything, including
looping works correctly in IE and Firefox.

Adobe Fireworks which supports animated gif editing, did not recognize that
looping was activated in all cases above. It looks as though IE and Firefox
are more tolerant of invalid gif formatting, but not WPFV or Fireworks.
Since WPFV is the default viewer in windows, supporting it is important to
us.

POTENTIAL BUG
-------------
In cases 2) and 3) above, when looking at the outputted gif with a hex
editor (sample output from case 2 is attached), I noticed that there are two
instances of the ASCII characters "NETSCAPE2.0" one after the other, with
only the second one looking right. There should be only one.

The string is (on two lines):
!ÿ.NETSCAPE2.0.
!ÿ.NETSCAPE2.0.....

With the corresponding hex values (on two lines):
21 FF 0B 4E 45 54 53 43 41 50 45 32 2E 30 13
21 FF 0B 4E 45 54 53 43 41 50 45 32 2E 30 03 01 00 00 00

It looks as though the encoder is adding a redundant netscape extension (the
first lines above)

Any ideas, suggestions?

I am pasting some code below. It is not self runnable. The entry point is
the exportToGif method. The Frame object here simply holds a RenderedImage
and a frame delay. Hopefully this can also help someone else who is
struggling with outputing animated gifs like me. Maybe I'll just pay for the
Gif4J library...

Thanks

Marc Boscher
Communications Digicharm Inc.

/**
* Export a sequence of Frame objects to the animated GIF file format.
This
* will use frame timing information.
* @param frames the sequence of Frame objects to export.
* @param outputFile the File to export to. No file extension will be
added.
* @throws IOException if there was an error while exporting.
*/
private static void exportToGif(Frame[] frames, File outputFile)
throws IOException {
ImageOutputStream ios = null;
IIOImage iioimg;
ImageWriter writer = null;

try {
// Prepare for writing
Iterator writerIterator = ImageIO.getImageWritersByFormatName("GIF");
writer = (ImageWriter)writerIterator.next();
// Uncomment following line to use
// com.sun.imageio.plugins.gif.GIFImageWriter instead of
// com.sun.media.imageioimpl.plugins.gif.GIFImageWriter
// writer = (ImageWriter)writerIterator.next();
System.out.println(writer);
ios = ImageIO.createImageOutputStream(outputFile);
writer.setOutput(ios);
ImageWriteParam writeParam = writer.getDefaultWriteParam();
IIOMetadata imageMetadata = writer.getDefaultImageMetadata(

ImageTypeSpecifier.createFromRenderedImage(frames[0].getRendering()),
writeParam);
IIOMetadata imageMetadata2 = writer.getDefaultImageMetadata(

ImageTypeSpecifier.createFromRenderedImage(frames[0].getRendering()),
writeParam);
IIOMetadata streamMetadata =
writer.getDefaultStreamMetadata(writeParam);

// Uncomment next line to try to set looping in the stream metadata
// streamMetadata = setGifToLoopIndefinitely_old(streamMetadata);

writer.prepareWriteSequence(streamMetadata);

// Build image
for (int i = 0; i < frames.length; i++) {
// Change to "if (i == -1)" to remove the looping setup from the
first
// image metadata
if (i == 0) {
imageMetadata2 = updateGifFrameDelay(imageMetadata2, frames[i]
.getDuration() / 10);

imageMetadata2 = setGifToLoopIndefinitely(imageMetadata2);
iioimg = new IIOImage(PlanarImage.wrapRenderedImage(frames[i]
.getRendering()), null, imageMetadata2);
writer.writeToSequence(iioimg, writeParam);
}
else {
imageMetadata = updateGifFrameDelay(imageMetadata, frames[i]
.getDuration() / 10);

iioimg = new IIOImage(PlanarImage.wrapRenderedImage(frames[i]
.getRendering()), null, imageMetadata);
writer.writeToSequence(iioimg, writeParam);
}

}
writer.endWriteSequence();
}
catch (IOException e) {
throw e;
}

// The finally block will handle cleanup after a premature return, an
// exception or a success.
finally {
if (ios != null) {
try {
ios.close();
}
catch (IOException e) {
// ignore
}
}
if (writer != null)
writer.dispose();
}
}

/**
* Set the frame delay of a GIF image IIOMetaData.
* @param metaData the image IIOMetadata to modify. The argument is
modified
* directly.
* @param delay the new delay to set in 1/100 of a second.
* @return the modified IIOMetadata metaData argument for convenience.
*/
private static IIOMetadata updateGifFrameDelay(IIOMetadata metaData, int
delay) {

String formatName = "javax_imageio_gif_image_1.0";
String gceNodeName = "GraphicControlExtension";
String delayNodeName = "delayTime";

Node tree = metaData.getAsTree(formatName);

// Drill down to GraphicControlExtension element
NodeList nodeList = tree.getChildNodes();
Node gceNode = null;
for (int i = 0, len = nodeList.getLength(); i < len; i++) {
Node curNode = nodeList.item(i);
if (curNode.getNodeName().equals(gceNodeName)) {
gceNode = curNode;
break;
}
}
if (gceNode == null)
throw new IllegalArgumentException(
"Invalid image metadata, could not find " + gceNodeName + "
node.");

// Drill down to delayTime attribute
NamedNodeMap nodeMap = gceNode.getAttributes();
Node delayNode = nodeMap.getNamedItem(delayNodeName);
// Create attribute if non-existent
if (delayNode == null) {
delayNode = tree.getOwnerDocument().createAttribute(delayNodeName);
gceNode.appendChild(delayNode);
}
delayNode.setNodeValue(Integer.valueOf(delay).toString());

try {
metaData.setFromTree(formatName, tree);
}
catch (IIOInvalidTreeException e) {
throw new IllegalArgumentException(
"Invalid image metadata, could not merge tree back in.");
}
return metaData;
}

private static IIOMetadata setGifToLoopIndefinitely(IIOMetadata metaData)
{

String formatName = "javax_imageio_gif_image_1.0";

Node tree = metaData.getAsTree(formatName);

// Set loop counter
IIOMetadataNode aes = new IIOMetadataNode("ApplicationExtensions");
IIOMetadataNode ae = new IIOMetadataNode("ApplicationExtension");
ae.setAttribute("applicationID", "NETSCAPE");
ae.setAttribute("authenticationCode", "2.0");
byte[] uo = new byte[] { (byte)0x21, (byte)0xff, (byte)0x0b, (byte)'N',
(byte)'E', (byte)'T', (byte)'S', (byte)'C', (byte)'A', (byte)'P',
(byte)'E', (byte)'2', (byte)'.', (byte)'0', (byte)0x03, (byte)0x01,
(byte)0x00, (byte)0x00, (byte)0x00 };
ae.setUserObject(uo);
aes.appendChild(ae);
tree.appendChild(aes);

try {
metaData.setFromTree(formatName, tree);
}
catch (IIOInvalidTreeException e) {
throw new IllegalArgumentException(
"Invalid image metadata, could not merge tree back in.");
}
return metaData;
}

/**
* Update a GIF stream IIOMetaData so that the GIF animation loops
* indefinitely. This actually sets the loop count to 0 using the Netscape
* extension for loop count (loop count is not part of the official GIF
* specifications).
* @param metaData the stream IIOMetadata to modify. The argument is
modified
* directly.
* @return the modified IIOMetadata metaData argument for convenience.
*/
private static IIOMetadata setGifToLoopIndefinitely_old(IIOMetadata
metaData) {

String formatName = "javax_imageio_gif_stream_1.0";
String lsdNodeName = "LogicalScreenDescriptor";

Node tree = metaData.getAsTree(formatName);

// Drill down to LogicalScreenDescriptor element
NodeList nodeList = tree.getChildNodes();
IIOMetadataNode lsdNode = null;
for (int i = 0, len = nodeList.getLength(); i < len; i++) {
Node curNode = nodeList.item(i);
if (curNode.getNodeName().equals(lsdNodeName)) {
lsdNode = (IIOMetadataNode)curNode;
break;
}
}
if (lsdNode == null)
throw new IllegalArgumentException(
"Invalid stream metadata, could not find " + lsdNodeName + "
node.");

// Setup the LogicalScreenDescriptor attributes
lsdNode.setAttribute("logicalScreenWidth", "120");
lsdNode.setAttribute("logicalScreenHeight", "60");
lsdNode.setAttribute("colorResolution", "5");
lsdNode.setAttribute("pixelAspectRatio", "0");

// Create and append application extension for loop count
// see http://www.let.rug.nl/~kleiweg/gif/netscape.html for details on
// extension specs.
IIOMetadataNode aes = new IIOMetadataNode("ApplicationExtensions");
IIOMetadataNode ae = new IIOMetadataNode("ApplicationExtension");
ae.setAttribute("applicationID", "NETSCAPE");
ae.setAttribute("authenticationCode", "2.0");
byte[] uo = new byte[] { (byte)0x21, (byte)0xff, (byte)0x0b, (byte)'N',
(byte)'E', (byte)'T', (byte)'S', (byte)'C', (byte)'A', (byte)'P',
(byte)'E', (byte)'2', (byte)'.', (byte)'0', (byte)0x03, (byte)0x01,
(byte)0x00, (byte)0x00, (byte)0x00 };
ae.setUserObject(uo);
aes.appendChild(ae);
lsdNode.appendChild(aes);

try {
metaData.setFromTree(formatName, tree);
}
catch (IIOInvalidTreeException e) {
throw new IllegalArgumentException(
"Invalid stream metadata, could not merge tree back in.", e);
}
return metaData;
}

-----Original Message-----
From: Brian.Burkhalter@Sun.COM [mailto:Brian.Burkhalter@Sun.COM]
Sent: April 2, 2007 6:29 PM
To: interest@jai-imageio.dev.java.net
Subject: RE: [JAI-IMAGEIO] Wirting image metadata to control animated gif
delays

On Mon, 2 Apr 2007, Marc Boscher wrote:

> Ok, got the frame delay set correctly. For reference purposes you need to:
>
> - For each IIOImage you create, get the tree for the
> "javax_imageio_gif_image_1.0" format
> - Find the "GraphicControlExtension" element, usually a child of the
> tree root
> - Modify the value of the "delayTime" attribute
> - overwrite the existing tree with the new one.

Thanks.

> Now I need to modify the looping behaviour of the GIF file. By default
> the produced animations only play once. I need to set the loop count
> to 0 (infinite), but cannot seem to find the right node in the DOM
> tree. I have run two animations (one with infinite looping and one
> looping once) through the MetadataReadExample and cannot seem to find the
property there either.
> Has anyone done this before?
>
> Note that the loop count seems to be a Netscape extension not
> officially part of the W3C speciciations.

That's correct. One listing of the "spec" is here:

http://www.let.rug.nl/~kleiweg/gif/netscape.html

I have used this code with success:

IIOMetadataNode root =
new IIOMetadataNode("javax_imageio_gif_image_1.0");
IIOMetadataNode gce =
new IIOMetadataNode("GraphicControlExtension");
gce.setAttribute("disposalMethod", "none");
gce.setAttribute("userInputFlag", "FALSE");
gce.setAttribute("transparentColorFlag", "FALSE");
gce.setAttribute("delayTime", delayTime);
gce.setAttribute("transparentColorIndex", "255");
root.appendChild(gce);
IIOMetadataNode aes =
new IIOMetadataNode("ApplicationExtensions");
IIOMetadataNode ae =
new IIOMetadataNode("ApplicationExtension");
ae.setAttribute("applicationID", "NETSCAPE");
ae.setAttribute("authenticationCode", "2.0");
byte[] uo = new byte[] {
(byte)0x21, (byte)0xff, (byte)0x0b,
(byte)'N', (byte)'E', (byte)'T', (byte)'S',
(byte)'C', (byte)'A', (byte)'P', (byte)'E',
(byte)'2', (byte)'.', (byte)'0',
(byte)0x03, (byte)0x01, (byte)0x00, (byte)0x00,
(byte)0x00
};
ae.setUserObject(uo);
aes.appendChild(ae);
root.appendChild(aes);

> Cheers!
>
> Marc Boscher
> Communications Digicharm Inc.
>
>
> -----Original Message-----
> From: Brian.Burkhalter@Sun.COM [mailto:Brian.Burkhalter@Sun.COM]
> Sent: April 2, 2007 4:52 PM
> To: interest@jai-imageio.dev.java.net
> Subject: Re: [JAI-IMAGEIO] Wirting image metadata to control animated
> gif delays
>
> I would think that you would need for each frame to do something
> similar to that which is done for setting the TIFF DPI here
>
> https://jai-imageio.dev.java.net/servlets/ReadMsg?list=interest&msgNo=
> 1306
>
> in the setDPIViaDOM() method. In this method a tree containing modes
> with the desired values is used to update the tree obtained from the
> image metadata object.
>
> Brian
>
> On Mon, 2 Apr 2007, Marc Boscher wrote:
>
>> Hi, I'm trying to write an animated gif with variable frame delays
>> (they may differ for each frame). I understand I have to manually
>> specify the image metadata to the ImageWriter, but I can't seem to
>> find any straightforward guidelines on how to do so.
>>
>> Basically I need to specify the "Delay Time" field of the the
>> "Graphic Control Extension" block before each image in the GIF file
output.
>>
>> I am using the prepareWriteSequence, writeToSequence and
>> endWriteSequence methods to create the animated GIF. This currently
>> works but sets the delay on each frame to 0. I would assume I need to
>> change the ImageWriteParam argument of writeToSequence every time I
>> call it, but I'm not sure how what I should be doing on the
> ImageWriteParam.
>>
>> Any advice or pointers to info would be greatly appreciated!
>>
>> Thanks!
>>
>> Marc Boscher
>> Communications Digicharm Inc.
>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
>> For additional commands, e-mail:
>> interest-help@jai-imageio.dev.java.net
>>
>>
>
> ----------------
> Brian Burkhalter
> Java Media, Imaging, and Graphics
> Sun Microsystems, Inc.
>
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> This email message is for the sole use of the intended recipient(s)
> and may contain confidential and privileged information. Any
> unauthorized review, use, disclosure or distribution is prohibited.
> If you are not the intended recipient, please contact the sender by
> reply email and destroy all copies of the original message.
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> ---------------------------------------------------------------------
> 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
>
>

----------------
Brian Burkhalter
Java Media, Imaging, and Graphics
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s) and may
contain confidential and privileged information. Any unauthorized review,
use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by reply
email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

Brian Burkhalter

Are you using the GIF writer in JAI Image I/O Tools or the one in Java SE 6?
What behavior do you see in the one you have *not* been using?

----------------
Brian Burkhalter
Java Media, Imaging, and Graphics
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s)
and may contain confidential and privileged information. Any
unauthorized review, use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

Marc Boscher

I believe I tried both. As described in my last email, I tried with the two
available writers for GIF on JSE6 (1.6.0.1), ie:
1- com.sun.imageio.plugins.gif.GIFImageWriter
2- com.sun.media.imageioimpl.plugins.gif.GIFImageWriter

I am actually getting worse results with writer 1) above (the JSE6 writer I
think). With writer 1) and writing the ApplicationExtension (looping) to
only the first image metadata, the output is no longer viewable by Windows
Picture and Fax Viewer: get a "drawing failed". With writer 2) the animation
is viewable but I am still unable to get looping setup correctly (see my
last email) and still get the weird double NETSCAPE2.0 string in the gif
file.

In other word, I am having problems with both writers.

Marc Boscher
Communications Digicharm Inc.
(514) 448-4475

-----Original Message-----
From: Brian.Burkhalter@Sun.COM [mailto:Brian.Burkhalter@Sun.COM]
Sent: April 3, 2007 5:32 PM
To: interest@jai-imageio.dev.java.net
Subject: RE: [JAI-IMAGEIO] Wirting image metadata to control animated gif
delays

Are you using the GIF writer in JAI Image I/O Tools or the one in Java SE 6?

What behavior do you see in the one you have *not* been using?

----------------
Brian Burkhalter
Java Media, Imaging, and Graphics
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s) and may
contain confidential and privileged information. Any unauthorized review,
use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by reply
email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

---------------------------------------------------------------------
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

Brian Burkhalter

On Wed, 4 Apr 2007, Marc Boscher wrote:

> I believe I tried both. As described in my last email, I tried with the two
> available writers for GIF on JSE6 (1.6.0.1), ie:
> 1- com.sun.imageio.plugins.gif.GIFImageWriter
> 2- com.sun.media.imageioimpl.plugins.gif.GIFImageWriter

Sorry, I missed that.

> I am actually getting worse results with writer 1) above (the JSE6 writer I
> think). With writer 1) and writing the ApplicationExtension (looping) to
> only the first image metadata, the output is no longer viewable by Windows
> Picture and Fax Viewer: get a "drawing failed". With writer 2) the animation
> is viewable but I am still unable to get looping setup correctly (see my
> last email) and still get the weird double NETSCAPE2.0 string in the gif
> file.
>
> In other word, I am having problems with both writers.

OK. We'll review your original posting again and respond later.

Meanwhile I have attached a complete example class which generates a GIF that
infintely loops in Mozilla 1.0 (Firefox) and IE 7. This is the class from
which the excerpt I previously posted in this thread was cut.

----------------
Brian Burkhalter
Java Media, Imaging, and Graphics
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s)
and may contain confidential and privileged information. Any
unauthorized review, use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[FadeToGray.java]
---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
For additional commands, e-mail: interest-help@jai-imageio.dev.java.net

Marc Boscher

Brian,

I ran your sample code "as is" on both JSE 1.6.0.1 and JSE 1.5.0.11. It used
the JAI Image IO tools com.sun.media.imageioimpl.plugins.gif.GIFImageWriter.
In all cases I get an output gif that does not work in Windows Picture and
Fax Viewer. With 1.5.0.11 I get a "No Preview Available" and with 1.6.0.1, I
get "Drawing Failed". In both cases the gif played correctly in both IE and
Firefox. The outputed gif also exhibited the "double NETSCAPE2.0 string"
problem described before.

So I guess you can use that sample code as your test case since it appears
to exhibit the same behaviour.

Thanks again Brian for your great help!

Marc Boscher
Communications Digicharm Inc.

-----Original Message-----
From: Brian.Burkhalter@Sun.COM [mailto:Brian.Burkhalter@Sun.COM]
Sent: April 4, 2007 12:32 PM
To: interest@jai-imageio.dev.java.net
Subject: RE: [JAI-IMAGEIO] Wirting image metadata to control animated gif
delays

On Wed, 4 Apr 2007, Marc Boscher wrote:

> I believe I tried both. As described in my last email, I tried with
> the two available writers for GIF on JSE6 (1.6.0.1), ie:
> 1- com.sun.imageio.plugins.gif.GIFImageWriter
> 2- com.sun.media.imageioimpl.plugins.gif.GIFImageWriter

Sorry, I missed that.

> I am actually getting worse results with writer 1) above (the JSE6
> writer I think). With writer 1) and writing the ApplicationExtension
> (looping) to only the first image metadata, the output is no longer
> viewable by Windows Picture and Fax Viewer: get a "drawing failed".
> With writer 2) the animation is viewable but I am still unable to get
> looping setup correctly (see my last email) and still get the weird
> double NETSCAPE2.0 string in the gif file.
>
> In other word, I am having problems with both writers.

OK. We'll review your original posting again and respond later.

Meanwhile I have attached a complete example class which generates a GIF
that infintely loops in Mozilla 1.0 (Firefox) and IE 7. This is the class
from which the excerpt I previously posted in this thread was cut.

----------------
Brian Burkhalter
Java Media, Imaging, and Graphics
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s) and may
contain confidential and privileged information. Any unauthorized review,
use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by reply
email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

Brian Burkhalter

Marc,

Do you have an example that you could provide of a such an animated GIF which
_does_ work in Windows Picture and Fax Viewer?

Brian

On Wed, 4 Apr 2007, Marc Boscher wrote:

> Brian,
>
> I ran your sample code "as is" on both JSE 1.6.0.1 and JSE 1.5.0.11. It used
> the JAI Image IO tools com.sun.media.imageioimpl.plugins.gif.GIFImageWriter.
> In all cases I get an output gif that does not work in Windows Picture and
> Fax Viewer. With 1.5.0.11 I get a "No Preview Available" and with 1.6.0.1, I
> get "Drawing Failed". In both cases the gif played correctly in both IE and
> Firefox. The outputed gif also exhibited the "double NETSCAPE2.0 string"
> problem described before.
>
> So I guess you can use that sample code as your test case since it appears
> to exhibit the same behaviour.
>
> Thanks again Brian for your great help!
>
> Marc Boscher
> Communications Digicharm Inc.
>
>
> -----Original Message-----
> From: Brian.Burkhalter@Sun.COM [mailto:Brian.Burkhalter@Sun.COM]
> Sent: April 4, 2007 12:32 PM
> To: interest@jai-imageio.dev.java.net
> Subject: RE: [JAI-IMAGEIO] Wirting image metadata to control animated gif
> delays
>
> On Wed, 4 Apr 2007, Marc Boscher wrote:
>
>> I believe I tried both. As described in my last email, I tried with
>> the two available writers for GIF on JSE6 (1.6.0.1), ie:
>> 1- com.sun.imageio.plugins.gif.GIFImageWriter
>> 2- com.sun.media.imageioimpl.plugins.gif.GIFImageWriter
>
> Sorry, I missed that.
>
>> I am actually getting worse results with writer 1) above (the JSE6
>> writer I think). With writer 1) and writing the ApplicationExtension
>> (looping) to only the first image metadata, the output is no longer
>> viewable by Windows Picture and Fax Viewer: get a "drawing failed".
>> With writer 2) the animation is viewable but I am still unable to get
>> looping setup correctly (see my last email) and still get the weird
>> double NETSCAPE2.0 string in the gif file.
>>
>> In other word, I am having problems with both writers.
>
> OK. We'll review your original posting again and respond later.
>
> Meanwhile I have attached a complete example class which generates a GIF
> that infintely loops in Mozilla 1.0 (Firefox) and IE 7. This is the class
> from which the excerpt I previously posted in this thread was cut.
>
> ----------------
> Brian Burkhalter
> Java Media, Imaging, and Graphics
> Sun Microsystems, Inc.
>
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> This email message is for the sole use of the intended recipient(s) and may
> contain confidential and privileged information. Any unauthorized review,
> use, disclosure or distribution is prohibited.
> If you are not the intended recipient, please contact the sender by reply
> email and destroy all copies of the original message.
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
>

----------------
Brian Burkhalter
Java Media, Imaging, and Graphics
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s)
and may contain confidential and privileged information. Any
unauthorized review, use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

npgall
Offline
Joined: 2008-10-20
Points: 0

Hi,

I've also found that the code above adds a duplicate "NETSCAPE2.0 ... NETSCAPE2.0" to output files. I'm using the ImageIO API built into Java 6.

This seems to break GIF images only in some browsers - specifically the GIF images display and loop correctly in Firefox 2 & 3 browser and in the Nokia N95 phone browser, but it causes the 'broken image' symbol in the browsers of Samsung and Windows mobile phones.

Anyway the fix is to replace this code (from above):

byte[] uo = new byte[] {
(byte)0x21, (byte)0xff, (byte)0x0b,
(byte)'N', (byte)'E', (byte)'T', (byte)'S',
(byte)'C', (byte)'A', (byte)'P', (byte)'E',
(byte)'2', (byte)'.', (byte)'0',
(byte)0x03, (byte)0x01, (byte)0x00, (byte)0x00,
(byte)0x00
};

with this code:

int loopCount = 0; // 0 = loop continuously; 1-65535 = a specific number of loops
byte[] uo = new byte[]{
0x1,
(byte) (loopCount & 0xFF), // first byte of loopCount as an unsigned integer in lo-hi byte format
(byte) ((loopCount >> 8) & 0xFF) // second byte of loopCount as an unsigned integer in lo-hi byte format
};

I give credit for the byte array above to Mark from University of Leicester who posted the basis for the code above: http://www.javakb.com/Uwe/Forum.aspx/java-programmer/32892/Help-with-GIF...

A big thanks to Brian for the general example code.

Cheers,

Niall

Brian Burkhalter

I would think that you would need for each frame to do something similar to
that which is done for setting the TIFF DPI here

https://jai-imageio.dev.java.net/servlets/ReadMsg?list=interest&msgNo=1306

in the setDPIViaDOM() method. In this method a tree containing modes with the
desired values is used to update the tree obtained from the image metadata
object.

Brian

On Mon, 2 Apr 2007, Marc Boscher wrote:

> Hi, I'm trying to write an animated gif with variable frame delays (they may
> differ for each frame). I understand I have to manually specify the image
> metadata to the ImageWriter, but I can't seem to find any straightforward
> guidelines on how to do so.
>
> Basically I need to specify the "Delay Time" field of the the "Graphic
> Control Extension" block before each image in the GIF file output.
>
> I am using the prepareWriteSequence, writeToSequence and endWriteSequence
> methods to create the animated GIF. This currently works but sets the delay
> on each frame to 0. I would assume I need to change the ImageWriteParam
> argument of writeToSequence every time I call it, but I'm not sure how what
> I should be doing on the ImageWriteParam.
>
> Any advice or pointers to info would be greatly appreciated!
>
> Thanks!
>
> Marc Boscher
> Communications Digicharm Inc.
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
> For additional commands, e-mail: interest-help@jai-imageio.dev.java.net
>
>

----------------
Brian Burkhalter
Java Media, Imaging, and Graphics
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s)
and may contain confidential and privileged information. Any
unauthorized review, use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

Marc Boscher

Ok, got the frame delay set correctly. For reference purposes you need to:

- For each IIOImage you create, get the tree for the
"javax_imageio_gif_image_1.0" format
- Find the "GraphicControlExtension" element, usually a child of the tree
root
- Modify the value of the "delayTime" attribute
- overwrite the existing tree with the new one.

Now I need to modify the looping behaviour of the GIF file. By default the
produced animations only play once. I need to set the loop count to 0
(infinite), but cannot seem to find the right node in the DOM tree. I have
run two animations (one with infinite looping and one looping once) through
the MetadataReadExample and cannot seem to find the property there either.
Has anyone done this before?

Note that the loop count seems to be a Netscape extension not officially
part of the W3C speciciations.

Cheers!

Marc Boscher
Communications Digicharm Inc.

-----Original Message-----
From: Brian.Burkhalter@Sun.COM [mailto:Brian.Burkhalter@Sun.COM]
Sent: April 2, 2007 4:52 PM
To: interest@jai-imageio.dev.java.net
Subject: Re: [JAI-IMAGEIO] Wirting image metadata to control animated gif
delays

I would think that you would need for each frame to do something similar to
that which is done for setting the TIFF DPI here

https://jai-imageio.dev.java.net/servlets/ReadMsg?list=interest&msgNo=1306

in the setDPIViaDOM() method. In this method a tree containing modes with
the desired values is used to update the tree obtained from the image
metadata object.

Brian

On Mon, 2 Apr 2007, Marc Boscher wrote:

> Hi, I'm trying to write an animated gif with variable frame delays
> (they may differ for each frame). I understand I have to manually
> specify the image metadata to the ImageWriter, but I can't seem to
> find any straightforward guidelines on how to do so.
>
> Basically I need to specify the "Delay Time" field of the the "Graphic
> Control Extension" block before each image in the GIF file output.
>
> I am using the prepareWriteSequence, writeToSequence and
> endWriteSequence methods to create the animated GIF. This currently
> works but sets the delay on each frame to 0. I would assume I need to
> change the ImageWriteParam argument of writeToSequence every time I
> call it, but I'm not sure how what I should be doing on the
ImageWriteParam.
>
> Any advice or pointers to info would be greatly appreciated!
>
> Thanks!
>
> Marc Boscher
> Communications Digicharm Inc.
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
> For additional commands, e-mail:
> interest-help@jai-imageio.dev.java.net
>
>

----------------
Brian Burkhalter
Java Media, Imaging, and Graphics
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s) and may
contain confidential and privileged information. Any unauthorized review,
use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by reply
email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

---------------------------------------------------------------------
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

Brian Burkhalter

On Mon, 2 Apr 2007, Marc Boscher wrote:

> Ok, got the frame delay set correctly. For reference purposes you need to:
>
> - For each IIOImage you create, get the tree for the
> "javax_imageio_gif_image_1.0" format
> - Find the "GraphicControlExtension" element, usually a child of the tree
> root
> - Modify the value of the "delayTime" attribute
> - overwrite the existing tree with the new one.

Thanks.

> Now I need to modify the looping behaviour of the GIF file. By default the
> produced animations only play once. I need to set the loop count to 0
> (infinite), but cannot seem to find the right node in the DOM tree. I have
> run two animations (one with infinite looping and one looping once) through
> the MetadataReadExample and cannot seem to find the property there either.
> Has anyone done this before?
>
> Note that the loop count seems to be a Netscape extension not officially
> part of the W3C speciciations.

That's correct. One listing of the "spec" is here:

http://www.let.rug.nl/~kleiweg/gif/netscape.html

I have used this code with success:

IIOMetadataNode root =
new IIOMetadataNode("javax_imageio_gif_image_1.0");
IIOMetadataNode gce =
new IIOMetadataNode("GraphicControlExtension");
gce.setAttribute("disposalMethod", "none");
gce.setAttribute("userInputFlag", "FALSE");
gce.setAttribute("transparentColorFlag", "FALSE");
gce.setAttribute("delayTime", delayTime);
gce.setAttribute("transparentColorIndex", "255");
root.appendChild(gce);
IIOMetadataNode aes =
new IIOMetadataNode("ApplicationExtensions");
IIOMetadataNode ae =
new IIOMetadataNode("ApplicationExtension");
ae.setAttribute("applicationID", "NETSCAPE");
ae.setAttribute("authenticationCode", "2.0");
byte[] uo = new byte[] {
(byte)0x21, (byte)0xff, (byte)0x0b,
(byte)'N', (byte)'E', (byte)'T', (byte)'S',
(byte)'C', (byte)'A', (byte)'P', (byte)'E',
(byte)'2', (byte)'.', (byte)'0',
(byte)0x03, (byte)0x01, (byte)0x00, (byte)0x00,
(byte)0x00
};
ae.setUserObject(uo);
aes.appendChild(ae);
root.appendChild(aes);

> Cheers!
>
> Marc Boscher
> Communications Digicharm Inc.
>
>
> -----Original Message-----
> From: Brian.Burkhalter@Sun.COM [mailto:Brian.Burkhalter@Sun.COM]
> Sent: April 2, 2007 4:52 PM
> To: interest@jai-imageio.dev.java.net
> Subject: Re: [JAI-IMAGEIO] Wirting image metadata to control animated gif
> delays
>
> I would think that you would need for each frame to do something similar to
> that which is done for setting the TIFF DPI here
>
> https://jai-imageio.dev.java.net/servlets/ReadMsg?list=interest&msgNo=1306
>
> in the setDPIViaDOM() method. In this method a tree containing modes with
> the desired values is used to update the tree obtained from the image
> metadata object.
>
> Brian
>
> On Mon, 2 Apr 2007, Marc Boscher wrote:
>
>> Hi, I'm trying to write an animated gif with variable frame delays
>> (they may differ for each frame). I understand I have to manually
>> specify the image metadata to the ImageWriter, but I can't seem to
>> find any straightforward guidelines on how to do so.
>>
>> Basically I need to specify the "Delay Time" field of the the "Graphic
>> Control Extension" block before each image in the GIF file output.
>>
>> I am using the prepareWriteSequence, writeToSequence and
>> endWriteSequence methods to create the animated GIF. This currently
>> works but sets the delay on each frame to 0. I would assume I need to
>> change the ImageWriteParam argument of writeToSequence every time I
>> call it, but I'm not sure how what I should be doing on the
> ImageWriteParam.
>>
>> Any advice or pointers to info would be greatly appreciated!
>>
>> Thanks!
>>
>> Marc Boscher
>> Communications Digicharm Inc.
>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
>> For additional commands, e-mail:
>> interest-help@jai-imageio.dev.java.net
>>
>>
>
> ----------------
> Brian Burkhalter
> Java Media, Imaging, and Graphics
> Sun Microsystems, Inc.
>
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> This email message is for the sole use of the intended recipient(s) and may
> contain confidential and privileged information. Any unauthorized review,
> use, disclosure or distribution is prohibited.
> If you are not the intended recipient, please contact the sender by reply
> email and destroy all copies of the original message.
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> ---------------------------------------------------------------------
> 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
>
>

----------------
Brian Burkhalter
Java Media, Imaging, and Graphics
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s)
and may contain confidential and privileged information. Any
unauthorized review, use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

Simone Giannecchini

Ciao,
I wrote some code to convert a bunch of generate wind vectors gif into
an aniamted gif a while ago but I never had time to actually publich
it on my website.

So, since I am afraid that time will never come, I am just sharing the
code with whoever needs it. So here below you find the relevant code.
It is extract fom a command line tool so I am sorry if it is a bit
messy but I got zero time to clean it up.

package it.geosolutions.graphics.gifanimated;

import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.event.IIOWriteProgressListener;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageOutputStream;
import javax.media.jai.CollectionOp;

import org.apache.commons.cli2.Argument;
import org.apache.commons.cli2.CommandLine;
import org.apache.commons.cli2.Group;
import org.apache.commons.cli2.builder.ArgumentBuilder;
import org.apache.commons.cli2.builder.DefaultOptionBuilder;
import org.apache.commons.cli2.builder.GroupBuilder;
import org.apache.commons.cli2.commandline.Parser;
import org.apache.commons.cli2.option.DefaultOption;
import org.apache.commons.cli2.util.HelpFormatter;
import org.apache.commons.cli2.validation.FileValidator;
import org.apache.commons.cli2.validation.NumberValidator;
import org.geotools.image.ImageWorker;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.sun.media.imageioimpl.plugins.gif.GIFImageWriter;
import com.sun.media.imageioimpl.plugins.gif.GIFImageWriterSpi;
import com.sun.media.jai.operator.ImageWriteDescriptor;

/**
* @author Simone Giannecchini
*
*/
public final class GIFAnimated implements IIOWriteProgressListener {
private final static Logger LOGGER = Logger.getLogger(GIFAnimated.class
.toString());

/**
* Argument parser for the SHRIMP command line interface. It builds the
* options for the CLI and parse the command line arguments.
*
* @author Simone Giannecchini.
*
*/
private class GIFAnimatedArgsParser {

/**
* Default delay for the animated gif is 200ms
*/
private static final int DEFAULT_DELAY = 200;

private final GroupBuilder groupBuilder = new GroupBuilder();

private final ArgumentBuilder argumentBuilder = new ArgumentBuilder();

private final DefaultOptionBuilder optionBuilder = new DefaultOptionBuilder();

private final Parser parser = new Parser();

private final FileValidator existingFileValidator = FileValidator
.getExistingFileInstance();

private final NumberValidator integerValidator = NumberValidator
.getIntegerInstance();

private final FileValidator existingDirectoryValidator = FileValidator
.getExistingDirectoryInstance();

private final FileValidator writingFileValidator = FileValidator
.getExistingInstance();

private CommandLine cline;

private Group group;

private DefaultOption optionSource;

private DefaultOption optionDestination;

private DefaultOption optionDelay;

private DefaultOption optionHelp;

private File destFile;

private int delay = DEFAULT_DELAY;

private File inDir;

/**
* Constructor for the arg parser of the animated gif constructor
*
* @param args
* @throws IOException
*/
public GIFAnimatedArgsParser(String[] args) throws IOException {

// /////////////////////////////////////////////////////////
// Initialization
// /////////////////////////////////////////////////////////
initValidators();
initOptions();

// /////////////////////////////////////////////////////////
// Parsing
// /////////////////////////////////////////////////////////
cline = parser.parseAndHelp(args);
if (cline != null) {
// /////////////////////////////////////////////////////////
// CHECKING VARIOUS PARAMS
// /////////////////////////////////////////////////////////

inDir = new File((String) cline.getValue(optionSource));
destFile = new File((String) cline.getValue(optionDestination));

if (cline.hasOption(optionDelay)) {

delay = ((Long) cline.getValue(optionDelay)).intValue();
} else {
delay = DEFAULT_DELAY;
}

// /////////////////////////////////////////////////////////
// ERRORS CONDITIONS
// /////////////////////////////////////////////////////////
if (!inDir.exists() | !inDir.isDirectory())
throw new IOException("Input directory must exists!!!");
if (destFile.exists()
&& (!destFile.isFile() || !destFile.canWrite()))
throw new IOException(
"Destination file must be writable and must be a files!!!");
else if (destFile.exists())
destFile.delete();

}

}

private void initValidators() {
existingFileValidator.setDirectory(false);
existingFileValidator.setExisting(true);
existingFileValidator.setReadable(true);

writingFileValidator.setDirectory(false);
writingFileValidator.setExisting(false);
writingFileValidator.setWritable(true);

existingDirectoryValidator.setDirectory(true);
existingDirectoryValidator.setExisting(true);
existingDirectoryValidator.setReadable(true);

integerValidator.setMaximum(new Integer(10000));
integerValidator.setMinimum(new Integer(10));

}

/**
* Initializing the various options for the command line.
*
*/
private void initOptions() {
Argument argument;
// /////////////////////////////////////////////////////////////////
// SOURCE DIR
// /////////////////////////////////////////////////////////////////
argument = argumentBuilder.withDescription(
"path_to_the_in_directory").withMaximum(1).withMinimum(1)
.withName("in_dir").withId(0).create();
optionSource = optionBuilder.withArgument(argument).withRequired(
true).withLongName("inDirPath").withShortName("in").withId(
0).withDescription("path_to_the_in_directory").create();

// /////////////////////////////////////////////////////////////////
// OUT IMAGE
// /////////////////////////////////////////////////////////////////
argument = argumentBuilder.withDescription(
"path_to_the_referece_image").withMaximum(1).withMinimum(1)//
.withValidator(writingFileValidator)
.withName("reference_path").withId(1).create();
optionDestination = optionBuilder.withArgument(argument)
.withRequired(true).withLongName("outFilePath")
.withShortName("o").withId(1).withDescription(
"path_to_the_output_file").create();

// /////////////////////////////////////////////////////////////////
// FRAME DELAY
// /////////////////////////////////////////////////////////////////
argument = argumentBuilder.withDescription("frame_delay")
.withMaximum(1).withMinimum(0).withName("frame_delay")
.withValidator(integerValidator).withId(2).create();
optionDelay = optionBuilder.withArgument(argument).withRequired(
false).withLongName("frame_delay").withShortName("d")
.withId(2).withDescription("frame_delay").create();

// /////////////////////////////////////////////////////////////////
// HELP OPTION
// /////////////////////////////////////////////////////////////////
optionHelp = optionBuilder.withRequired(false).withLongName("help")
.withShortName("h").withId(3).withDescription("help")
.create();

// /////////////////////////////////////////////////////////////////
// HELP FORMATTER
// /////////////////////////////////////////////////////////////////
HelpFormatter helpFormatter = new HelpFormatter("|", "--", "|", 100);
helpFormatter.setHeader("GifAnimator Command Line utility");
helpFormatter.setFooter("Copyright: GeoSolutions s.a.s.");
helpFormatter
.setDivider("@==================================================================================================@");

group = groupBuilder.withOption(optionSource).withOption(
optionDestination).withOption(optionDelay).create();
parser.setGroup(group);
parser.setHelpOption(optionHelp);
parser.setHelpFormatter(helpFormatter);
}

public int getDelay() {
return delay;
}

public File getDestFile() {
return destFile;
}

public File getInDir() {
return inDir;
}

}

private GIFAnimatedArgsParser parser;

private int imageIndexAtWork = 0;

public GIFAnimated(String[] args) throws IOException {

parser = new GIFAnimatedArgsParser(args);

}

/**
* @param args
* @throws IOException
* @throws FileNotFoundException
*/
public static void main(String[] args) {

GIFAnimated test = null;
try {
test = new GIFAnimated(args);
} catch (IOException e) {
// something really bad happened!!!!
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
System.exit(1);
}
try {
test.generate();
} catch (FileNotFoundException e) {
// something really bad happened!!!!
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
System.exit(1);
} catch (IOException e) {
// something really bad happened!!!!
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
System.exit(1);
}

}

/**
* This method must be run after having parsed the input arguments. It
* parsers the input directory and load all the images found in that
* directory except for the Nurc logo.
*
* @throws IOException
*
* @throws FileNotFoundException
* @throws IOException
*/
private void generate() throws IOException {

// /////////////////////////////////////////////////////////////////////
//
// Getting input files
//
// /////////////////////////////////////////////////////////////////////
LOGGER.info("Loading files...");
final File[] files = parser.getInDir().listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {

return name.toLowerCase().indexOf("nurc") == -1
&& new File(dir, name).isFile();

}
});
final int numfiles = files.length;

// /////////////////////////////////////////////////////////////////////
//
// Getting log.
//
// I prefere to watermrk myself rather than relying too much on the
// underlying lib
//
// /////////////////////////////////////////////////////////////////////
LOGGER.info("Loading logo...");
final BufferedImage logo = ImageIO.read(new File(parser.getInDir(),
"nurc.png"));
final int logoWidth = logo.getWidth();
final int logoHeight = logo.getHeight();

// /////////////////////////////////////////////////////////////////////
//
// Getting inpuit files
//
// /////////////////////////////////////////////////////////////////////
LOGGER.info("Preparing to loop over files...");
final GIFImageWriter gifWriter = new GIFImageWriter(
new GIFImageWriterSpi());

final ImageOutputStream otStream = ImageIO
.createImageOutputStream(parser.getDestFile());

// /////////////////////////////////////////////////////////////////////
//
// Getting inpuit files
//
// /////////////////////////////////////////////////////////////////////
Graphics2D g2D;
BufferedImage image;
final int marginX = 5;
final int marginY = 5;
final StringBuffer buffer = new StringBuffer();
IIOMetadata imageMetadata = null;
final IIOMetadata[] metadataArray = new IIOMetadata[numfiles];
final List listOfImages = new ArrayList(numfiles);
final ImageWorker worker = new ImageWorker();
for (int i = 0; i < numfiles; i++) {

// /////////////////////////////////////////////////////////////////////
//
// Getting input files
//
// /////////////////////////////////////////////////////////////////////

try {
// /////////////////////////////////////////////////////////////////////
//
// Read the file
//
// /////////////////////////////////////////////////////////////////////
image = ImageIO.read(files[i]);
if (image != null) {

// /////////////////////////////////////////////////////////////////////
//
// Watermark the files
//
// /////////////////////////////////////////////////////////////////////
g2D = (Graphics2D) image.getGraphics();
g2D.drawRenderedImage(logo, AffineTransform
.getTranslateInstance(image.getWidth() - logoWidth
- marginX, image.getHeight() - logoHeight
- marginY));
g2D.dispose();

// /////////////////////////////////////////////////////////////////////
//
// prepare metadata
//
// /////////////////////////////////////////////////////////////////////
imageMetadata = gifWriter.getDefaultImageMetadata(
new ImageTypeSpecifier(image), gifWriter
.getDefaultWriteParam());
prepareMetadata(imageMetadata);
metadataArray[i] = imageMetadata;
// /////////////////////////////////////////////////////////////////////
//
// Getting inpuit files
//
// /////////////////////////////////////////////////////////////////////

listOfImages.add(i, worker.setImage(image)
.forceIndexColorModelForGIF().getRenderedImage());
buffer.append("Added file ").append(
files[i].getAbsolutePath().toCharArray()).append(
"\n");
} else
buffer.append("Unable to add file ").append(
files[i].getAbsolutePath().toCharArray()).append(
"\n");
} catch (IOException e) {
LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);

}

}
LOGGER.info(buffer.toString());

// /////////////////////////////////////////////////////////////////////
//
// Encoding and leaving
//
// /////////////////////////////////////////////////////////////////////
LOGGER.info(new StringBuffer("Preparing to write to ").append(
parser.getDestFile().getAbsolutePath()).toString());
final CollectionOp collectionOp = (CollectionOp) ImageWriteDescriptor
.createCollection(listOfImages, otStream, "gif", Boolean.FALSE,
Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, null,
null, metadataArray, null,
new EventListener[] { this }, null, null, gifWriter,
null);
LOGGER.info("Done....");

}

private void prepareMetadata(IIOMetadata imageMetadata) throws IOException {
final Node root = imageMetadata
.getAsTree("javax_imageio_gif_image_1.0");

final NodeList nodes = root.getChildNodes();
final int length = nodes.getLength();
Node node;
NamedNodeMap attributes;
for (int i = 0; i < length; i++) {
node = nodes.item(i);
if (node.getNodeName().equalsIgnoreCase("GraphicControlExtension")) {
attributes = node.getAttributes();
attributes.getNamedItem("delayTime").setNodeValue(
Integer.toString(parser.getDelay()));
break;
}
}
try {
imageMetadata.mergeTree("javax_imageio_gif_image_1.0", root);
} catch (IIOInvalidTreeException e) {
final IOException ioe = new IOException(e.getLocalizedMessage());
ioe.initCause(e);
throw ioe;
}

}

public void imageStarted(ImageWriter source, int imageIndex) {
imageIndexAtWork = imageIndex;
LOGGER.info("Started image " + Integer.toString(imageIndex));

}

public void imageProgress(ImageWriter source, float percentageDone) {
// LOGGER.info("Writing image " + Integer.toString(imageIndexAtWork)
// + " percentage " + Float.toString(percentageDone));

}

public void imageComplete(ImageWriter source) {
LOGGER.info("Completed image " + Integer.toString(imageIndexAtWork));

}

public void thumbnailStarted(ImageWriter source, int imageIndex,
int thumbnailIndex) {

}

public void thumbnailProgress(ImageWriter source, float percentageDone) {

}

public void thumbnailComplete(ImageWriter source) {

}

public void writeAborted(ImageWriter source) {
LOGGER.info("Write aborted while writing image "
+ Integer.toString(imageIndexAtWork));

}
}

Ciao,
Simone.

On 4/3/07, Brian Burkhalter wrote:
> On Mon, 2 Apr 2007, Marc Boscher wrote:
>
> > Ok, got the frame delay set correctly. For reference purposes you need to:
> >
> > - For each IIOImage you create, get the tree for the
> > "javax_imageio_gif_image_1.0" format
> > - Find the "GraphicControlExtension" element, usually a child of the tree
> > root
> > - Modify the value of the "delayTime" attribute
> > - overwrite the existing tree with the new one.
>
> Thanks.
>
> > Now I need to modify the looping behaviour of the GIF file. By default the
> > produced animations only play once. I need to set the loop count to 0
> > (infinite), but cannot seem to find the right node in the DOM tree. I have
> > run two animations (one with infinite looping and one looping once) through
> > the MetadataReadExample and cannot seem to find the property there either.
> > Has anyone done this before?
> >
> > Note that the loop count seems to be a Netscape extension not officially
> > part of the W3C speciciations.
>
> That's correct. One listing of the "spec" is here:
>
> http://www.let.rug.nl/~kleiweg/gif/netscape.html
>
> I have used this code with success:
>
> IIOMetadataNode root =
> new IIOMetadataNode("javax_imageio_gif_image_1.0");
> IIOMetadataNode gce =
> new IIOMetadataNode("GraphicControlExtension");
> gce.setAttribute("disposalMethod", "none");
> gce.setAttribute("userInputFlag", "FALSE");
> gce.setAttribute("transparentColorFlag", "FALSE");
> gce.setAttribute("delayTime", delayTime);
> gce.setAttribute("transparentColorIndex", "255");
> root.appendChild(gce);
> IIOMetadataNode aes =
> new IIOMetadataNode("ApplicationExtensions");
> IIOMetadataNode ae =
> new IIOMetadataNode("ApplicationExtension");
> ae.setAttribute("applicationID", "NETSCAPE");
> ae.setAttribute("authenticationCode", "2.0");
> byte[] uo = new byte[] {
> (byte)0x21, (byte)0xff, (byte)0x0b,
> (byte)'N', (byte)'E', (byte)'T', (byte)'S',
> (byte)'C', (byte)'A', (byte)'P', (byte)'E',
> (byte)'2', (byte)'.', (byte)'0',
> (byte)0x03, (byte)0x01, (byte)0x00, (byte)0x00,
> (byte)0x00
> };
> ae.setUserObject(uo);
> aes.appendChild(ae);
> root.appendChild(aes);
>
> > Cheers!
> >
> > Marc Boscher
> > Communications Digicharm Inc.
> >
> >
> > -----Original Message-----
> > From: Brian.Burkhalter@Sun.COM [mailto:Brian.Burkhalter@Sun.COM]
> > Sent: April 2, 2007 4:52 PM
> > To: interest@jai-imageio.dev.java.net
> > Subject: Re: [JAI-IMAGEIO] Wirting image metadata to control animated gif
> > delays
> >
> > I would think that you would need for each frame to do something similar to
> > that which is done for setting the TIFF DPI here
> >
> > https://jai-imageio.dev.java.net/servlets/ReadMsg?list=interest&msgNo=1306
> >
> > in the setDPIViaDOM() method. In this method a tree containing modes with
> > the desired values is used to update the tree obtained from the image
> > metadata object.
> >
> > Brian
> >
> > On Mon, 2 Apr 2007, Marc Boscher wrote:
> >
> >> Hi, I'm trying to write an animated gif with variable frame delays
> >> (they may differ for each frame). I understand I have to manually
> >> specify the image metadata to the ImageWriter, but I can't seem to
> >> find any straightforward guidelines on how to do so.
> >>
> >> Basically I need to specify the "Delay Time" field of the the "Graphic
> >> Control Extension" block before each image in the GIF file output.
> >>
> >> I am using the prepareWriteSequence, writeToSequence and
> >> endWriteSequence methods to create the animated GIF. This currently
> >> works but sets the delay on each frame to 0. I would assume I need to
> >> change the ImageWriteParam argument of writeToSequence every time I
> >> call it, but I'm not sure how what I should be doing on the
> > ImageWriteParam.
> >>
> >> Any advice or pointers to info would be greatly appreciated!
> >>
> >> Thanks!
> >>
> >> Marc Boscher
> >> Communications Digicharm Inc.
> >>
> >>
> >>
> >> ---------------------------------------------------------------------
> >> To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
> >> For additional commands, e-mail:
> >> interest-help@jai-imageio.dev.java.net
> >>
> >>
> >
> > ----------------
> > Brian Burkhalter
> > Java Media, Imaging, and Graphics
> > Sun Microsystems, Inc.
> >
> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > This email message is for the sole use of the intended recipient(s) and may
> > contain confidential and privileged information. Any unauthorized review,
> > use, disclosure or distribution is prohibited.
> > If you are not the intended recipient, please contact the sender by reply
> > email and destroy all copies of the original message.
> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> >
> > ---------------------------------------------------------------------
> > 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
> >
> >
>
> ----------------
> Brian Burkhalter
> Java Media, Imaging, and Graphics
> Sun Microsystems, Inc.
>
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> This email message is for the sole use of the intended recipient(s)
> and may contain confidential and privileged information. Any
> unauthorized review, use, disclosure or distribution is prohibited.
> If you are not the intended recipient, please contact the sender by
> reply email and destroy all copies of the original message.
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
> For additional commands, e-mail: interest-help@jai-imageio.dev.java.net
>
>

--
-------------------------------------------------------
Eng. Simone Giannecchini
President /CEO GeoSolutions

http://www.geo-solutions.it

-------------------------------------------------------

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