Alien-FreeImage

 view release on metacpan or  search on metacpan

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

#define GIF_DISPOSAL_BACKGROUND		2
#define GIF_DISPOSAL_PREVIOUS		3

// ==========================================================
//   Constant/Typedef declarations
// ==========================================================


struct GIFinfo {
	BOOL read;
	//only really used when reading
	size_t global_color_table_offset;
	int global_color_table_size;
	BYTE background_color;
	std::vector<size_t> application_extension_offsets;
	std::vector<size_t> comment_extension_offsets;
	std::vector<size_t> graphic_control_extension_offsets;
	std::vector<size_t> image_descriptor_offsets;

	GIFinfo() : read(0), global_color_table_offset(0), global_color_table_size(0), background_color(0)
	{
	}
};

struct PageInfo {
	PageInfo(int d, int l, int t, int w, int h) { 
		disposal_method = d; left = (WORD)l; top = (WORD)t; width = (WORD)w; height = (WORD)h; 
	}
	int disposal_method;
	WORD left, top, width, height;
};

//GIF defines a max of 12 bits per code
#define MAX_LZW_CODE			4096

class StringTable
{
public:
	StringTable();
	~StringTable();
	void Initialize(int minCodeSize);
	BYTE *FillInputBuffer(int len);
	void CompressStart(int bpp, int width);
	int CompressEnd(BYTE *buf); //0-4 bytes
	bool Compress(BYTE *buf, int *len);
	bool Decompress(BYTE *buf, int *len);
	void Done(void);

protected:
	bool m_done;

	int m_minCodeSize, m_clearCode, m_endCode, m_nextCode;

	int m_bpp, m_slack; //Compressor information

	int m_prefix; //Compressor state variable
	int m_codeSize, m_codeMask; //Compressor/Decompressor state variables
	int m_oldCode; //Decompressor state variable
	int m_partial, m_partialSize; //Compressor/Decompressor bit buffer

	int firstPixelPassed; // A specific flag that indicates if the first pixel
	                      // of the whole image had already been read

	std::string m_strings[MAX_LZW_CODE]; //This is what is really the "string table" data for the Decompressor
	int* m_strmap;

	//input buffer
	BYTE *m_buffer;
	int m_bufferSize, m_bufferRealSize, m_bufferPos, m_bufferShift;

	void ClearCompressorTable(void);
	void ClearDecompressorTable(void);
};

#define GIF_PACKED_LSD_HAVEGCT		0x80
#define GIF_PACKED_LSD_COLORRES		0x70
#define GIF_PACKED_LSD_GCTSORTED	0x08
#define GIF_PACKED_LSD_GCTSIZE		0x07
#define GIF_PACKED_ID_HAVELCT		0x80
#define GIF_PACKED_ID_INTERLACED	0x40
#define GIF_PACKED_ID_LCTSORTED		0x20
#define GIF_PACKED_ID_RESERVED		0x18
#define GIF_PACKED_ID_LCTSIZE		0x07
#define GIF_PACKED_GCE_RESERVED		0xE0
#define GIF_PACKED_GCE_DISPOSAL		0x1C
#define GIF_PACKED_GCE_WAITINPUT	0x02
#define GIF_PACKED_GCE_HAVETRANS	0x01

#define GIF_BLOCK_IMAGE_DESCRIPTOR	0x2C
#define GIF_BLOCK_EXTENSION			0x21
#define GIF_BLOCK_TRAILER			0x3B

#define GIF_EXT_PLAINTEXT			0x01
#define GIF_EXT_GRAPHIC_CONTROL		0xF9
#define GIF_EXT_COMMENT				0xFE
#define GIF_EXT_APPLICATION			0xFF

#define GIF_INTERLACE_PASSES		4
static int g_GifInterlaceOffset[GIF_INTERLACE_PASSES] = {0, 4, 2, 1};
static int g_GifInterlaceIncrement[GIF_INTERLACE_PASSES] = {8, 8, 4, 2};

// ==========================================================
// Helpers Functions
// ==========================================================

