/**
 * Graphics::PNG is PNG library
 *
 * @author DATE Ken (as Itacchi) / ge6537@i.bekkoame.ne.jp
 * @code-name Aoi
 * @see http://www.isc.meiji.ac.jp/~ee77038/ruby/
 * @ToDo progressive-read, any streaming input support, png_permit_empty_plte
 * $Id: reader.c,v 1.1 2000/09/27 17:11:43 date Exp date $
 */

#include "libpng.h"

#define PNG_AFTER_IDAT              0x08
#define READ_STATUS_FN              "read_status_fn"
#define READ_USER_TRANSFORM_FN      "read_user_transform_fn"

VALUE cReader;



/*
 * -----------------------
 * utility function for
 *   Graphics::PNG::Reader
 * -----------------------
 */
static void
libpng_reader_free(png_obj)
  png_object *png_obj;
{
  if (png_obj->fp)
    fclose(png_obj->fp);
  png_destroy_read_struct(&png_obj->obj, &png_obj->info, &png_obj->end_info);
  free(png_obj);
}



static void
png_default_error(png_ptr, message)
  png_structp png_ptr;
  png_const_charp message;
{
  rb_raise(ePngError, "Ruby/libpng error: %s\n", message);
}



static void
png_default_warning(png_ptr, message)
  png_structp png_ptr;
  png_const_charp message;
{
  rb_warning("Ruby/libpng warning: %s\n", message);
}



static void
read_row_callback(png_ptr, row_number, pass)
  png_structp png_ptr;
  png_uint_32 row_number;
  int pass;
{
  VALUE proc;

  if(png_ptr == NULL || row_number > PNG_MAX_UINT)
    return;

  proc = rb_iv_get(cReader, READ_STATUS_FN);

  if (NIL_P(proc))
    return;

  rb_funcall(proc, rb_intern("call"), 2, INT2NUM(row_number), INT2FIX(pass));
}



/*
static void
read_user_transform(png_ptr, row_info, data)
  png_structp png_ptr;
  png_row_infop row_info;
  png_bytep data;
{
  VALUE proc;

  proc = rb_iv_get(cReader, READ_STATUS_FN);

  if (NIL_P(proc))
    return;

  rb_funcall(proc, rb_intern("call"), 2, INT2NUM(row_number), rb_str_new2(data));
}
*/




/*
 * -----------------------
 * class method of
 *   Graphics::PNG::Reader
 * -----------------------
 */
static VALUE
libpng_reader_new(klass, file)
  VALUE klass, file;
{
  FILE *fp;
  OpenFile *fptr;
  VALUE new_obj;
  png_object *png_obj;
  char buf[PNG_BYTES_TO_CHECK];


  Check_Type(file, T_STRING);

  if ((fp = fopen(STR2CSTR(file), "rb")) == NULL){
    rb_raise(ePngError, "can't open %s", STR2CSTR(file));
    return Qnil;
  }

   if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK){
    rb_raise(rb_eException, "can't read %s", STR2CSTR(file));
    return Qnil;
   }

  if (png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK)){
    rb_raise(ePngError, "not PNG file %s", STR2CSTR(file));
    return Qnil;
  }

  /* create PNG object */

  new_obj = Data_Make_Struct(klass, png_object, NULL, libpng_reader_free, png_obj);

  png_obj->fp = fp;

  png_obj->obj = png_create_read_struct(PNG_LIBPNG_VER_STRING,
    (png_voidp)NULL, png_default_error, png_default_warning);

  if (png_obj->obj == NULL){
    fclose(fp);
    rb_raise(ePngError, "can't create PNG object (failer to create read struct)");
    return Qnil;
  }

  png_set_sig_bytes(png_obj->obj, PNG_BYTES_TO_CHECK);

  png_obj->info = png_create_info_struct(png_obj->obj);
  png_obj->end_info = png_create_info_struct(png_obj->obj);

  if (png_obj->info == NULL || png_obj->end_info == NULL){
    fclose(fp);
    png_destroy_read_struct(&png_obj->obj, &png_obj->info, &png_obj->end_info);
    rb_raise(ePngError, "can't create PNG object (failer to create info struct)");
    return Qnil;
  }

  if (setjmp(png_jmpbuf(png_obj->obj))){
     fclose(fp);
     png_destroy_read_struct(&png_obj->obj, &png_obj->info, &png_obj->end_info);
     rb_raise(ePngError, "PNG read error");
     return Qnil;
   }

  png_init_io(png_obj->obj, png_obj->fp);

  png_read_info(png_obj->obj, png_obj->info);

  png_set_read_status_fn(png_obj->obj, read_row_callback);

/*
  png_set_read_user_transform_fn(read_ptr, read_user_transform);
*/

  rb_obj_call_init(new_obj, 1, &file);

  return new_obj;
}




/*
 * -----------------------
 * instance method of
 *   Graphics::PNG::Reader
 * -----------------------
 */
static VALUE
libpng_reader_initialize(obj)
  VALUE obj;
{
  return Qnil;
}



#ifdef PNG_EASY_ACCESS_SUPPORTED
static VALUE
libpng_reader_get_bit_depth(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_bit_depth(png_obj->obj, png_obj->info));
}
#endif /* PNG_EASY_ACCESS_SUPPORTED */



#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
static VALUE
libpng_reader_get_bKGD(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_color_16p background;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);

  if (png_get_bKGD(png_obj->obj, png_obj->info, &background))
    return libpng_color_16_new2(cPngColor16, background);

  return Qnil;
}
#endif



static VALUE
libpng_reader_get_channels(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_channels(png_obj->obj, png_obj->info));
}



#ifdef PNG_EASY_ACCESS_SUPPORTED
static VALUE
libpng_reader_get_color_type(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_color_type(png_obj->obj, png_obj->info));
}
#endif /* PNG_EASY_ACCESS_SUPPORTED */



#if defined(PNG_READ_cHRM_SUPPORTED)
#ifdef PNG_FLOATING_POINT_SUPPORTED
static VALUE
libpng_reader_get_cHRM(obj)
  VALUE obj;
{
  png_object *png_obj;
  double white_x, red_x, green_x, blue_x;
  double white_y, red_y, green_y, blue_y;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);

  if(png_get_cHRM(png_obj->obj, png_obj->info,
                  &white_x, &white_y, &red_x, &red_y,
                  &green_x, &green_y, &blue_x, &blue_y)){
    ary = rb_ary_new();

    rb_ary_push(ary, rb_float_new(white_x));
    rb_ary_push(ary, rb_float_new(white_y));
    rb_ary_push(ary, rb_float_new(red_x));
    rb_ary_push(ary, rb_float_new(red_y));
    rb_ary_push(ary, rb_float_new(green_x));
    rb_ary_push(ary, rb_float_new(green_y));
    rb_ary_push(ary, rb_float_new(blue_y));
    rb_ary_push(ary, rb_float_new(blue_x));

    return ary;
  }

  return Qnil;
}
#endif /* PNG_FLOATING_POINT_SUPPORTED */



