/** @brief Allocates a new image of the specified type, width, height and bit depth and
 optionally fills it with the specified color.

 This function is an extension to FreeImage_AllocateT, which additionally supports specifying
 a palette to be set for the newly create image, as well as specifying a background color,
 the newly created image should initially be filled with.

 Basically, this function internally relies on function FreeImage_AllocateT, followed by a
 call to FreeImage_FillBackground. This is why both parameters color and options behave the
 same as it is documented for function FreeImage_FillBackground. So, please refer to the
 documentation of FreeImage_FillBackground to learn more about parameters color and options.

 The palette specified through parameter palette is only copied to the newly created
 image, if its image type is FIT_BITMAP and the desired bit depth is smaller than or equal
 to 8 bits per pixel. In other words, the palette parameter is only taken into account for
 palletized images. However, if the preceding conditions match and if palette is not NULL,
 the memory pointed to by the palette pointer is assumed to be at least as large as size
 of a fully populated palette for the desired bit depth. So, for an 8-bit image, this size
 is 256 x sizeof(RGBQUAD), for an 4-bit image it is 16 x sizeof(RGBQUAD) and it is
 2 x sizeof(RGBQUAD) for a 1-bit image. In other words, this function does not support
 partial palettes.

 However, specifying a palette is not necessarily needed, even for palletized images. This
 function is capable of implicitly creating a palette, if parameter palette is NULL. If the
 specified background color is a greyscale value (red = green = blue) or if option
 FI_COLOR_ALPHA_IS_INDEX is specified, a greyscale palette is created. For a 1-bit image, only
 if the specified background color is either black or white, a monochrome palette, consisting
 of black and white only is created. In any case, the darker colors are stored at the smaller
 palette indices.

 If the specified background color is not a greyscale value, or is neither black nor white
 for a 1-bit image, solely this single color is injected into the otherwise black-initialized
 palette. For this operation, option FI_COLOR_ALPHA_IS_INDEX is implicit, so the specified
 color is applied to the palette entry, specified by the background color's rgbReserved
 member. The image is then filled with this palette index.

 This function returns a newly created image as function FreeImage_AllocateT does, if both
 parameters color and palette are NULL. If only color is NULL, the palette pointed to by
 parameter palette is initially set for the new image, if a palletized image of type
 FIT_BITMAP is created. However, in the latter case, this function returns an image, whose
 pixels are all initialized with zeros so, the image will be filled with the color of the
 first palette entry.

 @param type Specifies the image type of the new image.
 @param width The desired width in pixels of the new image.
 @param height The desired height in pixels of the new image.
 @param bpp The desired bit depth of the new image.
 @param color A pointer to the color value to be used for filling the image. The
 memory pointed to by this pointer is always assumed to be at least as large as the
 image's color value but never smaller than the size of an RGBQUAD structure.
 @param options Options that affect the color search process for palletized images.
 @param red_mask Specifies the bits used to store the red components of a pixel.
 @param green_mask Specifies the bits used to store the green components of a pixel.
 @param blue_mask Specifies the bits used to store the blue components of a pixel.
 @return Returns a pointer to a newly allocated image on success, NULL otherwise.
 */
