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 )