#ifdef PNG_FIXED_POINT_SUPPORTED
static VALUE
libpng_reader_get_cHRM_fixed(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_uint_32 white_x, red_x, green_x, blue_x;
  png_uint_32 white_y, red_y, green_y, blue_y;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);

  if(png_get_cHRM_fixed(png_obj->obj, png_obj->info,
                        &white_x, &white_y, &red_x, &red_y,
                        &green_x, &green_y, &blue_x, &blue_y)){
    ary = rb_ary_new();

    rb_ary_push(ary, INT2NUM(white_x));
    rb_ary_push(ary, INT2NUM(white_y));
    rb_ary_push(ary, INT2NUM(red_x));
    rb_ary_push(ary, INT2NUM(red_y));
    rb_ary_push(ary, INT2NUM(green_x));
    rb_ary_push(ary, INT2NUM(green_y));
    rb_ary_push(ary, INT2NUM(blue_x));
    rb_ary_push(ary, INT2NUM(blue_y));

    return ary;
  }

  return Qnil;
}
#endif /* PNG_FIXED_POINT_SUPPORTED */
#endif /* PNG_READ_cHRM_SUPPORTED */



#ifdef PNG_EASY_ACCESS_SUPPORTED
static VALUE
libpng_reader_get_compression_type(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_compression_type(png_obj->obj, png_obj->info));
}
#endif /* PNG_EASY_ACCESS_SUPPORTED */



#ifdef PNG_EASY_ACCESS_SUPPORTED
static VALUE
libpng_reader_get_filter_type(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_filter_type(png_obj->obj, png_obj->info));
}
#endif /* PNG_EASY_ACCESS_SUPPORTED */



#if defined(PNG_gAMA_SUPPORTED)|| defined(PNG_READ_GAMMA_SUPPORTED)
static VALUE
libpng_reader_get_gAMA(obj)
  VALUE obj;
{
  png_object *png_obj;
  double image_gamma;

  GET_PNG_VAL(obj, png_obj);

  if(png_get_gAMA(png_obj->obj, png_obj->info, &image_gamma))
    return rb_float_new(image_gamma);

  return Qnil;
}



#ifdef PNG_FLOATING_POINT_SUPPORTED
static VALUE
libpng_reader_get_gAMA_fixed(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_uint_32 image_gamma;

  GET_PNG_VAL(obj, png_obj);

  if(png_get_gAMA_fixed(png_obj->obj, png_obj->info, &image_gamma))
    return INT2NUM(image_gamma);

  return Qnil;
}
#endif /* PNG_FLOATING_POINT_SUPPORTED */
#endif /* PNG_gAMA_SUPPORTED || PNG_READ_GAMMA_SUPPORTED */



#if defined(PNG_READ_hIST_SUPPORTED)
static VALUE
libpng_reader_get_hIST(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_colorp palette;
  png_uint_16p histogram;
  int i, j, num_palette;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);

  if (png_get_PLTE(png_obj->obj, png_obj->info, &palette, &num_palette)){
    if (png_get_hIST(png_obj->obj, png_obj->info, &histogram)){
      ary = rb_ary_new2(num_palette);
      for (i = 0; i < num_palette; i++)
        rb_ary_push(ary, INT2NUM(histogram[i]));
      return ary;
    }
  }

  return Qnil;
}
#endif /* PNG_READ_hIST_SUPPORTED */



#if defined(PNG_READ_iCCP_SUPPORTED)
static VALUE
libpng_reader_get_iCCP(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_charpp name, profile;
  int compression_type;
  png_uint_32 proflen;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);

  if (png_get_iCCP(png_obj->obj, png_obj->info,
                   name, &compression_type, profile, &proflen)){
    ary = rb_ary_new();
    rb_ary_push(ary, rb_str_new2(*name));
    rb_ary_push(ary, INT2FIX(compression_type));
    rb_ary_push(ary, rb_str_new(*profile, proflen));
    rb_ary_push(ary, INT2FIX(proflen));

    return ary;
  }

  return Qnil;
}
#endif /* PNG_READ_iCCP_SUPPORTED */



static VALUE
libpng_reader_get_IHDR(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_uint_32 width, height;
  int bit_depth, color_type, interlace_type;
  int compression_type, filter_type;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);

  if(png_get_IHDR(png_obj->obj, png_obj->info, &width, &height,
                  &bit_depth, &color_type, &interlace_type,
                  &compression_type, &filter_type)){
    ary = rb_ary_new();
    rb_ary_push(ary, INT2NUM(width));
    rb_ary_push(ary, INT2NUM(height));
    rb_ary_push(ary, INT2FIX(bit_depth));
    rb_ary_push(ary, INT2FIX(color_type));
    rb_ary_push(ary, INT2FIX(interlace_type));
    rb_ary_push(ary, INT2FIX(compression_type));
    rb_ary_push(ary, INT2FIX(filter_type));

    return ary;
  }

  return Qnil;
}



static VALUE
libpng_reader_get_image_container(obj)
  VALUE obj;
{
  png_object *png_obj;
  int height, row_bytes, i;
  VALUE ary, str;

  GET_PNG_VAL(obj, png_obj);

  height = png_get_image_height(png_obj->obj, png_obj->info);
  row_bytes = png_get_rowbytes(png_obj->obj, png_obj->info);

  ary = rb_ary_new();
  for (i=0; i<height; i++){
    rb_ary_push(ary, rb_str_new("", row_bytes));
  }

  return ary;
}



#ifdef PNG_EASY_ACCESS_SUPPORTED
static VALUE
libpng_reader_get_image_height(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_image_height(png_obj->obj, png_obj->info));
}



static VALUE
libpng_reader_get_image_width(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_image_width(png_obj->obj, png_obj->info));
}
#endif /* PNG_EASY_ACCESS_SUPPORTED */



#if defined(PNG_READ_oFFs_SUPPORTED)
static VALUE
libpng_reader_get_oFFs(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_uint_32 offset_x, offset_y;
  int unit_type;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);

  if (png_get_oFFs(png_obj->obj, png_obj->info,
                   &offset_x, &offset_y, &unit_type)){
    ary = rb_ary_new();
    rb_ary_push(ary, INT2NUM(offset_x));
    rb_ary_push(ary, INT2NUM(offset_y));
    rb_ary_push(ary, INT2FIX(unit_type));

    return ary;
  }

  return Qnil;
}



#ifdef PNG_EASY_ACCESS_SUPPORTED
static VALUE
libpng_reader_get_x_offset_pixels(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_x_offset_pixels(png_obj->obj, png_obj->info));
}



