0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-16 17:08:20 +02:00

ircd::png: Elaborate callback surface; add logger to prevent libpng writes to stderr.

This commit is contained in:
Jason Volk 2021-01-23 03:52:23 -08:00
parent 1d6325949d
commit 66639ee119
2 changed files with 118 additions and 37 deletions

View file

@ -14,6 +14,8 @@
/// Portable Network Graphics; wrappers a la carte /// Portable Network Graphics; wrappers a la carte
namespace ircd::png namespace ircd::png
{ {
IRCD_EXCEPTION(ircd::error, error)
bool is_animated(const const_buffer &); bool is_animated(const const_buffer &);
extern const info::versions version_api, version_abi; extern const info::versions version_api, version_abi;

View file

@ -10,6 +10,23 @@
#include <RB_INC_PNG_H #include <RB_INC_PNG_H
namespace ircd::png
{
static void handle_error(png_structp, const char *) noexcept(false);
static void handle_warn(png_structp, const char *) noexcept;
static void *handle_alloc(png_structp, size_t) noexcept;
static void handle_free(png_structp, void *) noexcept;
static void handle_read(png_structp, uint8_t *, size_t) noexcept;
extern log::log log;
}
decltype(ircd::png::log)
ircd::png::log
{
"png"
};
decltype(ircd::png::version_api) decltype(ircd::png::version_api)
ircd::png::version_api ircd::png::version_api
{ {
@ -43,7 +60,20 @@ ircd::png::is_animated(const const_buffer &buf)
return false; return false;
png_infop info {nullptr}; png_infop info {nullptr};
png_structp handle {nullptr}; png_structp handle
{
png_create_read_struct_2
(
PNG_LIBPNG_VER_STRING,
nullptr,
&handle_error,
&handle_warn,
nullptr,
&handle_alloc,
&handle_free
)
};
const unwind destroy const unwind destroy
{ {
[&handle, &info] [&handle, &info]
@ -52,20 +82,41 @@ ircd::png::is_animated(const const_buffer &buf)
} }
}; };
if(unlikely(!(handle = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr)))) if(unlikely(!handle))
return false; return false;
if(unlikely(!(info = png_create_info_struct(handle)))) if(unlikely(!(info = png_create_info_struct(handle))))
return false; return false;
const_buffer src {buf}; const_buffer src {buf};
png_set_read_fn(handle, &src, [] png_set_read_fn(handle, &src, &handle_read);
(auto handle, uint8_t *const ptr, size_t sz)
// 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 num_frames {0}, num_plays {0};
return png_get_acTL(handle, info, &num_frames, &num_plays) != 0;
}
#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;
}
#endif
void
ircd::png::handle_read(png_structp handle,
uint8_t *const ptr,
size_t size)
noexcept
{ {
// Destination where libpng wants data // Destination where libpng wants data
const mutable_buffer dst const mutable_buffer dst
{ {
reinterpret_cast<char *>(ptr), sz reinterpret_cast<char *>(ptr), size
}; };
// Get source buffer from our callback user data. // Get source buffer from our callback user data.
@ -87,23 +138,51 @@ ircd::png::is_animated(const const_buffer &buf)
}; };
assert(copied == consumed); 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
void *
ircd::png::handle_alloc(png_structp handle,
size_t size)
noexcept
{ {
// If there's no libpng there's no reason to decide APNG's right now return std::malloc(size);
// because they won't be thumbnailed by magick anyway. }
#warning "Upgrade your libpng version for animation detection."
return false; void
ircd::png::handle_free(png_structp handle,
void *const ptr)
noexcept
{
std::free(ptr);
}
void
ircd::png::handle_warn(png_structp handle,
const char *const msg)
noexcept
{
log::dwarning
{
log, "handle(%p) :%s",
static_cast<const void *>(handle),
msg,
};
}
void
ircd::png::handle_error(png_structp handle,
const char *const msg)
noexcept(false)
{
log::error
{
log, "handle(%p) :%s",
static_cast<const void *>(handle),
msg,
};
throw error
{
"%s", msg
};
} }
#endif