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
namespace ircd::png
{
IRCD_EXCEPTION(ircd::error, error)
bool is_animated(const const_buffer &);
extern const info::versions version_api, version_abi;

View File

@ -10,6 +10,23 @@
#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)
ircd::png::version_api
{
@ -43,7 +60,20 @@ ircd::png::is_animated(const const_buffer &buf)
return false;
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
{
[&handle, &info]
@ -52,52 +82,21 @@ 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;
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<char *>(ptr), sz
};
// Get source buffer from our callback user data.
const_buffer &src
{
*reinterpret_cast<const_buffer *>(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);
});
const_buffer src {buf};
png_set_read_fn(handle, &src, &handle_read);
// 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;
uint32_t num_frames {0}, num_plays {0};
return png_get_acTL(handle, info, &num_frames, &num_plays) != 0;
}
#else
{
@ -107,3 +106,83 @@ ircd::png::is_animated(const const_buffer &buf)
return false;
}
#endif
void
ircd::png::handle_read(png_structp handle,
uint8_t *const ptr,
size_t size)
noexcept
{
// Destination where libpng wants data
const mutable_buffer dst
{
reinterpret_cast<char *>(ptr), size
};
// Get source buffer from our callback user data.
const_buffer &src
{
*reinterpret_cast<const_buffer *>(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);
}
void *
ircd::png::handle_alloc(png_structp handle,
size_t size)
noexcept
{
return std::malloc(size);
}
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
};
}