diff mbox

libpng: bump version and add apng support (required by firefox)

Message ID 50240DDE.4000901@petroprogram.com
State Rejected
Headers show

Commit Message

Stefan Fröberg Aug. 9, 2012, 7:22 p.m. UTC
Signed-off-by: Stefan Froberg <stefan.froberg@petroprogram.com>
---
 package/libpng/libpng-1.5.9-apng.patch | 1641
++++++++++++++++++++++++++++++++
 package/libpng/libpng.mk               |    8 +-
 2 files changed, 1645 insertions(+), 4 deletions(-)
 create mode 100644 package/libpng/libpng-1.5.9-apng.patch

 LIBPNG_DEPENDENCIES = host-pkg-config zlib

Comments

Thomas Petazzoni Aug. 10, 2012, 7:02 a.m. UTC | #1
Hello Stefan,

Le Thu, 09 Aug 2012 22:22:06 +0300,
Stefan Fröberg <stefan.froberg@petroprogram.com> a écrit :

> Signed-off-by: Stefan Froberg <stefan.froberg@petroprogram.com>
> ---

Thanks for this patch. A few comments:

 (1) Your patch has been line-wrapped by your e-mail client. Try to use
     git send-email instead, which will guarantee that your patch
     remains intact and can be applied properly.

 (2) As far as I know, the bump from libpng 1.4 to libpng 1.5 changes
     the API, and is therefore causing build failures in other
     packages. Have you tested things like DirectFB and other packages
     that rely on libpng?

 (3) Is this apng feature something that is being integrated in the
     upstream version of libpng? In Buildroot, we don't like carrying
     patches that add features to packages. Imagine we later want to
     bump libpng to version 1.5.10 or 1.5.11, we'd have to refresh the
     patch, which becomes complicated if we accumulate many large
     patches.

Best regards,

Thomas
Stefan Fröberg Aug. 10, 2012, 9:47 a.m. UTC | #2
Good morning Thomas

10.8.2012 10:02, Thomas Petazzoni kirjoitti:
> Hello Stefan,
>
> Le Thu, 09 Aug 2012 22:22:06 +0300,
> Stefan Fröberg <stefan.froberg@petroprogram.com> a écrit :
>
>> Signed-off-by: Stefan Froberg <stefan.froberg@petroprogram.com>
>> ---
> Thanks for this patch. A few comments:
>
>  (1) Your patch has been line-wrapped by your e-mail client. Try to use
>      git send-email instead, which will guarantee that your patch
>      remains intact and can be applied properly.
Ok, I try to get used to using git for sending these patches.
Im still git newbie :-)

>  (2) As far as I know, the bump from libpng 1.4 to libpng 1.5 changes
>      the API, and is therefore causing build failures in other
>      packages. Have you tested things like DirectFB and other packages
>      that rely on libpng?
Well, firefox is working nicely.

And the only DirectFB depending package I have here is the graphical
version of links browser from buildroot.
Links start's okay too but freezes after exiting. But it had done it
before too, so I know it's not the issue of libpng
in this specific case.

But will I check what applications I have currently linked against that
1.5 version and test them all.

>  (3) Is this apng feature something that is being integrated in the
>      upstream version of libpng? In Buildroot, we don't like carrying
>      patches that add features to packages. Imagine we later want to
>      bump libpng to version 1.5.10 or 1.5.11, we'd have to refresh the
>      patch, which becomes complicated if we accumulate many large
>      patches.

That's a good guestion.

I had to check from http://en.wikipedia.org/wiki/APNG
and it says the following:

"The PNG group officially rejected APNG as an official extension on
April 20, 2007.[3]
There have been several subsequent proposals for a simple animated
graphics format based on PNG using several different approaches.[4]

Mozilla Firefox added support for APNG in version 3 trunk builds on
March 23, 2007.[5]
However, because libpng is the PNG Group's reference implementation of
the official specification,
APNG support can never be supported in the main libpng distribution so
long as it remains
unratified by the Group. Iceweasel 3 now supports APNG by using
Mozilla's unofficial variant of libpng."

So it seems that even tought APNG support is needed for libpng by
Firefox the PNG group will never add it :(

However, there is a solution:
Firefox tarball contains it's own private copy of libpng with apng
support included.

Ofcourse that's some extra disk space wasted for having two versions of
libpng (private and system-wide)
included in the final rootfs  and that's why I originally wanted to use
as much buildroot made system libs as possible.
But it's a simple matter to drop the "--with-system-png" from firefox
mozconfig and trash that apng patch.

By the way Thomas, how should I split my patches if they become too large ?
I have currently eight files for my firefox build and the total number
of lines is...well...large.

So, should I chunk it to separately patches with git ([PATCH 1/8],
[PATCH 2/8] etc ....) ,
with each patch containing just one file or how ?

Stefan

> Best regards,
>
> Thomas
Thomas Petazzoni Aug. 10, 2012, 8 p.m. UTC | #3
Hello Stefan,

Le Fri, 10 Aug 2012 12:47:33 +0300,
Stefan Fröberg <stefan.froberg@petroprogram.com> a écrit :

> >  (1) Your patch has been line-wrapped by your e-mail client. Try to use
> >      git send-email instead, which will guarantee that your patch
> >      remains intact and can be applied properly.
> Ok, I try to get used to using git for sending these patches.
> Im still git newbie :-)

No problem :)

> >  (2) As far as I know, the bump from libpng 1.4 to libpng 1.5 changes
> >      the API, and is therefore causing build failures in other
> >      packages. Have you tested things like DirectFB and other packages
> >      that rely on libpng?
> Well, firefox is working nicely.
> 
> And the only DirectFB depending package I have here is the graphical
> version of links browser from buildroot.
> Links start's okay too but freezes after exiting. But it had done it
> before too, so I know it's not the issue of libpng
> in this specific case.
> 
> But will I check what applications I have currently linked against that
> 1.5 version and test them all.

I was more talking about all the other packages that we have in
Buildroot and that rely on libpng:

$ git grep "select BR2_PACKAGE_LIBPNG"
cairo/Config.in:	select BR2_PACKAGE_LIBPNG
collectd/Config.in:	select BR2_PACKAGE_LIBPNG
directfb/Config.in:	select BR2_PACKAGE_LIBPNG
efl/libevas/Config.in:	select BR2_PACKAGE_LIBPNG
fbgrab/Config.in:	select BR2_PACKAGE_LIBPNG
fbv/Config.in:	select BR2_PACKAGE_LIBPNG
imlib2/Config.in:	select BR2_PACKAGE_LIBPNG
links/Config.in:	select BR2_PACKAGE_LIBPNG
multimedia/gst-plugins-good/Config.in:	select BR2_PACKAGE_LIBPNG
opencv/Config.in:	select BR2_PACKAGE_LIBPNG
qt/Config.in:	select BR2_PACKAGE_LIBPNG
rrdtool/Config.in:	select BR2_PACKAGE_LIBPNG
sdl_image/Config.in:	select BR2_PACKAGE_LIBPNG
x11r7/Config.in:	select BR2_PACKAGE_LIBPNG
x11r7/xapp_xcursorgen/Config.in:	select BR2_PACKAGE_LIBPNG

