/*
 *  picture.cpp
 *  PSP Development
 *
 *  Created by XArt on 19/03/2007.
 *  Copyright 2007 XArt. All rights reserved.
 *
 */

#include "picture.h"

#ifndef _PSP
#pragma mark Private Methods for Picture Class
#endif

Image* Picture::loadPNG(const char *filename, int offset)
{
   int number_passes, pass;
	png_structp png_ptr;
	png_infop info_ptr;
	unsigned int sig_read = 0;
	png_uint_32 width, height;
	int bit_depth, color_type, interlace_type, x, y;
	u32* line;
	FILE *fp;
   Image *image=NULL;
   int littleEndian = 1;
   littleEndian=(1 == *reinterpret_cast<char*>(&littleEndian))?1:0;
   
	if ((fp = fopen(filename, "rb")) == NULL) return NULL;
   fseek(fp,offset,SEEK_SET);
   
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL)
	{
		fclose(fp);
		return NULL;
	}
	
   // Optinal error function currently not used in this class
	//png_set_error_fn(png_ptr, (png_voidp) NULL, (png_error_ptr) NULL, user_warning_fn);
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL)
	{
      fclose(fp);
		png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
		return NULL;
	}
	
	png_init_io(png_ptr, fp);
	png_set_sig_bytes(png_ptr, sig_read);
	png_read_info(png_ptr, info_ptr);
	png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL);
	
	if (width > 512 || height > 512)
	{
		fclose(fp);
		png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
		return NULL;
	}
	
   image=(Image*)malloc(sizeof(Image));
   if (!image) return NULL;
	image->width    = width;
	image->height   = height;
   image->x=0;
   image->y=0;
   image->depth=32;
   
	// New (the check) - Convert 16-bits per colour component to 8-bits per colour component.
	if (bit_depth == 16) png_set_strip_16(png_ptr);
   
	// Extract multiple pixels with bit depths of 1, 2, and 4 from a single
	// byte into separate bytes (useful for paletted and grayscale images).
	png_set_packing(png_ptr);
   
	if (color_type == PNG_COLOR_TYPE_PALETTE)
		png_set_palette_to_rgb(png_ptr);
   
	// New - Convert grayscale to RGB triplets
	if ((color_type == PNG_COLOR_TYPE_GRAY) || (color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
		png_set_gray_to_rgb(png_ptr);
   
	if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
		png_set_gray_1_2_4_to_8(png_ptr);
   
	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
		png_set_tRNS_to_alpha(png_ptr);
   
	png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
   
	u32 *data = (u32 *) memalign(16, image->width * image->height * sizeof(u32));
   
   if (!data)
	{
      free(image);
		fclose(fp);
		png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
      return NULL;
	}
   
	line = (u32*) malloc(width * 4);
	if (!line)
	{
      free(image);
		free(data);
		fclose(fp);
		png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
		return NULL;
	}
	
	// New - Turn on interlace handling.
	number_passes = png_set_interlace_handling(png_ptr);
   
	// New - Call to gamma correct and add the background to the palette
	// and update info structure.
	png_read_update_info(png_ptr, info_ptr);
   
   // New - Read the image, one line at a line (convert from little-endian to big-endian for PPC)
   for (pass = 0; pass < number_passes; pass++) {
      for (y = 0; y < (int)height; y++)
      {
         png_read_row(png_ptr, (u8*) line, png_bytep_NULL);
         
         for (x = 0; x < (int)width; x++)
         {
            u32 color = *(line+x);
            data[x + y * image->width] = (littleEndian==1)?color:(color>>24)|((color>>8)&0xFF00)|((color<<8)&0xFF0000)|(color<<24);
         }
      }
   }
   free(line);
	
	png_read_end(png_ptr, info_ptr);
	png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
	fclose(fp);
	image->pixels=(void *)data;
   return image;
}
void Picture::savePNG(const char* filename, Image *image)
{
	png_structp png_ptr;
	png_infop info_ptr;
	FILE* fp;
	int i, x, y;
	u8* line;
   u8 *pixels=(u8 *)image->pixels;
	
	if ((fp = fopen(filename, "wb")) == NULL) return;
	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (!png_ptr) return;
	info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr) {
		png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
		return;
	}
	png_init_io(png_ptr, fp);
   
   if (image->depth==32) {
      png_set_IHDR(png_ptr, info_ptr, image->width, image->height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
   } else {
      png_set_IHDR(png_ptr, info_ptr, image->width, image->height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
   }
	
	png_write_info(png_ptr, info_ptr);
	line = (u8*) malloc(image->width * ((image->depth==32) ? 4 : 3));
   int littleEndian = 1;
   littleEndian=(1 == *reinterpret_cast<char*>(&littleEndian))?1:0;
	for (y = 0; y < image->height; y++) {
		for (i = 0, x = 0; x < image->width; x++) {
         if (littleEndian) {
            // Little-Endian
            if (image->depth==32) *(line+i++) = *(pixels+3);
            *(line+i++) = *pixels;
            *(line+i++) = *(pixels+1);
            *(line+i++) = *(pixels+2);
         } else {
            // Big-Endian
            *(line+i++) = *(pixels+3);
            *(line+i++) = *(pixels+2);
            *(line+i++) = *(pixels+1);
            if (image->depth==32) *(line+i++) = *pixels;
         }
         pixels+=4;
		}
		png_write_row(png_ptr, line);
	}
	free(line);
	png_write_end(png_ptr, info_ptr);
	png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
	fclose(fp);
}

#ifndef _PSP
#pragma mark Methods for Picture Class
#endif

/*
 * Picture.loadPicture Method
 *
 * Loads the specifyed pictue.
 * PNG format only >>> JPEG still to be added
 *
 * Picture.loadPicture filename, [offset]
 */

Image* Picture::loadPicture(const char* filename, int offset)
{
   return this->loadPNG(filename, offset);
}

Image* Picture::loadPicture(const char* filename)
{  
   return this->loadPNG(filename, 0);
}

/*
 * Picture.savePicture Method
 *
 * Saved the specifyed pictue.
 * PNG format only >>> JPEG still to be added
 *
 * Picture.savePicture filename, [offset]
 */

void Picture::savePicture(const char* filename, Image *image)
{  
   return this->savePNG(filename, image);
}

/*
 * Picture.freePicture Method
 *
 * Frees memory allocated to loaded picture.
 *
 *
 * Picture.freePicture image
 */

void Picture::freePicture(Image *image)
{
   if (image->pixels) free(image->pixels);
   if (image) free(image);
   image=NULL;
}
