Alien-FreeImage

 view release on metacpan or  search on metacpan

src/Source/FreeImage/MNGHelper.cpp  view on Meta::CPAN

// ==========================================================
// MNG / JNG helpers
//
// Design and implementation by
// - Hervé Drolon (drolon@infonie.fr)
//
// This file is part of FreeImage 3
//
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
// THIS DISCLAIMER.
//
// Use at your own risk!
// ==========================================================

#include "FreeImage.h"
#include "Utilities.h"

/**
References
http://www.libpng.org/pub/mng/spec/jng.html
http://www.w3.org/TR/PNG/
http://libpng.org/pub/mng/spec/
*/

// --------------------------------------------------------------------------

#define MNG_INCLUDE_JNG

#ifdef MNG_INCLUDE_JNG
#define MNG_COLORTYPE_JPEGGRAY           8       /* JHDR */
#define MNG_COLORTYPE_JPEGCOLOR         10
#define MNG_COLORTYPE_JPEGGRAYA         12
#define MNG_COLORTYPE_JPEGCOLORA        14

#define MNG_BITDEPTH_JPEG8               8       /* JHDR */
#define MNG_BITDEPTH_JPEG12             12
#define MNG_BITDEPTH_JPEG8AND12         20

#define MNG_COMPRESSION_BASELINEJPEG     8       /* JHDR */

#define MNG_INTERLACE_SEQUENTIAL         0       /* JHDR */
#define MNG_INTERLACE_PROGRESSIVE        8
#endif /* MNG_INCLUDE_JNG */

// --------------------------------------------------------------------------

#define JNG_SUPPORTED

/** Size of a JDAT chunk on writing */
const DWORD JPEG_CHUNK_SIZE	= 8192;

/** PNG signature */
static const BYTE g_png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
/** JNG signature */
static const BYTE g_jng_signature[8] = { 139, 74, 78, 71, 13, 10, 26, 10 };

// --------------------------------------------------------------------------

/** Chunk type converted to enum */
enum eChunckType {
	UNKNOWN_CHUNCK,
	MHDR,
	BACK,
	BASI,
	CLIP,
	CLON,
	DEFI,
	DHDR,
	DISC,
	ENDL,
	FRAM,
	IEND,
	IHDR,
	JHDR,
	LOOP,
	MAGN,
	MEND,
	MOVE,
	PAST,
	PLTE,
	SAVE,
	SEEK,
	SHOW,
	TERM,
	bKGD,
	cHRM,
	gAMA,
	iCCP,
	nEED,
	pHYg,
	vpAg,
	pHYs,
	sBIT,
	sRGB,
	tRNS,
	IDAT,
	JDAT,
	JDAA,
	JdAA,
	JSEP,
	oFFs,
	hIST,
	iTXt,
	sPLT,
	sTER,
	tEXt,
	tIME,
	zTXt
};

/**
Helper for map<key, value> where value is a pointer to a string. 
Used to store tEXt metadata. 

src/Source/FreeImage/MNGHelper.cpp  view on Meta::CPAN


	return TRUE;
}

static FIBITMAP* 
mng_LoadFromMemoryHandle(FIMEMORY *hmem, int flags = 0) {
	long offset = 0;
	FIBITMAP *dib = NULL;

	if(hmem) {
		// seek to the start of the stream
		FreeImage_SeekMemory(hmem, offset, SEEK_SET);

		// check the file signature and deduce its format
		// (the second argument is currently not used by FreeImage)
		FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(hmem, 0);
		if(fif != FIF_UNKNOWN) {
			dib = FreeImage_LoadFromMemory(fif, hmem, flags);
		}
	}
	
	return dib;
}

/**
Write a chunk in a PNG stream from the current position. 
@param chunk_name Name of the chunk
@param chunk_data Chunk array
@param length Chunk length
@param hPngMemory PNG stream handle
*/
static void
mng_WriteChunk(BYTE *chunk_name, BYTE *chunk_data, DWORD length, FIMEMORY *hPngMemory) {
	DWORD crc_file = 0;
	// write a PNG chunk ...
	// - length
	mng_SwapLong(&length);
	FreeImage_WriteMemory(&length, 1, 4, hPngMemory);
	mng_SwapLong(&length);
	// - chunk name
	FreeImage_WriteMemory(chunk_name, 1, 4, hPngMemory);
	if(chunk_data && length) {
		// - chunk data
		FreeImage_WriteMemory(chunk_data, 1, length, hPngMemory);
		// - crc
		crc_file = FreeImage_ZLibCRC32(0, chunk_name, 4);
		crc_file = FreeImage_ZLibCRC32(crc_file, chunk_data, length);
		mng_SwapLong(&crc_file);
		FreeImage_WriteMemory(&crc_file, 1, 4, hPngMemory);
	} else {
		// - crc
		crc_file = FreeImage_ZLibCRC32(0, chunk_name, 4);
		mng_SwapLong(&crc_file);
		FreeImage_WriteMemory(&crc_file, 1, 4, hPngMemory);
	}

}