static VALUE
libpng_reader_get_y_offset_pixels(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_y_offset_pixels(png_obj->obj, png_obj->info));
}



static VALUE
libpng_reader_get_x_offset_microns(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_x_offset_microns(png_obj->obj, png_obj->info));
}



static VALUE
libpng_reader_get_y_offset_microns(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_y_offset_microns(png_obj->obj, png_obj->info));
}
#endif /* PNG_EASY_ACCESS_SUPPORTED */
#endif /* PNG_READ_oFFs_SUPPORTED */



#ifdef PNG_EASY_ACCESS_SUPPORTED
static VALUE
libpng_reader_get_interlace_type(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_interlace_type(png_obj->obj, png_obj->info));
}
#endif



static VALUE
libpng_reader_get_PLTE(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_colorp palette;
  int i, num_palette;
  VALUE ary, color;

  GET_PNG_VAL(obj, png_obj);

  if (png_get_PLTE(png_obj->obj, png_obj->info, &palette, &num_palette)){
    ary = rb_ary_new();
    for (i = 0; i < num_palette; i++){
      color = libpng_color_new2(cPngColor, &palette[i]);
      rb_ary_push(ary, color);
    }
    return ary;
  }

  return Qnil;
}



#if defined(PNG_READ_sCAL_SUPPORTED)
#ifdef PNG_FLOATING_POINT_SUPPORTED
static VALUE
libpng_reader_get_sCAL(obj)
{
  png_object *png_obj;
  int unit;
  double width, height;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);

  if (png_get_sCAL(png_obj->obj, png_obj->info, &unit, &width, &height)){
    ary = rb_ary_new();

    rb_ary_push(ary, INT2FIX(unit));
    rb_ary_push(ary, rb_float_new(width));
    rb_ary_push(ary, rb_float_new(height));

    return ary;
  }

  return Qnil;
}
#else



#ifdef PNG_FIXED_POINT_SUPPORTED
static VALUE
libpng_reader_png_get_sCAL(obj)
{
  png_object *png_obj;
  int unit;
  png_charpp swidth, sheight;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);
  
  if (png_get_sCAL_s(png_obj->obj, png_obj->info, &unit, swidth, sheight)){
    ary = rb_ary_new();

    rb_ary_push(ary, INT2FIX(unit));
    rb_ary_push(ary, rb_str_new2(swidth));
    rb_ary_push(ary, rb_str_new2(sheight));

    return ary;
  }

  return Qnil;
}
#endif /* PNG_FIXED_POINT_SUPPORTED */
#endif
#endif /* PNG_READ_sCAL_SUPPORTED */



#ifdef PNG_FLOATING_POINT_SUPPORTED
static VALUE
libpng_reader_get_pixel_aspect_ratio(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return rb_float_new(png_get_pixel_aspect_ratio(png_obj->obj, png_obj->info));
}
#endif /* PNG_FLOATING_POINT_SUPPORTED */



#if defined(PNG_READ_pCAL_SUPPORTED)
static VALUE
libpng_reader_get_pCAL(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_charp purpose, units;
  png_int_32 X0, X1;
  int i, type, nparams;
  png_charpp params;
  VALUE ary, ary2;

  GET_PNG_VAL(obj, png_obj);

  if (png_get_pCAL(png_obj->obj, png_obj->info,
                   &purpose, &X0, &X1, &type, &nparams, &units, &params)){
    ary = rb_ary_new();

    rb_ary_push(ary, rb_str_new2(purpose));
    rb_ary_push(ary, INT2NUM(X0));
    rb_ary_push(ary, INT2NUM(X1));
    rb_ary_push(ary, INT2NUM(type));
    rb_ary_push(ary, rb_str_new2(units));

    ary2 = rb_ary_new();
    for (i = 0; i < nparams; i++)
      rb_ary_push(ary2, rb_str_new2(params[i]));

    rb_ary_push(ary, ary2);

    return ary;
  }

  return Qnil;
}
#endif /* PNG_READ_pCAL_SUPPORTED */



#if defined(PNG_READ_pHYs_SUPPORTED)
static VALUE
libpng_reader_get_pHYs(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_uint_32 res_x, res_y;
  int unit_type;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);

  if (png_get_pHYs(png_obj->obj, png_obj->info, &res_x, &res_y, &unit_type)){
    ary = rb_ary_new();

    rb_ary_push(ary, INT2NUM(res_x));
    rb_ary_push(ary, INT2NUM(res_y));
    rb_ary_push(ary, INT2FIX(unit_type));

    return ary;
  }

  return Qnil;
}
#endif /* PNG_READ_pHYs_SUPPORTED */



#ifdef PNG_EASY_ACCESS_SUPPORTED
static VALUE
libpng_reader_get_pixels_per_meter(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_pixels_per_meter(png_obj->obj, png_obj->info));
}



static VALUE
libpng_reader_get_x_pixels_per_meter(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_x_pixels_per_meter(png_obj->obj, png_obj->info));
}



static VALUE
libpng_reader_get_y_pixels_per_meter(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_y_pixels_per_meter(png_obj->obj, png_obj->info));
}
#endif



#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
static VALUE
libpng_reader_get_rgb_to_gray_status(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2NUM(png_get_rgb_to_gray_status(png_obj->obj));
}
#endif /* PNG_READ_RGB_TO_GRAY_SUPPORTED */



static VALUE
libpng_reader_get_signature(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return rb_str_new2(png_get_signature(png_obj->obj, png_obj->info));
}



#if defined(PNG_sBIT_SUPPORTED)
static VALUE
libpng_reader_get_sBIT(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_color_8p sig_bit;
  int bit_depth, i;

  GET_PNG_VAL(obj, png_obj);

  if (png_get_sBIT(png_obj->obj, png_obj->info, &sig_bit))
    return libpng_color_8_new2(cPngColor8, sig_bit);

  return Qnil;
}
#endif



#if defined(PNG_READ_sRGB_SUPPORTED)
static VALUE
libpng_reader_get_sRGB(obj)
  VALUE obj;
{
  png_object *png_obj;
  int intent;

  GET_PNG_VAL(obj, png_obj);

  if (png_get_sRGB(png_obj->obj, png_obj->info, &intent))
    return INT2FIX(intent);

  return Qnil;
}
#endif /* PNG_READ_sRGB_SUPPORTED */



