488 lines
13 KiB
C++
488 lines
13 KiB
C++
// ==========================================================
|
|
// Channel processing support
|
|
//
|
|
// 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 Retrieves the red, green, blue or alpha channel of a BGR[A] image.
|
|
@param src Input image to be processed.
|
|
@param channel Color channel to extract
|
|
@return Returns the extracted channel if successful, returns NULL otherwise.
|
|
*/
|
|
FIBITMAP * DLL_CALLCONV
|
|
FreeImage_GetChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
|
|
|
|
if(!src) return NULL;
|
|
|
|
FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
|
|
unsigned bpp = FreeImage_GetBPP(src);
|
|
|
|
// 24- or 32-bit
|
|
if(image_type == FIT_BITMAP && ((bpp == 24) || (bpp == 32))) {
|
|
int c;
|
|
|
|
// select the channel to extract
|
|
switch(channel) {
|
|
case FICC_BLUE:
|
|
c = FI_RGBA_BLUE;
|
|
break;
|
|
case FICC_GREEN:
|
|
c = FI_RGBA_GREEN;
|
|
break;
|
|
case FICC_RED:
|
|
c = FI_RGBA_RED;
|
|
break;
|
|
case FICC_ALPHA:
|
|
if(bpp != 32) return NULL;
|
|
c = FI_RGBA_ALPHA;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
// allocate a 8-bit dib
|
|
unsigned width = FreeImage_GetWidth(src);
|
|
unsigned height = FreeImage_GetHeight(src);
|
|
FIBITMAP *dst = FreeImage_Allocate(width, height, 8) ;
|
|
if(!dst) return NULL;
|
|
// build a greyscale palette
|
|
RGBQUAD *pal = FreeImage_GetPalette(dst);
|
|
for(int i = 0; i < 256; i++) {
|
|
pal[i].rgbBlue = pal[i].rgbGreen = pal[i].rgbRed = (BYTE)i;
|
|
}
|
|
|
|
// perform extraction
|
|
|
|
int bytespp = bpp / 8; // bytes / pixel
|
|
|
|
for(unsigned y = 0; y < height; y++) {
|
|
BYTE *src_bits = FreeImage_GetScanLine(src, y);
|
|
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
|
|
for(unsigned x = 0; x < width; x++) {
|
|
dst_bits[x] = src_bits[c];
|
|
src_bits += bytespp;
|
|
}
|
|
}
|
|
|
|
// copy metadata from src to dst
|
|
FreeImage_CloneMetadata(dst, src);
|
|
|
|
return dst;
|
|
}
|
|
|
|
// 48-bit RGB or 64-bit RGBA images
|
|
if((image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) {
|
|
int c;
|
|
|
|
// select the channel to extract (always RGB[A])
|
|
switch(channel) {
|
|
case FICC_BLUE:
|
|
c = 2;
|
|
break;
|
|
case FICC_GREEN:
|
|
c = 1;
|
|
break;
|
|
case FICC_RED:
|
|
c = 0;
|
|
break;
|
|
case FICC_ALPHA:
|
|
if(bpp != 64) return NULL;
|
|
c = 3;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
// allocate a greyscale dib
|
|
unsigned width = FreeImage_GetWidth(src);
|
|
unsigned height = FreeImage_GetHeight(src);
|
|
FIBITMAP *dst = FreeImage_AllocateT(FIT_UINT16, width, height) ;
|
|
if(!dst) return NULL;
|
|
|
|
// perform extraction
|
|
|
|
int bytespp = bpp / 16; // words / pixel
|
|
|
|
for(unsigned y = 0; y < height; y++) {
|
|
unsigned short *src_bits = (unsigned short*)FreeImage_GetScanLine(src, y);
|
|
unsigned short *dst_bits = (unsigned short*)FreeImage_GetScanLine(dst, y);
|
|
for(unsigned x = 0; x < width; x++) {
|
|
dst_bits[x] = src_bits[c];
|
|
src_bits += bytespp;
|
|
}
|
|
}
|
|
|
|
// copy metadata from src to dst
|
|
FreeImage_CloneMetadata(dst, src);
|
|
|
|
return dst;
|
|
}
|
|
|
|
// 96-bit RGBF or 128-bit RGBAF images
|
|
if((image_type == FIT_RGBF) || (image_type == FIT_RGBAF)) {
|
|
int c;
|
|
|
|
// select the channel to extract (always RGB[A])
|
|
switch(channel) {
|
|
case FICC_BLUE:
|
|
c = 2;
|
|
break;
|
|
case FICC_GREEN:
|
|
c = 1;
|
|
break;
|
|
case FICC_RED:
|
|
c = 0;
|
|
break;
|
|
case FICC_ALPHA:
|
|
if(bpp != 128) return NULL;
|
|
c = 3;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
// allocate a greyscale dib
|
|
unsigned width = FreeImage_GetWidth(src);
|
|
unsigned height = FreeImage_GetHeight(src);
|
|
FIBITMAP *dst = FreeImage_AllocateT(FIT_FLOAT, width, height) ;
|
|
if(!dst) return NULL;
|
|
|
|
// perform extraction
|
|
|
|
int bytespp = bpp / 32; // floats / pixel
|
|
|
|
for(unsigned y = 0; y < height; y++) {
|
|
float *src_bits = (float*)FreeImage_GetScanLine(src, y);
|
|
float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
|
|
for(unsigned x = 0; x < width; x++) {
|
|
dst_bits[x] = src_bits[c];
|
|
src_bits += bytespp;
|
|
}
|
|
}
|
|
|
|
// copy metadata from src to dst
|
|
FreeImage_CloneMetadata(dst, src);
|
|
|
|
return dst;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/** @brief Insert a greyscale dib into a RGB[A] image.
|
|
Both src and dst must have the same width and height.
|
|
@param dst Image to modify (RGB or RGBA)
|
|
@param src Input greyscale image to insert
|
|
@param channel Color channel to modify
|
|
@return Returns TRUE if successful, FALSE otherwise.
|
|
*/
|
|
BOOL DLL_CALLCONV
|
|
FreeImage_SetChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
|
|
int c;
|
|
|
|
if(!src || !dst) return FALSE;
|
|
|
|
// src and dst images should have the same width and height
|
|
unsigned src_width = FreeImage_GetWidth(src);
|
|
unsigned src_height = FreeImage_GetHeight(src);
|
|
unsigned dst_width = FreeImage_GetWidth(dst);
|
|
unsigned dst_height = FreeImage_GetHeight(dst);
|
|
if((src_width != dst_width) || (src_height != dst_height))
|
|
return FALSE;
|
|
|
|
// src image should be grayscale, dst image should be RGB or RGBA
|
|
FREE_IMAGE_COLOR_TYPE src_type = FreeImage_GetColorType(src);
|
|
FREE_IMAGE_COLOR_TYPE dst_type = FreeImage_GetColorType(dst);
|
|
if((dst_type != FIC_RGB) && (dst_type != FIC_RGBALPHA) || (src_type != FIC_MINISBLACK)) {
|
|
return FALSE;
|
|
}
|
|
|
|
FREE_IMAGE_TYPE src_image_type = FreeImage_GetImageType(src);
|
|
FREE_IMAGE_TYPE dst_image_type = FreeImage_GetImageType(dst);
|
|
|
|
if((dst_image_type == FIT_BITMAP) && (src_image_type == FIT_BITMAP)) {
|
|
|
|
// src image should be grayscale, dst image should be 24- or 32-bit
|
|
unsigned src_bpp = FreeImage_GetBPP(src);
|
|
unsigned dst_bpp = FreeImage_GetBPP(dst);
|
|
if((src_bpp != 8) || (dst_bpp != 24) && (dst_bpp != 32))
|
|
return FALSE;
|
|
|
|
|
|
// select the channel to modify
|
|
switch(channel) {
|
|
case FICC_BLUE:
|
|
c = FI_RGBA_BLUE;
|
|
break;
|
|
case FICC_GREEN:
|
|
c = FI_RGBA_GREEN;
|
|
break;
|
|
case FICC_RED:
|
|
c = FI_RGBA_RED;
|
|
break;
|
|
case FICC_ALPHA:
|
|
if(dst_bpp != 32) return FALSE;
|
|
c = FI_RGBA_ALPHA;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
// perform insertion
|
|
|
|
int bytespp = dst_bpp / 8; // bytes / pixel
|
|
|
|
for(unsigned y = 0; y < dst_height; y++) {
|
|
BYTE *src_bits = FreeImage_GetScanLine(src, y);
|
|
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
|
|
for(unsigned x = 0; x < dst_width; x++) {
|
|
dst_bits[c] = src_bits[x];
|
|
dst_bits += bytespp;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if(((dst_image_type == FIT_RGB16) || (dst_image_type == FIT_RGBA16)) && (src_image_type == FIT_UINT16)) {
|
|
|
|
// src image should be grayscale, dst image should be 48- or 64-bit
|
|
unsigned src_bpp = FreeImage_GetBPP(src);
|
|
unsigned dst_bpp = FreeImage_GetBPP(dst);
|
|
if((src_bpp != 16) || (dst_bpp != 48) && (dst_bpp != 64))
|
|
return FALSE;
|
|
|
|
|
|
// select the channel to modify (always RGB[A])
|
|
switch(channel) {
|
|
case FICC_BLUE:
|
|
c = 2;
|
|
break;
|
|
case FICC_GREEN:
|
|
c = 1;
|
|
break;
|
|
case FICC_RED:
|
|
c = 0;
|
|
break;
|
|
case FICC_ALPHA:
|
|
if(dst_bpp != 64) return FALSE;
|
|
c = 3;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
// perform insertion
|
|
|
|
int bytespp = dst_bpp / 16; // words / pixel
|
|
|
|
for(unsigned y = 0; y < dst_height; y++) {
|
|
unsigned short *src_bits = (unsigned short*)FreeImage_GetScanLine(src, y);
|
|
unsigned short *dst_bits = (unsigned short*)FreeImage_GetScanLine(dst, y);
|
|
for(unsigned x = 0; x < dst_width; x++) {
|
|
dst_bits[c] = src_bits[x];
|
|
dst_bits += bytespp;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if(((dst_image_type == FIT_RGBF) || (dst_image_type == FIT_RGBAF)) && (src_image_type == FIT_FLOAT)) {
|
|
|
|
// src image should be grayscale, dst image should be 96- or 128-bit
|
|
unsigned src_bpp = FreeImage_GetBPP(src);
|
|
unsigned dst_bpp = FreeImage_GetBPP(dst);
|
|
if((src_bpp != 32) || (dst_bpp != 96) && (dst_bpp != 128))
|
|
return FALSE;
|
|
|
|
|
|
// select the channel to modify (always RGB[A])
|
|
switch(channel) {
|
|
case FICC_BLUE:
|
|
c = 2;
|
|
break;
|
|
case FICC_GREEN:
|
|
c = 1;
|
|
break;
|
|
case FICC_RED:
|
|
c = 0;
|
|
break;
|
|
case FICC_ALPHA:
|
|
if(dst_bpp != 128) return FALSE;
|
|
c = 3;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
// perform insertion
|
|
|
|
int bytespp = dst_bpp / 32; // floats / pixel
|
|
|
|
for(unsigned y = 0; y < dst_height; y++) {
|
|
float *src_bits = (float*)FreeImage_GetScanLine(src, y);
|
|
float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
|
|
for(unsigned x = 0; x < dst_width; x++) {
|
|
dst_bits[c] = src_bits[x];
|
|
dst_bits += bytespp;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/** @brief Retrieves the real part, imaginary part, magnitude or phase of a complex image.
|
|
@param src Input image to be processed.
|
|
@param channel Channel to extract
|
|
@return Returns the extracted channel if successful, returns NULL otherwise.
|
|
*/
|
|
FIBITMAP * DLL_CALLCONV
|
|
FreeImage_GetComplexChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
|
|
unsigned x, y;
|
|
double mag, phase;
|
|
FICOMPLEX *src_bits = NULL;
|
|
double *dst_bits = NULL;
|
|
FIBITMAP *dst = NULL;
|
|
|
|
if(!src) return NULL;
|
|
|
|
if(FreeImage_GetImageType(src) == FIT_COMPLEX) {
|
|
// allocate a dib of type FIT_DOUBLE
|
|
unsigned width = FreeImage_GetWidth(src);
|
|
unsigned height = FreeImage_GetHeight(src);
|
|
dst = FreeImage_AllocateT(FIT_DOUBLE, width, height) ;
|
|
if(!dst) return NULL;
|
|
|
|
// perform extraction
|
|
|
|
switch(channel) {
|
|
case FICC_REAL: // real part
|
|
for(y = 0; y < height; y++) {
|
|
src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
|
|
dst_bits = (double *)FreeImage_GetScanLine(dst, y);
|
|
for(x = 0; x < width; x++) {
|
|
dst_bits[x] = src_bits[x].r;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FICC_IMAG: // imaginary part
|
|
for(y = 0; y < height; y++) {
|
|
src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
|
|
dst_bits = (double *)FreeImage_GetScanLine(dst, y);
|
|
for(x = 0; x < width; x++) {
|
|
dst_bits[x] = src_bits[x].i;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FICC_MAG: // magnitude
|
|
for(y = 0; y < height; y++) {
|
|
src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
|
|
dst_bits = (double *)FreeImage_GetScanLine(dst, y);
|
|
for(x = 0; x < width; x++) {
|
|
mag = src_bits[x].r * src_bits[x].r + src_bits[x].i * src_bits[x].i;
|
|
dst_bits[x] = sqrt(mag);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FICC_PHASE: // phase
|
|
for(y = 0; y < height; y++) {
|
|
src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
|
|
dst_bits = (double *)FreeImage_GetScanLine(dst, y);
|
|
for(x = 0; x < width; x++) {
|
|
if((src_bits[x].r == 0) && (src_bits[x].i == 0)) {
|
|
phase = 0;
|
|
} else {
|
|
phase = atan2(src_bits[x].i, src_bits[x].r);
|
|
}
|
|
dst_bits[x] = phase;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// copy metadata from src to dst
|
|
FreeImage_CloneMetadata(dst, src);
|
|
|
|
return dst;
|
|
}
|
|
|
|
/** @brief Set the real or imaginary part of a complex image.
|
|
Both src and dst must have the same width and height.
|
|
@param dst Image to modify (image of type FIT_COMPLEX)
|
|
@param src Input image of type FIT_DOUBLE
|
|
@param channel Channel to modify
|
|
@return Returns TRUE if successful, FALSE otherwise.
|
|
*/
|
|
BOOL DLL_CALLCONV
|
|
FreeImage_SetComplexChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
|
|
unsigned x, y;
|
|
double *src_bits = NULL;
|
|
FICOMPLEX *dst_bits = NULL;
|
|
|
|
if(!src || !dst) return FALSE;
|
|
|
|
// src image should be of type FIT_DOUBLE, dst image should be of type FIT_COMPLEX
|
|
const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(src);
|
|
const FREE_IMAGE_TYPE dst_type = FreeImage_GetImageType(dst);
|
|
if((src_type != FIT_DOUBLE) || (dst_type != FIT_COMPLEX))
|
|
return FALSE;
|
|
|
|
// src and dst images should have the same width and height
|
|
unsigned src_width = FreeImage_GetWidth(src);
|
|
unsigned src_height = FreeImage_GetHeight(src);
|
|
unsigned dst_width = FreeImage_GetWidth(dst);
|
|
unsigned dst_height = FreeImage_GetHeight(dst);
|
|
if((src_width != dst_width) || (src_height != dst_height))
|
|
return FALSE;
|
|
|
|
// select the channel to modify
|
|
switch(channel) {
|
|
case FICC_REAL: // real part
|
|
for(y = 0; y < dst_height; y++) {
|
|
src_bits = (double *)FreeImage_GetScanLine(src, y);
|
|
dst_bits = (FICOMPLEX *)FreeImage_GetScanLine(dst, y);
|
|
for(x = 0; x < dst_width; x++) {
|
|
dst_bits[x].r = src_bits[x];
|
|
}
|
|
}
|
|
break;
|
|
case FICC_IMAG: // imaginary part
|
|
for(y = 0; y < dst_height; y++) {
|
|
src_bits = (double *)FreeImage_GetScanLine(src, y);
|
|
dst_bits = (FICOMPLEX *)FreeImage_GetScanLine(dst, y);
|
|
for(x = 0; x < dst_width; x++) {
|
|
dst_bits[x].i = src_bits[x];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|