/**
Wrap a IDAT chunk as a PNG stream. 
The stream has the structure { g_png_signature, IHDR, IDAT, IEND }
The image is assumed to be a greyscale image. 

@param jng_width Image width
@param jng_height Image height
@param jng_alpha_sample_depth Bits per pixel
@param mChunk PNG grayscale IDAT format
@param mLength IDAT chunk length
@param hPngMemory Output memory stream
*/
static void 
mng_WritePNGStream(DWORD jng_width, DWORD jng_height, BYTE jng_alpha_sample_depth, BYTE *mChunk, DWORD mLength, FIMEMORY *hPngMemory) {
	// PNG grayscale IDAT format

	BYTE data[14];

	// wrap the IDAT chunk as a PNG stream

	// write PNG file signature
	FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory);

	// write a IHDR chunk ...
	/*
	The IHDR chunk must appear FIRST. It contains:
	Width:              4 bytes
	Height:             4 bytes
	Bit depth:          1 byte
	Color type:         1 byte
	Compression method: 1 byte
	Filter method:      1 byte
	Interlace method:   1 byte
	*/
	// - chunk data
	mng_SwapLong(&jng_width);
	mng_SwapLong(&jng_height);
	memcpy(&data[0], &jng_width, 4);
	memcpy(&data[4], &jng_height, 4);
	mng_SwapLong(&jng_width);
	mng_SwapLong(&jng_height);
	data[8] = jng_alpha_sample_depth;
	data[9] = 0;	// color_type gray (jng_color_type)
	data[10] = 0;	// compression method 0 (jng_alpha_compression_method)
	data[11] = 0;	// filter_method 0 (jng_alpha_filter_method)
	data[12] = 0;	// interlace_method 0 (jng_alpha_interlace_method)

	mng_WriteChunk(mng_IHDR, &data[0], 13, hPngMemory);

	// write a IDAT chunk ...
	mng_WriteChunk(mng_IDAT, mChunk, mLength, hPngMemory);

	// write a IEND chunk ...
	mng_WriteChunk(mng_IEND, NULL, 0, hPngMemory);

}

// --------------------------------------------------------------------------