#if defined(PNG_sPLT_SUPPORTED)
static VALUE
libpng_reader_get_suggested_palette(obj, palette_name)
  VALUE obj, palette_name;
{
  png_object *png_obj;
  png_sPLT_tpp entries = NULL;
  png_uint_32 num;
  png_sPLT_entryp entry;
  VALUE ary, tmp, sPLT_entry;
  int i, j;

  Check_Type(palette_name, T_STRING);

  GET_PNG_VAL(obj, png_obj);

  num = png_get_sPLT(png_obj->obj, png_obj->info, entries);

  if (num != 0 && entries){
    ary = rb_ary_new();

    for (i = 0; i < num; i++){
      if (strcmp(STR2CSTR(palette_name), (*entries)->name) == 0){
        tmp = rb_ary_new();

        rb_ary_push(tmp, rb_str_new2((*entries)->name));
        rb_ary_push(tmp, INT2FIX((*entries)->depth));
        entry = (*entries)->entries;

        for (j = 0; j < (*entries)->nentries; j++){
          sPLT_entry = libpng_sPLT_entry_new2(cSuggestedPaletteEntry, &entry[j]);
          rb_ary_push(tmp, sPLT_entry);
        }

        rb_ary_push(ary, tmp);
        entries++;
      }
    }

    return ary;
  }

  return Qnil;
}



static VALUE
libpng_reader_get_sPLT(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_sPLT_tpp entries = NULL;
  png_uint_32 num;
  png_sPLT_entryp entry;
  VALUE ary, tmp, sPLT_entry;
  int i, j;

  GET_PNG_VAL(obj, png_obj);

  num = png_get_sPLT(png_obj->obj, png_obj->info, entries);

  if (num != 0 && entries){
    ary = rb_ary_new();

    for (i = 0; i < num; i++){
      tmp = rb_ary_new();

      rb_ary_push(tmp, rb_str_new2((*entries)->name));
      rb_ary_push(tmp, INT2FIX((*entries)->depth));
      entry = (*entries)->entries;

      for (j = 0; j < (*entries)->nentries; j++){
        sPLT_entry = libpng_sPLT_entry_new2(cSuggestedPaletteEntry, &entry[j]);
        rb_ary_push(tmp, sPLT_entry);
      }

      rb_ary_push(ary, tmp);
      entries++;
    }

    return ary;
  }

  return Qnil;
}
#endif



#if defined(PNG_READ_TEXT_SUPPORTED)
static VALUE
libpng_reader_get_text(obj, key)
  VALUE obj, key;
{
  png_object *png_obj;
  png_textp text_ptr;
  png_infop info_ptr;
  int num_text, i;
  VALUE ary;

  Check_Type(key, T_STRING);

  GET_PNG_VAL(obj, png_obj);

  if (png_obj->obj->mode & PNG_AFTER_IDAT)
    info_ptr = png_obj->end_info;
  else
    info_ptr = png_obj->info;

  if (png_get_text(png_obj->obj, info_ptr, &text_ptr, &num_text) > 0){
    ary = rb_ary_new();

    for (i=0; i<num_text; i++){
      if (strcmp(text_ptr->key, STR2CSTR(key)) == 0)
        rb_ary_push(ary, libpng_text_new2(cText, &text_ptr[i]));
    }

    return ary;
  }

  return Qnil;
}



static VALUE
libpng_reader_get_texts(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_textp text_ptr;
  png_infop info_ptr;
  int num_text, i;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);

  if (png_obj->obj->mode & PNG_AFTER_IDAT)
    info_ptr = png_obj->end_info;
  else
    info_ptr = png_obj->info;

  if (png_get_text(png_obj->obj, info_ptr, &text_ptr, &num_text) > 0){
    ary = rb_ary_new();

    for (i=0; i<num_text; i++)
      rb_ary_push(ary, libpng_text_new2(cText, &text_ptr[i]));

    return ary;
  }

  return Qnil;
}
#endif /* PNG_READ_TEXT_SUPPORTED */



#if defined(PNG_READ_tIME_SUPPORTED)
static VALUE
libpng_reader_get_tIME(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_timep mod_time;
  png_infop info_ptr;
  VALUE time;

  GET_PNG_VAL(obj, png_obj);

  if (png_obj->obj->mode & PNG_AFTER_IDAT)
    info_ptr = png_obj->end_info;
  else
    info_ptr = png_obj->info;

  if (png_get_tIME(png_obj->obj, info_ptr, &mod_time)){
    time = rb_funcall(rb_eval_string("Time"), rb_intern("gm"), 6,
        INT2FIX(mod_time->year), INT2FIX(mod_time->month),
        INT2FIX(mod_time->day), INT2FIX(mod_time->hour),
        INT2FIX(mod_time->minute), INT2FIX(mod_time->second));

    return time;
  }

  return Qnil;
}
#endif /* PNG_READ_tIME_SUPPORTED */



#if defined(PNG_READ_tRNS_SUPPORTED)
static VALUE
libpng_reader_get_tRNS(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_byte color_type;
  png_bytep trans;
  png_colorp palette;
  int i, num_trans;
  png_color_16p trans_values;
  VALUE ary;

  GET_PNG_VAL(obj, png_obj);

  color_type = png_get_color_type(png_obj->obj, png_obj->info);

  if (color_type == PNG_COLOR_TYPE_PALETTE){
    if (png_get_tRNS(png_obj->obj, png_obj->info, &trans, &num_trans, &trans_values)){
      ary = rb_ary_new();

      for (i = 0; i < num_trans; i++)
        rb_ary_push(ary, INT2FIX(trans[i]));

      return ary;
    }
  }
  else if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_RGB){
    if (png_get_tRNS(png_obj->obj, png_obj->info, &trans, &num_trans, &trans_values))
      return libpng_color_16_new2(cPngColor16, trans_values);
  }

  return Qnil;
}
#endif /* PNG_READ_tRNS_SUPPORTED */



#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
static VALUE
libpng_reader_get_unknown_chunks(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_unknown_chunkpp unknowns;
  png_uint_32 num_unknowns;
  VALUE ary;
  int i;

  GET_PNG_VAL(obj, png_obj);

  if (num_unknowns = png_get_unknown_chunks(png_obj->obj, png_obj->info, unknowns)){
    ary = rb_ary_new();

    for (i=0; i<num_unknowns; i++)
      rb_ary_push(ary, libpng_unknown_chunk_new2(cUnknownChunk, unknowns[i]));

    return ary;
  }

  return Qnil;
}
#endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */



static VALUE
libpng_reader_get_valid_p(obj, flag)
  VALUE obj, flag;
{
  png_object *png_obj;

  FIXNUM_P(flag);

  GET_PNG_VAL(obj, png_obj);

  if (png_get_valid(png_obj->obj, png_obj->info, (png_uint_32)FIX2INT(flag)))
    return Qtrue;

  return Qfalse;
}



static VALUE
libpng_reader_read_end(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_read_end(png_obj->obj, png_obj->end_info);

  return Qnil;
}