static BOOL 
FreeImage_SetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, WORD id, FREE_IMAGE_MDTYPE type, DWORD count, DWORD length, const void *value)
{
	BOOL bResult = FALSE;
	FITAG *tag = FreeImage_CreateTag();
	if(tag) {
		FreeImage_SetTagKey(tag, key);
		FreeImage_SetTagID(tag, id);
		FreeImage_SetTagType(tag, type);
		FreeImage_SetTagCount(tag, count);
		FreeImage_SetTagLength(tag, length);
		FreeImage_SetTagValue(tag, value);
		if(model == FIMD_ANIMATION) {
			TagLib& s = TagLib::instance();
			// get the tag description
			const char *description = s.getTagDescription(TagLib::ANIMATION, id);

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

						info->application_extension_offsets.push_back(io->tell_proc(handle));
					}
				} else if( block == GIF_BLOCK_TRAILER ) {
					continue;
				} else {
					throw "Invalid GIF block found";
				}

				//Data Sub-blocks
				BYTE len;
				if( io->read_proc(&len, 1, 1, handle) < 1 ) {
					throw "EOF reading sub-block";
				}
				while( len != 0 ) {
					io->seek_proc(handle, len, SEEK_CUR);
					if( io->read_proc(&len, 1, 1, handle) < 1 ) {
						throw "EOF reading sub-block";
					}
				}
			}
		} catch (const char *msg) {
			FreeImage_OutputMessageProc(s_format_id, msg);
			delete info;
			return NULL;
		}
	} else {
		//Header
		io->write_proc((void *)"GIF89a", 6, 1, handle);
	}

	return info;
}

static void DLL_CALLCONV 
Close(FreeImageIO *io, fi_handle handle, void *data) {
	if( data == NULL ) {
		return;
	}
	GIFinfo *info = (GIFinfo *)data;

	if( !info->read ) {
		//Trailer
		BYTE b = GIF_BLOCK_TRAILER;
		io->write_proc(&b, 1, 1, handle);
	}

	delete info;
}

static int DLL_CALLCONV
PageCount(FreeImageIO *io, fi_handle handle, void *data) {
	if( data == NULL ) {
		return 0;
	}
	GIFinfo *info = (GIFinfo *)data;

	return (int) info->image_descriptor_offsets.size();
}

