1 // ==========================================================
2 // Display routines
3 //
4 // Design and implementation by
5 // - Herv� Drolon (drolon@infonie.fr)
6 //
7 // This file is part of FreeImage 3
8 //
9 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
10 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
11 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
12 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
13 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
14 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
15 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
16 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
17 // THIS DISCLAIMER.
18 //
19 // Use at your own risk!
20 // ==========================================================
21 
22 #include "FreeImage.h"
23 #include "Utilities.h"
24 
25 
26 /**
27 @brief Composite a foreground image against a background color or a background image.
28 
29 The equation for computing a composited sample value is:<br>
30 output = alpha * foreground + (1-alpha) * background<br>
31 where alpha and the input and output sample values are expressed as fractions in the range 0 to 1.
32 For colour images, the computation is done separately for R, G, and B samples.
33 
34 @param fg Foreground image
35 @param useFileBkg If TRUE and a file background is present, use it as the background color
36 @param appBkColor If not equal to NULL, and useFileBkg is FALSE, use this color as the background color
37 @param bg If not equal to NULL and useFileBkg is FALSE and appBkColor is NULL, use this as the background image
38 @return Returns the composite image if successful, returns NULL otherwise
39 @see FreeImage_IsTransparent, FreeImage_HasBackgroundColor
40 */
41 FIBITMAP * DLL_CALLCONV
FreeImage_Composite(FIBITMAP * fg,BOOL useFileBkg,RGBQUAD * appBkColor,FIBITMAP * bg)42 FreeImage_Composite(FIBITMAP *fg, BOOL useFileBkg, RGBQUAD *appBkColor, FIBITMAP *bg) {
43 	if(!FreeImage_HasPixels(fg)) return NULL;
44 
45 	int width  = FreeImage_GetWidth(fg);
46 	int height = FreeImage_GetHeight(fg);
47 	int bpp    = FreeImage_GetBPP(fg);
48 
49 	if((bpp != 8) && (bpp != 32))
50 		return NULL;
51 
52 	if(bg) {
53 		int bg_width  = FreeImage_GetWidth(bg);
54 		int bg_height = FreeImage_GetHeight(bg);
55 		int bg_bpp    = FreeImage_GetBPP(bg);
56 		if((bg_width != width) || (bg_height != height) || (bg_bpp != 24))
57 			return NULL;
58 	}
59 
60 	int bytespp = (bpp == 8) ? 1 : 4;
61 
62 
63 	int x, y, c;
64 	BYTE alpha = 0, not_alpha;
65 	BYTE index;
66 	RGBQUAD fgc;	// foreground color
67 	RGBQUAD bkc;	// background color
68 
69 	memset(&fgc, 0, sizeof(RGBQUAD));
70 	memset(&bkc, 0, sizeof(RGBQUAD));
71 
72 	// allocate the composite image
73 	FIBITMAP *composite = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
74 	if(!composite) return NULL;
75 
76 	// get the palette
77 	RGBQUAD *pal = FreeImage_GetPalette(fg);
78 
79 	// retrieve the alpha table from the foreground image
80 	BOOL bIsTransparent = FreeImage_IsTransparent(fg);
81 	BYTE *trns = FreeImage_GetTransparencyTable(fg);
82 
83 	// retrieve the background color from the foreground image
84 	BOOL bHasBkColor = FALSE;
85 
86 	if(useFileBkg && FreeImage_HasBackgroundColor(fg)) {
87 		FreeImage_GetBackgroundColor(fg, &bkc);
88 		bHasBkColor = TRUE;
89 	} else {
90 		// no file background color
91 		// use application background color ?
92 		if(appBkColor) {
93 			memcpy(&bkc, appBkColor, sizeof(RGBQUAD));
94 			bHasBkColor = TRUE;
95 		}
96 		// use background image ?
97 		else if(bg) {
98 			bHasBkColor = FALSE;
99 		}
100 	}
101 
102 	for(y = 0; y < height; y++) {
103 		// foreground
104 		BYTE *fg_bits = FreeImage_GetScanLine(fg, y);
105 		// background
106 		BYTE *bg_bits = FreeImage_GetScanLine(bg, y);
107 		// composite image
108 		BYTE *cp_bits = FreeImage_GetScanLine(composite, y);
109 
110 		for(x = 0; x < width; x++) {
111 
112 			// foreground color + alpha
113 
114 			if(bpp == 8) {
115 				// get the foreground color
116 				index = fg_bits[0];
117 				memcpy(&fgc, &pal[index], sizeof(RGBQUAD));
118 				// get the alpha
119 				if(bIsTransparent) {
120 					alpha = trns[index];
121 				} else {
122 					alpha = 255;
123 				}
124 			}
125 			else if(bpp == 32) {
126 				// get the foreground color
127 				fgc.rgbBlue  = fg_bits[FI_RGBA_BLUE];
128 				fgc.rgbGreen = fg_bits[FI_RGBA_GREEN];
129 				fgc.rgbRed   = fg_bits[FI_RGBA_RED];
130 				// get the alpha
131 				alpha = fg_bits[FI_RGBA_ALPHA];
132 			}
133 
134 			// background color
135 
136 			if(!bHasBkColor) {
137 				if(bg) {
138 					// get the background color from the background image
139 					bkc.rgbBlue  = bg_bits[FI_RGBA_BLUE];
140 					bkc.rgbGreen = bg_bits[FI_RGBA_GREEN];
141 					bkc.rgbRed   = bg_bits[FI_RGBA_RED];
142 				}
143 				else {
144 					// use a checkerboard pattern
145 					c = (((y & 0x8) == 0) ^ ((x & 0x8) == 0)) * 192;
146 					c = c ? c : 255;
147 					bkc.rgbBlue  = (BYTE)c;
148 					bkc.rgbGreen = (BYTE)c;
149 					bkc.rgbRed   = (BYTE)c;
150 				}
151 			}
152 
153 			// composition
154 
155 			if(alpha == 0) {
156 				// output = background
157 				cp_bits[FI_RGBA_BLUE] = bkc.rgbBlue;
158 				cp_bits[FI_RGBA_GREEN] = bkc.rgbGreen;
159 				cp_bits[FI_RGBA_RED] = bkc.rgbRed;
160 			}
161 			else if(alpha == 255) {
162 				// output = foreground
163 				cp_bits[FI_RGBA_BLUE] = fgc.rgbBlue;
164 				cp_bits[FI_RGBA_GREEN] = fgc.rgbGreen;
165 				cp_bits[FI_RGBA_RED] = fgc.rgbRed;
166 			}
167 			else {
168 				// output = alpha * foreground + (1-alpha) * background
169 				not_alpha = (BYTE)~alpha;
170 				cp_bits[FI_RGBA_BLUE] = (BYTE)((alpha * (WORD)fgc.rgbBlue  + not_alpha * (WORD)bkc.rgbBlue) >> 8);
171 				cp_bits[FI_RGBA_GREEN] = (BYTE)((alpha * (WORD)fgc.rgbGreen + not_alpha * (WORD)bkc.rgbGreen) >> 8);
172 				cp_bits[FI_RGBA_RED] = (BYTE)((alpha * (WORD)fgc.rgbRed   + not_alpha * (WORD)bkc.rgbRed) >> 8);
173 			}
174 
175 			fg_bits += bytespp;
176 			bg_bits += 3;
177 			cp_bits += 3;
178 		}
179 	}
180 
181 	// copy metadata from src to dst
182 	FreeImage_CloneMetadata(composite, fg);
183 
184 	return composite;
185 }
186 
187 /**
188 Pre-multiplies a 32-bit image's red-, green- and blue channels with it's alpha channel
189 for to be used with e.g. the Windows GDI function AlphaBlend().
190 The transformation changes the red-, green- and blue channels according to the following equation:
191 channel(x, y) = channel(x, y) * alpha_channel(x, y) / 255
192 @param dib Input/Output dib to be premultiplied
193 @return Returns TRUE on success, FALSE otherwise (e.g. when the bitdepth of the source dib cannot be handled).
194 */
195 BOOL DLL_CALLCONV
FreeImage_PreMultiplyWithAlpha(FIBITMAP * dib)196 FreeImage_PreMultiplyWithAlpha(FIBITMAP *dib) {
197 	if (!FreeImage_HasPixels(dib)) return FALSE;
198 
199 	if ((FreeImage_GetBPP(dib) != 32) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
200 		return FALSE;
201 	}
202 
203 	int width = FreeImage_GetWidth(dib);
204 	int height = FreeImage_GetHeight(dib);
205 
206 	for(int y = 0; y < height; y++) {
207 		BYTE *bits = FreeImage_GetScanLine(dib, y);
208 		for (int x = 0; x < width; x++, bits += 4) {
209 			const BYTE alpha = bits[FI_RGBA_ALPHA];
210 			// slightly faster: care for two special cases
211 			if(alpha == 0x00) {
212 				// special case for alpha == 0x00
213 				// color * 0x00 / 0xFF = 0x00
214 				bits[FI_RGBA_BLUE] = 0x00;
215 				bits[FI_RGBA_GREEN] = 0x00;
216 				bits[FI_RGBA_RED] = 0x00;
217 			} else if(alpha == 0xFF) {
218 				// nothing to do for alpha == 0xFF
219 				// color * 0xFF / 0xFF = color
220 				continue;
221 			} else {
222 				bits[FI_RGBA_BLUE] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_BLUE] + 127) / 255 );
223 				bits[FI_RGBA_GREEN] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_GREEN] + 127) / 255 );
224 				bits[FI_RGBA_RED] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_RED] + 127) / 255 );
225 			}
226 		}
227 	}
228 	return TRUE;
229 }
230 
231