PageRenderTime 84ms CodeModel.GetById 2ms app.highlight 73ms RepoModel.GetById 1ms app.codeStats 0ms

/src/FreeImage/Source/FreeImage/BitmapAccess.cpp

https://bitbucket.org/cabalistic/ogredeps/
C++ | 1210 lines | 781 code | 219 blank | 210 comment | 198 complexity | e6b90437b4e27d7d138f141d3f455c09 MD5 | raw file
   1// ==========================================================
   2// FreeImage implementation
   3//
   4// Design and implementation by
   5// - Floris van den Berg (flvdberg@wxs.nl)
   6// - Hervé Drolon (drolon@infonie.fr)
   7// - Detlev Vendt (detlev.vendt@brillit.de)
   8// - Petr Supina (psup@centrum.cz)
   9// - Carsten Klein (c.klein@datagis.com)
  10// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
  11//
  12// This file is part of FreeImage 3
  13//
  14// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
  15// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
  16// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
  17// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
  18// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
  19// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
  20// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
  21// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
  22// THIS DISCLAIMER.
  23//
  24// Use at your own risk!
  25// ==========================================================
  26
  27#ifdef _MSC_VER 
  28#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
  29#endif 
  30
  31#include <stdlib.h>
  32#if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__)
  33#include <malloc.h>
  34#endif // _WIN32 || _WIN64 || __MINGW32__
  35
  36#include "FreeImage.h"
  37#include "FreeImageIO.h"
  38#include "Utilities.h"
  39
  40#include "../Metadata/FreeImageTag.h"
  41
  42// ----------------------------------------------------------
  43//  Metadata definitions
  44// ----------------------------------------------------------
  45
  46// helper for map<key, value> where value is a pointer to a FreeImage tag
  47typedef std::map<std::string, FITAG*> TAGMAP;
  48
  49// helper for map<FREE_IMAGE_MDMODEL, TAGMAP*>
  50typedef std::map<int, TAGMAP*> METADATAMAP;
  51
  52// helper for metadata iterator
  53FI_STRUCT (METADATAHEADER) { 
  54	long pos;		// current position when iterating the map
  55	TAGMAP *tagmap;	// pointer to the tag map
  56};
  57
  58// ----------------------------------------------------------
  59//  FIBITMAP definition
  60// ----------------------------------------------------------
  61
  62FI_STRUCT (FREEIMAGEHEADER) {
  63	FREE_IMAGE_TYPE type;		// data type - bitmap, array of long, double, complex, etc
  64
  65	unsigned red_mask;			// bit layout of the red components
  66	unsigned green_mask;		// bit layout of the green components
  67	unsigned blue_mask;			// bit layout of the blue components
  68
  69	RGBQUAD bkgnd_color;		// background color used for RGB transparency
  70
  71	BOOL transparent;			// why another table? for easy transparency table retrieval!
  72	int  transparency_count;	// transparency could be stored in the palette, which is better
  73	BYTE transparent_table[256];// overall, but it requires quite some changes and it will render
  74								// FreeImage_GetTransparencyTable obsolete in its current form;
  75	FIICCPROFILE iccProfile;	// space to hold ICC profile
  76
  77	METADATAMAP *metadata;		// contains a list of metadata models attached to the bitmap
  78
  79	BOOL has_pixels;			// FALSE if the FIBITMAP only contains the header and no pixel data
  80
  81	FIBITMAP *thumbnail;		// optionally contains a thumbnail attached to the bitmap
  82
  83	//BYTE filler[1];			 // fill to 32-bit alignment
  84};
  85
  86// ----------------------------------------------------------
  87//  Memory allocation on a specified alignment boundary
  88// ----------------------------------------------------------
  89
  90#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__)
  91
  92void* FreeImage_Aligned_Malloc(size_t amount, size_t alignment) {
  93	assert(alignment == FIBITMAP_ALIGNMENT);
  94	return _aligned_malloc(amount, alignment);
  95}
  96
  97void FreeImage_Aligned_Free(void* mem) {
  98	_aligned_free(mem);
  99}
 100
 101#elif defined (__MINGW32__)
 102
 103void* FreeImage_Aligned_Malloc(size_t amount, size_t alignment) {
 104	assert(alignment == FIBITMAP_ALIGNMENT);
 105	return __mingw_aligned_malloc (amount, alignment);
 106}
 107
 108void FreeImage_Aligned_Free(void* mem) {
 109	__mingw_aligned_free (mem);
 110}
 111
 112#else
 113
 114void* FreeImage_Aligned_Malloc(size_t amount, size_t alignment) {
 115	assert(alignment == FIBITMAP_ALIGNMENT);
 116	/*
 117	In some rare situations, the malloc routines can return misaligned memory. 
 118	The routine FreeImage_Aligned_Malloc allocates a bit more memory to do
 119	aligned writes.  Normally, it *should* allocate "alignment" extra memory and then writes
 120	one dword back the true pointer.  But if the memory manager returns a
 121	misaligned block that is less than a dword from the next alignment, 
 122	then the writing back one dword will corrupt memory.
 123
 124	For example, suppose that alignment is 16 and malloc returns the address 0xFFFF.
 125
 126	16 - 0xFFFF % 16 + 0xFFFF = 16 - 15 + 0xFFFF = 0x10000.
 127
 128	Now, you subtract one dword from that and write and that will corrupt memory.
 129
 130	That's why the code below allocates *two* alignments instead of one. 
 131	*/
 132	void* mem_real = malloc(amount + 2 * alignment);
 133	if(!mem_real) return NULL;
 134	char* mem_align = (char*)((unsigned long)(2 * alignment - (unsigned long)mem_real % (unsigned long)alignment) + (unsigned long)mem_real);
 135	*((long*)mem_align - 1) = (long)mem_real;
 136	return mem_align;
 137}
 138
 139void FreeImage_Aligned_Free(void* mem) {
 140	free((void*)*((long*)mem - 1));
 141}
 142
 143#endif // _WIN32 || _WIN64
 144
 145// ----------------------------------------------------------
 146//  DIB information functions
 147// ----------------------------------------------------------
 148
 149/**
 150Calculate the size of a FreeImage image. 
 151Align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary.
 152
 153@param header_only If TRUE, calculate a 'header only' FIBITMAP size, otherwise calculate a full FIBITMAP size
 154@param width
 155@param height
 156@param bpp
 157@see FreeImage_AllocateHeaderT
 158*/
 159static size_t 
 160FreeImage_GetImageSizeHeader(BOOL header_only, unsigned width, unsigned height, unsigned bpp) {
 161	size_t dib_size = sizeof(FREEIMAGEHEADER); 
 162	dib_size += (dib_size % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - dib_size % FIBITMAP_ALIGNMENT : 0);  
 163	dib_size += FIBITMAP_ALIGNMENT - sizeof(BITMAPINFOHEADER) % FIBITMAP_ALIGNMENT; 
 164	dib_size += sizeof(BITMAPINFOHEADER);  
 165	// palette is aligned on a 16 bytes boundary
 166	dib_size += sizeof(RGBQUAD) * CalculateUsedPaletteEntries(bpp);  
 167	dib_size += (dib_size % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - dib_size % FIBITMAP_ALIGNMENT : 0);  
 168	if(!header_only) {
 169		const size_t header_size = dib_size;
 170
 171		// pixels are aligned on a 16 bytes boundary
 172		dib_size += (size_t)CalculatePitch(CalculateLine(width, bpp)) * (size_t)height; 
 173
 174		// check for possible malloc overflow using a KISS integer overflow detection mechanism
 175		{
 176			/*
 177			The following constant take into account the additionnal memory used by 
 178			aligned malloc functions as well as debug malloc functions. 
 179			It is supposed here that using a (8 * FIBITMAP_ALIGNMENT) risk margin will be enough
 180			for the target compiler. 
 181			*/
 182			const double FIBITMAP_MAX_MEMORY = (double)((size_t)-1) - 8 * FIBITMAP_ALIGNMENT;
 183			const double dPitch = floor( ((double)bpp * width + 31.0) / 32.0 ) * 4.0;
 184			const double dImageSize = (double)header_size + dPitch * height;
 185			if(dImageSize != (double)dib_size) {
 186				// here, we are sure to encounter a malloc overflow: try to avoid it ...
 187				return 0;
 188			}
 189			if(dImageSize > FIBITMAP_MAX_MEMORY) {
 190				// avoid possible overflow inside C allocation functions
 191				return 0;
 192			}
 193		}
 194	}
 195
 196	return dib_size;
 197}
 198
 199FIBITMAP * DLL_CALLCONV
 200FreeImage_AllocateHeaderT(BOOL header_only, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
 201	FIBITMAP *bitmap = (FIBITMAP *)malloc(sizeof(FIBITMAP));
 202
 203	if (bitmap != NULL) {
 204		width = abs(width);
 205		height = abs(height);
 206
 207		// check pixel bit depth
 208		switch(type) {
 209			case FIT_BITMAP:
 210				switch(bpp) {
 211					case 1:
 212					case 4:
 213					case 8:
 214					case 16:
 215					case 24:
 216					case 32:
 217						break;
 218					default:
 219						bpp = 8;
 220						break;
 221				}
 222				break;
 223			case FIT_UINT16:
 224				bpp = 8 * sizeof(unsigned short);
 225				break;
 226			case FIT_INT16:
 227				bpp = 8 * sizeof(short);
 228				break;
 229			case FIT_UINT32:
 230				bpp = 8 * sizeof(DWORD);
 231				break;
 232			case FIT_INT32:
 233				bpp = 8 * sizeof(LONG);
 234				break;
 235			case FIT_FLOAT:
 236				bpp = 8 * sizeof(float);
 237				break;
 238			case FIT_DOUBLE:
 239				bpp = 8 * sizeof(double);
 240				break;
 241			case FIT_COMPLEX:
 242				bpp = 8 * sizeof(FICOMPLEX);
 243				break;
 244			case FIT_RGB16:
 245				bpp = 8 * sizeof(FIRGB16);
 246				break;
 247			case FIT_RGBA16:
 248				bpp = 8 * sizeof(FIRGBA16);
 249				break;
 250			case FIT_RGBF:
 251				bpp = 8 * sizeof(FIRGBF);
 252				break;
 253			case FIT_RGBAF:
 254				bpp = 8 * sizeof(FIRGBAF);
 255				break;
 256			default:
 257				free(bitmap);
 258				return NULL;
 259		}
 260
 261		// calculate the size of a FreeImage image
 262		// align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary
 263		// palette is aligned on a 16 bytes boundary
 264		// pixels are aligned on a 16 bytes boundary
 265
 266		size_t dib_size = FreeImage_GetImageSizeHeader(header_only, width, height, bpp); 
 267
 268		if(dib_size == 0) {
 269			// memory allocation will fail (probably a malloc overflow)
 270			free(bitmap);
 271			return NULL;
 272		}
 273
 274		bitmap->data = (BYTE *)FreeImage_Aligned_Malloc(dib_size * sizeof(BYTE), FIBITMAP_ALIGNMENT);
 275
 276		if (bitmap->data != NULL) {
 277			memset(bitmap->data, 0, dib_size);
 278
 279			// write out the FREEIMAGEHEADER
 280
 281			FREEIMAGEHEADER *fih    = (FREEIMAGEHEADER *)bitmap->data;
 282			fih->type				= type;
 283
 284			fih->red_mask           = red_mask;
 285			fih->green_mask         = green_mask;
 286			fih->blue_mask          = blue_mask;
 287
 288			memset(&fih->bkgnd_color, 0, sizeof(RGBQUAD));
 289
 290			fih->transparent        = FALSE;
 291			fih->transparency_count = 0;
 292			memset(fih->transparent_table, 0xff, 256);
 293
 294			fih->has_pixels = header_only ? FALSE : TRUE;
 295
 296			// initialize FIICCPROFILE link
 297
 298			FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(bitmap);
 299			iccProfile->size		= 0;
 300			iccProfile->data		= 0;
 301			iccProfile->flags		= 0;
 302
 303			// initialize metadata models list
 304
 305			fih->metadata = new(std::nothrow) METADATAMAP;
 306
 307			// initialize attached thumbnail
 308
 309			fih->thumbnail = NULL;
 310
 311			// write out the BITMAPINFOHEADER
 312
 313			BITMAPINFOHEADER *bih   = FreeImage_GetInfoHeader(bitmap);
 314			bih->biSize             = sizeof(BITMAPINFOHEADER);
 315			bih->biWidth            = width;
 316			bih->biHeight           = height;
 317			bih->biPlanes           = 1;
 318			bih->biCompression      = 0;
 319			bih->biBitCount         = (WORD)bpp;
 320			bih->biClrUsed          = CalculateUsedPaletteEntries(bpp);
 321			bih->biClrImportant     = bih->biClrUsed;
 322			bih->biXPelsPerMeter	= 2835;	// 72 dpi
 323			bih->biYPelsPerMeter	= 2835;	// 72 dpi
 324
 325			if(bpp == 8) {
 326				// build a default greyscale palette (very useful for image processing)
 327				RGBQUAD *pal = FreeImage_GetPalette(bitmap);
 328				for(int i = 0; i < 256; i++) {
 329					pal[i].rgbRed	= (BYTE)i;
 330					pal[i].rgbGreen = (BYTE)i;
 331					pal[i].rgbBlue	= (BYTE)i;
 332				}
 333			}
 334
 335			return bitmap;
 336		}
 337
 338		free(bitmap);
 339	}
 340
 341	return NULL;
 342}
 343
 344FIBITMAP * DLL_CALLCONV
 345FreeImage_AllocateHeader(BOOL header_only, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
 346	return FreeImage_AllocateHeaderT(header_only, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask);
 347}
 348
 349FIBITMAP * DLL_CALLCONV
 350FreeImage_Allocate(int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
 351	return FreeImage_AllocateHeaderT(FALSE, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask);
 352}
 353
 354FIBITMAP * DLL_CALLCONV
 355FreeImage_AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
 356	return FreeImage_AllocateHeaderT(FALSE, type, width, height, bpp, red_mask, green_mask, blue_mask);
 357}
 358
 359void DLL_CALLCONV
 360FreeImage_Unload(FIBITMAP *dib) {
 361	if (NULL != dib) {	
 362		if (NULL != dib->data) {
 363			// delete possible icc profile ...
 364			if (FreeImage_GetICCProfile(dib)->data)
 365				free(FreeImage_GetICCProfile(dib)->data);
 366
 367			// delete metadata models
 368			METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
 369
 370			for(METADATAMAP::iterator i = (*metadata).begin(); i != (*metadata).end(); i++) {
 371				TAGMAP *tagmap = (*i).second;
 372
 373				if(tagmap) {
 374					for(TAGMAP::iterator j = tagmap->begin(); j != tagmap->end(); j++) {
 375						FITAG *tag = (*j).second;
 376						FreeImage_DeleteTag(tag);
 377					}
 378
 379					delete tagmap;
 380				}
 381			}
 382
 383			delete metadata;
 384
 385			// delete embedded thumbnail
 386			FreeImage_Unload(FreeImage_GetThumbnail(dib));
 387
 388			// delete bitmap ...
 389			FreeImage_Aligned_Free(dib->data);
 390		}
 391		free(dib);		// ... and the wrapper
 392	}
 393}
 394
 395// ----------------------------------------------------------
 396
 397FIBITMAP * DLL_CALLCONV
 398FreeImage_Clone(FIBITMAP *dib) {
 399	if(!dib) return NULL;
 400
 401	unsigned width  = FreeImage_GetWidth(dib);
 402	unsigned height = FreeImage_GetHeight(dib);
 403	unsigned bpp    = FreeImage_GetBPP(dib);
 404	
 405	// check for pixel availability ...
 406	BOOL header_only = FreeImage_HasPixels(dib) ? FALSE : TRUE;
 407
 408	// allocate a new dib
 409	FIBITMAP *new_dib = FreeImage_AllocateHeaderT(header_only, FreeImage_GetImageType(dib), width, height, bpp, 
 410			FreeImage_GetRedMask(dib), FreeImage_GetGreenMask(dib), FreeImage_GetBlueMask(dib));
 411
 412	if (new_dib) {
 413		// save ICC profile links
 414		FIICCPROFILE *src_iccProfile = FreeImage_GetICCProfile(dib);
 415		FIICCPROFILE *dst_iccProfile = FreeImage_GetICCProfile(new_dib);
 416
 417		// save metadata links
 418		METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
 419		METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)new_dib->data)->metadata;
 420
 421		// calculate the size of a FreeImage image
 422		// align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary
 423		// palette is aligned on a 16 bytes boundary
 424		// pixels are aligned on a 16 bytes boundary
 425
 426		size_t dib_size = FreeImage_GetImageSizeHeader(header_only, width, height, bpp); 
 427
 428		// copy the bitmap + internal pointers (remember to restore new_dib internal pointers later)
 429		memcpy(new_dib->data, dib->data, dib_size);
 430
 431		// reset ICC profile link for new_dib
 432		memset(dst_iccProfile, 0, sizeof(FIICCPROFILE));
 433
 434		// restore metadata link for new_dib
 435		((FREEIMAGEHEADER *)new_dib->data)->metadata = dst_metadata;
 436
 437		// reset thumbnail link for new_dib
 438		((FREEIMAGEHEADER *)new_dib->data)->thumbnail = NULL;
 439
 440		// copy possible ICC profile
 441		FreeImage_CreateICCProfile(new_dib, src_iccProfile->data, src_iccProfile->size);
 442		dst_iccProfile->flags = src_iccProfile->flags;
 443
 444		// copy metadata models
 445		for(METADATAMAP::iterator i = (*src_metadata).begin(); i != (*src_metadata).end(); i++) {
 446			int model = (*i).first;
 447			TAGMAP *src_tagmap = (*i).second;
 448
 449			if(src_tagmap) {
 450				// create a metadata model
 451				TAGMAP *dst_tagmap = new(std::nothrow) TAGMAP();
 452
 453				if(dst_tagmap) {
 454					// fill the model
 455					for(TAGMAP::iterator j = src_tagmap->begin(); j != src_tagmap->end(); j++) {
 456						std::string dst_key = (*j).first;
 457						FITAG *dst_tag = FreeImage_CloneTag( (*j).second );
 458
 459						// assign key and tag value
 460						(*dst_tagmap)[dst_key] = dst_tag;
 461					}
 462
 463					// assign model and tagmap
 464					(*dst_metadata)[model] = dst_tagmap;
 465				}
 466			}
 467		}
 468
 469		// copy the thumbnail
 470		FreeImage_SetThumbnail(new_dib, FreeImage_GetThumbnail(dib));
 471
 472		return new_dib;
 473	}
 474
 475	return NULL;
 476}
 477
 478// ----------------------------------------------------------
 479
 480FIBITMAP* DLL_CALLCONV
 481FreeImage_GetThumbnail(FIBITMAP *dib) {
 482	return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->thumbnail : NULL;
 483}
 484
 485BOOL DLL_CALLCONV
 486FreeImage_SetThumbnail(FIBITMAP *dib, FIBITMAP *thumbnail) {
 487	if(dib == NULL) {
 488		return FALSE;
 489	}
 490	FIBITMAP *currentThumbnail = ((FREEIMAGEHEADER *)dib->data)->thumbnail;
 491	if(currentThumbnail == thumbnail) {
 492		return TRUE;
 493	}
 494	FreeImage_Unload(currentThumbnail);
 495
 496	((FREEIMAGEHEADER *)dib->data)->thumbnail = FreeImage_HasPixels(thumbnail) ? FreeImage_Clone(thumbnail) : NULL;
 497
 498	return TRUE;
 499}
 500
 501// ----------------------------------------------------------
 502
 503FREE_IMAGE_COLOR_TYPE DLL_CALLCONV
 504FreeImage_GetColorType(FIBITMAP *dib) {
 505	RGBQUAD *rgb;
 506
 507	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
 508
 509	// special bitmap type
 510	if(image_type != FIT_BITMAP) {
 511		switch(image_type) {
 512			case FIT_RGB16:
 513			case FIT_RGBF:
 514				return FIC_RGB;
 515			case FIT_RGBA16:
 516			case FIT_RGBAF:
 517				return FIC_RGBALPHA;
 518		}
 519
 520		return FIC_MINISBLACK;
 521	}
 522
 523	// standard image type
 524	switch (FreeImage_GetBPP(dib)) {
 525		case 1:
 526		{
 527			rgb = FreeImage_GetPalette(dib);
 528
 529			if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) {
 530				rgb++;
 531
 532				if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255))
 533					return FIC_MINISBLACK;				
 534			}
 535
 536			if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) {
 537				rgb++;
 538
 539				if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0))
 540					return FIC_MINISWHITE;				
 541			}
 542
 543			return FIC_PALETTE;
 544		}
 545
 546		case 4:
 547		case 8:	// Check if the DIB has a color or a greyscale palette
 548		{
 549			int ncolors = FreeImage_GetColorsUsed(dib);
 550		    int minisblack = 1;
 551			rgb = FreeImage_GetPalette(dib);
 552
 553			for (int i = 0; i < ncolors; i++) {
 554				if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue))
 555					return FIC_PALETTE;
 556
 557				// The DIB has a color palette if the greyscale isn't a linear ramp
 558				// Take care of reversed grey images
 559				if (rgb->rgbRed != i) {
 560					if ((ncolors-i-1) != rgb->rgbRed)
 561						return FIC_PALETTE;
 562				    else
 563						minisblack = 0;
 564				}
 565
 566				rgb++;
 567			}
 568
 569			return minisblack ? FIC_MINISBLACK : FIC_MINISWHITE;
 570		}
 571
 572		case 16:
 573		case 24:
 574			return FIC_RGB;
 575
 576		case 32:
 577		{
 578			if (FreeImage_GetICCProfile(dib)->flags & FIICC_COLOR_IS_CMYK)
 579				return FIC_CMYK;
 580
 581			for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
 582				rgb = (RGBQUAD *)FreeImage_GetScanLine(dib, y);
 583
 584				for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++)
 585					if (rgb[x].rgbReserved != 0xFF)
 586						return FIC_RGBALPHA;			
 587			}
 588
 589			return FIC_RGB;
 590		}
 591				
 592		default :
 593			return FIC_MINISBLACK;
 594	}
 595}
 596
 597// ----------------------------------------------------------
 598
 599FREE_IMAGE_TYPE DLL_CALLCONV 
 600FreeImage_GetImageType(FIBITMAP *dib) {
 601	return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->type : FIT_UNKNOWN;
 602}
 603
 604// ----------------------------------------------------------
 605
 606BOOL DLL_CALLCONV 
 607FreeImage_HasPixels(FIBITMAP *dib) {
 608	return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->has_pixels : FALSE;
 609}
 610
 611// ----------------------------------------------------------
 612
 613unsigned DLL_CALLCONV
 614FreeImage_GetRedMask(FIBITMAP *dib) {
 615	return dib ? ((FREEIMAGEHEADER *)dib->data)->red_mask : 0;
 616}
 617
 618unsigned DLL_CALLCONV
 619FreeImage_GetGreenMask(FIBITMAP *dib) {
 620	return dib ? ((FREEIMAGEHEADER *)dib->data)->green_mask : 0;
 621}
 622
 623unsigned DLL_CALLCONV
 624FreeImage_GetBlueMask(FIBITMAP *dib) {
 625	return dib ? ((FREEIMAGEHEADER *)dib->data)->blue_mask : 0;
 626}
 627
 628// ----------------------------------------------------------
 629
 630BOOL DLL_CALLCONV
 631FreeImage_HasBackgroundColor(FIBITMAP *dib) {
 632	if(dib) {
 633		RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color;
 634		return (bkgnd_color->rgbReserved != 0) ? TRUE : FALSE;
 635	}
 636	return FALSE;
 637}
 638
 639BOOL DLL_CALLCONV
 640FreeImage_GetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor) {
 641	if(dib && bkcolor) {
 642		if(FreeImage_HasBackgroundColor(dib)) {
 643			// get the background color
 644			RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color;
 645			memcpy(bkcolor, bkgnd_color, sizeof(RGBQUAD));
 646			// get the background index
 647			if(FreeImage_GetBPP(dib) == 8) {
 648				RGBQUAD *pal = FreeImage_GetPalette(dib);
 649				for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
 650					if(bkgnd_color->rgbRed == pal[i].rgbRed) {
 651						if(bkgnd_color->rgbGreen == pal[i].rgbGreen) {
 652							if(bkgnd_color->rgbBlue == pal[i].rgbBlue) {
 653								bkcolor->rgbReserved = (BYTE)i;
 654								return TRUE;
 655							}
 656						}
 657					}
 658				}
 659			}
 660
 661			bkcolor->rgbReserved = 0;
 662
 663			return TRUE;
 664		}
 665	}
 666
 667	return FALSE;
 668}
 669
 670BOOL DLL_CALLCONV 
 671FreeImage_SetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor) {
 672	if(dib) {
 673		RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color;
 674		if(bkcolor) {
 675			// set the background color
 676			memcpy(bkgnd_color, bkcolor, sizeof(RGBQUAD));
 677			// enable the file background color
 678			bkgnd_color->rgbReserved = 1;
 679		} else {
 680			// clear and disable the file background color
 681			memset(bkgnd_color, 0, sizeof(RGBQUAD));
 682		}
 683		return TRUE;
 684	}
 685
 686	return FALSE;
 687}
 688
 689// ----------------------------------------------------------
 690
 691BOOL DLL_CALLCONV
 692FreeImage_IsTransparent(FIBITMAP *dib) {
 693	if(dib) {
 694		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
 695		switch(image_type) {
 696			case FIT_BITMAP:
 697				if(FreeImage_GetBPP(dib) == 32) {
 698					if(FreeImage_GetColorType(dib) == FIC_RGBALPHA) {
 699						return TRUE;
 700					}
 701				} else {
 702					return ((FREEIMAGEHEADER *)dib->data)->transparent ? TRUE : FALSE;
 703				}
 704				break;
 705			case FIT_RGBA16:
 706			case FIT_RGBAF:
 707				return TRUE;
 708			default:
 709				break;
 710		}
 711	}
 712	return FALSE;
 713}
 714
 715BYTE * DLL_CALLCONV
 716FreeImage_GetTransparencyTable(FIBITMAP *dib) {
 717	return dib ? ((FREEIMAGEHEADER *)dib->data)->transparent_table : NULL;
 718}
 719
 720void DLL_CALLCONV
 721FreeImage_SetTransparent(FIBITMAP *dib, BOOL enabled) {
 722	if (dib) {
 723		if ((FreeImage_GetBPP(dib) <= 8) || (FreeImage_GetBPP(dib) == 32)) {
 724			((FREEIMAGEHEADER *)dib->data)->transparent = enabled;
 725		} else {
 726			((FREEIMAGEHEADER *)dib->data)->transparent = FALSE;
 727		}
 728	}
 729}
 730
 731unsigned DLL_CALLCONV
 732FreeImage_GetTransparencyCount(FIBITMAP *dib) {
 733	return dib ? ((FREEIMAGEHEADER *)dib->data)->transparency_count : 0;
 734}
 735
 736void DLL_CALLCONV
 737FreeImage_SetTransparencyTable(FIBITMAP *dib, BYTE *table, int count) {
 738	if (dib) {
 739		count = MIN(count, 256);
 740		if (FreeImage_GetBPP(dib) <= 8) {
 741			((FREEIMAGEHEADER *)dib->data)->transparent = TRUE;
 742			((FREEIMAGEHEADER *)dib->data)->transparency_count = count;
 743
 744			if (table) {
 745				memcpy(((FREEIMAGEHEADER *)dib->data)->transparent_table, table, count);
 746			} else {
 747				memset(((FREEIMAGEHEADER *)dib->data)->transparent_table, 0xff, count);
 748			}
 749		} 
 750	}
 751}
 752
 753/** @brief Sets the index of the palette entry to be used as transparent color
 754 for the image specified. Does nothing on high color images. 
 755 
 756 This method sets the index of the palette entry to be used as single transparent
 757 color for the image specified. This works on palletised images only and does
 758 nothing for high color images.
 759 
 760 Although it is possible for palletised images to have more than one transparent
 761 color, this method sets the palette entry specified as the single transparent
 762 color for the image. All other colors will be set to be non-transparent by this
 763 method.
 764 
 765 As with FreeImage_SetTransparencyTable(), this method also sets the image's
 766 transparency property to TRUE (as it is set and obtained by
 767 FreeImage_SetTransparent() and FreeImage_IsTransparent() respectively) for
 768 palletised images.
 769 
 770 @param dib Input image, whose transparent color is to be set.
 771 @param index The index of the palette entry to be set as transparent color.
 772 */
 773void DLL_CALLCONV
 774FreeImage_SetTransparentIndex(FIBITMAP *dib, int index) {
 775	if (dib) {
 776		int count = FreeImage_GetColorsUsed(dib);
 777		if (count) {
 778			BYTE *new_tt = (BYTE *)malloc(count * sizeof(BYTE));
 779			memset(new_tt, 0xFF, count);
 780			if ((index >= 0) && (index < count)) {
 781				new_tt[index] = 0x00;
 782			}
 783			FreeImage_SetTransparencyTable(dib, new_tt, count);
 784			free(new_tt);
 785		}
 786	}
 787}
 788
 789/** @brief Returns the palette entry used as transparent color for the image
 790 specified. Works for palletised images only and returns -1 for high color
 791 images or if the image has no color set to be transparent. 
 792 
 793 Although it is possible for palletised images to have more than one transparent
 794 color, this function always returns the index of the first palette entry, set
 795 to be transparent. 
 796 
 797 @param dib Input image, whose transparent color is to be returned.
 798 @return Returns the index of the palette entry used as transparent color for
 799 the image specified or -1 if there is no transparent color found (e.g. the image
 800 is a high color image).
 801 */
 802int DLL_CALLCONV
 803FreeImage_GetTransparentIndex(FIBITMAP *dib) {
 804	int count = FreeImage_GetTransparencyCount(dib);
 805	BYTE *tt = FreeImage_GetTransparencyTable(dib);
 806	for (int i = 0; i < count; i++) {
 807		if (tt[i] == 0) {
 808			return i;
 809		}
 810	}
 811	return -1;
 812}
 813
 814// ----------------------------------------------------------
 815
 816FIICCPROFILE * DLL_CALLCONV
 817FreeImage_GetICCProfile(FIBITMAP *dib) {
 818	FIICCPROFILE *profile = (dib) ? (FIICCPROFILE *)&((FREEIMAGEHEADER *)dib->data)->iccProfile : NULL;
 819	return profile;
 820}
 821
 822FIICCPROFILE * DLL_CALLCONV
 823FreeImage_CreateICCProfile(FIBITMAP *dib, void *data, long size) {
 824	// clear the profile but preserve profile->flags
 825	FreeImage_DestroyICCProfile(dib);
 826	// create the new profile
 827	FIICCPROFILE *profile = FreeImage_GetICCProfile(dib);
 828	if(size && profile) {
 829		profile->data = malloc(size);
 830		if(profile->data) {
 831			memcpy(profile->data, data, profile->size = size);
 832		}
 833	}
 834	return profile;
 835}
 836
 837void DLL_CALLCONV
 838FreeImage_DestroyICCProfile(FIBITMAP *dib) {
 839	FIICCPROFILE *profile = FreeImage_GetICCProfile(dib);
 840	if(profile) {
 841		if (profile->data) {
 842			free (profile->data);
 843		}
 844		// clear the profile but preserve profile->flags
 845		profile->data = NULL;
 846		profile->size = 0;
 847	}
 848}
 849
 850// ----------------------------------------------------------
 851
 852unsigned DLL_CALLCONV
 853FreeImage_GetWidth(FIBITMAP *dib) {
 854	return dib ? FreeImage_GetInfoHeader(dib)->biWidth : 0;
 855}
 856
 857unsigned DLL_CALLCONV
 858FreeImage_GetHeight(FIBITMAP *dib) {
 859	return (dib) ? FreeImage_GetInfoHeader(dib)->biHeight : 0;
 860}
 861
 862unsigned DLL_CALLCONV
 863FreeImage_GetBPP(FIBITMAP *dib) {
 864	return dib ? FreeImage_GetInfoHeader(dib)->biBitCount : 0;
 865}
 866
 867unsigned DLL_CALLCONV
 868FreeImage_GetLine(FIBITMAP *dib) {
 869	return dib ? ((FreeImage_GetWidth(dib) * FreeImage_GetBPP(dib)) + 7) / 8 : 0;
 870}
 871
 872unsigned DLL_CALLCONV
 873FreeImage_GetPitch(FIBITMAP *dib) {
 874	return dib ? FreeImage_GetLine(dib) + 3 & ~3 : 0;
 875}
 876
 877unsigned DLL_CALLCONV
 878FreeImage_GetColorsUsed(FIBITMAP *dib) {
 879	return dib ? FreeImage_GetInfoHeader(dib)->biClrUsed : 0;
 880}
 881
 882unsigned DLL_CALLCONV
 883FreeImage_GetDIBSize(FIBITMAP *dib) {
 884	return (dib) ? sizeof(BITMAPINFOHEADER) + (FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD)) + (FreeImage_GetPitch(dib) * FreeImage_GetHeight(dib)) : 0;
 885}
 886
 887RGBQUAD * DLL_CALLCONV
 888FreeImage_GetPalette(FIBITMAP *dib) {
 889	return (dib && FreeImage_GetBPP(dib) < 16) ? (RGBQUAD *)(((BYTE *)FreeImage_GetInfoHeader(dib)) + sizeof(BITMAPINFOHEADER)) : NULL;
 890}
 891
 892unsigned DLL_CALLCONV
 893FreeImage_GetDotsPerMeterX(FIBITMAP *dib) {
 894	return (dib) ? FreeImage_GetInfoHeader(dib)->biXPelsPerMeter : 0;
 895}
 896
 897unsigned DLL_CALLCONV
 898FreeImage_GetDotsPerMeterY(FIBITMAP *dib) {
 899	return (dib) ? FreeImage_GetInfoHeader(dib)->biYPelsPerMeter : 0;
 900}
 901
 902void DLL_CALLCONV
 903FreeImage_SetDotsPerMeterX(FIBITMAP *dib, unsigned res) {
 904	if(dib) {
 905		FreeImage_GetInfoHeader(dib)->biXPelsPerMeter = res;
 906	}
 907}
 908
 909void DLL_CALLCONV
 910FreeImage_SetDotsPerMeterY(FIBITMAP *dib, unsigned res) {
 911	if(dib) {
 912		FreeImage_GetInfoHeader(dib)->biYPelsPerMeter = res;
 913	}
 914}
 915
 916BITMAPINFOHEADER * DLL_CALLCONV
 917FreeImage_GetInfoHeader(FIBITMAP *dib) {
 918	if(!dib) return NULL;
 919	size_t lp = (size_t)dib->data + sizeof(FREEIMAGEHEADER);
 920	lp += (lp % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - lp % FIBITMAP_ALIGNMENT : 0);
 921	lp += FIBITMAP_ALIGNMENT - sizeof(BITMAPINFOHEADER) % FIBITMAP_ALIGNMENT;
 922	return (BITMAPINFOHEADER *)lp;
 923}
 924
 925BITMAPINFO * DLL_CALLCONV
 926FreeImage_GetInfo(FIBITMAP *dib) {
 927	return (BITMAPINFO *)FreeImage_GetInfoHeader(dib);
 928}
 929
 930// ----------------------------------------------------------
 931//  Metadata routines
 932// ----------------------------------------------------------
 933
 934FIMETADATA * DLL_CALLCONV 
 935FreeImage_FindFirstMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag) {
 936	if(!dib)
 937		return NULL;
 938
 939	// get the metadata model
 940	METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
 941	TAGMAP *tagmap = NULL;
 942	if( (*metadata).find(model) != (*metadata).end() ) {
 943		tagmap = (*metadata)[model];
 944	}
 945	if(tagmap) {
 946		// allocate a handle
 947		FIMETADATA 	*handle = (FIMETADATA *)malloc(sizeof(FIMETADATA));
 948		if(handle) {
 949			// calculate the size of a METADATAHEADER
 950			int header_size = sizeof(METADATAHEADER);
 951
 952			handle->data = (BYTE *)malloc(header_size * sizeof(BYTE));
 953			
 954			if(handle->data) {
 955				memset(handle->data, 0, header_size * sizeof(BYTE));
 956
 957				// write out the METADATAHEADER
 958				METADATAHEADER *mdh = (METADATAHEADER *)handle->data;
 959
 960				mdh->pos = 1;
 961				mdh->tagmap = tagmap;
 962
 963				// get the first element
 964				TAGMAP::iterator i = tagmap->begin();
 965				*tag = (*i).second;
 966
 967				return handle;
 968			}
 969
 970			free(handle);
 971		}
 972	}
 973
 974	return NULL;
 975}
 976
 977BOOL DLL_CALLCONV 
 978FreeImage_FindNextMetadata(FIMETADATA *mdhandle, FITAG **tag) {
 979	if(!mdhandle)
 980		return FALSE;
 981
 982	METADATAHEADER *mdh = (METADATAHEADER *)mdhandle->data;
 983	TAGMAP *tagmap = mdh->tagmap;
 984
 985	int current_pos = mdh->pos;
 986	int mapsize     = (int)tagmap->size();
 987
 988	if(current_pos < mapsize) {
 989		// get the tag element at position pos
 990		int count = 0;
 991
 992		for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) {
 993			if(count == current_pos) {
 994				*tag = (*i).second;
 995				mdh->pos++;
 996				break;
 997			}
 998			count++;
 999		}
1000		
1001		return TRUE;
1002	}
1003
1004	return FALSE;
1005}
1006
1007void DLL_CALLCONV 
1008FreeImage_FindCloseMetadata(FIMETADATA *mdhandle) {
1009	if (NULL != mdhandle) {	// delete the handle
1010		if (NULL != mdhandle->data) {
1011			free(mdhandle->data);
1012		}
1013		free(mdhandle);		// ... and the wrapper
1014	}
1015}
1016
1017
1018// ----------------------------------------------------------
1019
1020BOOL DLL_CALLCONV
1021FreeImage_CloneMetadata(FIBITMAP *dst, FIBITMAP *src) {
1022	if(!src || !dst) return FALSE;
1023
1024	// get metadata links
1025	METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)src->data)->metadata;
1026	METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)dst->data)->metadata;
1027
1028	// copy metadata models, *except* the FIMD_ANIMATION model
1029	for(METADATAMAP::iterator i = (*src_metadata).begin(); i != (*src_metadata).end(); i++) {
1030		int model = (*i).first;
1031		if(model == (int)FIMD_ANIMATION) {
1032			continue;
1033		}
1034		TAGMAP *src_tagmap = (*i).second;
1035
1036		if(src_tagmap) {
1037			if( dst_metadata->find(model) != dst_metadata->end() ) {
1038				// destroy dst model
1039				FreeImage_SetMetadata((FREE_IMAGE_MDMODEL)model, dst, NULL, NULL);
1040			}
1041
1042			// create a metadata model
1043			TAGMAP *dst_tagmap = new(std::nothrow) TAGMAP();
1044
1045			if(dst_tagmap) {
1046				// fill the model
1047				for(TAGMAP::iterator j = src_tagmap->begin(); j != src_tagmap->end(); j++) {
1048					std::string dst_key = (*j).first;
1049					FITAG *dst_tag = FreeImage_CloneTag( (*j).second );
1050
1051					// assign key and tag value
1052					(*dst_tagmap)[dst_key] = dst_tag;
1053				}
1054
1055				// assign model and tagmap
1056				(*dst_metadata)[model] = dst_tagmap;
1057			}
1058		}
1059	}
1060
1061	// clone resolution 
1062	FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(src)); 
1063	FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(src)); 
1064
1065	return TRUE;
1066}
1067
1068// ----------------------------------------------------------
1069
1070BOOL DLL_CALLCONV 
1071FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag) {
1072	if(!dib) 
1073		return FALSE;
1074
1075	TAGMAP *tagmap = NULL;
1076
1077	// get the metadata model
1078	METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
1079	METADATAMAP::iterator model_iterator = metadata->find(model);
1080	if (model_iterator != metadata->end()) {
1081		tagmap = model_iterator->second;
1082	}
1083
1084	if(key != NULL) {
1085
1086		if(!tagmap) {
1087			// this model, doesn't exist: create it 
1088			tagmap = new(std::nothrow) TAGMAP();
1089			(*metadata)[model] = tagmap;
1090		}
1091		
1092		if(tag) {
1093			// first check the tag
1094			if(FreeImage_GetTagKey(tag) == NULL) {
1095				FreeImage_SetTagKey(tag, key);
1096			} else if(strcmp(key, FreeImage_GetTagKey(tag)) != 0) {
1097				// set the tag key
1098				FreeImage_SetTagKey(tag, key);
1099			}
1100			if(FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth(FreeImage_GetTagType(tag)) != FreeImage_GetTagLength(tag)) {
1101				FreeImage_OutputMessageProc(FIF_UNKNOWN, "Invalid data count for tag '%s'", key);
1102				return FALSE;
1103			}
1104
1105			// fill the tag ID if possible and if it's needed
1106			TagLib& tag_lib = TagLib::instance();
1107			switch(model) {
1108				case FIMD_IPTC:
1109				{
1110					int id = tag_lib.getTagID(TagLib::IPTC, key);
1111					/*
1112					if(id == -1) {
1113						FreeImage_OutputMessageProc(FIF_UNKNOWN, "IPTC: Invalid key '%s'", key);
1114					}
1115					*/
1116					FreeImage_SetTagID(tag, (WORD)id);
1117				}
1118				break;
1119
1120				default:
1121					break;
1122			}
1123
1124			// delete existing tag
1125			FITAG *old_tag = (*tagmap)[key];
1126			if(old_tag) {
1127				FreeImage_DeleteTag(old_tag);
1128			}
1129
1130			// create a new tag
1131			(*tagmap)[key] = FreeImage_CloneTag(tag);
1132		}
1133		else {
1134			// delete existing tag
1135			TAGMAP::iterator i = tagmap->find(key);
1136			if(i != tagmap->end()) {
1137				FITAG *old_tag = (*i).second;
1138				FreeImage_DeleteTag(old_tag);
1139				tagmap->erase(key);
1140			}
1141		}
1142	}
1143	else {
1144		// destroy the metadata model
1145		if(tagmap) {
1146			for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) {
1147				FITAG *tag = (*i).second;
1148				FreeImage_DeleteTag(tag);
1149			}
1150
1151			delete tagmap;
1152			metadata->erase(model_iterator);
1153		}
1154	}
1155
1156	return TRUE;
1157}
1158
1159BOOL DLL_CALLCONV 
1160FreeImage_GetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag) {
1161	if(!dib || !key || !tag) 
1162		return FALSE;
1163
1164	TAGMAP *tagmap = NULL;
1165	*tag = NULL;
1166
1167	// get the metadata model
1168	METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
1169	if(!(*metadata).empty()) {
1170		METADATAMAP::iterator model_iterator = metadata->find(model);
1171		if (model_iterator != metadata->end() ) {
1172			// this model exists : try to get the requested tag
1173			tagmap = model_iterator->second;
1174			TAGMAP::iterator tag_iterator = tagmap->find(key);
1175			if (tag_iterator != tagmap->end() ) {
1176				// get the requested tag
1177				*tag = tag_iterator->second;
1178			} 
1179		}
1180	}
1181
1182	return (*tag != NULL) ? TRUE : FALSE;
1183}
1184
1185// ----------------------------------------------------------
1186
1187unsigned DLL_CALLCONV 
1188FreeImage_GetMetadataCount(FREE_IMAGE_MDMODEL model, FIBITMAP *dib) {
1189	if(!dib) 
1190		return FALSE;
1191
1192	TAGMAP *tagmap = NULL;
1193
1194	// get the metadata model
1195	METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
1196	if( (*metadata).find(model) != (*metadata).end() ) {
1197		tagmap = (*metadata)[model];
1198	}
1199	if(!tagmap) {
1200		// this model, doesn't exist: return
1201		return 0;
1202	}
1203
1204	// get the tag count
1205	return (unsigned)tagmap->size();
1206}
1207
1208// ----------------------------------------------------------
1209
1210