FIBITMAP * DLL_CALLCONV
FreeImage_AllocateExT(FREE_IMAGE_TYPE type, int width, int height, int bpp, const void *color, int options, const RGBQUAD *palette, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {

	FIBITMAP *bitmap = FreeImage_AllocateT(type, width, height, bpp, red_mask, green_mask, blue_mask);
	
	if (!color) {
		if ((palette) && (type == FIT_BITMAP) && (bpp <= 8)) {
			memcpy(FreeImage_GetPalette(bitmap), palette, FreeImage_GetColorsUsed(bitmap) * sizeof(RGBQUAD));
		}
		return bitmap;
	}

	if (bitmap != NULL) {
		
		// Only fill the new bitmap if the specified color
		// differs from "black", that is not all bytes of the
		// color are equal to zero.
		switch (bpp) {
			case 1: {
				// although 1-bit implies FIT_BITMAP, better get an unsigned 
				// color and palette
				unsigned *urgb = (unsigned *)color;
				unsigned *upal = (unsigned *)FreeImage_GetPalette(bitmap);
				RGBQUAD rgbq = RGBQUAD();

				if (palette != NULL) {
					// clone the specified palette
					memcpy(FreeImage_GetPalette(bitmap), palette, 2 * sizeof(RGBQUAD));
				} else if (options & FI_COLOR_ALPHA_IS_INDEX) {
					CREATE_GREYSCALE_PALETTE(upal, 2);
				} else {
					// check, whether the specified color is either black or white
					if ((*urgb & 0xFFFFFF) == 0x000000) {
						// in any case build a FIC_MINISBLACK palette
						CREATE_GREYSCALE_PALETTE(upal, 2);
						color = &rgbq;
					} else if ((*urgb & 0xFFFFFF) == 0xFFFFFF) {
						// in any case build a FIC_MINISBLACK palette
						CREATE_GREYSCALE_PALETTE(upal, 2);
						rgbq.rgbReserved = 1;
						color = &rgbq;
					} else {
						// Otherwise inject the specified color into the so far
						// black-only palette. We use color->rgbReserved as the
						// desired palette index.
						BYTE index = ((RGBQUAD *)color)->rgbReserved & 0x01;
						upal[index] = *urgb & 0x00FFFFFF;  
					}
					options |= FI_COLOR_ALPHA_IS_INDEX;
				}
				// and defer to FreeImage_FillBackground
				FreeImage_FillBackground(bitmap, color, options);
				break;
			}
			case 4: {
				// 4-bit implies FIT_BITMAP so, get a RGBQUAD color
				RGBQUAD *rgb = (RGBQUAD *)color;
				RGBQUAD *pal = FreeImage_GetPalette(bitmap);
				RGBQUAD rgbq = RGBQUAD();
				
				if (palette != NULL) {
					// clone the specified palette
					memcpy(pal, palette, 16 * sizeof(RGBQUAD));
				} else if (options & FI_COLOR_ALPHA_IS_INDEX) {
					CREATE_GREYSCALE_PALETTE(pal, 16);
				} else {
					// check, whether the specified color is a grey one
					if ((rgb->rgbRed == rgb->rgbGreen) && (rgb->rgbRed == rgb->rgbBlue)) {
						// if so, build a greyscale palette
						CREATE_GREYSCALE_PALETTE(pal, 16);
						rgbq.rgbReserved = rgb->rgbRed >> 4;
						color = &rgbq;
					} else {
						// Otherwise inject the specified color into the so far
						// black-only palette. We use color->rgbReserved as the
						// desired palette index.
						BYTE index = (rgb->rgbReserved & 0x0F);
						((unsigned *)pal)[index] = *((unsigned *)rgb) & 0x00FFFFFF;
					}
					options |= FI_COLOR_ALPHA_IS_INDEX;
				}
				// and defer to FreeImage_FillBackground
				FreeImage_FillBackground(bitmap, color, options);
				break;
			}
			case 8: {
				// 8-bit implies FIT_BITMAP so, get a RGBQUAD color
				RGBQUAD *rgb = (RGBQUAD *)color;
				RGBQUAD *pal = FreeImage_GetPalette(bitmap);
				RGBQUAD rgbq;

				if (palette != NULL) {
					// clone the specified palette
					memcpy(pal, palette, 256 * sizeof(RGBQUAD));
				} else if (options & FI_COLOR_ALPHA_IS_INDEX) {
					CREATE_GREYSCALE_PALETTE(pal, 256);
				} else {
					// check, whether the specified color is a grey one
					if ((rgb->rgbRed == rgb->rgbGreen) && (rgb->rgbRed == rgb->rgbBlue)) {
						// if so, build a greyscale palette
						CREATE_GREYSCALE_PALETTE(pal, 256);
						rgbq.rgbReserved = rgb->rgbRed;
						color = &rgbq;
					} else {
						// Otherwise inject the specified color into the so far
						// black-only palette. We use color->rgbReserved as the
						// desired palette index.
						BYTE index = rgb->rgbReserved;
						((unsigned *)pal)[index] = *((unsigned *)rgb) & 0x00FFFFFF;  
					}
					options |= FI_COLOR_ALPHA_IS_INDEX;
				}
				// and defer to FreeImage_FillBackground
				FreeImage_FillBackground(bitmap, color, options);
				break;
			}
			case 16: {
				WORD wcolor = (type == FIT_BITMAP) ?
					RGBQUAD_TO_WORD(bitmap, ((RGBQUAD *)color)) : *((WORD *)color);
				if (wcolor != 0) {
					FreeImage_FillBackground(bitmap, color, options);
				}
				break;
			}
			default: {
				int bytespp = bpp / 8;
				for (int i = 0; i < bytespp; i++) {
					if (((BYTE *)color)[i] != 0) {
						FreeImage_FillBackground(bitmap, color, options);
						break;
					}
				}
				break;
			}
		}
Exemple #2
0
/** @brief Applies color mapping for one or several colors on a 1-, 4- or 8-bit
 palletized or a 16-, 24- or 32-bit high color image.

 This function maps up to <i>count</i> colors specified in <i>srccolors</i> to
 these specified in <i>dstcolors</i>. Thereby, color <i>srccolors[N]</i>,
 if found in the image, will be replaced by color <i>dstcolors[N]</i>. If
 parameter <i>swap</i> is TRUE, additionally all colors specified in
 <i>dstcolors</i> are also mapped to these specified in <i>srccolors</i>. For
 high color images, the actual image data will be modified whereas, for
 palletized images only the palette will be changed.<br>

 The function returns the number of pixels changed or zero, if no pixels were
 changed. 

 Both arrays <i>srccolors</i> and <i>dstcolors</i> are assumed not to hold less
 than <i>count</i> colors.<br>

 For 16-bit images, all colors specified are transparently converted to their 
 proper 16-bit representation (either in RGB555 or RGB565 format, which is
 determined by the image's red- green- and blue-mask).<br>

 <b>Note, that this behaviour is different from what FreeImage_ApplyPaletteIndexMapping()
 does, which modifies the actual image data on palletized images.</b>

 @param dib Input/output image to be processed.
 @param srccolors Array of colors to be used as the mapping source.
 @param dstcolors Array of colors to be used as the mapping destination.
 @param count The number of colors to be mapped. This is the size of both
 <i>srccolors</i> and <i>dstcolors</i>.  
 @param ignore_alpha If TRUE, 32-bit images and colors are treated as 24-bit.
 @param swap If TRUE, source and destination colors are swapped, that is,
 each destination color is also mapped to the corresponding source color.  
 @return Returns the total number of pixels changed. 
 */
unsigned DLL_CALLCONV
FreeImage_ApplyColorMapping(FIBITMAP *dib, RGBQUAD *srccolors, RGBQUAD *dstcolors, unsigned count, BOOL ignore_alpha, BOOL swap) {
	unsigned result = 0;

	if ((!dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
		return 0;
	}

	// validate parameters
	if ((!srccolors) || (!dstcolors)|| (count < 1)) {
		return 0;
	}

	int bpp = FreeImage_GetBPP(dib);
	switch (bpp) {
		case 1:
		case 4:
		case 8: {
			unsigned size = FreeImage_GetColorsUsed(dib);
			RGBQUAD *pal = FreeImage_GetPalette(dib);
			RGBQUAD *a, *b;
			for (unsigned x = 0; x < size; x++) {
				for (unsigned j = 0; j < count; j++) {
					a = srccolors;
					b = dstcolors;
					for (int i = (swap ? 0 : 1); i < 2; i++) {
						if ((pal[x].rgbBlue == a[j].rgbBlue)&&(pal[x].rgbGreen == a[j].rgbGreen) &&(pal[x].rgbRed== a[j].rgbRed)) {
							pal[x].rgbBlue = b[j].rgbBlue;
							pal[x].rgbGreen = b[j].rgbGreen;
							pal[x].rgbRed = b[j].rgbRed;
							result++;
							j = count;
							break;
						}
						a = dstcolors;
						b = srccolors;
					}
				}
			}
			return result;
		}
		case 16: {
			WORD *src16 = (WORD *)malloc(sizeof(WORD) * count);
			if (NULL == src16) {
				return 0;
			}

			WORD *dst16 = (WORD *)malloc(sizeof(WORD) * count);
			if (NULL == dst16) {
				free(src16);
				return 0;
			}

			for (unsigned j = 0; j < count; j++) {
				src16[j] = RGBQUAD_TO_WORD(dib, (srccolors + j));
				dst16[j] = RGBQUAD_TO_WORD(dib, (dstcolors + j));
			}

			unsigned height = FreeImage_GetHeight(dib);
			unsigned width = FreeImage_GetWidth(dib);
			WORD *a, *b;
			for (unsigned y = 0; y < height; y++) {
				WORD *bits = (WORD *)FreeImage_GetScanLine(dib, y);
				for (unsigned x = 0; x < width; x++, bits++) {
					for (unsigned j = 0; j < count; j++) {
						a = src16;
						b = dst16;
						for (int i = (swap ? 0 : 1); i < 2; i++) {
							if (*bits == a[j]) {
								*bits = b[j];
								result++;
								j = count;
								break;
							}
							a = dst16;
							b = src16;
						}
					}
				}
			}
			free(src16);
			free(dst16);
			return result;
		}
		case 24: {
			unsigned height = FreeImage_GetHeight(dib);
			unsigned width = FreeImage_GetWidth(dib);
			RGBQUAD *a, *b;
			for (unsigned y = 0; y < height; y++) {
				BYTE *bits = FreeImage_GetScanLine(dib, y);
				for (unsigned x = 0; x < width; x++, bits += 3) {
					for (unsigned j = 0; j < count; j++) {
						a = srccolors;
						b = dstcolors;
						for (int i = (swap ? 0 : 1); i < 2; i++) {
							if ((bits[FI_RGBA_BLUE] == a[j].rgbBlue) && (bits[FI_RGBA_GREEN] == a[j].rgbGreen) &&(bits[FI_RGBA_RED] == a[j].rgbRed)) {
								bits[FI_RGBA_BLUE] = b[j].rgbBlue;
								bits[FI_RGBA_GREEN] = b[j].rgbGreen;
								bits[FI_RGBA_RED] = b[j].rgbRed;
								result++;
								j = count;
								break;
							}
							a = dstcolors;
							b = srccolors;
						}
					}
				}
			}
			return result;
		}
		case 32: {
			unsigned height = FreeImage_GetHeight(dib);
			unsigned width = FreeImage_GetWidth(dib);
			RGBQUAD *a, *b;
			for (unsigned y = 0; y < height; y++) {
				BYTE *bits = FreeImage_GetScanLine(dib, y);
				for (unsigned x = 0; x < width; x++, bits += 4) {
					for (unsigned j = 0; j < count; j++) {
						a = srccolors;
						b = dstcolors;
						for (int i = (swap ? 0 : 1); i < 2; i++) {
							if ((bits[FI_RGBA_BLUE] == a[j].rgbBlue) &&(bits[FI_RGBA_GREEN] == a[j].rgbGreen) &&(bits[FI_RGBA_RED] == a[j].rgbRed)
								&&((ignore_alpha) || (bits[FI_RGBA_ALPHA] == a[j].rgbReserved))) {
								bits[FI_RGBA_BLUE] = b[j].rgbBlue;
								bits[FI_RGBA_GREEN] = b[j].rgbGreen;
								bits[FI_RGBA_RED] = b[j].rgbRed;
								if (!ignore_alpha) {
									bits[FI_RGBA_ALPHA] = b[j].rgbReserved;
								}
								result++;
								j = count;
								break;
							}
							a = dstcolors;
							b = srccolors;
						}
					}
				}
			}
			return result;
		}
		default: {
			return 0;
		}
	}
}
/** @brief Fills a FIT_BITMAP image with the specified color.

 This function does the dirty work for FreeImage_FillBackground for FIT_BITMAP
 images.
 @param dib The image to be filled.
 @param color The color, the specified image should be filled with.
 @param options Options that affect the color search process for palletized images.
 @return Returns TRUE on success, FALSE otherwise. This function fails if any of
 the dib and color is NULL or the provided image is not a FIT_BITMAP image.
 */
static BOOL
FillBackgroundBitmap(FIBITMAP *dib, const RGBQUAD *color, int options) {

	if ((!dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
		return FALSE;;
	}
	
	if (!color) {
		return FALSE;
	}
	
	const RGBQUAD *color_intl = color;
	unsigned bpp = FreeImage_GetBPP(dib);
	unsigned width = FreeImage_GetWidth(dib);
	unsigned height = FreeImage_GetHeight(dib);
	
	FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
	
	// get a pointer to the first scanline (bottom line)
	BYTE *src_bits = FreeImage_GetScanLine(dib, 0);
	BYTE *dst_bits = src_bits;	
	
	BOOL supports_alpha = ((bpp >= 24) || ((bpp == 8) && (color_type != FIC_PALETTE)));
	
	// Check for RGBA case if bitmap supports alpha 
	// blending (8-bit greyscale, 24- or 32-bit images)
	if (supports_alpha && (options & FI_COLOR_IS_RGBA_COLOR)) {
		
		if (color->rgbReserved == 0) {
			// the fill color is fully transparent; we are done
			return TRUE;
		}
		
		// Only if the fill color is NOT fully opaque, draw it with
		// the (much) slower FreeImage_DrawLine function and return.
		// Since we do not have the FreeImage_DrawLine function in this
		// release, just assume to have an unicolor background and fill
		// all with an 'alpha-blended' color.
		if (color->rgbReserved < 255) {
							
			// If we will draw on an unicolor background, it's
			// faster to draw opaque with an alpha blended color.
			// So, first get the color from the first pixel in the
			// image (bottom-left pixel).
			RGBQUAD bgcolor;
			if (bpp == 8) {
				bgcolor = FreeImage_GetPalette(dib)[*src_bits];
			} else {	
				bgcolor.rgbBlue = src_bits[FI_RGBA_BLUE];
				bgcolor.rgbGreen = src_bits[FI_RGBA_GREEN];
				bgcolor.rgbRed = src_bits[FI_RGBA_RED];
				bgcolor.rgbReserved = 0xFF;
			}
			RGBQUAD blend;
			GetAlphaBlendedColor(&bgcolor, color_intl, &blend);
			color_intl = &blend;
		}
	}
	
	int index = (bpp <= 8) ? GetPaletteIndex(dib, color_intl, options, &color_type) : 0;
	if (index == -1) {
		// No palette index found for a palletized
		// image. This should never happen...
		return FALSE;
	}
	
	// first, build the first scanline (line 0)
	switch (bpp) {
		case 1: {
			unsigned bytes = (width / 8);
			memset(dst_bits, ((index == 1) ? 0xFF : 0x00), bytes);
			//int n = width % 8;
			int n = width & 7;
			if (n) {
				if (index == 1) {
					// set n leftmost bits
					dst_bits[bytes] |= (0xFF << (8 - n));
				} else {
					// clear n leftmost bits
					dst_bits[bytes] &= (0xFF >> n);
				}
			}
			break;
		}
		case 4: {
			unsigned bytes = (width / 2);
			memset(dst_bits, (index | (index << 4)), bytes);
			//if (bytes % 2) {
			if (bytes & 1) {
				dst_bits[bytes] &= 0x0F;
				dst_bits[bytes] |= (index << 4);
			}
			break;
		}
		case 8: {
			memset(dst_bits, index, FreeImage_GetLine(dib));
			break;
		}
		case 16: {
			WORD wcolor = RGBQUAD_TO_WORD(dib, color_intl);
			for (unsigned x = 0; x < width; x++) {
				((WORD *)dst_bits)[x] = wcolor;
			}
			break;
		}
		case 24: {
			RGBTRIPLE rgbt = *((RGBTRIPLE *)color_intl);
			for (unsigned x = 0; x < width; x++) {
				((RGBTRIPLE *)dst_bits)[x] = rgbt;
			}
			break;
		}
		case 32: {
			RGBQUAD rgbq;
			rgbq.rgbBlue = ((RGBTRIPLE *)color_intl)->rgbtBlue;
			rgbq.rgbGreen = ((RGBTRIPLE *)color_intl)->rgbtGreen;
			rgbq.rgbRed = ((RGBTRIPLE *)color_intl)->rgbtRed;
			rgbq.rgbReserved = 0xFF;
			for (unsigned x = 0; x < width; x++) {
				((RGBQUAD *)dst_bits)[x] = rgbq;
			}
			break;
		}
		default:
			return FALSE;
	}

	// Then, copy the first scanline into all following scanlines.
	// 'src_bits' is a pointer to the first scanline and is already
	// set up correctly.
	if (src_bits) {
		unsigned pitch = FreeImage_GetPitch(dib);
		unsigned bytes = FreeImage_GetLine(dib);
		dst_bits = src_bits + pitch;
		for (unsigned y = 1; y < height; y++) {
			memcpy(dst_bits, src_bits, bytes);
			dst_bits += pitch;
		}
	}
	return TRUE;
}