/**
Build and set a FITAG whose type is FIDT_ASCII. 
The tag must be destroyed by the caller using FreeImage_DeleteTag.
@param model Metadata model to be filled
@param dib Image to be filled
@param key Tag key
@param value Tag value
@return Returns TRUE if successful, returns FALSE otherwise
*/
static BOOL 
mng_SetKeyValue(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, const char *value) {
	if(!dib || !key || !value) {
		return FALSE;
	}
	// create a tag
	FITAG *tag = FreeImage_CreateTag();
	if(tag) {
		BOOL bSuccess = TRUE;
		// fill the tag
		DWORD tag_length = (DWORD)(strlen(value) + 1);
		bSuccess &= FreeImage_SetTagKey(tag, key);
		bSuccess &= FreeImage_SetTagLength(tag, tag_length);
		bSuccess &= FreeImage_SetTagCount(tag, tag_length);

src/Source/FreeImage/MNGHelper.cpp  view on Meta::CPAN

					}
					break;

				case MEND:
					mEnd = TRUE;
					break;

				case LOOP:
				case ENDL:
					break;
				case DEFI:
					break;
				case SAVE:
				case SEEK:
				case TERM:
					break;
				case BACK:
					break;

					// Global "PLTE" and "tRNS" (if any).  PNG "PLTE" will be of 0 byte, as it uses global data.
				case PLTE:	// Global
					m_HasGlobalPalette = TRUE;
					PLTE_file_size = mLength + 12; // (lentgh, name, array, crc) = (4, 4, mLength, 4)
					PLTE_file_chunk = (BYTE*)realloc(PLTE_file_chunk, PLTE_file_size);
					if(!PLTE_file_chunk) {
						FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
						throw (const char*)NULL;
					} else {
						mOrigPos = io->tell_proc(handle);
						// seek to the start of the chunk
						io->seek_proc(handle, LastOffset, SEEK_SET);
						// load the whole chunk
						io->read_proc(PLTE_file_chunk, 1, PLTE_file_size, handle);
						// go to the start of the next chunk
						io->seek_proc(handle, mOrigPos, SEEK_SET);
					}
					break;

				case tRNS:	// Global
					break;
					
				case IHDR:
					Offset = LastOffset;
					// parse the PNG file and get its file size
					if(mng_CountPNGChunks(io, handle, Offset, &m_TotalBytesOfChunks) == FALSE) {
						// reach an unexpected end of file
						mEnd = TRUE;
						FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of PNG file", mChunkName);
						break;
					}
					
					// wrap the { IHDR, ..., IEND } chunks as a PNG stream
					if(hPngMemory == NULL) {
						hPngMemory = FreeImage_OpenMemory();
					}

					mOrigPos = io->tell_proc(handle);

					// write PNG file signature
					FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
					FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory);

					mChunk = (BYTE*)realloc(mChunk, m_TotalBytesOfChunks);
					if(!mChunk) {
						FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
						throw (const char*)NULL;
					}
					
					// on calling CountPNGChunks earlier, we were in Offset pos,
					// go back there
					io->seek_proc(handle, Offset, SEEK_SET);
					io->read_proc(mChunk, 1, m_TotalBytesOfChunks, handle);
					// Put back to original pos
					io->seek_proc(handle, mOrigPos, SEEK_SET);
					// write the PNG chunks
					FreeImage_WriteMemory(mChunk, 1, m_TotalBytesOfChunks, hPngMemory);

					// plug in global PLTE if local PLTE exists
					if(m_HasGlobalPalette) {
						// ensure we remove some local chunks, so that global
						// "PLTE" can be inserted right before "IDAT".
						mng_RemoveChunk(hPngMemory, mng_PLTE);
						mng_RemoveChunk(hPngMemory, mng_tRNS);
						mng_RemoveChunk(hPngMemory, mng_bKGD);
						// insert global "PLTE" chunk in its entirety before "IDAT"
						mng_InsertChunk(hPngMemory, mng_IDAT, PLTE_file_chunk, PLTE_file_size);
					}

					if(dib) FreeImage_Unload(dib);
					dib = mng_LoadFromMemoryHandle(hPngMemory, flags);

					// stop after the first image
					mEnd = TRUE;
					break;

				case JHDR:
					if(mLength == 16) {
						memcpy(&jng_width, &mChunk[0], 4);
						memcpy(&jng_height, &mChunk[4], 4);
						mng_SwapLong(&jng_width);
						mng_SwapLong(&jng_height);

						jng_color_type = mChunk[8];
						jng_image_sample_depth = mChunk[9];
						jng_image_compression_method = mChunk[10];
						//BYTE jng_image_interlace_method = mChunk[11];	// for debug only

						jng_alpha_sample_depth = mChunk[12];
						jng_alpha_compression_method = mChunk[13];
						jng_alpha_filter_method = mChunk[14];
						jng_alpha_interlace_method = mChunk[15];
					} else {
						FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: invalid chunk length", mChunkName);
						throw (const char*)NULL;
					}
					break;

				case JDAT:
					if(hJpegMemory == NULL) {
						hJpegMemory = FreeImage_OpenMemory();
					}

