1 // ==========================================================
2 // Channel processing support
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 /** @brief Retrieves the red, green, blue or alpha channel of a BGR[A] image.
27 @param src Input image to be processed.
28 @param channel Color channel to extract
29 @return Returns the extracted channel if successful, returns NULL otherwise.
30 */
31 FIBITMAP * DLL_CALLCONV
FreeImage_GetChannel(FIBITMAP * src,FREE_IMAGE_COLOR_CHANNEL channel)32 FreeImage_GetChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
33 
34 	if(!FreeImage_HasPixels(src)) return NULL;
35 
36 	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
37 	unsigned bpp = FreeImage_GetBPP(src);
38 
39 	// 24- or 32-bit
40 	if(image_type == FIT_BITMAP && ((bpp == 24) || (bpp == 32))) {
41 		int c;
42 
43 		// select the channel to extract
44 		switch(channel) {
45 			case FICC_BLUE:
46 				c = FI_RGBA_BLUE;
47 				break;
48 			case FICC_GREEN:
49 				c = FI_RGBA_GREEN;
50 				break;
51 			case FICC_RED:
52 				c = FI_RGBA_RED;
53 				break;
54 			case FICC_ALPHA:
55 				if(bpp != 32) return NULL;
56 				c = FI_RGBA_ALPHA;
57 				break;
58 			default:
59 				return NULL;
60 		}
61 
62 		// allocate a 8-bit dib
63 		unsigned width  = FreeImage_GetWidth(src);
64 		unsigned height = FreeImage_GetHeight(src);
65 		FIBITMAP *dst = FreeImage_Allocate(width, height, 8) ;
66 		if(!dst) return NULL;
67 		// build a greyscale palette
68 		RGBQUAD *pal = FreeImage_GetPalette(dst);
69 		for(int i = 0; i < 256; i++) {
70 			pal[i].rgbBlue = pal[i].rgbGreen = pal[i].rgbRed = (BYTE)i;
71 		}
72 
73 		// perform extraction
74 
75 		int bytespp = bpp / 8;	// bytes / pixel
76 
77 		for(unsigned y = 0; y < height; y++) {
78 			BYTE *src_bits = FreeImage_GetScanLine(src, y);
79 			BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
80 			for(unsigned x = 0; x < width; x++) {
81 				dst_bits[x] = src_bits[c];
82 				src_bits += bytespp;
83 			}
84 		}
85 
86 		// copy metadata from src to dst
87 		FreeImage_CloneMetadata(dst, src);
88 
89 		return dst;
90 	}
91 
92 	// 48-bit RGB or 64-bit RGBA images
93 	if((image_type == FIT_RGB16) ||  (image_type == FIT_RGBA16)) {
94 		int c;
95 
96 		// select the channel to extract (always RGB[A])
97 		switch(channel) {
98 			case FICC_BLUE:
99 				c = 2;
100 				break;
101 			case FICC_GREEN:
102 				c = 1;
103 				break;
104 			case FICC_RED:
105 				c = 0;
106 				break;
107 			case FICC_ALPHA:
108 				if(bpp != 64) return NULL;
109 				c = 3;
110 				break;
111 			default:
112 				return NULL;
113 		}
114 
115 		// allocate a greyscale dib
116 		unsigned width  = FreeImage_GetWidth(src);
117 		unsigned height = FreeImage_GetHeight(src);
118 		FIBITMAP *dst = FreeImage_AllocateT(FIT_UINT16, width, height) ;
119 		if(!dst) return NULL;
120 
121 		// perform extraction
122 
123 		int bytespp = bpp / 16;	// words / pixel
124 
125 		for(unsigned y = 0; y < height; y++) {
126 			unsigned short *src_bits = (unsigned short*)FreeImage_GetScanLine(src, y);
127 			unsigned short *dst_bits = (unsigned short*)FreeImage_GetScanLine(dst, y);
128 			for(unsigned x = 0; x < width; x++) {
129 				dst_bits[x] = src_bits[c];
130 				src_bits += bytespp;
131 			}
132 		}
133 
134 		// copy metadata from src to dst
135 		FreeImage_CloneMetadata(dst, src);
136 
137 		return dst;
138 	}
139 
140 	// 96-bit RGBF or 128-bit RGBAF images
141 	if((image_type == FIT_RGBF) ||  (image_type == FIT_RGBAF)) {
142 		int c;
143 
144 		// select the channel to extract (always RGB[A])
145 		switch(channel) {
146 			case FICC_BLUE:
147 				c = 2;
148 				break;
149 			case FICC_GREEN:
150 				c = 1;
151 				break;
152 			case FICC_RED:
153 				c = 0;
154 				break;
155 			case FICC_ALPHA:
156 				if(bpp != 128) return NULL;
157 				c = 3;
158 				break;
159 			default:
160 				return NULL;
161 		}
162 
163 		// allocate a greyscale dib
164 		unsigned width  = FreeImage_GetWidth(src);
165 		unsigned height = FreeImage_GetHeight(src);
166 		FIBITMAP *dst = FreeImage_AllocateT(FIT_FLOAT, width, height) ;
167 		if(!dst) return NULL;
168 
169 		// perform extraction
170 
171 		int bytespp = bpp / 32;	// floats / pixel
172 
173 		for(unsigned y = 0; y < height; y++) {
174 			float *src_bits = (float*)FreeImage_GetScanLine(src, y);
175 			float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
176 			for(unsigned x = 0; x < width; x++) {
177 				dst_bits[x] = src_bits[c];
178 				src_bits += bytespp;
179 			}
180 		}
181 
182 		// copy metadata from src to dst
183 		FreeImage_CloneMetadata(dst, src);
184 
185 		return dst;
186 	}
187 
188 	return NULL;
189 }
190 
191 /** @brief Insert a greyscale dib into a RGB[A] image.
192 Both src and dst must have the same width and height.
193 @param dst Image to modify (RGB or RGBA)
194 @param src Input greyscale image to insert
195 @param channel Color channel to modify
196 @return Returns TRUE if successful, FALSE otherwise.
197 */
198 BOOL DLL_CALLCONV
FreeImage_SetChannel(FIBITMAP * dst,FIBITMAP * src,FREE_IMAGE_COLOR_CHANNEL channel)199 FreeImage_SetChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
200 	int c;
201 
202 	if(!FreeImage_HasPixels(src) || !FreeImage_HasPixels(dst)) return FALSE;
203 
204 	// src and dst images should have the same width and height
205 	unsigned src_width  = FreeImage_GetWidth(src);
206 	unsigned src_height = FreeImage_GetHeight(src);
207 	unsigned dst_width  = FreeImage_GetWidth(dst);
208 	unsigned dst_height = FreeImage_GetHeight(dst);
209 	if((src_width != dst_width) || (src_height != dst_height))
210 		return FALSE;
211 
212 	// src image should be grayscale, dst image should be RGB or RGBA
213 	FREE_IMAGE_COLOR_TYPE src_type = FreeImage_GetColorType(src);
214 	FREE_IMAGE_COLOR_TYPE dst_type = FreeImage_GetColorType(dst);
215 	if((dst_type != FIC_RGB) && (dst_type != FIC_RGBALPHA) || (src_type != FIC_MINISBLACK)) {
216 		return FALSE;
217 	}
218 
219 	FREE_IMAGE_TYPE src_image_type = FreeImage_GetImageType(src);
220 	FREE_IMAGE_TYPE dst_image_type = FreeImage_GetImageType(dst);
221 
222 	if((dst_image_type == FIT_BITMAP) && (src_image_type == FIT_BITMAP)) {
223 
224 		// src image should be grayscale, dst image should be 24- or 32-bit
225 		unsigned src_bpp = FreeImage_GetBPP(src);
226 		unsigned dst_bpp = FreeImage_GetBPP(dst);
227 		if((src_bpp != 8) || (dst_bpp != 24) && (dst_bpp != 32))
228 			return FALSE;
229 
230 
231 		// select the channel to modify
232 		switch(channel) {
233 			case FICC_BLUE:
234 				c = FI_RGBA_BLUE;
235 				break;
236 			case FICC_GREEN:
237 				c = FI_RGBA_GREEN;
238 				break;
239 			case FICC_RED:
240 				c = FI_RGBA_RED;
241 				break;
242 			case FICC_ALPHA:
243 				if(dst_bpp != 32) return FALSE;
244 				c = FI_RGBA_ALPHA;
245 				break;
246 			default:
247 				return FALSE;
248 		}
249 
250 		// perform insertion
251 
252 		int bytespp = dst_bpp / 8;	// bytes / pixel
253 
254 		for(unsigned y = 0; y < dst_height; y++) {
255 			BYTE *src_bits = FreeImage_GetScanLine(src, y);
256 			BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
257 			for(unsigned x = 0; x < dst_width; x++) {
258 				dst_bits[c] = src_bits[x];
259 				dst_bits += bytespp;
260 			}
261 		}
262 
263 		return TRUE;
264 	}
265 
266 	if(((dst_image_type == FIT_RGB16) || (dst_image_type == FIT_RGBA16)) && (src_image_type == FIT_UINT16)) {
267 
268 		// src image should be grayscale, dst image should be 48- or 64-bit
269 		unsigned src_bpp = FreeImage_GetBPP(src);
270 		unsigned dst_bpp = FreeImage_GetBPP(dst);
271 		if((src_bpp != 16) || (dst_bpp != 48) && (dst_bpp != 64))
272 			return FALSE;
273 
274 
275 		// select the channel to modify (always RGB[A])
276 		switch(channel) {
277 			case FICC_BLUE:
278 				c = 2;
279 				break;
280 			case FICC_GREEN:
281 				c = 1;
282 				break;
283 			case FICC_RED:
284 				c = 0;
285 				break;
286 			case FICC_ALPHA:
287 				if(dst_bpp != 64) return FALSE;
288 				c = 3;
289 				break;
290 			default:
291 				return FALSE;
292 		}
293 
294 		// perform insertion
295 
296 		int bytespp = dst_bpp / 16;	// words / pixel
297 
298 		for(unsigned y = 0; y < dst_height; y++) {
299 			unsigned short *src_bits = (unsigned short*)FreeImage_GetScanLine(src, y);
300 			unsigned short *dst_bits = (unsigned short*)FreeImage_GetScanLine(dst, y);
301 			for(unsigned x = 0; x < dst_width; x++) {
302 				dst_bits[c] = src_bits[x];
303 				dst_bits += bytespp;
304 			}
305 		}
306 
307 		return TRUE;
308 	}
309 
310 	if(((dst_image_type == FIT_RGBF) || (dst_image_type == FIT_RGBAF)) && (src_image_type == FIT_FLOAT)) {
311 
312 		// src image should be grayscale, dst image should be 96- or 128-bit
313 		unsigned src_bpp = FreeImage_GetBPP(src);
314 		unsigned dst_bpp = FreeImage_GetBPP(dst);
315 		if((src_bpp != 32) || (dst_bpp != 96) && (dst_bpp != 128))
316 			return FALSE;
317 
318 
319 		// select the channel to modify (always RGB[A])
320 		switch(channel) {
321 			case FICC_BLUE:
322 				c = 2;
323 				break;
324 			case FICC_GREEN:
325 				c = 1;
326 				break;
327 			case FICC_RED:
328 				c = 0;
329 				break;
330 			case FICC_ALPHA:
331 				if(dst_bpp != 128) return FALSE;
332 				c = 3;
333 				break;
334 			default:
335 				return FALSE;
336 		}
337 
338 		// perform insertion
339 
340 		int bytespp = dst_bpp / 32;	// floats / pixel
341 
342 		for(unsigned y = 0; y < dst_height; y++) {
343 			float *src_bits = (float*)FreeImage_GetScanLine(src, y);
344 			float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
345 			for(unsigned x = 0; x < dst_width; x++) {
346 				dst_bits[c] = src_bits[x];
347 				dst_bits += bytespp;
348 			}
349 		}
350 
351 		return TRUE;
352 	}
353 
354 	return FALSE;
355 }
356 
357 /** @brief Retrieves the real part, imaginary part, magnitude or phase of a complex image.
358 @param src Input image to be processed.
359 @param channel Channel to extract
360 @return Returns the extracted channel if successful, returns NULL otherwise.
361 */
362 FIBITMAP * DLL_CALLCONV
FreeImage_GetComplexChannel(FIBITMAP * src,FREE_IMAGE_COLOR_CHANNEL channel)363 FreeImage_GetComplexChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
364 	unsigned x, y;
365 	double mag, phase;
366 	FICOMPLEX *src_bits = NULL;
367 	double *dst_bits = NULL;
368 	FIBITMAP *dst = NULL;
369 
370 	if(!FreeImage_HasPixels(src)) return NULL;
371 
372 	if(FreeImage_GetImageType(src) == FIT_COMPLEX) {
373 		// allocate a dib of type FIT_DOUBLE
374 		unsigned width  = FreeImage_GetWidth(src);
375 		unsigned height = FreeImage_GetHeight(src);
376 		dst = FreeImage_AllocateT(FIT_DOUBLE, width, height) ;
377 		if(!dst) return NULL;
378 
379 		// perform extraction
380 
381 		switch(channel) {
382 			case FICC_REAL: // real part
383 				for(y = 0; y < height; y++) {
384 					src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
385 					dst_bits = (double *)FreeImage_GetScanLine(dst, y);
386 					for(x = 0; x < width; x++) {
387 						dst_bits[x] = src_bits[x].r;
388 					}
389 				}
390 				break;
391 
392 			case FICC_IMAG: // imaginary part
393 				for(y = 0; y < height; y++) {
394 					src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
395 					dst_bits = (double *)FreeImage_GetScanLine(dst, y);
396 					for(x = 0; x < width; x++) {
397 						dst_bits[x] = src_bits[x].i;
398 					}
399 				}
400 				break;
401 
402 			case FICC_MAG: // magnitude
403 				for(y = 0; y < height; y++) {
404 					src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
405 					dst_bits = (double *)FreeImage_GetScanLine(dst, y);
406 					for(x = 0; x < width; x++) {
407 						mag = src_bits[x].r * src_bits[x].r + src_bits[x].i * src_bits[x].i;
408 						dst_bits[x] = sqrt(mag);
409 					}
410 				}
411 				break;
412 
413 			case FICC_PHASE: // phase
414 				for(y = 0; y < height; y++) {
415 					src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
416 					dst_bits = (double *)FreeImage_GetScanLine(dst, y);
417 					for(x = 0; x < width; x++) {
418 						if((src_bits[x].r == 0) && (src_bits[x].i == 0)) {
419 							phase = 0;
420 						} else {
421 							phase = atan2(src_bits[x].i, src_bits[x].r);
422 						}
423 						dst_bits[x] = phase;
424 					}
425 				}
426 				break;
427 		}
428 	}
429 
430 	// copy metadata from src to dst
431 	FreeImage_CloneMetadata(dst, src);
432 
433 	return dst;
434 }
435 
436 /** @brief Set the real or imaginary part of a complex image.
437 Both src and dst must have the same width and height.
438 @param dst Image to modify (image of type FIT_COMPLEX)
439 @param src Input image of type FIT_DOUBLE
440 @param channel Channel to modify
441 @return Returns TRUE if successful, FALSE otherwise.
442 */
443 BOOL DLL_CALLCONV
FreeImage_SetComplexChannel(FIBITMAP * dst,FIBITMAP * src,FREE_IMAGE_COLOR_CHANNEL channel)444 FreeImage_SetComplexChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
445 	unsigned x, y;
446 	double *src_bits = NULL;
447 	FICOMPLEX *dst_bits = NULL;
448 
449 	if(!FreeImage_HasPixels(src) || !FreeImage_HasPixels(dst)) return FALSE;
450 
451 	// src image should be of type FIT_DOUBLE, dst image should be of type FIT_COMPLEX
452 	const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(src);
453 	const FREE_IMAGE_TYPE dst_type = FreeImage_GetImageType(dst);
454 	if((src_type != FIT_DOUBLE) || (dst_type != FIT_COMPLEX))
455 		return FALSE;
456 
457 	// src and dst images should have the same width and height
458 	unsigned src_width  = FreeImage_GetWidth(src);
459 	unsigned src_height = FreeImage_GetHeight(src);
460 	unsigned dst_width  = FreeImage_GetWidth(dst);
461 	unsigned dst_height = FreeImage_GetHeight(dst);
462 	if((src_width != dst_width) || (src_height != dst_height))
463 		return FALSE;
464 
465 	// select the channel to modify
466 	switch(channel) {
467 		case FICC_REAL: // real part
468 			for(y = 0; y < dst_height; y++) {
469 				src_bits = (double *)FreeImage_GetScanLine(src, y);
470 				dst_bits = (FICOMPLEX *)FreeImage_GetScanLine(dst, y);
471 				for(x = 0; x < dst_width; x++) {
472 					dst_bits[x].r = src_bits[x];
473 				}
474 			}
475 			break;
476 		case FICC_IMAG: // imaginary part
477 			for(y = 0; y < dst_height; y++) {
478 				src_bits = (double *)FreeImage_GetScanLine(src, y);
479 				dst_bits = (FICOMPLEX *)FreeImage_GetScanLine(dst, y);
480 				for(x = 0; x < dst_width; x++) {
481 					dst_bits[x].i = src_bits[x];
482 				}
483 			}
484 			break;
485 	}
486 
487 	return TRUE;
488 }
489