> That's a good guestion.
> 
> I had to check from http://en.wikipedia.org/wiki/APNG
> and it says the following:
> 
> "The PNG group officially rejected APNG as an official extension on
> April 20, 2007.[3]
> There have been several subsequent proposals for a simple animated
> graphics format based on PNG using several different approaches.[4]
> 
> Mozilla Firefox added support for APNG in version 3 trunk builds on
> March 23, 2007.[5]
> However, because libpng is the PNG Group's reference implementation of
> the official specification,
> APNG support can never be supported in the main libpng distribution so
> long as it remains
> unratified by the Group. Iceweasel 3 now supports APNG by using
> Mozilla's unofficial variant of libpng."
> 
> So it seems that even tought APNG support is needed for libpng by
> Firefox the PNG group will never add it :(

Argh, so it sounds a bit nasty to include this apng patch.

> However, there is a solution:
> Firefox tarball contains it's own private copy of libpng with apng
> support included.
> 
> Ofcourse that's some extra disk space wasted for having two versions of
> libpng (private and system-wide)
> included in the final rootfs  and that's why I originally wanted to use
> as much buildroot made system libs as possible.
> But it's a simple matter to drop the "--with-system-png" from firefox
> mozconfig and trash that apng patch.

Yes, we usually don't like this solution either, but for now, it looks
like the easiest option.

> By the way Thomas, how should I split my patches if they become too large ?
> I have currently eight files for my firefox build and the total number
> of lines is...well...large.
> 
> So, should I chunk it to separately patches with git ([PATCH 1/8],
> [PATCH 2/8] etc ....) ,
> with each patch containing just one file or how ?

You can send all the files related to a particular package as a single
patch, even if it is large. However, we might be a bit reluctant to add
a package that requires such a number of big patches. We'll wait your
posting to see what those patches do, and whether they are acceptable
inside Buildroot.

Best regards,

Thomas
Stefan Fröberg Aug. 10, 2012, 11:22 p.m. UTC | #4
>> By the way Thomas, how should I split my patches if they become too large ?
>> I have currently eight files for my firefox build and the total number
>> of lines is...well...large.
>>
>> So, should I chunk it to separately patches with git ([PATCH 1/8],
>> [PATCH 2/8] etc ....) ,
>> with each patch containing just one file or how ?
> You can send all the files related to a particular package as a single
> patch, even if it is large. However, we might be a bit reluctant to add
> a package that requires such a number of big patches. We'll wait your
> posting to see what those patches do, and whether they are acceptable
> inside Buildroot.

Not to worry, the patches are actually very small and I was surprised
how little I had to change code.

But the actual build process was very ... messy ... and i had spend most
of the last four months of (and lot's of hair tearing)
finding just the right combination of build switches to make the damn
thing compile and actually run.

Firefox extensions work now and It even plays YouTube videos with gnash
plugin. Yay! :-)

So the biggest files are firefox.mk, which I have commented extensively
so that nobody has to go for this trouble again, and mozconfig file.
I also have added optional feature that when enabled will try to make
Firefox as privacy enhanced as possible
(using NoScript  and AdblockPlus extension plus my own about:config
settings)

If the yasm package patch and that little cairo option addition patch
are both okay for you I will then send one final patch (few little
changes needed to sqlite.mk)
and then im ready to send the actual firefox.

Stefan

> Best regards,
>
> Thomas
Thomas Petazzoni Aug. 11, 2012, 6:50 a.m. UTC | #5
Hello,

Le Sat, 11 Aug 2012 02:22:33 +0300,
Stefan Fröberg <stefan.froberg@petroprogram.com> a écrit :

> Not to worry, the patches are actually very small and I was surprised
> how little I had to change code.

Great!

> But the actual build process was very ... messy ... and i had spend most
> of the last four months of (and lot's of hair tearing)
> finding just the right combination of build switches to make the damn
> thing compile and actually run.

Yeah, I believe you that building Firefox is certainly not a piece of
cake. Congrats to you for working on this!

> Firefox extensions work now and It even plays YouTube videos with gnash
> plugin. Yay! :-)

Woh! I presume it only works under X.org, right?

> So the biggest files are firefox.mk, which I have commented extensively
> so that nobody has to go for this trouble again, and mozconfig file.

How complicated do you feel the Firefox version bumps will be,
considering the work you had to do for a specific version?

> I also have added optional feature that when enabled will try to make
> Firefox as privacy enhanced as possible
> (using NoScript  and AdblockPlus extension plus my own about:config
> settings)
> 
> If the yasm package patch and that little cairo option addition patch
> are both okay for you I will then send one final patch (few little
> changes needed to sqlite.mk)
> and then im ready to send the actual firefox.

I will try to catch up a bit on my Buildroot backlog today, hoping that
the 3G connection will be more cooperative than the other days.

Best regards,

Thomas
Stefan Fröberg Aug. 11, 2012, 10:54 a.m. UTC | #6
Hello Thomas

11.8.2012 9:50, Thomas Petazzoni kirjoitti:
> Hello,
>
> Le Sat, 11 Aug 2012 02:22:33 +0300,
> Stefan Fröberg <stefan.froberg@petroprogram.com> a écrit :
>
>> Not to worry, the patches are actually very small and I was surprised
>> how little I had to change code.
> Great!
>
>> But the actual build process was very ... messy ... and i had spend most
>> of the last four months of (and lot's of hair tearing)
>> finding just the right combination of build switches to make the damn
>> thing compile and actually run.
> Yeah, I believe you that building Firefox is certainly not a piece of
> cake. Congrats to you for working on this!
>
>> Firefox extensions work now and It even plays YouTube videos with gnash
>> plugin. Yay! :-)
> Woh! I presume it only works under X.org, right?

Yes but there is also an configure option
"--enable-default-toolkit=cairo-gtk2-dfb" for directfb and
whitch I have not tested at all and I remember that I have seen
somewhere in the net a way to
build firefox to use just DirectFB + Gtk2 and no Xorg at all.

There is also "--enable-mobile-optimize" configure switch whitch I
suspect will only affect building for Android phone
(at least theres are lot's of   #ifdef ANDROID stuff in the code)

So that switch combined with directfb could make a really small firefox
binary, at least for phones.

>> So the biggest files are firefox.mk, which I have commented extensively
>> so that nobody has to go for this trouble again, and mozconfig file.
> How complicated do you feel the Firefox version bumps will be,
> considering the work you had to do for a specific version?
>
>> I also have added optional feature that when enabled will try to make
>> Firefox as privacy enhanced as possible
>> (using NoScript  and AdblockPlus extension plus my own about:config
>> settings)
>>
>> If the yasm package patch and that little cairo option addition patch
>> are both okay for you I will then send one final patch (few little
>> changes needed to sqlite.mk)
>> and then im ready to send the actual firefox.
> I will try to catch up a bit on my Buildroot backlog today, hoping that
> the 3G connection will be more cooperative than the other days.

Great!

Stefan

> Best regards,
>
> Thomas
Stefan Fröberg Aug. 11, 2012, 11:29 a.m. UTC | #7
11.8.2012 9:50, Thomas Petazzoni kirjoitti:
> So the biggest files are firefox.mk, which I have commented extensively
> so that nobody has to go for this trouble again, and mozconfig file.
> How complicated do you feel the Firefox version bumps will be,
> considering the work you had to do for a specific version?

I think the bumps will be trivial.

The firefox.mk is more or less in stable state now and
simply changing FIREFOX_VERSION should be enough.

Ofcourse adding more optional configure options like DBus support,
libnotify etc.,
can be added later but right now it will build a very minimal binary and
im satisfied with that.

The mozconfig file which is Mozilla way of feeding all the configure
switches to build process
is pretty much stable and static now and will unlikely change for very
long time.

And the only changes I had to make to actual code was to replace finite()
with more standard isinf() and handle glibc specific malloc_usable_size and
execinfo.h stuff.

When I started the version number was 11.0 and now they have released
14.0 so those things might be
even fixed by now. :-)

If not, then I will try to contact upstream for including those minor
fixes and if failing that Im
more than willing to keep those lil patches updated.

Stefan

>> I also have added optional feature that when enabled will try to make
>> Firefox as privacy enhanced as possible
>> (using NoScript  and AdblockPlus extension plus my own about:config
>> settings)
>>
>> If the yasm package patch and that little cairo option addition patch
>> are both okay for you I will then send one final patch (few little
>> changes needed to sqlite.mk)
>> and then im ready to send the actual firefox.
> I will try to catch up a bit on my Buildroot backlog today, hoping that
> the 3G connection will be more cooperative than the other days.
>
> Best regards,
>
> Thomas
Arnout Vandecappelle Aug. 21, 2012, 9:30 p.m. UTC | #8
On 08/10/12 11:47, Stefan Fröberg wrote:
> Good morning Thomas
>
> 10.8.2012 10:02, Thomas Petazzoni kirjoitti:

[snip]
>>   (3) Is this apng feature something that is being integrated in the
>>       upstream version of libpng? In Buildroot, we don't like carrying
>>       patches that add features to packages. Imagine we later want to
>>       bump libpng to version 1.5.10 or 1.5.11, we'd have to refresh the
>>       patch, which becomes complicated if we accumulate many large
>>       patches.
>
> That's a good guestion.
>
> I had to check from http://en.wikipedia.org/wiki/APNG
> and it says the following:
>
> "The PNG group officially rejected APNG as an official extension on
> April 20, 2007.[3]
> There have been several subsequent proposals for a simple animated
> graphics format based on PNG using several different approaches.[4]
>
> Mozilla Firefox added support for APNG in version 3 trunk builds on
> March 23, 2007.[5]
> However, because libpng is the PNG Group's reference implementation of
> the official specification,
> APNG support can never be supported in the main libpng distribution so
> long as it remains
> unratified by the Group. Iceweasel 3 now supports APNG by using
> Mozilla's unofficial variant of libpng."

  The most logical solution for me seems to be to add a libapng package
that uses the Mozilla code base.  Of course, that introduces the same
variants mess we have for expat/libxml2 and lua/luajit.

  Regards,
  Arnout
Stefan Fröberg Aug. 21, 2012, 10:01 p.m. UTC | #9
22.8.2012 0:30, Arnout Vandecappelle kirjoitti:
> On 08/10/12 11:47, Stefan Fröberg wrote:
>> Good morning Thomas
>>
>> 10.8.2012 10:02, Thomas Petazzoni kirjoitti:
>
> [snip]
>>>   (3) Is this apng feature something that is being integrated in the
>>>       upstream version of libpng? In Buildroot, we don't like carrying
>>>       patches that add features to packages. Imagine we later want to
>>>       bump libpng to version 1.5.10 or 1.5.11, we'd have to refresh the
>>>       patch, which becomes complicated if we accumulate many large
>>>       patches.
>>
>> That's a good guestion.
>>
>> I had to check from http://en.wikipedia.org/wiki/APNG
>> and it says the following:
>>
>> "The PNG group officially rejected APNG as an official extension on
>> April 20, 2007.[3]
>> There have been several subsequent proposals for a simple animated
>> graphics format based on PNG using several different approaches.[4]
>>
>> Mozilla Firefox added support for APNG in version 3 trunk builds on
>> March 23, 2007.[5]
>> However, because libpng is the PNG Group's reference implementation of
>> the official specification,
>> APNG support can never be supported in the main libpng distribution so
>> long as it remains
>> unratified by the Group. Iceweasel 3 now supports APNG by using
>> Mozilla's unofficial variant of libpng."
>
>  The most logical solution for me seems to be to add a libapng package
> that uses the Mozilla code base.  Of course, that introduces the same
> variants mess we have for expat/libxml2 and lua/luajit.
>

Yes and it would still duplicate libpng code in the target (having
system-wide libpng and libapng package with private  Mozilla libpng +
apng stuff).

I forgotted to mention Thomas that that APNG patch is steadily updated
in http://sourceforge.net/projects/libpng-apng/

So keeping it updated between libpng versions is really not an issue.

It is even less issue if that patch can be made optional.

What I have now in mind is something like this:

################################
ifeq ($(BR2_PACKAGE_LIBPNG_APNG_SUPPORT),y)

define    LIBPNG_ADD_APNG
        cp package/libpng/patches/libpng-apng.patch package/libpng/
endif

LIBPNG_PRE_PATCH_HOOKS += LIBPNG_ADD_APNG

else

    if [ -f package/libpng/libpng-apng.patch ]; then
        rm package/libpng/libpng-apng.patch
    fi

endif
################################

That way apng patch could be switched on and off
and the duplicate code bloat of having two copies of libpng could be
avoided.

Im not even sure if there is a PRE_PATCH_HOOKS.
At least http://buildroot.org/downloads/buildroot.html#add_packages does
not mention it
But I think I saw it somewhere.


Stefan

>  Regards,
>  Arnout
Arnout Vandecappelle Aug. 26, 2012, 9:35 p.m. UTC | #10
On 08/22/12 00:01, Stefan Fröberg wrote:
> I forgotted to mention Thomas that that APNG patch is steadily updated
> inhttp://sourceforge.net/projects/libpng-apng/
>
> So keeping it updated between libpng versions is really not an issue.

  Even better!  Then you can make something like

ifeq ($(BR2_PACKAGE_LIBPNG_APNG_SUPPORT),y)
LIBPNG_PATCH = sf://libpng-apng/files/libpng15/$(LIBPNG_VERSION)/libpng-$(LIBPNG_VERSION)-apng.patch.gz
endif

(where sf:// is Yann's soon-to-be-interoduced abbreviation of the sourceforge
URLs).  As far as I know (but never tried it), the above should download and
apply the patch.

  Regards,
  Arnout
diff mbox

Patch

diff --git a/package/libpng/libpng-1.5.9-apng.patch
b/package/libpng/libpng-1.5.9-apng.patch
new file mode 100644
index 0000000..0ec7b38
--- /dev/null
+++ b/package/libpng/libpng-1.5.9-apng.patch
@@ -0,0 +1,1641 @@ 
+diff -Naru libpng-1.5.9.org/png.h libpng-1.5.9/png.h
+--- libpng-1.5.9.org/png.h    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/png.h    2012-02-19 17:29:25.000000000 +0900
+@@ -432,6 +432,10 @@
+ #   include "pnglibconf.h"
+ #endif
+
++#define PNG_APNG_SUPPORTED
++#define PNG_READ_APNG_SUPPORTED
++#define PNG_WRITE_APNG_SUPPORTED
++
+ #ifndef PNG_VERSION_INFO_ONLY
+ #  ifndef PNG_BUILDING_SYMBOL_TABLE
+   /*
+@@ -822,6 +826,10 @@
+ #define PNG_INFO_sPLT 0x2000   /* ESR, 1.0.6 */
+ #define PNG_INFO_sCAL 0x4000   /* ESR, 1.0.6 */
+ #define PNG_INFO_IDAT 0x8000   /* ESR, 1.0.6 */
++#ifdef PNG_APNG_SUPPORTED
++#define PNG_INFO_acTL 0x10000L
++#define PNG_INFO_fcTL 0x20000L
++#endif
+
+ /* This is used for the transformation routines, as some of them
+  * change these values for the row.  It also should enable using
+@@ -880,6 +888,10 @@
+  */
+ typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp,
png_bytep,
+     png_uint_32, int));
++#ifdef PNG_APNG_SUPPORTED
++typedef PNG_CALLBACK(void, *png_progressive_frame_ptr, (png_structp,
++    png_uint_32));
++#endif
+ #endif
+
+ #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+@@ -1550,6 +1562,18 @@
+ PNG_EXPORT(60, void, png_write_image,
+     (png_structp png_ptr, png_bytepp image));
+
++#ifdef PNG_WRITE_APNG_SUPPORTED
++PNG_EXPORT(1001, void, png_write_frame_head, (png_structp png_ptr,
++    png_infop png_info, png_bytepp row_pointers,
++    png_uint_32 width, png_uint_32 height,
++    png_uint_32 x_offset, png_uint_32 y_offset,
++    png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
++    png_byte blend_op));
++
++PNG_EXPORT(1002, void,png_write_frame_tail, (png_structp png_ptr,
++    png_infop png_info));
++#endif
++
+ /* Write the end of the PNG file. */
+ PNG_EXPORT(61, void, png_write_end,
+     (png_structp png_ptr, png_infop info_ptr));
+@@ -1834,6 +1858,11 @@
+ PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structp png_ptr,
+     png_voidp progressive_ptr, png_progressive_info_ptr info_fn,
+     png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn));
++#ifdef PNG_READ_APNG_SUPPORTED
++PNG_EXPORT(1003, void, png_set_progressive_frame_fn, (png_structp png_ptr,
++    png_progressive_frame_ptr frame_info_fn,
++    png_progressive_frame_ptr frame_end_fn));
++#endif
+
+ /* Returns the user pointer associated with the push read functions */
+ PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, (png_const_structp
png_ptr));
+@@ -2333,6 +2362,62 @@
+     int unit, png_const_charp swidth, png_const_charp sheight));
+ #endif /* PNG_sCAL_SUPPORTED */
+
++#ifdef PNG_APNG_SUPPORTED
++PNG_EXPORT(1004, png_uint_32, png_get_acTL, (png_structp png_ptr,
++    png_infop info_ptr, png_uint_32 *num_frames, png_uint_32 *num_plays));
++PNG_EXPORT(1005, png_uint_32, png_set_acTL, (png_structp png_ptr,
++    png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays));
++PNG_EXPORT(1006, png_uint_32, png_get_num_frames, (png_structp png_ptr,
++    png_infop info_ptr));
++PNG_EXPORT(1007, png_uint_32, png_get_num_plays, (png_structp png_ptr,
++    png_infop info_ptr));
++
++PNG_EXPORT(1008, png_uint_32, png_get_next_frame_fcTL,
++    (png_structp png_ptr, png_infop info_ptr, png_uint_32 *width,
++    png_uint_32 *height, png_uint_32 *x_offset, png_uint_32 *y_offset,
++    png_uint_16 *delay_num, png_uint_16 *delay_den, png_byte *dispose_op,
++    png_byte *blend_op));
++PNG_EXPORT(1009, png_uint_32, png_set_next_frame_fcTL,
++    (png_structp png_ptr, png_infop info_ptr, png_uint_32 width,
++    png_uint_32 height, png_uint_32 x_offset, png_uint_32 y_offset,
++    png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
++    png_byte blend_op));
++PNG_EXPORT(1010, png_uint_32, png_get_next_frame_width,
++    (png_structp png_ptr, png_infop info_ptr));
++PNG_EXPORT(1011, png_uint_32, png_get_next_frame_height,
++    (png_structp png_ptr, png_infop info_ptr));
++PNG_EXPORT(1012, png_uint_32, png_get_next_frame_x_offset,
++    (png_structp png_ptr, png_infop info_ptr));
++PNG_EXPORT(1013, png_uint_32, png_get_next_frame_y_offset,
++    (png_structp png_ptr, png_infop info_ptr));
++PNG_EXPORT(1014, png_uint_16, png_get_next_frame_delay_num,
++    (png_structp png_ptr, png_infop info_ptr));
++PNG_EXPORT(1015, png_uint_16, png_get_next_frame_delay_den,
++    (png_structp png_ptr, png_infop info_ptr));
++PNG_EXPORT(1016, png_byte, png_get_next_frame_dispose_op,
++    (png_structp png_ptr, png_infop info_ptr));
++PNG_EXPORT(1017, png_byte, png_get_next_frame_blend_op,
++    (png_structp png_ptr, png_infop info_ptr));
++PNG_EXPORT(1018, png_byte, png_get_first_frame_is_hidden,
++    (png_structp png_ptr, png_infop info_ptr));
++PNG_EXPORT(1019, png_uint_32, png_set_first_frame_is_hidden,
++    (png_structp png_ptr, png_infop info_ptr, png_byte is_hidden));
++
++/* dispose_op flags from inside fcTL */
++#define PNG_DISPOSE_OP_NONE        0x00
++#define PNG_DISPOSE_OP_BACKGROUND  0x01
++#define PNG_DISPOSE_OP_PREVIOUS    0x02
++
++/* blend_op flags from inside fcTL */
++#define PNG_BLEND_OP_SOURCE        0x00
++#define PNG_BLEND_OP_OVER          0x01
++#endif /* PNG_APNG_SUPPORTED */
++
++#ifdef PNG_READ_APNG_SUPPORTED
++PNG_EXPORT(1020, void,png_read_frame_head, (png_structp png_ptr,
++    png_infop info_ptr));
++#endif
++
+ #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ /* Provide a list of chunks and how they are to be handled, if the
built-in
+    handling or default unknown chunk handling is not desired.  Any
chunks not
+diff -Naru libpng-1.5.9.org/pngget.c libpng-1.5.9/pngget.c
+--- libpng-1.5.9.org/pngget.c    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/pngget.c    2012-02-19 17:29:25.000000000 +0900
+@@ -1032,6 +1032,167 @@
+ }
+ #endif
+
++#ifdef PNG_APNG_SUPPORTED
++png_uint_32 PNGAPI
++png_get_acTL(png_structp png_ptr, png_infop info_ptr,
++             png_uint_32 *num_frames, png_uint_32 *num_plays)
++{
++    png_debug1(1, "in %s retrieval function", "acTL");
++    
++    if (png_ptr != NULL && info_ptr != NULL &&
++        (info_ptr->valid & PNG_INFO_acTL) &&
++        num_frames != NULL && num_plays != NULL)
++    {
++        *num_frames = info_ptr->num_frames;
++        *num_plays = info_ptr->num_plays;
++        return (1);
++    }
++    
++    return (0);
++}
++
++png_uint_32 PNGAPI
++png_get_num_frames(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_get_num_frames()");
++    
++    if (png_ptr != NULL && info_ptr != NULL)
++        return (info_ptr->num_frames);
++    return (0);
++}
++
++png_uint_32 PNGAPI
++png_get_num_plays(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_get_num_plays()");
++    
++    if (png_ptr != NULL && info_ptr != NULL)
++        return (info_ptr->num_plays);
++    return (0);
++}
++
++png_uint_32 PNGAPI
++png_get_next_frame_fcTL(png_structp png_ptr, png_infop info_ptr,
++             png_uint_32 *width, png_uint_32 *height,
++             png_uint_32 *x_offset, png_uint_32 *y_offset,
++             png_uint_16 *delay_num, png_uint_16 *delay_den,
++             png_byte *dispose_op, png_byte *blend_op)
++{
++    png_debug1(1, "in %s retrieval function", "fcTL");
++    
++    if (png_ptr != NULL && info_ptr != NULL &&
++        (info_ptr->valid & PNG_INFO_fcTL) &&
++        width != NULL && height != NULL &&
++        x_offset != NULL && x_offset != NULL &&
++        delay_num != NULL && delay_den != NULL &&
++    dispose_op != NULL && blend_op != NULL)
++    {
++        *width = info_ptr->next_frame_width;
++        *height = info_ptr->next_frame_height;
++        *x_offset = info_ptr->next_frame_x_offset;
++        *y_offset = info_ptr->next_frame_y_offset;
++        *delay_num = info_ptr->next_frame_delay_num;
++        *delay_den = info_ptr->next_frame_delay_den;
++        *dispose_op = info_ptr->next_frame_dispose_op;
++        *blend_op = info_ptr->next_frame_blend_op;
++        return (1);
++    }
++    
++    return (0);
++}
++
++png_uint_32 PNGAPI
++png_get_next_frame_width(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_get_next_frame_width()");
++    
++    if (png_ptr != NULL && info_ptr != NULL)
++        return (info_ptr->next_frame_width);
++    return (0);
++}
++
++png_uint_32 PNGAPI
++png_get_next_frame_height(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_get_next_frame_height()");
++    
++    if (png_ptr != NULL && info_ptr != NULL)
++        return (info_ptr->next_frame_height);
++    return (0);
++}
++
++png_uint_32 PNGAPI
++png_get_next_frame_x_offset(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_get_next_frame_x_offset()");
++    
++    if (png_ptr != NULL && info_ptr != NULL)
++        return (info_ptr->next_frame_x_offset);
++    return (0);
++}
++
++png_uint_32 PNGAPI
++png_get_next_frame_y_offset(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_get_next_frame_y_offset()");
++    
++    if (png_ptr != NULL && info_ptr != NULL)
++        return (info_ptr->next_frame_y_offset);
++    return (0);
++}
++
++png_uint_16 PNGAPI
++png_get_next_frame_delay_num(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_get_next_frame_delay_num()");
++    
++    if (png_ptr != NULL && info_ptr != NULL)
++        return (info_ptr->next_frame_delay_num);
++    return (0);
++}
++
++png_uint_16 PNGAPI
++png_get_next_frame_delay_den(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_get_next_frame_delay_den()");
++    
++    if (png_ptr != NULL && info_ptr != NULL)
++        return (info_ptr->next_frame_delay_den);
++    return (0);
++}
++
++png_byte PNGAPI
++png_get_next_frame_dispose_op(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_get_next_frame_dispose_op()");
++    
++    if (png_ptr != NULL && info_ptr != NULL)
++        return (info_ptr->next_frame_dispose_op);
++    return (0);
++}
++
++png_byte PNGAPI
++png_get_next_frame_blend_op(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_get_next_frame_blend_op()");
++    
++    if (png_ptr != NULL && info_ptr != NULL)
++        return (info_ptr->next_frame_blend_op);
++    return (0);
++}
++
++png_byte PNGAPI
++png_get_first_frame_is_hidden(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_first_frame_is_hidden()");
++    
++    if (png_ptr != NULL)
++       return (png_byte)(png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN);
++    
++    return 0;
++}
++#endif /* PNG_APNG_SUPPORTED */
++
+ #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+ int PNGAPI
+ png_get_unknown_chunks(png_const_structp png_ptr, png_const_infop
info_ptr,
+diff -Naru libpng-1.5.9.org/pnginfo.h libpng-1.5.9/pnginfo.h
+--- libpng-1.5.9.org/pnginfo.h    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/pnginfo.h    2012-02-19 17:29:25.000000000 +0900
+@@ -265,5 +265,18 @@
+    png_bytepp row_pointers;        /* the image bits */
+ #endif
+
++#ifdef PNG_APNG_SUPPORTED
++   png_uint_32 num_frames; /* including default image */
++   png_uint_32 num_plays;
++   png_uint_32 next_frame_width;
++   png_uint_32 next_frame_height;
++   png_uint_32 next_frame_x_offset;
++   png_uint_32 next_frame_y_offset;
++   png_uint_16 next_frame_delay_num;
++   png_uint_16 next_frame_delay_den;
++   png_byte next_frame_dispose_op;
++   png_byte next_frame_blend_op;
++#endif
++
+ };
+ #endif /* PNGINFO_H */
+diff -Naru libpng-1.5.9.org/pngpread.c libpng-1.5.9/pngpread.c
+--- libpng-1.5.9.org/pngpread.c    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/pngpread.c    2012-02-19 17:29:25.000000000 +0900
+@@ -235,9 +235,106 @@
+       png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+       png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
+    }
+-
++   
+    chunk_name = png_ptr->chunk_name;
+
++#ifdef PNG_READ_APNG_SUPPORTED
++   if (png_ptr->num_frames_read > 0 &&
++       png_ptr->num_frames_read < info_ptr->num_frames)
++   {
++      if (chunk_name == png_IDAT)
++      {
++         /* Discard trailing IDATs for the first frame */
++         if (png_ptr->mode & PNG_HAVE_fcTL || png_ptr->num_frames_read
> 1)
++            png_error(png_ptr, "out of place IDAT");
++         
++         if (png_ptr->push_length + 4 > png_ptr->buffer_size)
++         {
++            png_push_save_buffer(png_ptr);
++            return;
++         }
++         png_push_crc_skip(png_ptr, png_ptr->push_length);
++         png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
++         return;
++      }
++      else if (chunk_name == png_fdAT)
++      {
++         if (png_ptr->buffer_size < 4)
++         {
++            png_push_save_buffer(png_ptr);
++            return;
++         }
++         png_ensure_sequence_number(png_ptr, 4);
++         
++         if (!(png_ptr->mode & PNG_HAVE_fcTL))
++         {
++            /* Discard trailing fdATs for frames other than the first */
++            if (png_ptr->num_frames_read < 2)
++               png_error(png_ptr, "out of place fdAT");
++            
++            if (png_ptr->push_length + 4 > png_ptr->buffer_size)
++            {
++               png_push_save_buffer(png_ptr);
++               return;
++            }
++            png_push_crc_skip(png_ptr, png_ptr->push_length);
++            png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
++            return;
++         }
++         else
++         {
++            /* frame data follows */
++            png_ptr->idat_size = png_ptr->push_length - 4;
++            png_ptr->mode |= PNG_HAVE_IDAT;
++            png_ptr->process_mode = PNG_READ_IDAT_MODE;
++            
++            return;
++         }
++      }
++      else if(chunk_name == png_fcTL)
++      {
++         if (png_ptr->push_length + 4 > png_ptr->buffer_size)
++         {
++            png_push_save_buffer(png_ptr);
++            return;
++         }
++         
++         png_read_reset(png_ptr);
++         png_ptr->mode &= ~PNG_HAVE_fcTL;
++         
++         png_handle_fcTL(png_ptr, info_ptr, png_ptr->push_length);
++         
++         if (!(png_ptr->mode & PNG_HAVE_fcTL))
++            png_error(png_ptr, "missing required fcTL chunk");
++         
++         png_read_reinit(png_ptr, info_ptr);
++         png_progressive_read_reset(png_ptr);
++         
++         if (png_ptr->frame_info_fn != NULL)
++            (*(png_ptr->frame_info_fn))(png_ptr,
png_ptr->num_frames_read);
++         
++         png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
++         
++         return;
++      }
++      else
++      {
++         if (png_ptr->push_length + 4 > png_ptr->buffer_size)
++         {
++            png_push_save_buffer(png_ptr);
++            return;
++         }
++         png_warning(png_ptr, "Skipped (ignored) a chunk "
++                              "between APNG chunks");
++         png_push_crc_skip(png_ptr, png_ptr->push_length);
++         png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
++         return;
++      }
++      
++      return;
++   }
++#endif /* PNG_READ_APNG_SUPPORTED */
++
+    if (chunk_name == png_IDAT)
+    {
+       /* This is here above the if/else case statement below because
if the
+@@ -341,6 +438,9 @@
+             png_benign_error(png_ptr, "Too many IDATs found");
+       }
+
++#ifdef PNG_READ_APNG_SUPPORTED
++      png_have_info(png_ptr, info_ptr);
++#endif
+       png_ptr->idat_size = png_ptr->push_length;
+       png_ptr->mode |= PNG_HAVE_IDAT;
+       png_ptr->process_mode = PNG_READ_IDAT_MODE;
+@@ -573,6 +673,38 @@
+    }
+
+ #endif
++#ifdef PNG_READ_APNG_SUPPORTED
++   else if (chunk_name == png_acTL)
++   {
++      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
++      {
++         png_push_save_buffer(png_ptr);
++         return;
++      }
++
++      png_handle_acTL(png_ptr, info_ptr, png_ptr->push_length);
++   }
++   else if (chunk_name == png_fcTL)
++   {
++      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
++      {
++         png_push_save_buffer(png_ptr);
++         return;
++      }
++
++      png_handle_fcTL(png_ptr, info_ptr, png_ptr->push_length);
++   }
++   else if (chunk_name == png_fdAT)
++   {
++      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
++      {
++         png_push_save_buffer(png_ptr);
++         return;
++      }
++
++      png_handle_fdAT(png_ptr, info_ptr, png_ptr->push_length);
++   }
++#endif /* PNG_READ_APNG_SUPPORTED */
+    else
+    {
+       if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+@@ -772,7 +904,7 @@
+       png_byte chunk_tag[4];
+
+       /* TODO: this code can be commoned up with the same code in
push_read */
+-      if (png_ptr->buffer_size < 8)
++      if (png_ptr->buffer_size < 12)
+       {
+          png_push_save_buffer(png_ptr);
+          return;
+@@ -785,17 +917,64 @@
+       png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);
+       png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
+
+-      if (png_ptr->chunk_name != png_IDAT)
++#ifdef PNG_READ_APNG_SUPPORTED
++      if ((png_ptr->chunk_name != png_fdAT)
++          && png_ptr->num_frames_read > 0)
++      {
++          if (png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)
++          {
++              png_ptr->process_mode = PNG_READ_CHUNK_MODE;
++              if (png_ptr->frame_end_fn != NULL)
++                 (*(png_ptr->frame_end_fn))(png_ptr,
png_ptr->num_frames_read);
++              png_ptr->num_frames_read++;
++              return;
++          }
++          else
++          {
++              if (png_ptr->chunk_name == png_IEND)
++                  png_error(png_ptr, "Not enough image data");
++              if (png_ptr->push_length + 4 > png_ptr->buffer_size)
++              {
++                 png_push_save_buffer(png_ptr);
++                 return;
++              }
++              png_warning(png_ptr, "Skipping (ignoring) a chunk between "
++                                   "APNG chunks");
++              png_crc_finish(png_ptr, png_ptr->push_length);
++              png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
++              return;
++          }
++      }
++      else
++#endif
++      if ((png_ptr->chunk_name != png_IDAT)
++#ifdef PNG_READ_APNG_SUPPORTED
++                && (png_ptr->num_frames_read == 0)
++#endif
++         )
+       {
+          png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+
+          if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+             png_error(png_ptr, "Not enough compressed data");
+
++#ifdef PNG_READ_APNG_SUPPORTED
++         if (png_ptr->frame_end_fn != NULL)
++            (*(png_ptr->frame_end_fn))(png_ptr, png_ptr->num_frames_read);
++         png_ptr->num_frames_read++;
++#endif
+          return;
+       }
+
+       png_ptr->idat_size = png_ptr->push_length;
++      
++#ifdef PNG_READ_APNG_SUPPORTED
++      if (png_ptr->num_frames_read > 0)
++      {
++         png_ensure_sequence_number(png_ptr, 4);
++         png_ptr->idat_size -= 4;
++      }
++#endif
+    }
+
+    if (png_ptr->idat_size && png_ptr->save_buffer_size)
+@@ -1832,6 +2011,17 @@
+    png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer);
+ }
+
++#ifdef PNG_READ_APNG_SUPPORTED
++void PNGAPI
++png_set_progressive_frame_fn(png_structp png_ptr,
++   png_progressive_frame_ptr frame_info_fn,
++   png_progressive_frame_ptr frame_end_fn)
++{
++   png_ptr->frame_info_fn = frame_info_fn;
++   png_ptr->frame_end_fn = frame_end_fn;
++}
++#endif
++
+ png_voidp PNGAPI
+ png_get_progressive_ptr(png_const_structp png_ptr)
+ {
+diff -Naru libpng-1.5.9.org/pngpriv.h libpng-1.5.9/pngpriv.h
+--- libpng-1.5.9.org/pngpriv.h    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/pngpriv.h    2012-02-19 17:29:25.000000000 +0900
+@@ -426,6 +426,10 @@
+ #define PNG_BACKGROUND_IS_GRAY     0x800
+ #define PNG_HAVE_PNG_SIGNATURE    0x1000
+ #define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after
IDAT */
++#ifdef PNG_APNG_SUPPORTED
++#define PNG_HAVE_acTL             0x4000
++#define PNG_HAVE_fcTL             0x8000L
++#endif
+
+ /* Flags for the transformations the PNG library does on the image data */
+ #define PNG_BGR                 0x0001
+@@ -620,6 +624,14 @@
+ #define png_tIME PNG_CHUNK(116,  73,  77,  69)
+ #define png_tRNS PNG_CHUNK(116,  82,  78,  83)
+ #define png_zTXt PNG_CHUNK(122,  84,  88, 116)
++#ifdef PNG_APNG_SUPPORTED
++#define png_acTL PNG_CHUNK( 97,  99,  84,  76)
++#define png_fcTL PNG_CHUNK(102,  99,  84,  76)
++#define png_fdAT PNG_CHUNK(102, 100,  65,  84)
++
++/* For png_struct.apng_flags: */
++#define PNG_FIRST_FRAME_HIDDEN       0x0001
++#endif
+
+ /* The following will work on (signed char*) strings, whereas the
get_uint_32
+  * macro will fail on top-bit-set values because of the sign extension.
+@@ -895,6 +907,17 @@
+     int unit, png_const_charp width, png_const_charp height));
+ #endif
+
++#ifdef PNG_WRITE_APNG_SUPPORTED
++PNG_EXTERN void png_write_acTL PNGARG((png_structp png_ptr,
++   png_uint_32 num_frames, png_uint_32 num_plays));
++
++PNG_EXTERN void png_write_fcTL PNGARG((png_structp png_ptr,
++   png_uint_32 width, png_uint_32 height,
++   png_uint_32 x_offset, png_uint_32 y_offset,
++   png_uint_16 delay_num, png_uint_16 delay_den,
++   png_byte dispose_op, png_byte blend_op));
++#endif
++
+ /* Called when finished processing a row of data */
+ PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr));
+
+@@ -986,6 +1009,20 @@
+     png_infop info_ptr));
+ #endif
+
++#ifdef PNG_READ_APNG_SUPPORTED
++/* Private, reset some things to become ready for reading next frame */
++PNG_EXTERN void png_read_reset PNGARG((png_structp png_ptr));
++PNG_EXTERN void png_read_reinit PNGARG((png_structp png_ptr,
++   png_infop info_ptr));
++PNG_EXTERN void png_progressive_read_reset PNGARG((png_structp png_ptr));
++#endif
++#ifdef PNG_WRITE_APNG_SUPPORTED
++/* Private, reset some things to become ready for writing next frame */
++PNG_EXTERN void png_write_reset PNGARG((png_structp png_ptr));
++PNG_EXTERN void png_write_reinit PNGARG((png_structp png_ptr,
++   png_infop info_ptr, png_uint_32 width, png_uint_32 height));
++#endif
++
+ /* These are the functions that do the transformations */
+ #ifdef PNG_READ_FILLER_SUPPORTED
+ PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info,
+@@ -1218,6 +1255,23 @@
+     png_uint_32 length));
+ #endif
+
++#ifdef PNG_READ_APNG_SUPPORTED
++PNG_EXTERN void png_handle_acTL PNGARG((png_structp png_ptr, png_infop
info_ptr,
++   png_uint_32 length));
++PNG_EXTERN void png_handle_fcTL PNGARG((png_structp png_ptr, png_infop
info_ptr,
++   png_uint_32 length));
++PNG_EXTERN void png_have_info PNGARG((png_structp png_ptr, png_infop
info_ptr));
++PNG_EXTERN void png_handle_fdAT PNGARG((png_structp png_ptr, png_infop
info_ptr,
++   png_uint_32 length));
++PNG_EXTERN void png_ensure_sequence_number PNGARG((png_structp png_ptr,
++   png_uint_32 length));
++PNG_EXTERN void png_ensure_fcTL_is_valid PNGARG((png_structp png_ptr,
++   png_uint_32 width, png_uint_32 height,
++   png_uint_32 x_offset, png_uint_32 y_offset,
++   png_uint_16 delay_num, png_uint_16 delay_den,
++   png_byte dispose_op, png_byte blend_op));
++#endif
++
+ #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr,
+     png_infop info_ptr, png_uint_32 length));
+diff -Naru libpng-1.5.9.org/pngread.c libpng-1.5.9/pngread.c
+--- libpng-1.5.9.org/pngread.c    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/pngread.c    2012-02-19 17:29:25.000000000 +0900
+@@ -244,6 +244,9 @@
+              !(png_ptr->mode & PNG_HAVE_PLTE))
+             png_error(png_ptr, "Missing PLTE before IDAT");
+
++#ifdef PNG_READ_APNG_SUPPORTED
++         png_have_info(png_ptr, info_ptr);
++#endif
+          png_ptr->idat_size = length;
+          png_ptr->mode |= PNG_HAVE_IDAT;
+          break;
+@@ -334,12 +337,95 @@
+          png_handle_iTXt(png_ptr, info_ptr, length);
+ #endif
+
++#ifdef PNG_READ_APNG_SUPPORTED
++      else if (chunk_name == png_acTL)
++         png_handle_acTL(png_ptr, info_ptr, length);
++      else if (chunk_name == png_fcTL)
++         png_handle_fcTL(png_ptr, info_ptr, length);
++      else if (chunk_name == png_fdAT)
++         png_handle_fdAT(png_ptr, info_ptr, length);
++#endif
++
+       else
+          png_handle_unknown(png_ptr, info_ptr, length);
+    }
+ }
+ #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+
++#ifdef PNG_READ_APNG_SUPPORTED
++void PNGAPI
++png_read_frame_head(png_structp png_ptr, png_infop info_ptr)
++{
++    png_byte have_chunk_after_DAT; /* after IDAT or after fdAT */
++    
++    png_debug(0, "Reading frame head");
++    
++    if (!(png_ptr->mode & PNG_HAVE_acTL))
++        png_error(png_ptr, "attempt to png_read_frame_head() but "
++                           "no acTL present");
++    
++    /* do nothing for the main IDAT */
++    if (png_ptr->num_frames_read == 0)
++        return;
++    
++    png_crc_finish(png_ptr, 0); /* CRC from last IDAT or fdAT chunk */
++    
++    png_read_reset(png_ptr);
++    png_ptr->mode &= ~PNG_HAVE_fcTL;
++    
++    have_chunk_after_DAT = 0;
++    for (;;)
++    {
++        png_byte chunk_length[4];
++        png_byte chunk_tag[4];
++        png_uint_32 length;
++        
++        png_read_data(png_ptr, chunk_length, 4);
++        length = png_get_uint_31(png_ptr, chunk_length);
++        
++        png_reset_crc(png_ptr);
++        png_crc_read(png_ptr, chunk_tag, 4);
++        png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);
++        
++        if (png_ptr->chunk_name == png_IDAT)
++        {
++            /* discard trailing IDATs for the first frame */
++            if (have_chunk_after_DAT || png_ptr->num_frames_read > 1)
++                png_error(png_ptr, "png_read_frame_head(): out of
place IDAT");
++            png_crc_finish(png_ptr, length);
++        }
++        else if (png_ptr->chunk_name == png_fcTL)
++        {
++            png_handle_fcTL(png_ptr, info_ptr, length);
++            have_chunk_after_DAT = 1;
++        }
++        else if (png_ptr->chunk_name == png_fdAT)
++        {
++            png_ensure_sequence_number(png_ptr, length);
++            
++            /* discard trailing fdATs for frames other than the first */
++            if (!have_chunk_after_DAT && png_ptr->num_frames_read > 1)
++                png_crc_finish(png_ptr, length - 4);
++            else if(png_ptr->mode & PNG_HAVE_fcTL)
++            {
++                png_ptr->idat_size = length - 4;
++                png_ptr->mode |= PNG_HAVE_IDAT;
++                
++                break;
++            }
++            else
++                png_error(png_ptr, "png_read_frame_head(): out of
place fdAT");
++        }
++        else
++        {
++            png_warning(png_ptr, "Skipped (ignored) a chunk "
++                                 "between APNG chunks");
++            png_crc_finish(png_ptr, length);
++        }
++    }
++}
++#endif /* PNG_READ_APNG_SUPPORTED */
++
+ /* Optional call to update the users info_ptr structure */
+ void PNGAPI
+ png_read_update_info(png_structp png_ptr, png_infop info_ptr)
+@@ -541,13 +627,39 @@
+    {
+       if (!(png_ptr->zstream.avail_in))
+       {
+-         while (!png_ptr->idat_size)
++         png_uint_32 bytes_to_skip = 0;
++         
++         while (!png_ptr->idat_size || bytes_to_skip != 0)
+          {
+-            png_crc_finish(png_ptr, 0);
++            png_crc_finish(png_ptr, bytes_to_skip);
++            bytes_to_skip = 0;
+
+             png_ptr->idat_size = png_read_chunk_header(png_ptr);
+-            if (png_ptr->chunk_name != png_IDAT)
+-               png_error(png_ptr, "Not enough image data");
++#ifdef PNG_READ_APNG_SUPPORTED
++            if (png_ptr->num_frames_read == 0)
++            {
++#endif
++               if (png_ptr->chunk_name != png_IDAT)
++                  png_error(png_ptr, "Not enough image data");
++#ifdef PNG_READ_APNG_SUPPORTED
++            }
++            else
++            {
++               if (png_ptr->chunk_name == png_IEND)
++                  png_error(png_ptr, "Not enough image data");
++               if (png_ptr->chunk_name != png_fdAT)
++               {
++                  png_warning(png_ptr, "Skipped (ignored) a chunk "
++                                       "between APNG chunks");
++                  bytes_to_skip = png_ptr->idat_size;
++                  continue;
++               }
++               
++               png_ensure_sequence_number(png_ptr, png_ptr->idat_size);
++               
++               png_ptr->idat_size -= 4;
++            }
++#endif
+          }
+          png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size;
+          png_ptr->zstream.next_in = png_ptr->zbuf;
+@@ -567,6 +679,9 @@
+             png_benign_error(png_ptr, "Extra compressed data");
+          png_ptr->mode |= PNG_AFTER_IDAT;
+          png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
++#ifdef PNG_READ_APNG_SUPPORTED
++         png_ptr->num_frames_read++;
++#endif
+          break;
+       }
+
+@@ -929,6 +1044,15 @@
+          png_handle_iTXt(png_ptr, info_ptr, length);
+ #endif
+
++#ifdef PNG_READ_APNG_SUPPORTED
++      else if (chunk_name == png_acTL)
++         png_handle_acTL(png_ptr, info_ptr, length);
++      else if (chunk_name == png_fcTL)
++         png_handle_fcTL(png_ptr, info_ptr, length);
++      else if (chunk_name == png_fdAT)
++         png_handle_fdAT(png_ptr, info_ptr, length);
++#endif
++
+       else
+          png_handle_unknown(png_ptr, info_ptr, length);
+    } while (!(png_ptr->mode & PNG_HAVE_IEND));
+diff -Naru libpng-1.5.9.org/pngrutil.c libpng-1.5.9/pngrutil.c
+--- libpng-1.5.9.org/pngrutil.c    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/pngrutil.c    2012-02-19 17:29:25.000000000 +0900
+@@ -549,6 +549,11 @@
+    filter_type = buf[11];
+    interlace_type = buf[12];
+
++#ifdef PNG_READ_APNG_SUPPORTED
++   png_ptr->first_frame_width = width;
++   png_ptr->first_frame_height = height;
++#endif
++
+    /* Set internal variables */
+    png_ptr->width = width;
+    png_ptr->height = height;
+@@ -2613,6 +2618,172 @@
+ }
+ #endif
+
++#ifdef PNG_READ_APNG_SUPPORTED
++void /* PRIVATE */
++png_handle_acTL(png_structp png_ptr, png_infop info_ptr, png_uint_32
length)
++{
++    png_byte data[8];
++    png_uint_32 num_frames;
++    png_uint_32 num_plays;
++    png_uint_32 didSet;
++    
++    png_debug(1, "in png_handle_acTL");
++
++    if (!(png_ptr->mode & PNG_HAVE_IHDR))
++    {
++        png_error(png_ptr, "Missing IHDR before acTL");
++    }
++    else if (png_ptr->mode & PNG_HAVE_IDAT)
++    {
++        png_warning(png_ptr, "Invalid acTL after IDAT skipped");
++        png_crc_finish(png_ptr, length);
++        return;
++    }
++    else if (png_ptr->mode & PNG_HAVE_acTL)
++    {
++        png_warning(png_ptr, "Duplicate acTL skipped");
++        png_crc_finish(png_ptr, length);
++        return;
++    }
++    else if (length != 8)
++    {
++        png_warning(png_ptr, "acTL with invalid length skipped");
++        png_crc_finish(png_ptr, length);
++        return;
++    }
++    
++    png_crc_read(png_ptr, data, 8);
++    png_crc_finish(png_ptr, 0);
++    
++    num_frames = png_get_uint_31(png_ptr, data);
++    num_plays = png_get_uint_31(png_ptr, data + 4);
++    
++    /* the set function will do error checking on num_frames */
++    didSet = png_set_acTL(png_ptr, info_ptr, num_frames, num_plays);
++    if(didSet)
++        png_ptr->mode |= PNG_HAVE_acTL;
++}
++
++void /* PRIVATE */
++png_handle_fcTL(png_structp png_ptr, png_infop info_ptr, png_uint_32
length)
++{
++    png_byte data[22];
++    png_uint_32 width;
++    png_uint_32 height;
++    png_uint_32 x_offset;
++    png_uint_32 y_offset;
++    png_uint_16 delay_num;
++    png_uint_16 delay_den;
++    png_byte dispose_op;
++    png_byte blend_op;
++
++    png_debug(1, "in png_handle_fcTL");
++
++    png_ensure_sequence_number(png_ptr, length);
++
++    if (!(png_ptr->mode & PNG_HAVE_IHDR))
++    {
++        png_error(png_ptr, "Missing IHDR before fcTL");
++    }
++    else if (png_ptr->mode & PNG_HAVE_IDAT)
++    {
++        /* for any frames other then the first this message may be
misleading,
++        * but correct. PNG_HAVE_IDAT is unset before the frame head is
read
++        * i can't think of a better message */
++        png_warning(png_ptr, "Invalid fcTL after IDAT skipped");
++        png_crc_finish(png_ptr, length-4);
++        return;
++    }
++    else if (png_ptr->mode & PNG_HAVE_fcTL)
++    {
++        png_warning(png_ptr, "Duplicate fcTL within one frame skipped");
++        png_crc_finish(png_ptr, length-4);
++        return;
++    }
++    else if (length != 26)
++    {
++        png_warning(png_ptr, "fcTL with invalid length skipped");
++        png_crc_finish(png_ptr, length-4);
++        return;
++    }
++
++    png_crc_read(png_ptr, data, 22);
++    png_crc_finish(png_ptr, 0);
++
++    width = png_get_uint_31(png_ptr, data);
++    height = png_get_uint_31(png_ptr, data + 4);
++    x_offset = png_get_uint_31(png_ptr, data + 8);
++    y_offset = png_get_uint_31(png_ptr, data + 12);
++    delay_num = png_get_uint_16(data + 16);
++    delay_den = png_get_uint_16(data + 18);
++    dispose_op = data[20];
++    blend_op = data[21];
++
++    if (png_ptr->num_frames_read == 0 && (x_offset != 0 || y_offset != 0))
++        png_error(png_ptr, "fcTL for the first frame must have zero
offset");
++
++    if (info_ptr != NULL)
++    {
++        if (png_ptr->num_frames_read == 0 &&
++            (width != info_ptr->width || height != info_ptr->height))
++            png_error(png_ptr, "size in first frame's fcTL must match "
++                               "the size in IHDR");
++
++        /* the set function will do more error checking */
++        png_set_next_frame_fcTL(png_ptr, info_ptr, width, height,
++                                x_offset, y_offset, delay_num, delay_den,
++                                dispose_op, blend_op);
++
++        png_read_reinit(png_ptr, info_ptr);
++    }
++
++    png_ptr->mode |= PNG_HAVE_fcTL;
++}
++
++void /* PRIVATE */
++png_have_info(png_structp png_ptr, png_infop info_ptr)
++{
++    if((info_ptr->valid & PNG_INFO_acTL) && !(info_ptr->valid &
PNG_INFO_fcTL))
++    {
++        png_ptr->apng_flags |= PNG_FIRST_FRAME_HIDDEN;
++        info_ptr->num_frames++;
++    }
++}
++
++void /* PRIVATE */
++png_handle_fdAT(png_structp png_ptr, png_infop info_ptr, png_uint_32
length)
++{
++    png_ensure_sequence_number(png_ptr, length);
++    
++    /* This function is only called from png_read_end(), png_read_info(),
++    * and png_push_read_chunk() which means that:
++    * - the user doesn't want to read this frame
++    * - or this is an out-of-place fdAT
++    * in either case it is safe to ignore the chunk with a warning */
++    png_warning(png_ptr, "ignoring fdAT chunk");
++    png_crc_finish(png_ptr, length - 4);
++}
++
++void /* PRIVATE */
++png_ensure_sequence_number(png_structp png_ptr, png_uint_32 length)
++{
++    png_byte data[4];
++    png_uint_32 sequence_number;
++    
++    if (length < 4)
++        png_error(png_ptr, "invalid fcTL or fdAT chunk found");
++    
++    png_crc_read(png_ptr, data, 4);
++    sequence_number = png_get_uint_31(png_ptr, data);
++    
++    if (sequence_number != png_ptr->next_seq_num)
++        png_error(png_ptr, "fcTL or fdAT chunk with out-of-order
sequence "
++                           "number found");
++    
++    png_ptr->next_seq_num++;
++}
++#endif /* PNG_READ_APNG_SUPPORTED */
++
+ /* This function is called when we haven't found a handler for a
+  * chunk.  If there isn't a problem with the chunk itself (ie bad
+  * chunk name, CRC, or a critical chunk), the chunk is silently ignored
+@@ -4155,4 +4326,76 @@
+
+    png_ptr->flags |= PNG_FLAG_ROW_INIT;
+ }
++
++#ifdef PNG_READ_APNG_SUPPORTED
++/* This function is to be called after the main IDAT set has been read and
++ * before a new IDAT is read. It resets some parts of png_ptr
++ * to make them usable by the read functions again */
++void /* PRIVATE */
++png_read_reset(png_structp png_ptr)
++{
++    png_ptr->mode &= ~PNG_HAVE_IDAT;
++    png_ptr->mode &= ~PNG_AFTER_IDAT;
++    png_ptr->row_number = 0;
++    png_ptr->pass = 0;
++    png_ptr->flags &= ~PNG_FLAG_ROW_INIT;
++}
++
++void /* PRIVATE */
++png_read_reinit(png_structp png_ptr, png_infop info_ptr)
++{
++    png_ptr->width = info_ptr->next_frame_width;
++    png_ptr->height = info_ptr->next_frame_height;
++    png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width);
++    png_ptr->info_rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,
++        png_ptr->width);
++    if (png_ptr->prev_row)
++        png_memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
++}
++
++/* same as png_read_reset() but for the progressive reader */
++void /* PRIVATE */
++png_progressive_read_reset(png_structp png_ptr)
++{
++    /* start of interlace block */
++    const int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
++
++    /* offset to next interlace block */
++    const int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
++
++    /* start of interlace block in the y direction */
++    const int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
++
++    /* offset to next interlace block in the y direction */
++    const int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
++
++    if (png_ptr->interlaced)
++    {
++        if (!(png_ptr->transformations & PNG_INTERLACE))
++            png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
++                                png_pass_ystart[0]) / png_pass_yinc[0];
++        else
++           png_ptr->num_rows = png_ptr->height;
++
++        png_ptr->iwidth = (png_ptr->width +
++                           png_pass_inc[png_ptr->pass] - 1 -
++                           png_pass_start[png_ptr->pass]) /
++                           png_pass_inc[png_ptr->pass];
++    }
++    else
++    {
++        png_ptr->num_rows = png_ptr->height;
++        png_ptr->iwidth = png_ptr->width;
++    }
++
++    png_ptr->flags &= ~PNG_FLAG_ZLIB_FINISHED;
++    if (inflateReset(&(png_ptr->zstream)) != Z_OK)
++        png_error(png_ptr, "inflateReset failed");
++    png_ptr->zstream.avail_in = 0;
++    png_ptr->zstream.next_in = 0;
++    png_ptr->zstream.next_out = png_ptr->row_buf;
++    png_ptr->zstream.avail_out = (uInt)PNG_ROWBYTES(png_ptr->pixel_depth,
++        png_ptr->iwidth) + 1;
++}
++#endif /* PNG_READ_APNG_SUPPORTED */
+ #endif /* PNG_READ_SUPPORTED */
+diff -Naru libpng-1.5.9.org/pngset.c libpng-1.5.9/pngset.c
+--- libpng-1.5.9.org/pngset.c    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/pngset.c    2012-02-19 17:29:25.000000000 +0900
+@@ -262,6 +262,11 @@
+       info_ptr->rowbytes = 0;
+    else
+       info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width);
++   
++#ifdef PNG_APNG_SUPPORTED
++   /* for non-animated png. this may be overritten from an acTL chunk
later */
++   info_ptr->num_frames = 1;
++#endif
+ }
+
+ #ifdef PNG_oFFs_SUPPORTED
+@@ -1013,6 +1018,143 @@
+ }
+ #endif /* PNG_sPLT_SUPPORTED */
+
++#ifdef PNG_APNG_SUPPORTED
++png_uint_32 PNGAPI
++png_set_acTL(png_structp png_ptr, png_infop info_ptr,
++    png_uint_32 num_frames, png_uint_32 num_plays)
++{
++    png_debug1(1, "in %s storage function", "acTL");
++
++    if (png_ptr == NULL || info_ptr == NULL)
++    {
++        png_warning(png_ptr,
++                    "Call to png_set_acTL() with NULL png_ptr "
++                    "or info_ptr ignored");
++        return (0);
++    }
++    if (num_frames == 0)
++    {
++        png_warning(png_ptr,
++                    "Ignoring attempt to set acTL with num_frames zero");
++        return (0);
++    }
++    if (num_frames > PNG_UINT_31_MAX)
++    {
++        png_warning(png_ptr,
++                    "Ignoring attempt to set acTL with num_frames >
2^31-1");
++        return (0);
++    }
++    if (num_plays > PNG_UINT_31_MAX)
++    {
++        png_warning(png_ptr,
++                    "Ignoring attempt to set acTL with num_plays "
++                    "> 2^31-1");
++        return (0);
++    }
++    
++    info_ptr->num_frames = num_frames;
++    info_ptr->num_plays = num_plays;
++    
++    info_ptr->valid |= PNG_INFO_acTL;
++    
++    return (1);
++}
++
++/* delay_num and delay_den can hold any 16-bit values including zero */
++png_uint_32 PNGAPI
++png_set_next_frame_fcTL(png_structp png_ptr, png_infop info_ptr,
++    png_uint_32 width, png_uint_32 height,
++    png_uint_32 x_offset, png_uint_32 y_offset,
++    png_uint_16 delay_num, png_uint_16 delay_den,
++    png_byte dispose_op, png_byte blend_op)
++{
++    png_debug1(1, "in %s storage function", "fcTL");
++
++    if (png_ptr == NULL || info_ptr == NULL)
++    {
++        png_warning(png_ptr,
++                    "Call to png_set_fcTL() with NULL png_ptr or
info_ptr "
++                    "ignored");
++        return (0);
++    }
++    
++    png_ensure_fcTL_is_valid(png_ptr, width, height, x_offset, y_offset,
++                             delay_num, delay_den, dispose_op, blend_op);
++    
++    if (blend_op == PNG_BLEND_OP_OVER)
++    {
++        if (!(png_ptr->color_type & PNG_COLOR_MASK_ALPHA) &&
++            !(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)))
++        {
++          png_warning(png_ptr,
++                      "PNG_BLEND_OP_OVER is meaningless and wasteful "
++                      "for opaque images, ignored");
++          blend_op = PNG_BLEND_OP_SOURCE;
++        }
++    }
++
++    info_ptr->next_frame_width = width;
++    info_ptr->next_frame_height = height;
++    info_ptr->next_frame_x_offset = x_offset;
++    info_ptr->next_frame_y_offset = y_offset;
++    info_ptr->next_frame_delay_num = delay_num;
++    info_ptr->next_frame_delay_den = delay_den;
++    info_ptr->next_frame_dispose_op = dispose_op;
++    info_ptr->next_frame_blend_op = blend_op;
++    
++    info_ptr->valid |= PNG_INFO_fcTL;
++    
++    return (1);
++}
++
++void /* PRIVATE */
++png_ensure_fcTL_is_valid(png_structp png_ptr,
++    png_uint_32 width, png_uint_32 height,
++    png_uint_32 x_offset, png_uint_32 y_offset,
++    png_uint_16 delay_num, png_uint_16 delay_den,
++    png_byte dispose_op, png_byte blend_op)
++{
++    if (width + x_offset > png_ptr->first_frame_width ||
++        height + y_offset > png_ptr->first_frame_height)
++        png_error(png_ptr, "dimensions of a frame are greater than"
++                           "the ones in IHDR");
++    if (width > PNG_UINT_31_MAX)
++        png_error(png_ptr, "invalid width in fcTL (> 2^31-1)");
++    if (height > PNG_UINT_31_MAX)
++        png_error(png_ptr, "invalid height in fcTL (> 2^31-1)");
++    if (x_offset > PNG_UINT_31_MAX)
++        png_error(png_ptr, "invalid x_offset in fcTL (> 2^31-1)");
++    if (y_offset > PNG_UINT_31_MAX)
++        png_error(png_ptr, "invalid y_offset in fcTL (> 2^31-1)");
++
++    if (dispose_op != PNG_DISPOSE_OP_NONE &&
++    dispose_op != PNG_DISPOSE_OP_BACKGROUND &&
++    dispose_op != PNG_DISPOSE_OP_PREVIOUS)
++        png_error(png_ptr, "invalid dispose_op in fcTL");
++
++    if (blend_op != PNG_BLEND_OP_SOURCE &&
++    blend_op != PNG_BLEND_OP_OVER)
++        png_error(png_ptr, "invalid blend_op in fcTL");
++}
++
++png_uint_32 PNGAPI
++png_set_first_frame_is_hidden(png_structp png_ptr, png_infop info_ptr,
++                              png_byte is_hidden)
++{
++    png_debug(1, "in png_first_frame_is_hidden()");
++    
++    if (png_ptr == NULL)
++        return 0;
++    
++    if(is_hidden)
++        png_ptr->apng_flags |= PNG_FIRST_FRAME_HIDDEN;
++    else
++        png_ptr->apng_flags &= ~PNG_FIRST_FRAME_HIDDEN;
++    
++    return 1;
++}
++#endif /* PNG_APNG_SUPPORTED */
++
+ #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+ void PNGAPI
+ png_set_unknown_chunks(png_structp png_ptr,
+diff -Naru libpng-1.5.9.org/pngstruct.h libpng-1.5.9/pngstruct.h
+--- libpng-1.5.9.org/pngstruct.h    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/pngstruct.h    2012-02-19 17:29:25.000000000 +0900
+@@ -332,6 +332,27 @@
+    png_alloc_size_t user_chunk_malloc_max;
+ #endif
+
++#ifdef PNG_APNG_SUPPORTED
++   png_uint_32 apng_flags;
++   png_uint_32 next_seq_num;         /* next fcTL/fdAT chunk sequence
number */
++   png_uint_32 first_frame_width;
++   png_uint_32 first_frame_height;
++
++#ifdef PNG_READ_APNG_SUPPORTED
++   png_uint_32 num_frames_read;      /* incremented after all image
data of */
++                                     /* a frame is read */
++#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
++   png_progressive_frame_ptr frame_info_fn; /* frame info read callback */
++   png_progressive_frame_ptr frame_end_fn;  /* frame data read callback */
++#endif
++#endif
++
++#ifdef PNG_WRITE_APNG_SUPPORTED
++   png_uint_32 num_frames_to_write;
++   png_uint_32 num_frames_written;
++#endif
++#endif /* PNG_APNG_SUPPORTED */
++
+ /* New member added in libpng-1.0.25 and 1.2.17 */
+ #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+    /* Storage for unknown chunk that the library doesn't recognize. */
+diff -Naru libpng-1.5.9.org/pngtest.c libpng-1.5.9/pngtest.c
+--- libpng-1.5.9.org/pngtest.c    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/pngtest.c    2012-02-19 17:29:25.000000000 +0900
+@@ -785,6 +785,10 @@
+    jmp_buf tmp_jmpbuf;
+ #endif
+ #endif
++#ifdef PNG_APNG_SUPPORTED
++   png_uint_32 num_frames;
++   png_uint_32 num_plays;
++#endif
+
+    char inbuf[256], outbuf[256];
+
+@@ -1215,6 +1219,20 @@
+       }
+    }
+ #endif
++#ifdef PNG_APNG_SUPPORTED
++   if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_acTL))
++   {
++      if (png_get_acTL(read_ptr, read_info_ptr, &num_frames, &num_plays))
++      {
++         png_byte is_hidden;
++         pngtest_debug2("Handling acTL chunks (frames %ld, plays %ld)",
++                    num_frames, num_plays);
++         png_set_acTL(write_ptr, write_info_ptr, num_frames, num_plays);
++         is_hidden = png_get_first_frame_is_hidden(read_ptr,
read_info_ptr);
++         png_set_first_frame_is_hidden(write_ptr, write_info_ptr,
is_hidden);
++      }
++   }
++#endif
+ #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+    {
+       png_unknown_chunkp unknowns;
+@@ -1307,6 +1325,89 @@
+    t_misc += (t_stop - t_start);
+    t_start = t_stop;
+ #endif
++#ifdef PNG_APNG_SUPPORTED
++   if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_acTL))
++   {
++      png_uint_32 frame;
++      for (frame = 0; frame < num_frames; frame++)
++      {
++         png_uint_32 frame_width;
++         png_uint_32 frame_height;
++         png_uint_32 x_offset;
++         png_uint_32 y_offset;
++         png_uint_16 delay_num;
++         png_uint_16 delay_den;
++         png_byte dispose_op;
++         png_byte blend_op;
++         png_read_frame_head(read_ptr, read_info_ptr);
++         if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_fcTL))
++         {
++            png_get_next_frame_fcTL(read_ptr, read_info_ptr,
++                                    &frame_width, &frame_height,
++                                    &x_offset, &y_offset,
++                                    &delay_num, &delay_den,
++                                    &dispose_op, &blend_op);
++         }
++         else
++         {
++            frame_width = width;
++            frame_height = height;
++            x_offset = 0;
++            y_offset = 0;
++            delay_num = 1;
++            delay_den = 1;
++            dispose_op = PNG_DISPOSE_OP_NONE;
++            blend_op = PNG_BLEND_OP_SOURCE;
++         }
++#ifdef PNG_WRITE_APNG_SUPPORTED
++         png_write_frame_head(write_ptr, write_info_ptr,
(png_bytepp)&row_buf,
++                              frame_width, frame_height,
++                              x_offset, y_offset,
++                              delay_num, delay_den,
++                              dispose_op, blend_op);
++#endif
++         for (pass = 0; pass < num_pass; pass++)
++         {
++            pngtest_debug1("Writing row data for pass %d", pass);
++            for (y = 0; y < frame_height; y++)
++            {
++#ifndef SINGLE_ROWBUF_ALLOC
++               pngtest_debug2("Allocating row buffer (pass %d, y =
%ld)...", pass, y);
++               row_buf = (png_bytep)png_malloc(read_ptr,
++                  png_get_rowbytes(read_ptr, read_info_ptr));
++               pngtest_debug2("0x%08lx (%ld bytes)", (unsigned
long)row_buf,
++                  png_get_rowbytes(read_ptr, read_info_ptr));
++#endif /* !SINGLE_ROWBUF_ALLOC */
++               png_read_rows(read_ptr, (png_bytepp)&row_buf, NULL, 1);
++
++#ifdef PNG_WRITE_SUPPORTED
++#ifdef PNGTEST_TIMING
++               t_stop = (float)clock();
++               t_decode += (t_stop - t_start);
++               t_start = t_stop;
++#endif
++               png_write_rows(write_ptr, (png_bytepp)&row_buf, 1);
++#ifdef PNGTEST_TIMING
++               t_stop = (float)clock();
++               t_encode += (t_stop - t_start);
++               t_start = t_stop;
++#endif
++#endif /* PNG_WRITE_SUPPORTED */
++
++#ifndef SINGLE_ROWBUF_ALLOC
++               pngtest_debug2("Freeing row buffer (pass %d, y = %ld)",
pass, y);
++               png_free(read_ptr, row_buf);
++               row_buf = NULL;
++#endif /* !SINGLE_ROWBUF_ALLOC */
++            }
++         }
++#ifdef PNG_WRITE_APNG_SUPPORTED
++         png_write_frame_tail(write_ptr, write_info_ptr);
++#endif
++      }
++   }
++   else
++#endif
+    for (pass = 0; pass < num_pass; pass++)
+    {
+       pngtest_debug1("Writing row data for pass %d", pass);
+diff -Naru libpng-1.5.9.org/pngwrite.c libpng-1.5.9/pngwrite.c
+--- libpng-1.5.9.org/pngwrite.c    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/pngwrite.c    2012-02-19 17:29:25.000000000 +0900
+@@ -58,6 +58,10 @@
+    /* The rest of these check to see if the valid field has the
appropriate
+     * flag set, and if it does, writes the chunk.
+     */
++#ifdef PNG_WRITE_APNG_SUPPORTED
++   if (info_ptr->valid & PNG_INFO_acTL)
++      png_write_acTL(png_ptr, info_ptr->num_frames, info_ptr->num_plays);
++#endif
+ #ifdef PNG_WRITE_gAMA_SUPPORTED
+    if (info_ptr->valid & PNG_INFO_gAMA)
+       png_write_gAMA_fixed(png_ptr, info_ptr->gamma);
+@@ -304,6 +308,10 @@
+
+    if (!(png_ptr->mode & PNG_HAVE_IDAT))
+       png_error(png_ptr, "No IDATs written into file");
++#ifdef PNG_WRITE_APNG_SUPPORTED
++   if (png_ptr->num_frames_written != png_ptr->num_frames_to_write)
++      png_error(png_ptr, "Not enough frames written");
++#endif
+
+    /* See if user wants us to write information chunks */
+    if (info_ptr != NULL)
+@@ -1652,4 +1660,39 @@
+    PNG_UNUSED(params)
+ }
+ #endif
++
++#ifdef PNG_WRITE_APNG_SUPPORTED
++void PNGAPI
++png_write_frame_head(png_structp png_ptr, png_infop info_ptr,
++    png_bytepp row_pointers, png_uint_32 width, png_uint_32 height,
++    png_uint_32 x_offset, png_uint_32 y_offset,
++    png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
++    png_byte blend_op)
++{
++    png_debug(1, "in png_write_frame_head");
++    
++    /* there is a chance this has been set after png_write_info was
called,
++    * so it would be set but not written. is there a way to be sure? */
++    if (!(info_ptr->valid & PNG_INFO_acTL))
++        png_error(png_ptr, "png_write_frame_head(): acTL not set");
++    
++    png_write_reset(png_ptr);
++    
++    png_write_reinit(png_ptr, info_ptr, width, height);
++    
++    if ( !(png_ptr->num_frames_written == 0 &&
++           (png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN) ) )
++        png_write_fcTL(png_ptr, width, height, x_offset, y_offset,
++                       delay_num, delay_den, dispose_op, blend_op);
++}
++
++void PNGAPI
++png_write_frame_tail(png_structp png_ptr, png_infop info_ptr)
++{
++    png_debug(1, "in png_write_frame_tail");
++    
++    png_ptr->num_frames_written++;
++}
++#endif /* PNG_WRITE_APNG_SUPPORTED */
++
+ #endif /* PNG_WRITE_SUPPORTED */
+diff -Naru libpng-1.5.9.org/pngwutil.c libpng-1.5.9/pngwutil.c
+--- libpng-1.5.9.org/pngwutil.c    2012-02-19 17:29:25.000000000 +0900
++++ libpng-1.5.9/pngwutil.c    2012-02-19 17:29:25.000000000 +0900
+@@ -820,6 +820,11 @@
+    /* Write the chunk */
+    png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13);
+
++#ifdef PNG_WRITE_APNG_SUPPORTED
++   png_ptr->first_frame_width = width;
++   png_ptr->first_frame_height = height;
++#endif
++
+    /* Initialize zlib with PNG info */
+    png_ptr->zstream.zalloc = png_zalloc;
+    png_ptr->zstream.zfree = png_zfree;
+@@ -1034,7 +1039,28 @@
+    }
+ #endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */
+
++#ifdef PNG_WRITE_APNG_SUPPORTED
++   if(png_ptr->num_frames_written == 0)
++#endif
+    png_write_complete_chunk(png_ptr, png_IDAT, data, length);
++#ifdef PNG_WRITE_APNG_SUPPORTED
++   else
++   {
++      png_byte buf[4];
++      
++      png_write_chunk_header(png_ptr, png_fdAT, 4 + length);
++      
++      png_save_uint_32(buf, png_ptr->next_seq_num);
++      png_write_chunk_data(png_ptr, buf, 4);
++      
++      png_write_chunk_data(png_ptr, data, length);
++      
++      png_write_chunk_end(png_ptr);
++      
++      png_ptr->next_seq_num++;
++   }
++#endif
++
+    png_ptr->mode |= PNG_HAVE_IDAT;
+
+    /* Prior to 1.5.4 this code was replicated in every caller (except
at the
+@@ -1998,6 +2024,64 @@
+ }
+ #endif
+
++#ifdef PNG_WRITE_APNG_SUPPORTED
++void /* PRIVATE */
++png_write_acTL(png_structp png_ptr,
++   png_uint_32 num_frames, png_uint_32 num_plays)
++{
++    png_byte data[16];
++    
++    png_debug(1, "in png_write_acTL");
++    
++    png_ptr->num_frames_to_write = num_frames;
++    
++    if (png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN)
++        num_frames--;
++    
++    png_save_uint_32(data, num_frames);
++    png_save_uint_32(data + 4, num_plays);
++    
++    png_write_complete_chunk(png_ptr, png_acTL, data, (png_size_t)8);
++}
++
++void /* PRIVATE */
++png_write_fcTL(png_structp png_ptr, png_uint_32 width, png_uint_32
height,
++    png_uint_32 x_offset, png_uint_32 y_offset,
++    png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
++    png_byte blend_op)
++{
++    png_byte data[26];
++    
++    png_debug(1, "in png_write_fcTL");
++    
++    if (png_ptr->num_frames_written == 0 && (x_offset != 0 || y_offset
!= 0))
++        png_error(png_ptr, "x and/or y offset for the first frame
aren't 0");
++    if (png_ptr->num_frames_written == 0 &&
++        (width != png_ptr->first_frame_width ||
++         height != png_ptr->first_frame_height))
++        png_error(png_ptr, "width and/or height in the first frame's
fcTL "
++                           "don't match the ones in IHDR");
++    
++    /* more error checking */
++    png_ensure_fcTL_is_valid(png_ptr, width, height, x_offset, y_offset,
++                             delay_num, delay_den, dispose_op, blend_op);
++    
++    png_save_uint_32(data, png_ptr->next_seq_num);
++    png_save_uint_32(data + 4, width);
++    png_save_uint_32(data + 8, height);
++    png_save_uint_32(data + 12, x_offset);
++    png_save_uint_32(data + 16, y_offset);
++    png_save_uint_16(data + 20, delay_num);
++    png_save_uint_16(data + 22, delay_den);
++    data[24] = dispose_op;
++    data[25] = blend_op;
++    
++    png_write_complete_chunk(png_ptr, png_fcTL, data, (png_size_t)26);
++    
++    png_ptr->next_seq_num++;
++}
++#endif /* PNG_WRITE_APNG_SUPPORTED */
++
+ /* Initializes the row writing capability of libpng */
+ void /* PRIVATE */
+ png_write_start_row(png_structp png_ptr)
+@@ -3177,4 +3261,39 @@
+    }
+ #endif
+ }
++
++#ifdef PNG_WRITE_APNG_SUPPORTED
++void /* PRIVATE */
++png_write_reset(png_structp png_ptr)
++{
++    png_ptr->row_number = 0;
++    png_ptr->pass = 0;
++    png_ptr->mode &= ~PNG_HAVE_IDAT;
++}
++
++void /* PRIVATE */
++png_write_reinit(png_structp png_ptr, png_infop info_ptr,
++                 png_uint_32 width, png_uint_32 height)
++{
++    if (png_ptr->num_frames_written == 0 &&
++        (width != png_ptr->first_frame_width ||
++         height != png_ptr->first_frame_height))
++        png_error(png_ptr, "width and/or height in the first frame's
fcTL "
++                           "don't match the ones in IHDR");
++    if (width > png_ptr->first_frame_width ||
++        height > png_ptr->first_frame_height)
++        png_error(png_ptr, "width and/or height for a frame greater than"
++                           "the ones in IHDR");
++    
++    png_set_IHDR(png_ptr, info_ptr, width, height,
++                 info_ptr->bit_depth, info_ptr->color_type,
++                 info_ptr->interlace_type, info_ptr->compression_type,
++                 info_ptr->filter_type);
++   
++    png_ptr->width = width;
++    png_ptr->height = height;
++    png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width);
++    png_ptr->usr_width = png_ptr->width;
++}
++#endif
+ #endif /* PNG_WRITE_SUPPORTED */
diff --git a/package/libpng/libpng.mk b/package/libpng/libpng.mk
index 6edabc4..d986123 100644
--- a/package/libpng/libpng.mk
+++ b/package/libpng/libpng.mk
@@ -4,10 +4,10 @@ 
 #
 #############################################################
 
-LIBPNG_VERSION = 1.4.12
-LIBPNG_SERIES = 14
-LIBPNG_SITE =
http://$(BR2_SOURCEFORGE_MIRROR).dl.sourceforge.net/project/libpng/libpng$(LIBPNG_SERIES)/$(LIBPNG_VERSION)
-LIBPNG_SOURCE = libpng-$(LIBPNG_VERSION).tar.bz2
+LIBPNG_VERSION = 1.5.9
+LIBPNG_SERIES = 15
+LIBPNG_SITE =
http://$(BR2_SOURCEFORGE_MIRROR).dl.sourceforge.net/project/libpng/libpng$(LIBPNG_SERIES)/older-releases/$(LIBPNG_VERSION)
+LIBPNG_SOURCE = libpng-$(LIBPNG_VERSION).tar.xz
 LIBPNG_INSTALL_STAGING = YES