OZ++ Class: PNGFileReader
/******************************************************************************
 *
 * Copyright (c) 2015 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 *
 *
 *  PNGFileReader.h
 *
 *****************************************************************************/

// 2015/07/22 This is a very simple PNG file reader based on libpng.
// See https://en.wikibooks.org/wiki/OpenGL_Programming/Intermediate/Textures 

#pragma once

#include <oz++/PNGFile.h>
#include <oz++/Pair.h>

namespace OZ {

class PNGFileReader :public PNGFile {

public:
  PNGFileReader(const char* filename, bool deleteData=true)
  :PNGFile(READ, filename, deleteData)
  {
  }

  ~PNGFileReader()
  {
  }

public:
  void read(bool flipVertical = false) 
  {
    if (validateSignature() == false) {
      throw IException("Invalid header");
    }

    // Set the header signature bytes.
    setSignatureBytes(SIGNATURE_SIZE);

    // Read a png_info. 
    readInfo();

    // Get the png_info header withoud interlaceMethod, 
    // compressionMethod and filterMethod. 
    png_uint_32 width = 0;
    png_uint_32 height = 0;
    int bitDepth = 0;
    int colorType = 0;

    getInfoHeader(width, height, bitDepth, colorType);

    //setBackground();

    expandPalette();

    // Update the png_info
    updateInfo();

    // Get a row bytes.
    int rowbytes = getRowBytes();

    // Allocate a row_pointers. 
    png_bytep* rows = new png_bytep[height];

    // Allocate an imageData 
    png_byte* imageData = new png_byte[rowbytes * height];

    setImageData(imageData);

    // Set the rows pointer from the imageData 
    for (int i = 0; i < height; ++i) {
      if (flipVertical) {
        rows[height - 1 - i] = imageData + i * rowbytes;
      } else {
        rows[i] = imageData + i * rowbytes;
      }
    }

    // Read the imageData into the rows.
    readImage(rows);

    readEnd();

    delete [] rows;

    close();
  }

  void readInfo()
  {
    if (setJump()) {
      throw IException("Failed to png_read_info.");
    }
    png_read_info(png_ptr, info_ptr);
  }

  // Get png_info header withoud interlaceMethod, compressionMethod and filterMethod.
  int getInfoHeader(png_uint_32& width, png_uint_32& height,
        int& bitDepth, int& colorType)
  {
    if (setJump()) {
      throw IException("Failed to png_get_IHDR.");
    }

    return png_get_IHDR(png_ptr, info_ptr, &width, &height,
           &bitDepth, &colorType,
           NULL, NULL, NULL);
  }

  // Get png_info header
  int getInfoHeader(png_uint_32& width, png_uint_32& height,
        int& bitDepth, int& colorType, int& interlaceMethod,
        int& compressionMethod, int& filterMethod)
  {
    if (setJump()) {
      throw IException("Failed to png_get_IHDR.");
    } 
    return png_get_IHDR(png_ptr, info_ptr, &width, &height,
           &bitDepth, &colorType,
           &interlaceMethod, &compressionMethod, &filterMethod);
  }

 // Read the png into imageData
  void readImage(png_bytep* rows)
  {
    if (setJump()) {
      throw IException("Failed to png_read_image.");
    }

    png_read_image(png_ptr, rows);
  }

  void  readEnd()
  {
    if (setJump()) {
      throw IException("Failed to png_read_end.");
    }
    png_read_end(png_ptr, NULL);
  }

  void setFilter(int method, int filters)
  {
    if (setJump()) {
      throw IException("Failed to png_set_filter.");
    }
    png_set_filter(png_ptr, method, filters);
  }

  void properties()
  {
     printf("width:      %d\n", (int)getImageWidth()); 
     printf("height:     %d\n", (int)getImageHeight()); 
     printf("depth:      %d\n", (int)getBitDepth()); 
     printf("colorType:  %s\n", colorTypeToString(getColorType() )); 
  }

  const char* colorTypeToString(int type)
  {
     static Pair<int, const char*> pairs[] = {
      {PNG_COLOR_TYPE_GRAY,        "PNG_COLOR_TYPE_GRAY"},
      {PNG_COLOR_TYPE_GRAY_ALPHA,  "PNG_COLOR_TYPE_GRAY_ALPHA"},
      {PNG_COLOR_TYPE_PALETTE,     "PNG_COLOR_TYPE_PALETTE"},
      {PNG_COLOR_TYPE_RGB,         "PNG_COLOR_TYPE_RGB"},
      {PNG_COLOR_TYPE_RGB_ALPHA,   "PNG_COLOR_TYPE_RGB_ALPHA"},
      {PNG_COLOR_MASK_PALETTE,     "PNG_COLOR_MASK_PALETTE"},
      {PNG_COLOR_MASK_COLOR,       "PNG_COLOR_MASK_COLOR"},
      {PNG_COLOR_MASK_ALPHA,       "PNG_COLOR_MASK_ALPHA"},
    };
    const char* name = "";
    for (int i = 0; i<SizeOf(pairs); i++) {
      if (pairs[i].first == type) {
        name = pairs[i].second;
        break;
      }
    } 
    return name;
  }
};
}