text: /*
    Copyright 2009 Larry Gritz and the other authors and contributors.
    All Rights Reserved.
  
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the software's owners nor the names of its
      contributors may be used to endorse or promote products derived from
      this software without specific prior written permission.
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
    (This is the Modified BSD License)
  */
  
  #include <cstdio>
  #include <cstdlib>
  #include <cmath>
  
  #include "ico.h"
  #include "../png.imageio/png_pvt.h"
  
  #include "OpenImageIO/dassert.h"
  #include "OpenImageIO/typedesc.h"
  #include "OpenImageIO/imageio.h"
  #include "OpenImageIO/thread.h"
  #include "OpenImageIO/strutil.h"
  #include "OpenImageIO/fmath.h"
  
  OIIO_PLUGIN_NAMESPACE_BEGIN
  
  using namespace ICO_pvt;
  
  class ICOInput : public ImageInput {
  public:
      ICOInput () { init(); }
      virtual ~ICOInput () { close(); }
      virtual const char * format_name (void) const { return "ico"; }
      virtual bool open (const std::string &name, ImageSpec &newspec);
      virtual bool close ();
      virtual int current_subimage (void) const { return m_subimage; }
      virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec);
      virtual bool read_native_scanline (int y, int z, void *data);
  
  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?
      int m_bpp;                        ///< Bits per pixel
      int m_offset;                     ///< Offset to image data
      int m_subimage_size;              ///< Length (in bytes) of image data
      int m_palette_size;               ///< Number of colours in palette (0 means 256)
  
      png_structp m_png;                ///< PNG read structure pointer
      png_infop m_info;                 ///< PNG image info structure pointer
      int m_color_type;                 ///< PNG color model type
      int m_interlace_type;             ///< PNG interlace type
      Imath::Color3f m_bg;              ///< PNG background color
  
      /// Reset everything to initial state
      ///
      void init () {
          m_subimage = -1;
          m_file = NULL;
          m_png = NULL;
          m_info = NULL;
          memset (&m_ico, 0, sizeof (m_ico));
          m_buf.clear ();
      }
  
      /// Helper function: read the image.
      ///
      bool readimg ();
  
      /// Helper: read, with error detection
      ///
      bool fread (void *buf, size_t itemsize, size_t nitems) {
          size_t n = ::fread (buf, itemsize, nitems, m_file);
          if (n != nitems)
              error ("Read error");
          return n == nitems;
      }
  };
  
  
  
  // Obligatory material to make this a recognizeable imageio plugin:
  OIIO_PLUGIN_EXPORTS_BEGIN
  
  OIIO_EXPORT ImageInput *ico_input_imageio_create () { return new ICOInput; }
  
  OIIO_EXPORT int ico_imageio_version = OIIO_PLUGIN_VERSION;
  
  OIIO_EXPORT const char * ico_input_extensions[] = {
      "ico", NULL
  };
  
  OIIO_PLUGIN_EXPORTS_END
  
  
  
  bool
  ICOInput::open (const std::string &name, ImageSpec &newspec)
  {
      m_filename = name;
  
      m_file = Filesystem::fopen (name, "rb");
      if (! m_file) {
          error ("Could not open file \"%s\"", name.c_str());
          return false;
      }
  
      if (! fread (&m_ico, 1, sizeof(m_ico)))
          return false;
  
      if (bigendian()) {
          // ICOs are little endian
          //swap_endian (&m_ico.reserved); // no use flipping, it's 0 anyway
          swap_endian (&m_ico.type);
          swap_endian (&m_ico.count);
      }
      if (m_ico.reserved != 0 || m_ico.type != 1) {
          error ("File failed ICO header check");
          return false;
      }
  
      // default to subimage #0, according to convention
      seek_subimage (0, 0, m_spec);
  
      newspec = spec ();
  
      return true;
  }
  
  
  
  bool
  ICOInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec)
  {
      /*std::cerr << "[ico] seeking subimage " << index << " (current "
                << m_subimage << ") out of " << m_ico.count << "\n";*/
      if (miplevel != 0 || subimage < 0 || subimage >= m_ico.count)
          return false;
  
      if (subimage == m_subimage) {
          newspec = spec();
          return true;
      }
  
      // clear the buffer of previous data
      m_buf.clear ();
  
      // deinitialize PNG structs, in case they were used
      if (m_png && m_info)
          PNG_pvt::destroy_read_struct (m_png, m_info);
  
      m_subimage = subimage;
  
      // read subimage header
      fseek (m_file, sizeof(ico_header) + m_subimage * sizeof(ico_subimage), SEEK_SET);
      ico_subimage subimg;
      if (! fread (&subimg, 1, sizeof(subimg)))
          return false;
  
      if (bigendian()) {
          // ICOs are little endian
          swap_endian (&subimg.bpp);
          swap_endian (&subimg.width);
          swap_endian (&subimg.height);
          swap_endian (&subimg.len);
          swap_endian (&subimg.ofs);
          swap_endian (&subimg.numColours);
      }
  
      fseek (m_file, subimg.ofs, SEEK_SET);
  
      // test for a PNG icon
      char temp[8];
      if (! fread (temp, 1, sizeof(temp)))
          return false;
      if (temp[1] == 'P' && temp[2] == 'N' && temp[3] == 'G') {
          // standard PNG initalization
          if (png_sig_cmp ((png_bytep)temp, 0, 7)) {
              error ("Subimage failed PNG signature check");
              return false;
          }
  
          //std::cerr << "[ico] creating PNG read struct\n";
  
          std::string s = PNG_pvt::create_read_struct (m_png, m_info);
          if (s.length ()) {
              error ("%s", s.c_str ());
              return false;
          }
  
          //std::cerr << "[ico] reading PNG info\n";
  
          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, m_bg, m_spec, true);
  
          m_spec.attribute ("oiio:BitsPerSample", m_bpp / m_spec.nchannels);
  
          newspec = spec ();
          return true;
      }
  
      // 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);
  
      ico_bitmapinfo bmi;
      if (! fread (&bmi, 1, sizeof(bmi)))
          return false;
      if (bigendian()) {
          // ICOs are little endian
          // according to MSDN, only these are valid in an ICO DIB header
          swap_endian (&bmi.size);
          swap_endian (&bmi.bpp);
          swap_endian (&bmi.width);
          swap_endian (&bmi.height);
          swap_endian (&bmi.len);
      }
  
      /*std::cerr << "[ico] " << (int)subimg.width << "x" << (int)subimg.height << "@"
                << (int)bmi.bpp << " (subimg len=" << (int)subimg.len << ", bm len="
                << (int)bmi.len << ", ofs=" << (int)subimg.ofs << "), c#"
                << (int)subimg.numColours << ", p#" << (int)subimg.planes << ":"
                << (int)bmi.planes << "\n";*/
  
      // copy off values for later use
      m_bpp = bmi.bpp;
      // some sanity checking
      if (m_bpp != 1 && m_bpp != 4 && m_bpp != 8
          /*&& m_bpp != 16*/ && m_bpp != 24 && m_bpp != 32) {
          error ("Unsupported image color depth, probably corrupt file");
          return false;
      }
      m_offset = subimg.ofs;
      m_subimage_size = subimg.len;
      // palette size of 0 actually indicates 256 colours
      m_palette_size = (subimg.numColours == 0 && m_bpp < 16)
                       ? 256 : (int)subimg.numColours;
  
      m_spec = ImageSpec ((int)subimg.width, (int)subimg.height,
                          4, // always RGBA
                          TypeDesc::UINT8); // 4- and 16-bit are expanded to 8bpp
      m_spec.default_channel_names ();
      // add 1 bit for < 32bpp images due to the 1-bit alpha mask
      m_spec.attribute ("oiio:BitsPerSample", m_bpp/m_spec.nchannels + (m_bpp == 32 ? 0 : 1));
  
      /*std::cerr << "[ico] expected bytes: scanline " << m_spec.scanline_bytes()
                << ", image " << m_spec.image_bytes() << "\n";*/
  
      newspec = spec ();
      return true;
  }
  
  
  
  bool
  ICOInput::readimg ()
  {
      if (m_png) {
          // subimage is a PNG
          std::string s = PNG_pvt::read_into_buffer (m_png, m_info, m_spec,
                                                     m_bpp, m_color_type,
                                                     m_buf);
  
          //std::cerr << "[ico] PNG buffer size = " << m_buf.size () << "\n";
  
          if (s.length ()) {
              error ("%s", s.c_str ());
              return false;
          }
  
          return true;
      }
  
      // otherwise we're dealing with a DIB
      DASSERT (m_spec.scanline_bytes() == ((size_t)m_spec.width * 4));
      m_buf.resize (m_spec.image_bytes());
  
      //std::cerr << "[ico] DIB buffer size = " << m_buf.size () << "\n";
  
      // icons < 16bpp are colour-indexed, so load the palette
      // a palette consists of 4-byte BGR quads, with the last byte unused (reserved)
      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)))
                  return false;
      }
  
      // read the colour data (the 1-bit transparency is added later on)
      // scanline length in bytes (aligned to a multiple of 32 bits)
      int slb = (m_spec.width * m_bpp + 7) / 8 // real data bytes
                + (4 - ((m_spec.width * m_bpp + 7) / 8) % 4) % 4; // padding
      std::vector<unsigned char> scanline (slb);
      ico_palette_entry *pe;
      int k;
      for (int y = m_spec.height - 1; y >= 0; y--) {
          if (! fread (&scanline[0], 1, slb))
              return false;
          for (int x = 0; x < m_spec.width; x++) {
              k = y * m_spec.width * 4 + x * 4;
              // fill the buffer
              switch (m_bpp) {
              case 1:
                  pe = &palette[(scanline[x / 8] & (1 << (7 - x % 8))) != 0];
                  m_buf[k + 0] = pe->r;
                  m_buf[k + 1] = pe->g;
                  m_buf[k + 2] = pe->b;
                  break;
              case 4:
                  pe = &palette[(scanline[x / 2] & 0xF0) >> 4];
                  m_buf[k + 0] = pe->r;
                  m_buf[k + 1] = pe->g;
                  m_buf[k + 2] = pe->b;
                  // 2 pixels per byte
                  pe = &palette[scanline[x / 2] & 0x0F];
                  if (x == m_spec.width - 1)
                      break; // avoid buffer overflows
                  x++;
                  m_buf[k + 4] = pe->r;
                  m_buf[k + 5] = pe->g;
                  m_buf[k + 6] = pe->b;
                  /*std::cerr << "[ico] " << y << " 2*4bit pixel: "
                            << ((int)scanline[x / 2]) << " -> "
                            << ((int)(scanline[x / 2] & 0xF0) >> 4)
                            << " & " << ((int)(scanline[x / 2]) & 0x0F)
                            << "\n";*/
                  break;
              case 8:
                  pe = &palette[scanline[x]];
                  m_buf[k + 0] = pe->r;
                  m_buf[k + 1] = pe->g;
                  m_buf[k + 2] = pe->b;
                  break;
              // bpp values > 8 mean non-indexed BGR(A) images
  #if 0
              // doesn't seem like ICOs can really be 16-bit, where did I even get
              // this notion from?
              case 16:
                  // FIXME: find out exactly which channel gets the 1 extra
                  // bit; currently I assume it's green: 5B, 6G, 5R
                  // extract and shift the bits
                  m_buf[k + 0] = (scanline[x * 2 + 1] & 0x1F) << 3;
                  m_buf[k + 1] = ((scanline[x * 2 + 1] & 0xE0) >> 3)
                                 | ((scanline[x * 2 + 0] & 0x07) << 5);
                  m_buf[k + 2] = scanline[x * 2 + 0] & 0xF8;
                  break;
  #endif
              case 24:
                  m_buf[k + 0] = scanline[x * 3 + 2];
                  m_buf[k + 1] = scanline[x * 3 + 1];
                  m_buf[k + 2] = scanline[x * 3 + 0];
                  break;
              case 32:
                  m_buf[k + 0] = scanline[x * 4 + 2];
                  m_buf[k + 1] = scanline[x * 4 + 1];
                  m_buf[k + 2] = scanline[x * 4 + 0];
                  m_buf[k + 3] = scanline[x * 4 + 3];
                  break;
              }
          }
      }
  
      // read the 1-bit transparency for < 32-bit icons
      if (m_bpp < 32) {
          // also aligned to a multiple of 32 bits
          slb = (m_spec.width + 7) / 8 // real data bytes
                + (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))
                  return false;
              for (int x = 0; x < m_spec.width; x += 8) {
                  for (int b = 0; b < 8; b++) { // bit
                      k = y * m_spec.width * 4 + (x + 7 - b) * 4;
                      if (scanline[x / 8] & (1 << b))
                          m_buf[k + 3] = 0;
                      else
                          m_buf[k + 3] = 255;
                  }
              }
          }
      }
  
      return true;
  }
  
  
  
  bool
  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;
  }
  
  
  
  bool
  ICOInput::read_native_scanline (int y, int z, void *data)
  {
      if (m_buf.empty ()) {
          if (!readimg ())
              return false;
      }
  
      size_t size = spec().scanline_bytes();
      //std::cerr << "[ico] reading scanline " << y << " (" << size << " bytes)\n";
      memcpy (data, &m_buf[y * size], size);
      return true;
  }
  
  OIIO_PLUGIN_NAMESPACE_END
  
  