src/Source/FreeImage/MNGHelper.cpp  view on Meta::CPAN

		SwapLong(&jng_height);
		buffer[8] = jng_color_type;
		buffer[9] = jng_image_sample_depth;
		buffer[10] = jng_image_compression_method;
		buffer[11] = jng_image_interlace_method;
		buffer[12] = jng_alpha_sample_depth;
		buffer[13] = jng_alpha_compression_method;
		buffer[14] = jng_alpha_filter_method;
		buffer[15] = jng_alpha_interlace_method;
		mng_WriteChunk(mng_JHDR, &buffer[0], 16, hJngMemory);

		// --- write a sequence of JDAT chunks ---
		hJpegMemory = FreeImage_OpenMemory();
		flags |= JPEG_BASELINE;
		if(!FreeImage_SaveToMemory(FIF_JPEG, dib_rgb, hJpegMemory, flags)) {
			throw (const char*)NULL;
		}
		if(dib_rgb != dib) {
			FreeImage_Unload(dib_rgb);
			dib_rgb = NULL;
		}
		{
			BYTE *jpeg_data = NULL;
			DWORD size_in_bytes = 0;
			
			// get a pointer to the stream buffer
			FreeImage_AcquireMemory(hJpegMemory, &jpeg_data, &size_in_bytes);
			// write chunks
			for(DWORD k = 0; k < size_in_bytes;) {
				DWORD bytes_left = size_in_bytes - k;
				DWORD chunk_size = MIN(JPEG_CHUNK_SIZE, bytes_left);
				mng_WriteChunk(mng_JDAT, &jpeg_data[k], chunk_size, hJngMemory);
				k += chunk_size;
			}
		}
		FreeImage_CloseMemory(hJpegMemory);
		hJpegMemory = NULL;

		// --- write alpha layer as a sequence of IDAT chunk ---
		if((bpp == 32) && (jng_color_type == MNG_COLORTYPE_JPEGCOLORA)) {
			dib_alpha = FreeImage_GetChannel(dib, FICC_ALPHA);

			hPngMemory = FreeImage_OpenMemory();
			if(!FreeImage_SaveToMemory(FIF_PNG, dib_alpha, hPngMemory, PNG_DEFAULT)) {
				throw (const char*)NULL;
			}
			FreeImage_Unload(dib_alpha);
			dib_alpha = NULL;
			// get the IDAT chunk
			{		
				BOOL bResult = FALSE;
				DWORD start_pos = 0;
				DWORD next_pos = 0;
				long offset = 8;
				
				do {
					// find the next IDAT chunk from 'offset' position
					bResult = mng_FindChunk(hPngMemory, mng_IDAT, offset, &start_pos, &next_pos);
					if(!bResult) break;
					
					BYTE *png_data = NULL;
					DWORD size_in_bytes = 0;
					
					// get a pointer to the stream buffer
					FreeImage_AcquireMemory(hPngMemory, &png_data, &size_in_bytes);
					// write the IDAT chunk
					mng_WriteChunk(mng_IDAT, &png_data[start_pos+8], next_pos - start_pos - 12, hJngMemory);

					offset = next_pos;

				} while(bResult);
			}

			FreeImage_CloseMemory(hPngMemory);
			hPngMemory = NULL;
		}

		// --- write a IEND chunk ---
		mng_WriteChunk(mng_IEND, NULL, 0, hJngMemory);

		// write the JNG on output stream
		{
			BYTE *jng_data = NULL;
			DWORD size_in_bytes = 0;
			FreeImage_AcquireMemory(hJngMemory, &jng_data, &size_in_bytes);
			io->write_proc(jng_data, 1, size_in_bytes, handle);			
		}

		FreeImage_CloseMemory(hJngMemory);
		FreeImage_CloseMemory(hJpegMemory);
		FreeImage_CloseMemory(hPngMemory);

		return TRUE;

	} catch(const char *text) {
		FreeImage_CloseMemory(hJngMemory);
		FreeImage_CloseMemory(hJpegMemory);
		FreeImage_CloseMemory(hPngMemory);
		if(dib_rgb && (dib_rgb != dib)) {
			FreeImage_Unload(dib_rgb);
		}
		FreeImage_Unload(dib_alpha);
		if(text) {
			FreeImage_OutputMessageProc(format_id, text);
		}
		return FALSE;
	}
}



( run in 0.691 second using v1.01-cache-2.11-cpan-df04353d9ac )