Skip to content

Commit

Permalink
feat(ico): Add IOProxy support to ICOInput (#3919)
Browse files Browse the repository at this point in the history
This updates ICOInput to use the IOProxy model so it can read from files
or the IOMemReader

Signed-off-by: Jason Baumeister <jason@jasonbaumeister.com>
  • Loading branch information
jasonbaumeister authored Jul 23, 2023
1 parent 88d354e commit 9fd9fbf
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 31 deletions.
18 changes: 18 additions & 0 deletions src/doc/builtinplugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,24 @@ Windows. ICO files use the file extension :file:`.ico`.
- int
- if nonzero, will cause the ICO to be written out using PNG format.

**Configuration settings for ICO input**

When opening an ICO ImageInput with a *configuration* (see
Section :ref:`sec-input-with-config`), the following special configuration
attributes are supported:

.. list-table::
:widths: 30 10 65
:header-rows: 1

* - Input Configuration Attribute
- Type
- Meaning
* - ``oiio:ioproxy``
- ptr
- Pointer to a ``Filesystem::IOProxy`` that will handle the I/O, for
example by reading from memory rather than the file system.

**Configuration settings for ICO output**

When opening an ICO ImageOutput, the following special metadata tokens
Expand Down
70 changes: 39 additions & 31 deletions src/ico.imageio/icoinput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ class ICOInput final : public ImageInput {
ICOInput() { init(); }
~ICOInput() override { close(); }
const char* format_name(void) const override { return "ico"; }
int supports(string_view feature) const override
{
return feature == "ioproxy";
}
bool open(const std::string& name, ImageSpec& newspec) override;
bool open(const std::string& name, ImageSpec& newspec,
const ImageSpec& config) override;
bool close() override;
int current_subimage(void) const override
{
Expand All @@ -38,7 +44,6 @@ class ICOInput final : public ImageInput {

private:
std::string m_filename; ///< Stash the filename
FILE* m_file; ///< Open image handle
ico_header m_ico; ///< ICO header
std::vector<unsigned char> m_buf; ///< Buffer the image pixels
int m_subimage; ///< What subimage are we looking at?
Expand All @@ -58,25 +63,26 @@ class ICOInput final : public ImageInput {
void init()
{
m_subimage = -1;
m_file = NULL;
m_png = NULL;
m_info = NULL;
memset(&m_ico, 0, sizeof(m_ico));
m_buf.clear();
ioproxy_clear();
}

/// Helper function: read the image.
///
bool readimg();

/// Helper: read, with error detection
///
bool fread(void* buf, size_t itemsize, size_t nitems)
// Callback for PNG that reads from an IOProxy.
static void PngReadCallback(png_structp png_ptr, png_bytep data,
png_size_t length)
{
size_t n = ::fread(buf, itemsize, nitems, m_file);
if (n != nitems)
errorf("Read error");
return n == nitems;
ICOInput* icoinput = (ICOInput*)png_get_io_ptr(png_ptr);
OIIO_DASSERT(icoinput);
if (!icoinput->ioread(data, length)) {
png_chunk_error(png_ptr, icoinput->geterror(false).c_str());
}
}
};

Expand All @@ -103,20 +109,26 @@ OIIO_EXPORT const char* ico_input_extensions[] = { "ico", nullptr };

OIIO_PLUGIN_EXPORTS_END



bool
ICOInput::open(const std::string& name, ImageSpec& newspec)
{
ImageSpec emptyconfig;
return open(name, newspec, emptyconfig);
}

bool
ICOInput::open(const std::string& name, ImageSpec& newspec,
const ImageSpec& config)
{
m_filename = name;

m_file = Filesystem::fopen(name, "rb");
if (!m_file) {
errorf("Could not open file \"%s\"", name);
ioproxy_retrieve_from_config(config);
if (!ioproxy_use_or_open(name))
return false;
}

if (!fread(&m_ico, 1, sizeof(m_ico)))
ioseek(0);

if (!ioread(&m_ico, 1, sizeof(m_ico)))
return false;

if (bigendian()) {
Expand Down Expand Up @@ -163,10 +175,9 @@ ICOInput::seek_subimage(int subimage, int miplevel)
m_subimage = subimage;

// read subimage header
fseek(m_file, sizeof(ico_header) + m_subimage * sizeof(ico_subimage),
SEEK_SET);
ioseek(sizeof(ico_header) + m_subimage * sizeof(ico_subimage), SEEK_SET);
ico_subimage subimg;
if (!fread(&subimg, 1, sizeof(subimg)))
if (!ioread(&subimg, 1, sizeof(subimg)))
return false;

if (bigendian()) {
Expand All @@ -179,11 +190,11 @@ ICOInput::seek_subimage(int subimage, int miplevel)
swap_endian(&subimg.numColours);
}

fseek(m_file, subimg.ofs, SEEK_SET);
ioseek(subimg.ofs, SEEK_SET);

// test for a PNG icon
char temp[8];
if (!fread(temp, 1, sizeof(temp)))
if (!ioread(temp, 1, sizeof(temp)))
return false;
if (temp[1] == 'P' && temp[2] == 'N' && temp[3] == 'G') {
// standard PNG initialization
Expand All @@ -201,8 +212,9 @@ ICOInput::seek_subimage(int subimage, int miplevel)
}

//std::cerr << "[ico] reading PNG info\n";
// Tell libpng to use our read callback to read from the IOProxy
png_set_read_fn(m_png, this, PngReadCallback);

png_init_io(m_png, m_file);
png_set_sig_bytes(m_png, 8); // already read 8 bytes

PNG_pvt::read_info(m_png, m_info, m_bpp, m_color_type, m_interlace_type,
Expand All @@ -215,10 +227,10 @@ ICOInput::seek_subimage(int subimage, int miplevel)

// otherwise it's a plain, ol' windoze DIB (device-independent bitmap)
// roll back to where we began and read in the DIB header
fseek(m_file, subimg.ofs, SEEK_SET);
ioseek(subimg.ofs, SEEK_SET);

ico_bitmapinfo bmi;
if (!fread(&bmi, 1, sizeof(bmi)))
if (!ioread(&bmi, 1, sizeof(bmi)))
return false;
if (bigendian()) {
// ICOs are little endian
Expand Down Expand Up @@ -297,7 +309,7 @@ ICOInput::readimg()
std::vector<ico_palette_entry> palette(m_palette_size);
if (m_bpp < 16) { // >= 16-bit icons are unpaletted
for (int i = 0; i < m_palette_size; i++)
if (!fread(&palette[i], 1, sizeof(ico_palette_entry)))
if (!ioread(&palette[i], 1, sizeof(ico_palette_entry)))
return false;
}

Expand All @@ -310,7 +322,7 @@ ICOInput::readimg()
int k;
int index;
for (int y = m_spec.height - 1; y >= 0; y--) {
if (!fread(&scanline[0], 1, slb))
if (!ioread(&scanline[0], 1, slb))
return false;
for (int x = 0; x < m_spec.width; x++) {
k = y * m_spec.width * 4 + x * 4;
Expand Down Expand Up @@ -398,7 +410,7 @@ ICOInput::readimg()
+ (4 - ((m_spec.width + 7) / 8) % 4) % 4; // padding
scanline.resize(slb);
for (int y = m_spec.height - 1; y >= 0; y--) {
if (!fread(&scanline[0], 1, slb))
if (!ioread(&scanline[0], 1, slb))
return false;
for (int x = 0; x < m_spec.width; x += 8) {
for (int b = 0; b < 8; b++) { // bit
Expand All @@ -422,10 +434,6 @@ ICOInput::close()
{
if (m_png && m_info)
PNG_pvt::destroy_read_struct(m_png, m_info);
if (m_file) {
fclose(m_file);
m_file = NULL;
}

init(); // Reset to initial state
return true;
Expand Down

0 comments on commit 9fd9fbf

Please sign in to comment.