static FIBITMAP * DLL_CALLCONV 
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
	if( data == NULL ) {
		return NULL;
	}
	GIFinfo *info = (GIFinfo *)data;

	if( page == -1 ) {
		page = 0;
	}
	if( page < 0 || page >= (int)info->image_descriptor_offsets.size() ) {
		return NULL;
	}

	FIBITMAP *dib = NULL;
	try {
		bool have_transparent = false, no_local_palette = false, interlaced = false;
		int disposal_method = GIF_DISPOSAL_LEAVE, delay_time = 0, transparent_color = 0;
		WORD left, top, width, height;
		BYTE packed, b;
		WORD w;

		//playback pages to generate what the user would see for this frame
		if( (flags & GIF_PLAYBACK) == GIF_PLAYBACK ) {
			//Logical Screen Descriptor
			io->seek_proc(handle, 6, SEEK_SET);
			WORD logicalwidth, logicalheight;
			io->read_proc(&logicalwidth, 2, 1, handle);
			io->read_proc(&logicalheight, 2, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
			SwapShort(&logicalwidth);
			SwapShort(&logicalheight);
#endif
			//set the background color with 0 alpha
			RGBQUAD background;
			if( info->global_color_table_offset != 0 && info->background_color < info->global_color_table_size ) {
				io->seek_proc(handle, (long)(info->global_color_table_offset + (info->background_color * 3)), SEEK_SET);
				io->read_proc(&background.rgbRed, 1, 1, handle);
				io->read_proc(&background.rgbGreen, 1, 1, handle);
				io->read_proc(&background.rgbBlue, 1, 1, handle);
			} else {
				background.rgbRed = 0;
				background.rgbGreen = 0;
				background.rgbBlue = 0;
			}
			background.rgbReserved = 0;

			//allocate entire logical area
			dib = FreeImage_Allocate(logicalwidth, logicalheight, 32);
			if( dib == NULL ) {
				throw FI_MSG_ERROR_DIB_MEMORY;
			}

			//fill with background color to start
			int x, y;
			RGBQUAD *scanline;
			for( y = 0; y < logicalheight; y++ ) {
				scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, y);
				for( x = 0; x < logicalwidth; x++ ) {
					*scanline++ = background;
				}
			}

			//cache some info about each of the pages so we can avoid decoding as many of them as possible
			std::vector<PageInfo> pageinfo;
			int start = page, end = page;
			while( start >= 0 ) {
				//Graphic Control Extension
				io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[start] + 1), SEEK_SET);
				io->read_proc(&packed, 1, 1, handle);
				have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false;
				disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2;
				//Image Descriptor
				io->seek_proc(handle, (long)(info->image_descriptor_offsets[start]), SEEK_SET);
				io->read_proc(&left, 2, 1, handle);
				io->read_proc(&top, 2, 1, handle);
				io->read_proc(&width, 2, 1, handle);
				io->read_proc(&height, 2, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
				SwapShort(&left);
				SwapShort(&top);
				SwapShort(&width);
				SwapShort(&height);
#endif

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

						for( int i = 0; i < count; i++ ) {
							if( table[i] == 0 ) {
								have_transparent = true;
								transparent_color = i;
								break;
							}
						}
					}
					//copy page data into logical buffer, with full alpha opaqueness
					for( y = 0; y < info.height; y++ ) {
						const int scanidx = logicalheight - (y + info.top) - 1;
						if ( scanidx < 0 ) {
							break;  // If data is corrupt, don't calculate in invalid scanline
						}
						scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, scanidx) + info.left;
						BYTE *pageline = FreeImage_GetScanLine(pagedib, info.height - y - 1);
						for( x = 0; x < info.width; x++ ) {
							if( !have_transparent || *pageline != transparent_color ) {
								*scanline = pal[*pageline];
								scanline->rgbReserved = 255;
							}
							scanline++;
							pageline++;
						}
					}
					//copy frame time
					if( page == end ) {
						FITAG *tag;
						if( FreeImage_GetMetadataEx(FIMD_ANIMATION, pagedib, "FrameTime", FIDT_LONG, &tag) ) {
							delay_time = *(LONG *)FreeImage_GetTagValue(tag);
						}
					}
					FreeImage_Unload(pagedib);
				}
			}

			//setup frame time
			FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", ANIMTAG_FRAMETIME, FIDT_LONG, 1, 4, &delay_time);
			return dib;
		}

		//get the actual frame image data for a single frame

		//Image Descriptor
		io->seek_proc(handle, (long)info->image_descriptor_offsets[page], SEEK_SET);
		io->read_proc(&left, 2, 1, handle);
		io->read_proc(&top, 2, 1, handle);
		io->read_proc(&width, 2, 1, handle);
		io->read_proc(&height, 2, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
		SwapShort(&left);
		SwapShort(&top);
		SwapShort(&width);
		SwapShort(&height);
#endif
		io->read_proc(&packed, 1, 1, handle);
		interlaced = (packed & GIF_PACKED_ID_INTERLACED) ? true : false;
		no_local_palette = (packed & GIF_PACKED_ID_HAVELCT) ? false : true;

		int bpp = 8;
		if( (flags & GIF_LOAD256) == 0 ) {
			if( !no_local_palette ) {
				int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE);
				if( size <= 2 ) bpp = 1;
				else if( size <= 16 ) bpp = 4;
			} else if( info->global_color_table_offset != 0 ) {
				if( info->global_color_table_size <= 2 ) bpp = 1;
				else if( info->global_color_table_size <= 16 ) bpp = 4;
			}
		}
		dib = FreeImage_Allocate(width, height, bpp);
		if( dib == NULL ) {
			throw FI_MSG_ERROR_DIB_MEMORY;
		}

		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", ANIMTAG_FRAMELEFT, FIDT_SHORT, 1, 2, &left);
		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", ANIMTAG_FRAMETOP, FIDT_SHORT, 1, 2, &top);
		b = no_local_palette ? 1 : 0;
		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", ANIMTAG_NOLOCALPALETTE, FIDT_BYTE, 1, 1, &b);
		b = interlaced ? 1 : 0;
		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", ANIMTAG_INTERLACED, FIDT_BYTE, 1, 1, &b);

		//Palette
		RGBQUAD *pal = FreeImage_GetPalette(dib);
		if( !no_local_palette ) {
			int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE);

			int i = 0;
			while( i < size ) {
				io->read_proc(&pal[i].rgbRed, 1, 1, handle);
				io->read_proc(&pal[i].rgbGreen, 1, 1, handle);
				io->read_proc(&pal[i].rgbBlue, 1, 1, handle);
				i++;
			}
		} else if( info->global_color_table_offset != 0 ) {
			long pos = io->tell_proc(handle);
			io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET);

			int i = 0;
			while( i < info->global_color_table_size ) {
				io->read_proc(&pal[i].rgbRed, 1, 1, handle);
				io->read_proc(&pal[i].rgbGreen, 1, 1, handle);
				io->read_proc(&pal[i].rgbBlue, 1, 1, handle);
				i++;
			}

			io->seek_proc(handle, pos, SEEK_SET);
		} else {
			//its legal to have no palette, but we're going to generate *something*
			for( int i = 0; i < 256; i++ ) {
				pal[i].rgbRed   = (BYTE)i;
				pal[i].rgbGreen = (BYTE)i;
				pal[i].rgbBlue  = (BYTE)i;
			}
		}

		//LZW Minimum Code Size
		io->read_proc(&b, 1, 1, handle);
		StringTable *stringtable = new(std::nothrow) StringTable;
		stringtable->Initialize(b);

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


			//Comment Extension
			for( idx = 0; idx < info->comment_extension_offsets.size(); idx++ ) {
				io->seek_proc(handle, (long)info->comment_extension_offsets[idx], SEEK_SET);
				std::string comment;
				char buf[255];
				io->read_proc(&b, 1, 1, handle);
				while( b ) {
					io->read_proc(buf, b, 1, handle);
					comment.append(buf, b);
					io->read_proc(&b, 1, 1, handle);
				}
				comment.append(1, '\0');
				sprintf(buf, "Comment%d", idx);
				DWORD comment_size = (DWORD)comment.size();
				FreeImage_SetMetadataEx(FIMD_COMMENTS, dib, buf, 1, FIDT_ASCII, comment_size, comment_size, comment.c_str());
			}
		}

		//Graphic Control Extension
		if( info->graphic_control_extension_offsets[page] != 0 ) {
			io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[page] + 1), SEEK_SET);
			io->read_proc(&packed, 1, 1, handle);
			io->read_proc(&w, 2, 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
			SwapShort(&w);
#endif
			io->read_proc(&b, 1, 1, handle);
			have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false;
			disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2;
			delay_time = w * 10; //convert cs to ms
			transparent_color = b;
			if( have_transparent ) {
				int size = 1 << bpp;
				if( transparent_color <= size ) {
					BYTE table[256];
					memset(table, 0xFF, size);
					table[transparent_color] = 0;
					FreeImage_SetTransparencyTable(dib, table, size);
				}
			}
		}
		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", ANIMTAG_FRAMETIME, FIDT_LONG, 1, 4, &delay_time);
		b = (BYTE)disposal_method;
		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "DisposalMethod", ANIMTAG_DISPOSALMETHOD, FIDT_BYTE, 1, 1, &b);

		delete stringtable;

	} catch (const char *msg) {
		if( dib != NULL ) {
			FreeImage_Unload(dib);
		}
		FreeImage_OutputMessageProc(s_format_id, msg);
		return NULL;
	}

	return dib;
}

