1037 lines
30 KiB
C++
1037 lines
30 KiB
C++
/*************************************************************************
|
|
* *
|
|
* Vega FEM Simulation Library Version 2.2 *
|
|
* *
|
|
* "imageIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2015 USC *
|
|
* All rights reserved. *
|
|
* *
|
|
* Code author: Yili Zhao, Jernej Barbic *
|
|
* http://www.jernejbarbic.com/code *
|
|
* *
|
|
* Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder, *
|
|
* Doug L. James, Jovan Popovic *
|
|
* *
|
|
* Funding: National Science Foundation, Link Foundation, *
|
|
* Singapore-MIT GAMBIT Game Lab, *
|
|
* Zumberge Research and Innovation Fund at USC *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the BSD-style license that is *
|
|
* included with this library in the file LICENSE.txt *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file *
|
|
* LICENSE.TXT for more details. *
|
|
* *
|
|
*************************************************************************/
|
|
|
|
/*
|
|
Load/save PPM, PNG, JPEG, TIFF, TGA image formats.
|
|
|
|
PPM and TGA are built-in (no external dependencies).
|
|
PNG depends on libpng, JPEG depends on jpeglib and TIFF depends on libtiff.
|
|
|
|
In order to enable PNG, JPEG or TIFF, uncomment the corresponding line in imageFormats.h, and link against the external library.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#if defined(_WIN32) || defined(WIN32)
|
|
#pragma warning(disable : 4996)
|
|
#endif
|
|
|
|
#include "imageFormats.h"
|
|
|
|
#ifdef ENABLE_TIFF
|
|
#include <tiffio.h>
|
|
#endif
|
|
|
|
#ifdef ENABLE_JPEG
|
|
extern "C"
|
|
{
|
|
#include <jpeglib.h>
|
|
}
|
|
|
|
#ifdef WIN32
|
|
#pragma comment(lib, "jpeg.lib")
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_PNG
|
|
extern "C"
|
|
{
|
|
#include <png.h>
|
|
}
|
|
#endif
|
|
|
|
#include "imageIO.h"
|
|
|
|
#define BITS_PER_CHANNEL_8 8
|
|
#define BITS_PER_CHANNEL_16 16
|
|
#define IMAGE_IO_RGB 3
|
|
#define IMAGE_IO_RGB_ALPHA 4
|
|
#define IMAGE_IO_HORIZONTAL_DIFFERENCING 2
|
|
#define IMAGE_IO_UNCOMPRESSED_RGB 2
|
|
|
|
ImageIO::ImageIO()
|
|
{
|
|
width = 0;
|
|
height = 0;
|
|
bytesPerPixel = 0;
|
|
pixels = nullptr;
|
|
ownPixels = 0;
|
|
}
|
|
|
|
ImageIO::ImageIO(unsigned int width_, unsigned int height_, unsigned int bytesPerPixel_, unsigned char * pixels_, int makeInternalCopy) :
|
|
width(width_), height(height_), bytesPerPixel(bytesPerPixel_)
|
|
{
|
|
if (makeInternalCopy == 0)
|
|
{
|
|
pixels = pixels_;
|
|
ownPixels = 0;
|
|
}
|
|
else
|
|
{
|
|
pixels = (unsigned char*) malloc (sizeof(unsigned char) * width * height * bytesPerPixel);
|
|
memcpy(pixels, pixels_, sizeof(unsigned char) * width * height * bytesPerPixel);
|
|
ownPixels = 1;
|
|
}
|
|
}
|
|
|
|
ImageIO::~ImageIO()
|
|
{
|
|
if (ownPixels)
|
|
free(pixels);
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::loadPPM(const char * filename)
|
|
{
|
|
FILE * file = fopen(filename, "rb");
|
|
if(!file)
|
|
return IO_ERROR;
|
|
|
|
char buf[4096];
|
|
char * result = fgets(buf, 4096, file);
|
|
result = result + 1; // to avoid a compiler warning
|
|
if(strncmp(buf, "P6", 2))
|
|
{
|
|
printf("Error in loadPPM: File is not a raw RGB ppm.\n");
|
|
fclose(file);
|
|
return INVALID_FILE_FORMAT;
|
|
}
|
|
|
|
// read image width and height
|
|
int i = 0;
|
|
int maxval;
|
|
while(i < 3)
|
|
{
|
|
char * dummy = fgets(buf, 4096, file);
|
|
dummy = dummy + 1; // to suppress compiler warning
|
|
if(buf[0] == '#') // ignore comments
|
|
continue;
|
|
if(i == 0)
|
|
i += sscanf(buf, "%d %d %d", &width, &height, &maxval);
|
|
else if (i == 1)
|
|
i += sscanf(buf, "%d %d", &height, &maxval);
|
|
else if (i == 2)
|
|
i += sscanf(buf, "%d", &maxval);
|
|
}
|
|
|
|
bytesPerPixel = 3;
|
|
|
|
// read the pixels
|
|
free(pixels);
|
|
pixels = (unsigned char*) malloc (sizeof(unsigned char) * 3 * width * height);
|
|
if(fread(pixels, sizeof(unsigned char), 3 * width * height, file) < 3 * width * height)
|
|
{
|
|
printf("Error in loadPPM: Error reading ppm image from %s.\n", filename);
|
|
free(pixels);
|
|
fclose(file);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
flipVertically();
|
|
|
|
return OK;
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::savePPM(const char * filename)
|
|
{
|
|
FILE * file = fopen(filename, "wb");
|
|
if(!file)
|
|
return IO_ERROR;
|
|
|
|
unsigned char * pixelsNoAlphaChannel = nullptr;
|
|
if (bytesPerPixel == 4) // special case, alpha channel byte will be dropped
|
|
{
|
|
printf("Warning in savePPM: Alpha channel has been dropped when the image is saved in PPM format.\n");
|
|
pixelsNoAlphaChannel = (unsigned char *) malloc (sizeof(unsigned char) * width * height * 3);
|
|
for(unsigned int pixelIndex = 0; pixelIndex < width * height; pixelIndex++)
|
|
memcpy(&pixelsNoAlphaChannel[pixelIndex*3], &pixels[pixelIndex*4], sizeof(unsigned char) * 3);
|
|
}
|
|
else
|
|
pixelsNoAlphaChannel = pixels;
|
|
|
|
fprintf(file, "P6 %d %d 255\n", width, height);
|
|
|
|
for (int row = height - 1; row >= 0; row--)
|
|
{
|
|
unsigned int pos = row * width * 3;
|
|
if (fwrite(&pixelsNoAlphaChannel[pos], sizeof(unsigned char), 3 * width, file) != 3 * width)
|
|
{
|
|
printf("Error in savePPM: Error while saving ppm image to %s.\n", filename);
|
|
fclose(file);
|
|
return IO_ERROR;
|
|
}
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
if (bytesPerPixel == 4)
|
|
free(pixelsNoAlphaChannel);
|
|
|
|
return OK;
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::loadTGA(const char * filename)
|
|
{
|
|
FILE * file = fopen(filename, "rb");
|
|
if (!file)
|
|
{
|
|
printf("Error in loadTGA: Cannot open file %s.\n", filename);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
unsigned char type[4];
|
|
if (fread (&type, sizeof (char), 3, file) < 3)
|
|
{
|
|
printf("Error in loadTGA: Error reading tga image from %s.\n", filename);
|
|
fclose(file);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
if (fseek (file, 12, SEEK_SET) != 0)
|
|
{
|
|
printf("Error in loadTGA: Error reading tga image from %s.\n", filename);
|
|
fclose(file);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
unsigned char info[6];
|
|
if ( fread (&info, sizeof (char), 6, file) < 6 )
|
|
{
|
|
printf("Error in loadTGA: Error reading tga image from %s.\n", filename);
|
|
fclose(file);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
// image type is either 2 (color) or 3 (grayscale)
|
|
if ((type[1] != 0) || ((type[2] != 2) && (type[2] != 3)))
|
|
{
|
|
fclose(file);
|
|
printf("Error in loadTGA: Invalid file type.\n");
|
|
printf("type[1]=%d type[2]=%d\n", type[1], type[2]);
|
|
return INVALID_FILE_FORMAT;
|
|
}
|
|
|
|
width = info[0] + 256 * info[1];
|
|
height = info[2] + 256 * info[3];
|
|
bytesPerPixel = info[4] / 8;
|
|
|
|
if ((bytesPerPixel != 3) && (bytesPerPixel != 4))
|
|
{
|
|
fclose(file);
|
|
printf("Error in loadTGA: Invalid number of bytes per pixel: %d.\n", bytesPerPixel);
|
|
return INVALID_FILE_FORMAT;
|
|
}
|
|
|
|
long imageSize = width * height * bytesPerPixel;
|
|
|
|
// allocate memory for image data
|
|
free(pixels);
|
|
pixels = (unsigned char*) malloc (sizeof(unsigned char) * imageSize);
|
|
|
|
// read image data
|
|
if ( (int)fread(pixels, sizeof(unsigned char), imageSize, file) < imageSize)
|
|
{
|
|
printf("Error in loadTGA: Error reading tga image from %s.\n", filename);
|
|
free(pixels);
|
|
fclose(file);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
// The order of the color in tga is BGR(A) not RGB(A), so
|
|
for(unsigned int pixelIndex = 0; pixelIndex < width * height; pixelIndex++)
|
|
{
|
|
unsigned int offset = pixelIndex * bytesPerPixel;
|
|
unsigned char temp;
|
|
enum{R, G, B, A};
|
|
temp = pixels[offset + R];
|
|
pixels[offset + R] = pixels[offset + B];
|
|
pixels[offset + B] = temp;
|
|
}
|
|
|
|
// close file
|
|
fclose(file);
|
|
|
|
return OK;
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::saveTGA(const char * filename)
|
|
{
|
|
// This routine writes the result as a uncompressed TGA file
|
|
FILE *file = fopen(filename, "wb");
|
|
if (!file)
|
|
{
|
|
printf("Error in saveTGA: Cannot open file %s.\n", filename);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
// we write the header in little endian manner,
|
|
// namely, information is stored with the low order byte followed by the high order byte.
|
|
|
|
unsigned char buf;
|
|
#define WRITECHAR(ch)\
|
|
buf = (ch);\
|
|
fwrite(&buf, sizeof(unsigned char), 1, file);
|
|
|
|
WRITECHAR(0); // id length, length of a string located after the header, 0 = no string
|
|
WRITECHAR(0); // color map type
|
|
WRITECHAR(IMAGE_IO_UNCOMPRESSED_RGB); // data type code
|
|
|
|
WRITECHAR(0);
|
|
WRITECHAR(0); // short integer, color map origin (little endian)
|
|
|
|
WRITECHAR(0);
|
|
WRITECHAR(0); // short integer, color map length (little endian)
|
|
|
|
WRITECHAR(0); // color map depth
|
|
|
|
WRITECHAR(0);
|
|
WRITECHAR(0); // short integer, x origin (little endian)
|
|
|
|
WRITECHAR(0);
|
|
WRITECHAR(0); // short integer, y origin (little endian)
|
|
|
|
WRITECHAR(width % 256);
|
|
WRITECHAR((width / 256) % 256); // image width (little endian)
|
|
WRITECHAR(height % 256);
|
|
WRITECHAR((height / 256) % 256); // image height (little endian)
|
|
|
|
switch (bytesPerPixel)
|
|
{
|
|
case IMAGE_IO_RGB_ALPHA:
|
|
WRITECHAR(32); // 32 bit bitmap
|
|
break;
|
|
|
|
case IMAGE_IO_RGB:
|
|
WRITECHAR(24); // 24 bit map
|
|
break;
|
|
|
|
default:
|
|
printf("Error in saveTGA: cannot handle the case where bytes per pixel is neither 3 nor 4.\n");
|
|
fclose(file);
|
|
return IO_ERROR;
|
|
break;
|
|
}
|
|
|
|
WRITECHAR(0); // image descriptor
|
|
|
|
unsigned char * pixelBuf = (unsigned char*) malloc (sizeof(unsigned char) * width * height * bytesPerPixel);
|
|
if (pixelBuf == nullptr)
|
|
{
|
|
printf("Error in saveTGA: cannot allocate memory for pixel buffer.\n");
|
|
fclose(file);
|
|
return IO_ERROR;
|
|
}
|
|
memcpy(pixelBuf, pixels, sizeof(unsigned char) * width * height * bytesPerPixel);
|
|
|
|
// The order of the color is BGR(A) not RGB(A)
|
|
for(unsigned int pixelIndex = 0; pixelIndex < width * height; pixelIndex++)
|
|
{
|
|
unsigned int offset = pixelIndex * bytesPerPixel;
|
|
unsigned char temp;
|
|
enum{R, G, B, A};
|
|
temp = pixelBuf[offset + R];
|
|
pixelBuf[offset + R] = pixelBuf[offset + B];
|
|
pixelBuf[offset + B] = temp;
|
|
}
|
|
|
|
fwrite(pixelBuf, sizeof(unsigned char), width * height * bytesPerPixel, file);
|
|
|
|
free(pixelBuf);
|
|
fclose(file);
|
|
return OK;
|
|
|
|
#undef WRITECHAR
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::loadJPEG(const char * filename)
|
|
{
|
|
#ifdef ENABLE_JPEG
|
|
FILE * file = fopen(filename, "rb");
|
|
if(!file)
|
|
return IO_ERROR;
|
|
|
|
struct jpeg_decompress_struct jpgPicture;
|
|
|
|
struct jpeg_error_mgr jpgErrorMessage;
|
|
|
|
jpgPicture.err = jpeg_std_error(&jpgErrorMessage);
|
|
j_decompress_ptr jpgPicturePtr = (j_decompress_ptr)(&jpgPicture);
|
|
|
|
jpeg_create_decompress(jpgPicturePtr); // Init
|
|
jpeg_stdio_src(jpgPicturePtr, file); // setup image source
|
|
|
|
// read information of the image
|
|
jpeg_read_header(jpgPicturePtr, TRUE);
|
|
|
|
jpeg_start_decompress(jpgPicturePtr);
|
|
|
|
width = jpgPicturePtr->image_width;
|
|
height = jpgPicturePtr->image_height;
|
|
|
|
// CAREFULL we must be!! the bytesPerPixel information is not in the jpeg header.
|
|
// It is only available after calling jpeg_start_decompress()
|
|
bytesPerPixel = jpgPicturePtr->output_components;
|
|
|
|
free(pixels);
|
|
pixels = (unsigned char *) malloc(sizeof(unsigned char) * width * height * bytesPerPixel);
|
|
// printf("Width = %d; Height = %d, bytesPerPixel = %d\n", width, height, bytesPerPixel);
|
|
// fflush(nullptr);
|
|
|
|
JSAMPROW rowPtr[1];
|
|
for(int row= jpgPicturePtr->output_height - 1; jpgPicturePtr->output_scanline < jpgPicturePtr->output_height; row--)
|
|
{
|
|
rowPtr[0] = (JSAMPROW) &pixels[row * width * bytesPerPixel];
|
|
JDIMENSION maxNumLines = 1;
|
|
if (jpeg_read_scanlines(jpgPicturePtr, (JSAMPARRAY)rowPtr, maxNumLines) != maxNumLines)
|
|
{
|
|
printf("Error in loadJPEG: Error reading jpg image from %s.\n", filename);
|
|
free(pixels);
|
|
jpeg_destroy_decompress(jpgPicturePtr);
|
|
fclose(file);
|
|
return IO_ERROR;
|
|
}
|
|
}
|
|
|
|
jpeg_finish_decompress(jpgPicturePtr);
|
|
|
|
jpeg_destroy_decompress(jpgPicturePtr);
|
|
|
|
fclose(file);
|
|
return OK;
|
|
|
|
#else
|
|
return INVALID_FILE_FORMAT;
|
|
#endif
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::saveJPEG(const char * filename)
|
|
{
|
|
return saveJPEGWithGivenQuality(filename, 95);
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::saveJPEGWithGivenQuality(const char * filename, int quality)
|
|
{
|
|
#ifdef ENABLE_JPEG
|
|
|
|
FILE * file = fopen(filename, "wb");
|
|
if(!file)
|
|
return IO_ERROR;
|
|
|
|
struct jpeg_compress_struct jpgPicture;
|
|
struct jpeg_error_mgr jpgErrorMessage;
|
|
|
|
jpgPicture.err = jpeg_std_error(&jpgErrorMessage);
|
|
jpeg_create_compress(&jpgPicture); // Init
|
|
jpeg_stdio_dest(&jpgPicture, file); // setup image destination
|
|
|
|
unsigned char * pixelsNoAlphaChannel = nullptr;
|
|
if (bytesPerPixel == 4) // special case, alpha channel byte will be dropped
|
|
{
|
|
printf("Warning in saveJPEG: Alpha channel has been dropped when the image is saved in JPEG format.\n");
|
|
pixelsNoAlphaChannel = (unsigned char *) malloc (sizeof(unsigned char) * width * height * 3);
|
|
for(unsigned int pixelIndex = 0; pixelIndex < width * height; pixelIndex++)
|
|
memcpy(&pixelsNoAlphaChannel[pixelIndex*3], &pixels[pixelIndex*4], sizeof(unsigned char) * 3);
|
|
}
|
|
else
|
|
pixelsNoAlphaChannel = pixels;
|
|
|
|
jpgPicture.image_width = width;
|
|
jpgPicture.image_height = height;
|
|
jpgPicture.input_components = 3;
|
|
jpgPicture.in_color_space = JCS_RGB; // color space of the image
|
|
|
|
jpeg_set_defaults(&jpgPicture);
|
|
jpeg_set_quality(&jpgPicture, quality, TRUE);
|
|
jpeg_start_compress(&jpgPicture, TRUE);
|
|
|
|
JSAMPROW rowPtr[1];
|
|
unsigned int numBytesPerRow = width * 3;
|
|
for(int row = jpgPicture.image_height - 1; jpgPicture.next_scanline < jpgPicture.image_height; row--)
|
|
{
|
|
rowPtr[0] = &pixelsNoAlphaChannel[row * numBytesPerRow];
|
|
JDIMENSION maxNumLines = 1;
|
|
if (jpeg_write_scanlines(&jpgPicture, rowPtr, maxNumLines) != maxNumLines)
|
|
{
|
|
printf("Error in saveJPEG: Error while saving jpg image to %s.\n", filename);
|
|
jpeg_destroy_compress(&jpgPicture);
|
|
if(bytesPerPixel == 4)
|
|
free(pixelsNoAlphaChannel);
|
|
fclose(file);
|
|
return IO_ERROR;
|
|
}
|
|
}
|
|
|
|
jpeg_finish_compress(&jpgPicture);
|
|
|
|
jpeg_destroy_compress(&jpgPicture);
|
|
|
|
fclose(file);
|
|
|
|
if(bytesPerPixel == 4)
|
|
free(pixelsNoAlphaChannel);
|
|
return OK;
|
|
#else
|
|
return INVALID_FILE_FORMAT;
|
|
#endif
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::loadTIFF(const char * filename)
|
|
{
|
|
#ifdef ENABLE_TIFF
|
|
TIFF * tiff = TIFFOpen(filename, "r");
|
|
if (!tiff)
|
|
return IO_ERROR;
|
|
|
|
// read the dimensions
|
|
uint32 tiff_width, tiff_height;
|
|
uint16 tiff_samplesPerPixel;
|
|
uint16 tiff_bits;
|
|
TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &tiff_width);
|
|
TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &tiff_height);
|
|
TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &tiff_samplesPerPixel);
|
|
TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &tiff_bits);
|
|
|
|
//printf("tiff_width: %d tiff_height: %d tiff_samplesPerPixel: %d tiff_bits: %d\n", tiff_width, tiff_height, tiff_samplesPerPixel, tiff_bits);
|
|
|
|
if ((tiff_samplesPerPixel != IMAGE_IO_RGB) && (tiff_samplesPerPixel != IMAGE_IO_RGB_ALPHA))
|
|
{
|
|
printf("Error in loadTIFF: Sorry, cannot handle %d-channel images.\n", tiff_samplesPerPixel);
|
|
TIFFClose(tiff);
|
|
return OTHER_ERROR;
|
|
}
|
|
|
|
if (tiff_bits != BITS_PER_CHANNEL_8)
|
|
{
|
|
printf("Error in loadTIFF: Sorry, cannot handle %d-bit images.\n", tiff_bits);
|
|
TIFFClose(tiff);
|
|
return OTHER_ERROR;
|
|
}
|
|
|
|
width = tiff_width;
|
|
height = tiff_height;
|
|
bytesPerPixel = tiff_samplesPerPixel;
|
|
|
|
uint32 * tiff_pixels = (uint32*) _TIFFmalloc(tiff_width * tiff_height * sizeof(uint32));
|
|
if (!tiff_pixels)
|
|
{
|
|
TIFFClose(tiff);
|
|
return MEMORY_ERROR;
|
|
}
|
|
|
|
printf("Loading TIFF image from file %s: resolution: %d x %d, %d-bit.\n", filename, width, height, 8 * bytesPerPixel);
|
|
|
|
int stopOnError = 1;
|
|
if (!TIFFReadRGBAImage(tiff, tiff_width, tiff_height, tiff_pixels, stopOnError))
|
|
{
|
|
_TIFFfree(tiff_pixels);
|
|
TIFFClose(tiff);
|
|
printf("Error in loadTIFF: Unknown error when calling TIFFReadRGBAImage.\n");
|
|
return IO_ERROR;
|
|
}
|
|
|
|
pixels = (unsigned char*) malloc (sizeof(unsigned char) * width * height * bytesPerPixel);
|
|
|
|
// write tiff_pixels into the pixels array
|
|
int counter = 0;
|
|
for(unsigned int row=0; row < height; row++)
|
|
{
|
|
for(unsigned int column=0; column < width; column++)
|
|
{
|
|
// read the uint32 pixel
|
|
uint32 tiff_pixel = tiff_pixels[row * tiff_width + column];
|
|
|
|
// write R,G,B,A in place into pixels
|
|
pixels[counter] = TIFFGetR(tiff_pixel);
|
|
counter++;
|
|
|
|
if (bytesPerPixel < 3)
|
|
continue;
|
|
|
|
pixels[counter] = TIFFGetG(tiff_pixel);
|
|
counter++;
|
|
pixels[counter] = TIFFGetB(tiff_pixel);
|
|
counter++;
|
|
|
|
if (bytesPerPixel < 4)
|
|
continue;
|
|
|
|
// alpha channel
|
|
pixels[counter] = TIFFGetA(tiff_pixel);
|
|
counter++;
|
|
}
|
|
}
|
|
|
|
_TIFFfree(tiff_pixels);
|
|
TIFFClose(tiff);
|
|
|
|
return OK;
|
|
|
|
#else
|
|
return INVALID_FILE_FORMAT;
|
|
#endif
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::saveTIFF(const char * filename)
|
|
{
|
|
#ifdef ENABLE_TIFF
|
|
|
|
TIFF *tif;
|
|
|
|
tif = TIFFOpen(filename, "w");
|
|
if( !tif )
|
|
{
|
|
printf("Error in saveTIFF: Cannot open file %s.\n", filename);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
|
|
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
|
|
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, bytesPerPixel);
|
|
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
|
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, BITS_PER_CHANNEL_8);
|
|
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
|
if (bytesPerPixel < IMAGE_IO_RGB)
|
|
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
|
|
else
|
|
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
|
|
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
|
|
TIFFSetField(tif, TIFFTAG_PREDICTOR, IMAGE_IO_HORIZONTAL_DIFFERENCING);
|
|
|
|
tsize_t bytesPerRow = bytesPerPixel * width;
|
|
|
|
if (TIFFScanlineSize(tif) != bytesPerRow)
|
|
{
|
|
printf("Error in saveTIFF: the scanline size mismatches the length in memory of one row of pixels in the image.\n");
|
|
TIFFClose(tif);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
unsigned char * imageBuf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
|
|
if (imageBuf == nullptr)
|
|
{
|
|
printf("Error in saveTIFF: cannot allocate memory for the storage of a row of pixels.\n");
|
|
TIFFClose(tif);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
// Set the strip size of the file to be the size of one row of pixels
|
|
TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, (uint32)bytesPerRow));
|
|
|
|
for (unsigned int row = 0; row < height; row++)
|
|
{
|
|
memcpy(imageBuf, &pixels[(height - row - 1) * bytesPerRow], sizeof(unsigned char) * bytesPerRow);
|
|
if (TIFFWriteScanline(tif, imageBuf, row, 0) < 0) // an error occurred while writing the image
|
|
{
|
|
printf("Error in saveTIFF: Error while saving TIFF image to %s.\n", filename);
|
|
_TIFFfree(imageBuf);
|
|
TIFFClose(tif);
|
|
return IO_ERROR;
|
|
}
|
|
}
|
|
|
|
_TIFFfree(imageBuf);
|
|
TIFFClose(tif);
|
|
|
|
return OK;
|
|
#else
|
|
return INVALID_FILE_FORMAT;
|
|
#endif
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::loadPNG(const char * filename)
|
|
{
|
|
#ifdef ENABLE_PNG
|
|
FILE *file = fopen(filename, "rb");
|
|
if (!file)
|
|
{
|
|
printf("Error in loadPNG: Cannot open file %s.\n", filename);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
// read the header of the image file
|
|
const unsigned int pngHeaderBytes = 8;
|
|
png_byte pngHeader[pngHeaderBytes];
|
|
if (fread(pngHeader, 1, pngHeaderBytes, file) != pngHeaderBytes)
|
|
{
|
|
printf("Error in loadPNG: cannot read the header of the png file.\n");
|
|
fclose(file);
|
|
return (INVALID_FILE_FORMAT);
|
|
};
|
|
|
|
int pngFlag = !png_sig_cmp(pngHeader, 0, pngHeaderBytes);
|
|
if (!pngFlag)
|
|
{
|
|
printf("Error in loadPNG: %s is not a png file.\n", filename);
|
|
fclose(file);
|
|
return (INVALID_FILE_FORMAT);
|
|
}
|
|
|
|
// initialize png_struct and png_info
|
|
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
|
if (!png_ptr)
|
|
{
|
|
printf("Error in loadPNG: Creating the internal structure failed.\n");
|
|
fclose(file);
|
|
return (IO_ERROR);
|
|
}
|
|
|
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
|
if (!info_ptr)
|
|
{
|
|
png_destroy_read_struct(&png_ptr,
|
|
(png_infopp)nullptr, (png_infopp)nullptr);
|
|
printf("Error in loadPNG: Creating the information structure failed.\n");
|
|
fclose(file);
|
|
return (IO_ERROR);
|
|
}
|
|
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
|
{
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)nullptr);
|
|
fclose(file);
|
|
printf("Error in loadPNG: cannot setup the error handling.\n");
|
|
return (IO_ERROR);
|
|
}
|
|
|
|
png_init_io(png_ptr, file);
|
|
png_set_sig_bytes(png_ptr, pngHeaderBytes);
|
|
|
|
png_read_info(png_ptr, info_ptr);
|
|
|
|
int color_type;
|
|
int bit_depth;
|
|
|
|
png_get_IHDR(png_ptr, info_ptr, (png_uint_32*)(&width), (png_uint_32*)(&height), &bit_depth, &color_type, nullptr, nullptr, nullptr);
|
|
|
|
// !!ATTETNION!! The following transformations are designed in the order that they should occur.
|
|
// DO NOT change the sequence!!
|
|
|
|
bool setBackgroundNeeded = false;
|
|
|
|
switch (color_type)
|
|
{
|
|
case PNG_COLOR_TYPE_PALETTE:
|
|
png_set_palette_to_rgb(png_ptr); // change palette to RGB
|
|
setBackgroundNeeded = true;
|
|
break;
|
|
|
|
case PNG_COLOR_TYPE_GRAY:
|
|
if (bit_depth < BITS_PER_CHANNEL_8) // transforms grayscale images of less than 8 to 8 bits.
|
|
{
|
|
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
|
setBackgroundNeeded = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// adds a full alpha channel if there is transparency information in a tRNS chunk
|
|
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
|
png_set_tRNS_to_alpha(png_ptr);
|
|
|
|
// The image has 16 bits per channel for every pixel. Strip the pixels down to 8 bits.
|
|
if (bit_depth == BITS_PER_CHANNEL_16)
|
|
png_set_strip_16(png_ptr);
|
|
|
|
// PNG files pack pixels of bit depths 1,2, and 4 into bytes.
|
|
// The following code expands to 1 pixel per byte without changing the value of the pixels
|
|
if (bit_depth < BITS_PER_CHANNEL_8)
|
|
png_set_packing(png_ptr);
|
|
|
|
// convert a grayscale image to be represented as RGB
|
|
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
|
{
|
|
png_set_gray_to_rgb(png_ptr);
|
|
setBackgroundNeeded = true;
|
|
}
|
|
|
|
// set background color if needed
|
|
if (setBackgroundNeeded)
|
|
{
|
|
png_color_16 myBackground = {0, 0, 0, 0, 0};
|
|
png_color_16 * imageBackground;
|
|
|
|
if (png_get_bKGD(png_ptr, info_ptr, &imageBackground))
|
|
png_set_background(png_ptr, imageBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
|
|
else
|
|
png_set_background(png_ptr, &myBackground, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
|
|
}
|
|
|
|
|
|
// PNG files store 16 bit pixels in net work byte order (big-endian)
|
|
// The following code may be needed on PC as to change the storage to the little-endian.
|
|
|
|
// if (bit_depth == 16)
|
|
// png_set_swap(png_ptr);
|
|
|
|
// After setting the transformations, update the png_info structure to reflect any transformations you have requested
|
|
png_read_update_info(png_ptr, info_ptr);
|
|
|
|
// get color type again. It may be changed if any transformation is applied ??
|
|
color_type = png_get_color_type(png_ptr, info_ptr);
|
|
switch (color_type)
|
|
{
|
|
case PNG_COLOR_TYPE_RGB:
|
|
bytesPerPixel = IMAGE_IO_RGB;
|
|
break;
|
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
|
bytesPerPixel = IMAGE_IO_RGB_ALPHA;
|
|
break;
|
|
default:
|
|
printf("Error in loadPNG: image transformation failed.\n");
|
|
fclose(file);
|
|
exit(0); // comment this out after debugging, use return IO_ERROR
|
|
//return (IO_ERROR);
|
|
break;
|
|
}
|
|
|
|
// sanity check here
|
|
if (png_get_rowbytes(png_ptr, info_ptr) != bytesPerPixel * width)
|
|
{
|
|
printf("Error in loadPNG: the number of bytes per row, which is %lu, does not match bytesPerPixel * width, which is %d.\n", png_get_rowbytes(png_ptr, info_ptr), bytesPerPixel * width);
|
|
fclose(file);
|
|
exit(0); // comment this out after debugging, use return IO_ERROR
|
|
return (IO_ERROR);
|
|
}
|
|
|
|
unsigned int bytesPerRow = bytesPerPixel * width;
|
|
pixels = (unsigned char *) malloc (sizeof(unsigned char) * bytesPerRow * height);
|
|
|
|
png_bytep * row_pointers = (png_bytep*) malloc (sizeof(png_bytep) * height);
|
|
for(unsigned int row = 0; row < height; row++)
|
|
row_pointers[row] = (png_byte*)(&pixels[(height - row - 1) * bytesPerRow]);
|
|
|
|
png_read_image(png_ptr, row_pointers);
|
|
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)nullptr);
|
|
free(row_pointers);
|
|
|
|
fclose(file);
|
|
return OK;
|
|
|
|
#else
|
|
return INVALID_FILE_FORMAT;
|
|
#endif
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::savePNG(const char * filename)
|
|
{
|
|
#ifdef ENABLE_PNG
|
|
FILE *file = fopen(filename, "wb");
|
|
if (!file)
|
|
{
|
|
printf("Error in savePNG: Cannot open file %s.\n", filename);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
|
|
|
if (!png_ptr)
|
|
{
|
|
printf("Error in savePNG: Creating the internal structure failed.\n");
|
|
fclose(file);
|
|
return (IO_ERROR);
|
|
}
|
|
|
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
|
|
|
if (!info_ptr)
|
|
{
|
|
png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
|
|
printf("Error in savePNG: Creating the information structure failed.\n");
|
|
fclose(file);
|
|
return (IO_ERROR);
|
|
}
|
|
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
|
{
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
fclose(file);
|
|
printf("Error in savePNG: cannot setup the error handling.\n");
|
|
return (IO_ERROR);
|
|
}
|
|
|
|
// setup the output
|
|
png_init_io(png_ptr, file);
|
|
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
|
{
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
fclose(file);
|
|
printf("Error in savePNG: cannot write the png header.\n");
|
|
return (IO_ERROR);
|
|
}
|
|
|
|
int bit_depth = BITS_PER_CHANNEL_8; // currently we only support 8 bits per channel
|
|
int color_type;
|
|
|
|
switch(bytesPerPixel)
|
|
{
|
|
case IMAGE_IO_RGB:
|
|
color_type = PNG_COLOR_TYPE_RGB;
|
|
break;
|
|
case IMAGE_IO_RGB_ALPHA:
|
|
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
|
|
break;
|
|
default:
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
printf("Error in savePNG: cannot handle bytesPerPixel that is not 3 or 4.\n");
|
|
return OTHER_ERROR;
|
|
break;
|
|
}
|
|
|
|
png_set_IHDR(png_ptr, info_ptr, (png_uint_32)width, (png_uint_32)height,
|
|
bit_depth, color_type, PNG_INTERLACE_NONE,
|
|
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
|
|
|
png_write_info(png_ptr, info_ptr);
|
|
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
|
{
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
fclose(file);
|
|
printf("Error in savePNG: cannot write the png file.\n");
|
|
return (IO_ERROR);
|
|
}
|
|
unsigned int bytesPerRow = bytesPerPixel * width;
|
|
png_bytep * row_pointers = (png_bytep*) malloc (sizeof(png_bytep) * height);
|
|
for(unsigned int row = 0; row < height; row++)
|
|
row_pointers[row] = (png_byte*)(&pixels[(height - row - 1) * bytesPerRow]);
|
|
|
|
png_write_image(png_ptr, row_pointers);
|
|
|
|
free(row_pointers);
|
|
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
|
{
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
fclose(file);
|
|
printf("Error in savePNG: unknown error occurred during end of file.\n");
|
|
return (IO_ERROR);
|
|
}
|
|
|
|
png_write_end(png_ptr, nullptr);
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
fclose(file);
|
|
|
|
return OK;
|
|
|
|
#else
|
|
return INVALID_FILE_FORMAT;
|
|
#endif
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::loadNONE(const char * filename)
|
|
{
|
|
printf("Error: ImageIO load: unknown file format.\n");
|
|
return INVALID_FILE_FORMAT;
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::saveNONE(const char * filename)
|
|
{
|
|
printf("Error: ImageIO save: unknown file format.\n");
|
|
return INVALID_FILE_FORMAT;
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::load(const char * filename, fileFormatType * fileFormat)
|
|
{
|
|
// determine the file format
|
|
*fileFormat = FORMAT_NONE;
|
|
|
|
const char * extension = filename + strlen(filename) - 3;
|
|
|
|
if ((strcmp(extension, "ppm") == 0) || (strcmp(extension, "PPM") == 0))
|
|
*fileFormat = FORMAT_PPM;
|
|
|
|
if ((strcmp(extension, "tga") == 0) || (strcmp(extension, "TGA") == 0))
|
|
*fileFormat = FORMAT_TGA;
|
|
|
|
#ifdef ENABLE_JPEG
|
|
if ((strcmp(extension, "jpg") == 0) || (strcmp(extension, "JPEG") == 0))
|
|
*fileFormat = FORMAT_JPEG;
|
|
#endif
|
|
|
|
#ifdef ENABLE_TIFF
|
|
if ((strcmp(extension, "tif") == 0) || (strcmp(extension, "TIF") == 0))
|
|
*fileFormat = FORMAT_TIFF;
|
|
#endif
|
|
|
|
#ifdef ENABLE_PNG
|
|
if ((strcmp(extension, "png") == 0) || (strcmp(extension, "PNG") == 0))
|
|
*fileFormat = FORMAT_PNG;
|
|
#endif
|
|
|
|
typedef errorType (ImageIO::*loadRoutineType) (const char*);
|
|
loadRoutineType loadRoutine [] = { &ImageIO::loadPPM, &ImageIO::loadTGA, &ImageIO::loadJPEG, &ImageIO::loadTIFF, &ImageIO::loadPNG, &ImageIO::loadNONE };
|
|
errorType errorCode = (this->*loadRoutine[*fileFormat])(filename);
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ImageIO::errorType ImageIO::save(const char * filename, fileFormatType fileFormat)
|
|
{
|
|
typedef errorType (ImageIO::*saveRoutineType) (const char*);
|
|
saveRoutineType saveRoutine [] = { &ImageIO::savePPM, &ImageIO::saveTGA, &ImageIO::saveJPEG, &ImageIO::saveTIFF, &ImageIO::savePNG, &ImageIO::saveNONE };
|
|
errorType errorCode = (this->*saveRoutine[fileFormat])(filename);
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
void ImageIO::flipVertically()
|
|
{
|
|
unsigned char * rowBuffer = (unsigned char*) malloc (sizeof(unsigned char) * bytesPerPixel * width);
|
|
|
|
for(unsigned int row=0; row < height / 2; row++)
|
|
{
|
|
int otherRow = height - 1 - row;
|
|
|
|
// swap row and otherRow
|
|
|
|
unsigned char * rowPixels = &pixels[bytesPerPixel * width * row];
|
|
unsigned char * otherRowPixels = &pixels[bytesPerPixel * width * otherRow];
|
|
|
|
// copy row to rowBuffer
|
|
for(unsigned int i=0; i<bytesPerPixel * width; i++)
|
|
rowBuffer[i] = rowPixels[i];
|
|
|
|
// copy otherRow to row
|
|
for(unsigned int i=0; i<bytesPerPixel * width; i++)
|
|
rowPixels[i] = otherRowPixels[i];
|
|
|
|
// copy rowBuffer to otherRow
|
|
for(unsigned int i=0; i<bytesPerPixel * width; i++)
|
|
otherRowPixels[i] = rowBuffer[i];
|
|
}
|
|
free(rowBuffer);
|
|
}
|
|
|