// ==========================================================
// Display routines
//
// 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"
/**
@brief Composite a foreground image against a background color or a background image.
The equation for computing a composited sample value is:
output = alpha * foreground + (1-alpha) * background
where alpha and the input and output sample values are expressed as fractions in the range 0 to 1.
For colour images, the computation is done separately for R, G, and B samples.
@param fg Foreground image
@param useFileBkg If TRUE and a file background is present, use it as the background color
@param appBkColor If not equal to NULL, and useFileBkg is FALSE, use this color as the background color
@param bg If not equal to NULL and useFileBkg is FALSE and appBkColor is NULL, use this as the background image
@return Returns the composite image if successful, returns NULL otherwise
@see FreeImage_IsTransparent, FreeImage_HasBackgroundColor
*/
FIBITMAP * DLL_CALLCONV
FreeImage_Composite(FIBITMAP *fg, BOOL useFileBkg, RGBQUAD *appBkColor, FIBITMAP *bg) {
if (!FreeImage_HasPixels(fg)) return NULL;
int width = FreeImage_GetWidth(fg);
int height = FreeImage_GetHeight(fg);
int bpp = FreeImage_GetBPP(fg);
if ((bpp != 8) && (bpp != 32))
return NULL;
if(bg) {
int bg_width = FreeImage_GetWidth(bg);
int bg_height = FreeImage_GetHeight(bg);
int bg_bpp = FreeImage_GetBPP(bg);
if ((bg_width != width) || (bg_height != height) || (bg_bpp != 24))
return NULL;
}
int bytespp = (bpp == 8) ? 1 : 4;
int x, y, c;
BYTE alpha = 0, not_alpha;
BYTE index;
RGBQUAD fgc; // foreground color
RGBQUAD bkc; // background color
memset(&fgc, 0, sizeof(RGBQUAD));
memset(&bkc, 0, sizeof(RGBQUAD));
// allocate the composite image
FIBITMAP *composite = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
if (!composite) return NULL;
// get the palette
RGBQUAD *pal = FreeImage_GetPalette(fg);
// retrieve the alpha table from the foreground image
BOOL bIsTransparent = FreeImage_IsTransparent(fg);
BYTE *trns = FreeImage_GetTransparencyTable(fg);
// retrieve the background color from the foreground image
BOOL bHasBkColor = FALSE;
if(useFileBkg && FreeImage_HasBackgroundColor(fg)) {
FreeImage_GetBackgroundColor(fg, &bkc);
bHasBkColor = TRUE;
} else {
// no file background color
// use application background color ?
if(appBkColor) {
memcpy(&bkc, appBkColor, sizeof(RGBQUAD));
bHasBkColor = TRUE;
}
// use background image ?
else if(bg) {
bHasBkColor = FALSE;
}
}
for(y = 0; y < height; y++) {
// foreground
BYTE *fg_bits = FreeImage_GetScanLine(fg, y);
// background
BYTE *bg_bits = FreeImage_GetScanLine(bg, y);
// composite image
BYTE *cp_bits = FreeImage_GetScanLine(composite, y);
for(x = 0; x < width; x++) {
// foreground color + alpha
if(bpp == 8) {
// get the foreground color
index = fg_bits[0];
memcpy(&fgc, &pal[index], sizeof(RGBQUAD));
// get the alpha
if(bIsTransparent) {
alpha = trns[index];
} else {
alpha = 255;
}
}
else if(bpp == 32) {
// get the foreground color
fgc.rgbBlue = fg_bits[FI_RGBA_BLUE];
fgc.rgbGreen = fg_bits[FI_RGBA_GREEN];
fgc.rgbRed = fg_bits[FI_RGBA_RED];
// get the alpha
alpha = fg_bits[FI_RGBA_ALPHA];
}
// background color
if (!bHasBkColor) {
if(bg) {
// get the background color from the background image
bkc.rgbBlue = bg_bits[FI_RGBA_BLUE];
bkc.rgbGreen = bg_bits[FI_RGBA_GREEN];
bkc.rgbRed = bg_bits[FI_RGBA_RED];
}
else {
// use a checkerboard pattern
c = (((y & 0x8) == 0) ^ ((x & 0x8) == 0)) * 192;
c = c ? c : 255;
bkc.rgbBlue = (BYTE)c;
bkc.rgbGreen = (BYTE)c;
bkc.rgbRed = (BYTE)c;
}
}
// composition
if(alpha == 0) {
// output = background
cp_bits[FI_RGBA_BLUE] = bkc.rgbBlue;
cp_bits[FI_RGBA_GREEN] = bkc.rgbGreen;
cp_bits[FI_RGBA_RED] = bkc.rgbRed;
}
else if(alpha == 255) {
// output = foreground
cp_bits[FI_RGBA_BLUE] = fgc.rgbBlue;
cp_bits[FI_RGBA_GREEN] = fgc.rgbGreen;
cp_bits[FI_RGBA_RED] = fgc.rgbRed;
}
else {
// output = alpha * foreground + (1-alpha) * background
not_alpha = (BYTE)~alpha;
cp_bits[FI_RGBA_BLUE] = (BYTE)((alpha * (WORD)fgc.rgbBlue + not_alpha * (WORD)bkc.rgbBlue) >> 8);
cp_bits[FI_RGBA_GREEN] = (BYTE)((alpha * (WORD)fgc.rgbGreen + not_alpha * (WORD)bkc.rgbGreen) >> 8);
cp_bits[FI_RGBA_RED] = (BYTE)((alpha * (WORD)fgc.rgbRed + not_alpha * (WORD)bkc.rgbRed) >> 8);
}
fg_bits += bytespp;
bg_bits += 3;
cp_bits += 3;
}
}
// copy metadata from src to dst
FreeImage_CloneMetadata(composite, fg);
return composite;
}
/**
Pre-multiplies a 32-bit image's red-, green- and blue channels with it's alpha channel
for to be used with e.g. the Windows GDI function AlphaBlend().
The transformation changes the red-, green- and blue channels according to the following equation:
channel(x, y) = channel(x, y) * alpha_channel(x, y) / 255
@param dib Input/Output dib to be premultiplied
@return Returns TRUE on success, FALSE otherwise (e.g. when the bitdepth of the source dib cannot be handled).
*/
BOOL DLL_CALLCONV
FreeImage_PreMultiplyWithAlpha(FIBITMAP *dib) {
if (!FreeImage_HasPixels(dib)) return FALSE;
if ((FreeImage_GetBPP(dib) != 32) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
return FALSE;
}
int width = FreeImage_GetWidth(dib);
int height = FreeImage_GetHeight(dib);
for(int y = 0; y < height; y++) {
BYTE *bits = FreeImage_GetScanLine(dib, y);
for (int x = 0; x < width; x++, bits += 4) {
const BYTE alpha = bits[FI_RGBA_ALPHA];
// slightly faster: care for two special cases
if(alpha == 0x00) {
// special case for alpha == 0x00
// color * 0x00 / 0xFF = 0x00
bits[FI_RGBA_BLUE] = 0x00;
bits[FI_RGBA_GREEN] = 0x00;
bits[FI_RGBA_RED] = 0x00;
} else if(alpha == 0xFF) {
// nothing to do for alpha == 0xFF
// color * 0xFF / 0xFF = color
continue;
} else {
bits[FI_RGBA_BLUE] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_BLUE] + 127) / 255 );
bits[FI_RGBA_GREEN] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_GREEN] + 127) / 255 );
bits[FI_RGBA_RED] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_RED] + 127) / 255 );
}
}
}
return TRUE;
}