static VALUE
libpng_reader_read_image(obj)
  VALUE obj;
{
  png_object *png_obj;
  png_bytepp row_pointers;
  int number_passes, pass, height, row, row_bytes;
  VALUE str, ary;

  GET_PNG_VAL(obj, png_obj);

  number_passes = png_set_interlace_handling(png_obj->obj);
  height = png_get_image_height(png_obj->obj, png_obj->info);
  row_bytes = png_get_rowbytes(png_obj->obj, png_obj->info);

  row_pointers = ALLOC_N(png_bytep, height);

  for (row=0; row<height; row++)
    row_pointers[row] = ALLOC_N(png_byte, row_bytes);

/*  png_read_image(png_obj->obj, row_pointers); */

  for (pass=0; pass<number_passes; pass++)
    for (row=0; row<height; row++)
      png_read_rows(png_obj->obj, &row_pointers[row], NULL, 1);

  ary = rb_ary_new();
  for (row=0; row<height; row++){
    str = rb_str_new(row_pointers[row], row_bytes);
    rb_ary_push(ary, str);
  }

  for (row=0; row<height; row++)
    free(row_pointers[row]);

  free(row_pointers);

  return ary;
}



static VALUE
libpng_reader_read_row(obj, row, display_row)
  VALUE obj, row, display_row;
{
  png_object *png_obj;
  png_bytep row_pointer, display_row_pointer;
  int row_bytes, i;

  GET_PNG_VAL(obj, png_obj);

  switch(TYPE(row)){
    case T_NIL:
      row_pointer = NULL;
      break;

    case T_STRING:
      row_bytes = png_get_rowbytes(png_obj->obj, png_obj->info);
      if (RSTRING(row)->len < row_bytes)
        rb_raise(ePngError, "not enough to byte string size");
      row_pointer = RSTRING(row)->ptr;
      break;

    default:
      rb_raise(rb_eTypeError, "wrong argument type %s (expected String/nil)",
               rb_class2name(CLASS_OF(row)));
      return Qnil;
  }

  switch(TYPE(display_row)){
    case T_NIL:
      display_row_pointer = NULL;
      break;

    case T_STRING:
      row_bytes = png_get_rowbytes(png_obj->obj, png_obj->info);
      if (RSTRING(display_row)->len < row_bytes)
        rb_raise(ePngError, "not enough to byte string size");
      display_row_pointer = RSTRING(display_row)->ptr;
      break;

    default:
      rb_raise(rb_eTypeError, "wrong argument type %s (expected String/nil)",
               rb_class2name(CLASS_OF(row)));
      return Qnil;
  }

  if (row_pointer != NULL || display_row_pointer != NULL){
    png_read_row(png_obj->obj, row_pointer, display_row_pointer);
  }

  return Qnil;
}



static VALUE
libpng_reader_read_rows(obj, row, display_row)
  VALUE obj, row, display_row;
{
  png_object *png_obj;
  png_bytepp row_pointers, display_row_pointers;
  int num_rows, row_bytes, i;

  FIXNUM_P(num_rows);

  GET_PNG_VAL(obj, png_obj);

  row_bytes = png_get_rowbytes(png_obj->obj, png_obj->info);

  switch(TYPE(row)){
    case T_NIL:
      row_pointers = NULL;
      break;

    case T_ARRAY:
      num_rows = RARRAY(row)->len;
      row_pointers = ALLOC_N(png_bytep, num_rows);
      for (i=0; i<num_rows; i++){
        Check_Type(RARRAY(row)->ptr[i], T_STRING);
        if (RSTRING(RARRAY(row)->ptr[i])->len < row_bytes)
          rb_raise(ePngError, "not enough to byte string size");
        row_pointers[i] = RSTRING(RARRAY(row)->ptr[i])->ptr;
      }
      break;

    default:
      rb_raise(rb_eTypeError, "wrong argument type %s (expected Array/nil)",
               rb_class2name(CLASS_OF(row)));
      return Qnil;
  }

  switch(TYPE(display_row)){
    case T_NIL:
      display_row_pointers = NULL;
      break;

    case T_ARRAY:
      num_rows = RARRAY(display_row)->len;
      display_row_pointers = ALLOC_N(png_bytep, num_rows);
      for (i=0; i<num_rows; i++){
        Check_Type(RARRAY(display_row)->ptr[i], T_STRING);
        if (RSTRING(RARRAY(display_row)->ptr[i])->len < row_bytes)
          rb_raise(ePngError, "not enough to byte string size");
        display_row_pointers[i] = RSTRING(RARRAY(display_row)->ptr[i])->ptr;
      }
      break;

    default:
      rb_raise(rb_eTypeError, "wrong argument type %s (expected Array/nil)",
               rb_class2name(CLASS_OF(display_row)));
      return Qnil;
  }

  if (row_pointers != NULL || display_row_pointers != NULL)
    png_read_rows(png_obj->obj, row_pointers, display_row_pointers, num_rows);

  free(row_pointers);
  free(display_row_pointers);

  return Qnil;
}



static VALUE
libpng_reader_read_update_info(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_read_update_info(png_obj->obj, png_obj->info);

  return Qnil;
}



#if defined(PNG_READ_BACKGROUND_SUPPORTED)
#ifdef PNG_FLOATING_POINT_SUPPORTED
static VALUE
libpng_reader_set_background(obj, background_color, 
                          background_gamma_code, need_expand, background_gamma)
  VALUE obj, background_color,
        background_gamma_code, need_expand, background_gamma;
{
  png_object *png_obj;
  png_color_16p bg_color;

  IS_COLOR_16_P(background_color);
  FIXNUM_P(background_gamma_code);
  FIXNUM_P(need_expand);
  Check_Type(background_gamma, T_FLOAT);
 
  GET_PNG_VAL(obj, png_obj);
  Data_Get_Struct(background_color, png_color_16, bg_color);

  png_set_background(png_obj->obj, bg_color,
                     NUM2INT(background_gamma_code), NUM2INT(need_expand),
                     RFLOAT(background_gamma)->value);

  return Qnil;
}
#endif /* PNG_FLOATING_POINT_SUPPORTED */
#endif /* PNG_READ_BACKGROUND_SUPPORTED */



