diff --git a/ircd/png.cc b/ircd/png.cc index bb1b57ef2..2fe2fc288 100644 --- a/ircd/png.cc +++ b/ircd/png.cc @@ -25,7 +25,7 @@ ircd::png::version_api #endif }; -/// Since libpng may not loaded prior to static initialization there is +/// Since libpng may not have loaded prior to static initialization there is /// no linked library identification. libmagick takes care of loading libpng /// dynamically if it requires it for us. decltype(ircd::png::version_abi) @@ -38,10 +38,71 @@ bool ircd::png::is_animated(const const_buffer &buf) #ifdef PNG_APNG_SUPPORTED { + // Cannot be a PNG + if(unlikely(size(buf) < 8)) + return false; + + png_infop info {nullptr}; + png_structp handle {nullptr}; + const unwind destroy + { + [&handle, &info] + { + png_destroy_read_struct(&handle, &info, nullptr); + } + }; + + if(unlikely(!(handle = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr)))) + return false; + + if(unlikely(!(info = png_create_info_struct(handle)))) + return false; + + const_buffer src{buf}; + png_set_read_fn(handle, &src, [] + (auto handle, uint8_t *const ptr, size_t sz) + { + // Destination where libpng wants data + const mutable_buffer dst + { + reinterpret_cast(ptr), sz + }; + + // Get source buffer from our callback user data. + const_buffer &src + { + *reinterpret_cast(png_get_io_ptr(handle)) + }; + + // Copy from our buffer to libpng buffer. + const size_t copied + { + copy(dst, src) + }; + + // Advance our source pointer for the amount copied. + const size_t consumed + { + consume(src, copied) + }; + + assert(copied == consumed); + }); + + // Invokes the read callback a few times to traverse the buffer. + png_read_info(handle, info); + + // If there's a non-zero result there's acTL for animation. + uint32_t frames(0), plays(0); + if(png_get_acTL(handle, info, &frames, &plays) != 0) + return true; + return false; } #else { + // If there's no libpng there's no reason to decide APNG's right now + // because they won't be thumbnailed by magick anyway. #warning "Upgrade your libpng version for animation detection." return false; }