static BOOL DLL_CALLCONV 
Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
	if( data == NULL ) {
		return FALSE;
	}
	//GIFinfo *info = (GIFinfo *)data;

	if( page == -1 ) {
		page = 0;
	}

	try {
		BYTE packed, b;
		WORD w;
		FITAG *tag;

		int bpp = FreeImage_GetBPP(dib);
		if( bpp != 1 && bpp != 4 && bpp != 8 ) {
			throw "Only 1, 4, or 8 bpp images supported";
		}

		bool have_transparent = false, no_local_palette = false, interlaced = false;
		int disposal_method = GIF_DISPOSAL_BACKGROUND, delay_time = 100, transparent_color = 0;
		WORD left = 0, top = 0, width = (WORD)FreeImage_GetWidth(dib), height = (WORD)FreeImage_GetHeight(dib);
		WORD output_height = height;
		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", FIDT_SHORT, &tag) ) {
			left = *(WORD *)FreeImage_GetTagValue(tag);
		}
		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", FIDT_SHORT, &tag) ) {
			top = *(WORD *)FreeImage_GetTagValue(tag);
		}
		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", FIDT_BYTE, &tag) ) {
			no_local_palette = *(BYTE *)FreeImage_GetTagValue(tag) ? true : false;
		}
		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", FIDT_BYTE, &tag) ) {
			interlaced = *(BYTE *)FreeImage_GetTagValue(tag) ? true : false;
		}
		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", FIDT_LONG, &tag) ) {
			delay_time = *(LONG *)FreeImage_GetTagValue(tag);
		}
		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "DisposalMethod", FIDT_BYTE, &tag) ) {
			disposal_method = *(BYTE *)FreeImage_GetTagValue(tag);
		}

		RGBQUAD *pal = FreeImage_GetPalette(dib);
#ifdef FREEIMAGE_BIGENDIAN
		SwapShort(&left);
		SwapShort(&top);
		SwapShort(&width);
		SwapShort(&height);
#endif

		if( page == 0 ) {
			//gather some info
			WORD logicalwidth = width; // width has already been swapped...
			if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "LogicalWidth", FIDT_SHORT, &tag) ) {
				logicalwidth = *(WORD *)FreeImage_GetTagValue(tag);
#ifdef FREEIMAGE_BIGENDIAN
				SwapShort(&logicalwidth);
#endif
			}
			WORD logicalheight = height; // height has already been swapped...



( run in 1.266 second using v1.01-cache-2.11-cpan-63c85eba8c4 )