#if defined(PNG_READ_BGR_SUPPORTED)
static VALUE
libpng_reader_set_bgr(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_bgr(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_BGR_SUPPORTED */



static VALUE
libpng_reader_set_crc_action(obj, crit_action, ancil_action)
  VALUE obj, crit_action, ancil_action;
{
  png_object *png_obj;
  int critical_action, ancillary_action;

  FIXNUM_P(crit_action);
  FIXNUM_P(ancil_action);

  GET_PNG_VAL(obj, png_obj);

  critical_action = FIX2INT(crit_action);
  ancillary_action = FIX2INT(ancil_action);

  if (critical_action < PNG_CRC_DEFAULT ||
      critical_action > PNG_CRC_NO_CHANGE){
    rb_raise(ePngError,
             "invalid type to handle CRC errors in critical chunks: %d",
             critical_action);
  }

  if (ancillary_action < PNG_CRC_DEFAULT ||
      ancillary_action > PNG_CRC_NO_CHANGE){
    rb_raise(ePngError,
             "invalid type to handle CRC errors in ancillary chunks: %d",
             ancillary_action);
  }

  png_set_crc_action(png_obj->obj, critical_action, ancillary_action);

  return Qnil;
}



#if defined(PNG_READ_DITHER_SUPPORTED)
static VALUE
libpng_reader_set_dither(obj, palettes, maximum_colors, histogram, full_dither)
  VALUE obj, palettes, maximum_colors, histogram, full_dither;
{
  png_object *png_obj;
  png_colorp dither_palette;
  png_uint_16p dither_histogram;
  png_color *color;
  VALUE cobj, val;
  int i;

  Check_Type(palettes, T_ARRAY);
  FIXNUM_P(maximum_colors);
  Check_Type(histogram, T_ARRAY);
  FIXNUM_P(full_dither);

  GET_PNG_VAL(obj, png_obj);

  dither_palette = ALLOCA_N(png_color, RARRAY(palettes)->len);
  for (i=0; i<RARRAY(palettes)->len; i++){
    cobj = RARRAY(palettes)->ptr[i];
    IS_COLOR_P(cobj);
    Data_Get_Struct(cobj, png_color, color);
    dither_palette[i] = *color;
  }

  dither_histogram = ALLOCA_N(png_uint_16, RARRAY(histogram)->len);
  for (i=0; i<RARRAY(histogram)->len; i++){
    val = RARRAY(histogram)->ptr[i];
    FIXNUM_P(val);
    dither_histogram[i] = NUM2LONG(val);
  }

  png_set_dither(png_obj->obj, dither_palette, FIX2INT(maximum_colors),
                 RARRAY(palettes)->len, dither_histogram, FIX2INT(full_dither));

  return Qnil;
}
#endif /* PNG_READ_DITHER_SUPPORTED */



#if defined(PNG_READ_EXPAND_SUPPORTED)
static VALUE
libpng_reader_set_expand(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_expand(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_EXPAND_SUPPORTED */



#if defined(PNG_READ_FILLER_SUPPORTED)
static VALUE
libpng_reader_set_filler(obj, filler, flags)
  VALUE obj, filler, flags;
{
  png_object *png_obj;
  int filler_flags;

  FIXNUM_P(filler);
  FIXNUM_P(flags);

  GET_PNG_VAL(obj, png_obj);

  filler_flags = FIX2INT(flags);

  if (filler_flags < PNG_FILLER_BEFORE || filler_flags > PNG_FILLER_AFTER)
    rb_raise(ePngError, "invalid filler flag type: %d", filler_flags);

  png_set_filler(png_obj->obj, NUM2LONG(filler), FIX2INT(flags));

  return Qnil;
}
#endif /* PNG_READ_FILLER_SUPPORTED */



#if defined(PNG_READ_GAMMA_SUPPORTED)
#ifdef PNG_FLOATING_POINT_SUPPORTED
static VALUE
libpng_reader_set_gamma(obj, screen_gamma, default_file_gamma)
  VALUE obj, screen_gamma, default_file_gamma;
{
  png_object *png_obj;

  Check_Type(screen_gamma, T_FLOAT);
  Check_Type(default_file_gamma, T_FLOAT);

  GET_PNG_VAL(obj, png_obj);

  png_set_gamma(png_obj->obj, RFLOAT(screen_gamma)->value,
                              RFLOAT(default_file_gamma)->value);

  return Qnil;
}
#endif /* PNG_FLOATING_POINT_SUPPORTED */
#endif /* PNG_READ_GAMMA_SUPPORTED */



#if defined(PNG_READ_EXPAND_SUPPORTED)
static VALUE
libpng_reader_set_gray_1_2_4_to_8(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_gray_1_2_4_to_8(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_EXPAND_SUPPORTED */



#if defined(PNG_READ_INTERLACING_SUPPORTED)
static VALUE
libpng_reader_set_interlace_handling(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  return INT2FIX(png_set_interlace_handling(png_obj->obj));
}
#endif /* PNG_READ_INTERLACING_SUPPORTE */



#if defined(PNG_READ_INVERT_SUPPORTED)
static VALUE
libpng_reader_set_invert_mono(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_invert_mono(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_INVERT_SUPPORTE */



#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
static VALUE
libpng_reader_set_keep_unknown_chunks(obj, keep, chunk_list)
  VALUE obj, keep, chunk_list;
{
  png_object *png_obj;
  int keep_condition, num_chunks, i;
  png_bytep affect_chunk_list;

  FIXNUM_P(keep);

  keep_condition = FIX2INT(keep);

  if (keep_condition < HANDLE_CHUNK_AS_DEFAULT ||
      keep_condition > HANDLE_CHUNK_ALWAYS){
    rb_raise(ePngError,
             "invalid \"keep\" directive for unknown chunks: %d",
             keep_condition);
  }

  GET_PNG_VAL(obj, png_obj);

  switch(TYPE(chunk_list)){
    case T_NIL:
      png_set_keep_unknown_chunks(png_obj->obj, keep_condition, NULL, 0);
      break;

    case T_ARRAY:
      num_chunks = RARRAY(chunk_list)->len;
      affect_chunk_list = ALLOCA_N(char, 5 * num_chunks);
      for (i=0; i<num_chunks; i++){
        Check_Type(RARRAY(chunk_list)->ptr[i], T_STRING);
        png_memcpy(affect_chunk_list, STR2CSTR(RARRAY(chunk_list)->ptr[i]), 4);
        png_memcpy(affect_chunk_list, '\0', 1);
      }
      png_set_keep_unknown_chunks(png_obj->obj, keep_condition,
                                  affect_chunk_list, num_chunks);
      break;

    default:
      rb_raise(rb_eTypeError, "wrong argument type %s (expected Array/nil)",
               rb_class2name(CLASS_OF(chunk_list)));
  }

  return Qnil;
}
#endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */



#if defined(PNG_READ_PACK_SUPPORTED)
static VALUE
libpng_reader_set_packing(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_packing(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_PACK_SUPPORTED */



#if defined(PNG_READ_PACKSWAP_SUPPORTED)
static VALUE
libpng_reader_set_packswap(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_packswap(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_PACKSWAP_SUPPORTED */



#if defined(PNG_READ_EXPAND_SUPPORTED)
static VALUE
libpng_reader_set_palette_to_rgb(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_palette_to_rgb(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_EXPAND_SUPPORTED */



static VALUE
libpng_reader_set_read_status_fn(obj, proc)
  VALUE obj, proc;
{
  if (!rb_respond_to(proc, rb_intern("call")))
    rb_raise(rb_eArgError, "argument have to respond to `call'");
  return rb_iv_set(cReader, READ_STATUS_FN, proc);
}



#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
static VALUE
libpng_reader_set_read_user_transform_fn(obj, proc)
  VALUE obj, proc;
{
  if (!rb_respond_to(proc, rb_intern("call")))
    rb_raise(rb_eArgError, "argument have to respond to `call'");
  return rb_iv_set(cReader, READ_USER_TRANSFORM_FN, proc);
}
#endif /* PNG_READ_USER_TRANSFORM_SUPPORTED */



#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
#ifdef PNG_FLOATING_POINT_SUPPORTED
static VALUE
libpng_reader_set_rgb_to_gray(obj, error_action, red, green)
  VALUE obj, error_action, red, green;
{
  png_object *png_obj;

  FIXNUM_P(error_action);
  Check_Type(red, T_FLOAT);
  Check_Type(green, T_FLOAT);

  GET_PNG_VAL(obj, png_obj);

  png_set_rgb_to_gray(png_obj->obj, FIX2INT(error_action),
                      RFLOAT(red)->value, RFLOAT(green)->value);

  return Qnil;
}
#endif /* PNG_FLOATING_POINT_SUPPORTED */

static VALUE
libpng_reader_set_rgb_to_gray_fixed(obj, error_action, red, green)
  VALUE obj, error_action, red, green;
{
  png_object *png_obj;

  FIXNUM_P(error_action);
  FIXNUM_P(red);
  FIXNUM_P(green);

  GET_PNG_VAL(obj, png_obj);

  png_set_rgb_to_gray(png_obj->obj, FIX2INT(error_action),
                      FIX2INT(red), FIX2INT(green));

  return Qnil;
}
#endif /* PNG_READ_RGB_TO_GRAY_SUPPORTED */



#if defined(PNG_READ_SHIFT_SUPPORTED)
static VALUE
libpng_reader_set_shift(obj, true_bits)
  VALUE obj, true_bits;
{
  png_object *png_obj;
  png_color_8p sig_bit;
  VALUE cobj;
  int i;

  IS_COLOR_8_P(cobj);

  GET_PNG_VAL(obj, png_obj);

  Data_Get_Struct(cobj, png_color_8, sig_bit);

  png_set_shift(png_obj->obj, sig_bit);

  return Qnil;
}
#endif



#if defined(PNG_READ_16_TO_8_SUPPORTED)
static VALUE
libpng_reader_set_strip_16(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_strip_16(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_16_TO_8_SUPPORTED */



#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
static VALUE
libpng_reader_set_strip_alpha(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_strip_alpha(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_STRIP_ALPHA_SUPPORTED */



#if defined(PNG_READ_SWAP_SUPPORTED)
static VALUE
libpng_reader_set_swap(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_swap(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_SWAP_SUPPORTED */



#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
static VALUE
libpng_reader_set_swap_alpha(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_swap_alpha(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_SWAP_ALPHA_SUPPORTED */



#if defined(PNG_READ_EXPAND_SUPPORTED)
static VALUE
libpng_reader_set_tRNS_to_alpha(obj)
  VALUE obj;
{
  png_object *png_obj;

  GET_PNG_VAL(obj, png_obj);

  png_set_tRNS_to_alpha(png_obj->obj);

  return Qnil;
}
#endif /* PNG_READ_EXPAND_SUPPORTED */




void
Init_reader()
{
  /*
   * ---------------------
   * Graphics::PNG::Reader
   * ---------------------
   */

 cReader = rb_define_class_under(mPng, "Reader", rb_cObject);


  /* define class methods */
  rb_define_singleton_method(cReader, "new", libpng_reader_new, 1);


  /* define instance methods */
  rb_define_method(cReader, "initialize", libpng_reader_initialize, -1);

  rb_define_method(cReader, "get_bit_depth", libpng_reader_get_bit_depth, 0);

#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
  rb_define_method(cReader, "get_bKGD", libpng_reader_get_bKGD, 0);
#endif

  rb_define_method(cReader, "get_channels", libpng_reader_get_channels, 0);

#if defined(PNG_cHRM_SUPPORTED)
  rb_define_method(cReader, "get_cHRM", libpng_reader_get_cHRM, 0);
  rb_define_method(cReader, "get_cHRM_fixed", libpng_reader_get_cHRM_fixed, 0);
#endif

  rb_define_method(cReader, "get_color_type", libpng_reader_get_color_type, 0);
  rb_define_method(cReader, "get_compression_type", libpng_reader_get_compression_type, 0);
  rb_define_method(cReader, "get_filter_type", libpng_reader_get_filter_type, 0);

#if defined(PNG_gAMA_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED)
  rb_define_method(cReader, "get_gAMA", libpng_reader_get_gAMA, 0);
  rb_define_method(cReader, "get_gAMA_fixed", libpng_reader_get_gAMA_fixed, 0);
#endif

#if defined(PNG_hIST_SUPPORTED)
  rb_define_method(cReader, "get_hIST", libpng_reader_get_hIST, 0);
#endif

#if defined(PNG_iCCP_SUPPORTED)
  rb_define_method(cReader, "get_iCCP", libpng_reader_get_iCCP, 0);
#endif

  rb_define_method(cReader, "get_IHDR", libpng_reader_get_IHDR, 0);

  rb_define_method(cReader, "get_image_container", libpng_reader_get_image_container, 0);
  rb_define_method(cReader, "get_image_height", libpng_reader_get_image_height, 0);
  rb_define_method(cReader, "get_image_width", libpng_reader_get_image_width, 0);

#if defined(PNG_oFFs_SUPPORTED)
  rb_define_method(cReader, "get_oFFs", libpng_reader_get_oFFs, 0);
#if defined(PNG_EASY_ACCESS_SUPPORTED)
  rb_define_method(cReader, "get_x_offset_pixels", libpng_reader_get_x_offset_pixels, 0);
  rb_define_method(cReader, "get_y_offset_pixels", libpng_reader_get_y_offset_pixels, 0);
  rb_define_method(cReader, "get_x_offset_microns", libpng_reader_get_x_offset_microns, 0);
  rb_define_method(cReader, "get_y_offset_microns", libpng_reader_get_y_offset_microns, 0);
#endif /* PNG_EASY_ACCESS_SUPPORTED */
#endif /* PNG_oFFs_SUPPORTED */

  rb_define_method(cReader, "get_interlace_type", libpng_reader_get_interlace_type, 0);
  rb_define_method(cReader, "get_PLTE", libpng_reader_get_PLTE, 0);

#if defined(PNG_READ_sCAL_SUPPORTED)
  rb_define_method(cReader, "get_sCAL", libpng_reader_get_sCAL, 0);
#endif

#ifdef PNG_FLOATING_POINT_SUPPORTED
  rb_define_method(cReader, "get_pixel_aspect_ratio", libpng_reader_get_pixel_aspect_ratio, 0);
#endif

#if defined(PNG_pCAL_SUPPORTED)
  rb_define_method(cReader, "get_pCAL", libpng_reader_get_pCAL, 0);
#endif

#if defined(PNG_pHYs_SUPPORTED)
  rb_define_method(cReader, "get_pHYs", libpng_reader_get_pHYs, 0);
#endif

#ifdef PNG_EASY_ACCESS_SUPPORTED
  rb_define_method(cReader, "get_pixels_per_meter", libpng_reader_get_pixels_per_meter, 0);
  rb_define_method(cReader, "get_x_pixels_per_meter", libpng_reader_get_x_pixels_per_meter, 0);
  rb_define_method(cReader, "get_y_pixels_per_meter", libpng_reader_get_y_pixels_per_meter, 0);
#endif

#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
  rb_define_method(cReader, "get_rgb_to_gray_status", libpng_reader_get_rgb_to_gray_status, 0);
#endif

  rb_define_method(cReader, "get_signature", libpng_reader_get_signature, 0);

#if defined(PNG_sBIT_SUPPORTED)
  rb_define_method(cReader, "get_sBIT", libpng_reader_get_sBIT, 0);
#endif

#if defined(PNG_READ_sRGB_SUPPORTED)
  rb_define_method(cReader, "get_sRGB", libpng_reader_get_sRGB, 0);
#endif

#if defined(PNG_sPLT_SUPPORTED)
/*
  rb_define_method(cReader, "suggested_palette", libpng_reader_get_suggested_palette, 1);
*/
  rb_define_method(cReader, "get_sPLT", libpng_reader_get_sPLT, 0);
#endif

#if defined(PNG_READ_TEXT_SUPPORTED)
  rb_define_method(cReader, "get_text", libpng_reader_get_text, 1);
  rb_define_method(cReader, "get_texts", libpng_reader_get_texts, 0);
#endif

#if defined(PNG_tIME_SUPPORTED)
  rb_define_method(cReader, "get_tIME", libpng_reader_get_tIME, 0);
#endif

#if defined(PNG_READ_tRNS_SUPPORTED)
  rb_define_method(cReader, "get_tRNS", libpng_reader_get_tRNS, 0);
#endif

  rb_define_method(cReader, "valid?", libpng_reader_get_valid_p, 1);

  rb_define_method(cReader, "read_end", libpng_reader_read_end, 0);
  rb_define_method(cReader, "read_image", libpng_reader_read_image, 0);
  rb_define_method(cReader, "read_row", libpng_reader_read_row, 2);
  rb_define_method(cReader, "read_rows", libpng_reader_read_rows, 2);
  rb_define_method(cReader, "read_update_info",
       libpng_reader_read_update_info, 0);

#if defined(PNG_READ_BACKGROUND_SUPPORTED)
#ifdef PNG_FLOATING_POINT_SUPPORTED
  rb_define_method(cReader, "set_background", libpng_reader_set_background, 4);
#endif
#endif

#if defined(PNG_READ_BGR_SUPPORTED)
  rb_define_method(cReader, "set_bgr", libpng_reader_set_bgr, 0);
#endif

  rb_define_method(cReader, "set_crc_action", libpng_reader_set_crc_action, 2);

#if defined(PNG_READ_DITHER_SUPPORTED)
  rb_define_method(cReader, "set_dither", libpng_reader_set_dither, 4);
#endif

#if defined(PNG_READ_EXPAND_SUPPORTED)
  rb_define_method(cReader, "set_expand", libpng_reader_set_expand, 0);
#endif

#if defined(PNG_READ_FILLER_SUPPORTED)
  rb_define_method(cReader, "set_filler", libpng_reader_set_filler, 2);
#endif

#if defined(PNG_READ_GAMMA_SUPPORTED)
#ifdef PNG_FLOATING_POINT_SUPPORTED
  rb_define_method(cReader, "set_gamma", libpng_reader_set_gamma, 2);
#endif
#endif

#if defined(PNG_READ_EXPAND_SUPPORTED)
  rb_define_method(cReader, "set_gray_1_2_4_to_8",
                   libpng_reader_set_gray_1_2_4_to_8, 0);
#endif

#if defined(PNG_READ_INTERLACING_SUPPORTED)
  rb_define_method(cReader, "set_interlace_handling",
       libpng_reader_set_interlace_handling, 0);
#endif

#if defined(PNG_READ_INVERT_SUPPORTED)
  rb_define_method(cReader, "set_invert_mono",
       libpng_reader_set_invert_mono, 0);
#endif

#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
  rb_define_method(cReader, "set_keep_unknown_chunks",
                   libpng_reader_set_keep_unknown_chunks, 2);
#endif

#if defined(PNG_READ_PACK_SUPPORTED)
  rb_define_method(cReader, "set_packing", libpng_reader_set_packing, 0);
#endif

#if defined(PNG_READ_PACKSWAP_SUPPORTED)
  rb_define_method(cReader, "set_packswap", libpng_reader_set_packswap, 0);
#endif

#if defined(PNG_READ_EXPAND_SUPPORTED)
  rb_define_method(cReader, "set_palette_to_rgb",
       libpng_reader_set_palette_to_rgb, 0);
#endif

/* related progressive reading
  rb_define_method(cReader, "set_progressive_read_fn", libpng_set_progressive_read_fn, 3);
png_process_data
png_progressive_combine_row
*/

  rb_define_method(cReader, "set_read_status_fn", libpng_reader_set_read_status_fn, 1);
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
  rb_define_method(cReader, "set_read_user_transform_fn",
                   libpng_reader_set_read_user_transform_fn, 1);
#endif

#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
#ifdef PNG_FLOATING_POINT_SUPPORTED
  rb_define_method(cReader, "set_rgb_to_gray", libpng_reader_set_rgb_to_gray, 3);
#endif
  rb_define_method(cReader, "set_rgb_to_gray_fixed",
                   libpng_reader_set_rgb_to_gray_fixed, 3);
#endif

#if defined(PNG_READ_SHIFT_SUPPORTED)
  rb_define_method(cReader, "set_shift", libpng_reader_set_shift, 1);
#endif

#if defined(PNG_READ_16_TO_8_SUPPORTED)
  rb_define_method(cReader, "set_strip_16", libpng_reader_set_strip_16, 0);
#endif

#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
  rb_define_method(cReader, "set_strip_alpha",
       libpng_reader_set_strip_alpha, 0);
#endif

#if defined(PNG_READ_SWAP_SUPPORTED)
  rb_define_method(cReader, "set_swap", libpng_reader_set_swap, 0);
#endif

#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
  rb_define_method(cReader, "set_swap_alpha", libpng_reader_set_swap_alpha, 0);
#endif

#if defined(PNG_READ_EXPAND_SUPPORTED)
  rb_define_method(cReader, "set_tRNS_to_alpha",
       libpng_reader_set_tRNS_to_alpha, 0);
#endif
}
