1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
// ==========================================================
// 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:<br>
output = alpha * foreground + (1-alpha) * background<br>
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(!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;
}
}
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 (!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]) / 255);
bits[FI_RGBA_GREEN] = (BYTE)((alpha * (WORD)bits[FI_RGBA_GREEN]) / 255);
bits[FI_RGBA_RED] = (BYTE)((alpha * (WORD)bits[FI_RGBA_RED]) / 255);
}
}
}
return TRUE;
}
|