#include "lexlib/defines.h"
#ifdef LIBPNG
# include<stdio.h>
# include<png.h>
#else
# define LEXLIB_EXPERIMENTAL
# include<internal/stbi.h>
#endif
#include<lexlib/image.h>
#include<stdbool.h>
#include<string.h>
uint8_t lexlibImageLoadPng(struct LexlibImage *image, const char *filename){
#ifdef LIBPNG
FILE *file = fopen(filename, "rb");
if(!file)
return LEXLIB_CANT_OPEN;
{ uint8_t head[8];
fread(head, 1, 8, file);
bool ispng = !png_sig_cmp(head, 0, 8);
if(!ispng){
fclose(file);
return LEXLIB_INVALID_TYPE;
}
}
png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(!pngPtr){
fclose(file);
return LEXLIB_OUT_OF_MEMORY;
}
png_infop infoPtr = png_create_info_struct(pngPtr);
if(!infoPtr){
fclose(file);
png_destroy_read_struct(&pngPtr, NULL, NULL);
return LEXLIB_OUT_OF_MEMORY;
}
png_infop endInfo = png_create_info_struct(pngPtr);
if(!endInfo){
fclose(file);
png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
return LEXLIB_OUT_OF_MEMORY;
}
int readTransforms = 0;
#if LEXLIB_LITTLE_ENDIAN
readTransforms |= PNG_TRANSFORM_SWAP_ENDIAN;
#endif
png_init_io(pngPtr, file);
png_set_sig_bytes(pngPtr, 8);
png_read_png(pngPtr, infoPtr, readTransforms, NULL);
image->width = png_get_image_width(pngPtr, infoPtr);
image->height = png_get_image_height(pngPtr, infoPtr);
image->bpc = png_get_bit_depth(pngPtr, infoPtr);
image->channels = png_get_channels(pngPtr, infoPtr);
switch(png_get_color_type(pngPtr, infoPtr)){
case PNG_COLOR_TYPE_GRAY:
image->profile = LEXLIB_GRAY;
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
image->profile = LEXLIB_GRAYA;
break;
case PNG_COLOR_TYPE_RGB:
image->profile = LEXLIB_RGB;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
image->profile = LEXLIB_RGBA;
break;
default:
png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo);
return LEXLIB_UNSUPORTED;
}
uint8_t err;
if((err = lexlibImageNew(image, image->width, image->height, image->profile, image->bpc))){
fclose(file);
png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo);
return err;
}
png_byte** rowPointers = png_get_rows(pngPtr, infoPtr);
size_t rowSize = image->width * image->bpp / 8;
size_t offset = 0;
uint8_t *data = image->data;
for(size_t y = 0; y < image->height; y++){
memcpy(data+offset, rowPointers[y], rowSize * sizeof(uint8_t));
offset += rowSize;
}
png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo);
fclose(file);
#else
switch(lexlibStbImageIs16(filename)){
case 0:
image->bpc = 8;
break;
case 1:
image->bpc = 16;
break;
case LEXLIB_CANT_OPEN:
return LEXLIB_CANT_OPEN;
default:
return LEXLIB_CANT_READ;
}
int channels;
switch(image->bpc){
case 8:
image->data = lexlibStbImageLoad(filename, (int*)&image->width, (int*)&image->height, &channels);
break;
case 16:
image->data = lexlibStbImage16Load(filename, (int*)&image->width, (int*)&image->height, &channels);
break;
}
image->channels = channels;
if(!image->data)
return LEXLIB_CANT_READ;
if(lexlibImageRepairStruct(image)){
lexlibImageDelete(image);
image->data = NULL;
return LEXLIB_ERROR;
}
#endif
return LEXLIB_OK;
}
uint8_t lexlibImageSavePng(const struct LexlibImage *image, const char *filename){
#ifdef LIBPNG
png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(!pngPtr)
return LEXLIB_OUT_OF_MEMORY;
png_infop infoPtr = png_create_info_struct(pngPtr);
if(!infoPtr){
png_destroy_write_struct(&pngPtr, NULL);
return LEXLIB_OUT_OF_MEMORY;
}
int colorType = 0;
switch(image->profile){
case LEXLIB_GRAY:
colorType = PNG_COLOR_TYPE_GRAY;
break;
case LEXLIB_GRAYA:
colorType = PNG_COLOR_TYPE_GRAY_ALPHA;
break;
case LEXLIB_RGB:
colorType = PNG_COLOR_TYPE_RGB;
break;
case LEXLIB_RGBA:
colorType = PNG_COLOR_TYPE_RGB_ALPHA;
break;
default:
png_destroy_write_struct(&pngPtr, &infoPtr);
return LEXLIB_UNSUPORTED;
}
png_set_IHDR(pngPtr, infoPtr, image->width, image->height, image->bpc, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_compression_level(pngPtr, 9);
FILE *file = fopen(filename, "wb");
if(!file){
png_destroy_write_struct(&pngPtr, &infoPtr);
return LEXLIB_CANT_WRITE;
}
png_init_io(pngPtr, file);
png_write_info(pngPtr, infoPtr);
png_write_flush(pngPtr);
#if LEXLIB_LITTLE_ENDIAN
if(image->bpc > 8)
png_set_swap(pngPtr);
#endif
size_t rowSize = image->width * (image->bpp / 8);
uint32_t offset = 0;
uint8_t *data = image->data;
for(uint32_t y = 0; y < image->height; y++){
png_write_row(pngPtr, data+offset);
offset += rowSize;
}
png_write_end(pngPtr, infoPtr);
png_destroy_write_struct(&pngPtr, &infoPtr);
fclose(file);
return LEXLIB_OK;
#else
if(image->bpc > 8)
return LEXLIB_UNSUPORTED;
return lexlibStbImageWritePng(filename, image->data, image->width, image->height, image->channels, 0); #endif
}