/src/FreeImage/Source/FreeImage/Conversion.cpp
C++ | 513 lines | 368 code | 85 blank | 60 comment | 112 complexity | 5c1a600f2e17c59771f2e394bb0851ff MD5 | raw file
1// ========================================================== 2// Bitmap conversion routines 3// 4// Design and implementation by 5// - Floris van den Berg (flvdberg@wxs.nl) 6// - Hervé Drolon (drolon@infonie.fr) 7// - Jani Kajala (janik@remedy.fi) 8// - Mihail Naydenov (mnaydenov@users.sourceforge.net) 9// 10// This file is part of FreeImage 3 11// 12// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY 13// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES 14// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE 15// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED 16// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT 17// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY 18// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL 19// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER 20// THIS DISCLAIMER. 21// 22// Use at your own risk! 23// ========================================================== 24 25#include "FreeImage.h" 26#include "Utilities.h" 27#include "Quantizers.h" 28 29// ---------------------------------------------------------- 30 31#define CONVERT(from, to) case to : FreeImage_ConvertLine##from##To##to(bits, scanline, FreeImage_GetWidth(dib)); break; 32#define CONVERTWITHPALETTE(from, to) case to : FreeImage_ConvertLine##from##To##to(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); break; 33 34#define CONVERTTO16(from) \ 35 case 16 : \ 36 if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) { \ 37 FreeImage_ConvertLine##from##To16_555(bits, scanline, FreeImage_GetWidth(dib)); \ 38 } else { \ 39 FreeImage_ConvertLine##from##To16_565(bits, scanline, FreeImage_GetWidth(dib)); \ 40 } \ 41 break; 42 43#define CONVERTTO16WITHPALETTE(from) \ 44 case 16 : \ 45 if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) { \ 46 FreeImage_ConvertLine##from##To16_555(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); \ 47 } else { \ 48 FreeImage_ConvertLine##from##To16_565(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); \ 49 } \ 50 break; 51 52// ========================================================== 53// Utility functions declared in Utilities.h 54 55BOOL SwapRedBlue32(FIBITMAP* dib) { 56 if(FreeImage_GetImageType(dib) != FIT_BITMAP) { 57 return FALSE; 58 } 59 60 const unsigned bytesperpixel = FreeImage_GetBPP(dib) / 8; 61 if(bytesperpixel > 4 || bytesperpixel < 3) { 62 return FALSE; 63 } 64 65 const unsigned height = FreeImage_GetHeight(dib); 66 const unsigned pitch = FreeImage_GetPitch(dib); 67 const unsigned lineSize = FreeImage_GetLine(dib); 68 69 BYTE* line = FreeImage_GetBits(dib); 70 for(unsigned y = 0; y < height; ++y, line += pitch) { 71 for(BYTE* pixel = line; pixel < line + lineSize ; pixel += bytesperpixel) { 72 INPLACESWAP(pixel[0], pixel[2]); 73 } 74 } 75 76 return TRUE; 77} 78 79// ---------------------------------------------------------- 80 81static inline void 82assignRGB(WORD r, WORD g, WORD b, WORD* out) { 83 out[0] = r; 84 out[1] = g; 85 out[2] = b; 86} 87 88static inline void 89assignRGB(BYTE r, BYTE g, BYTE b, BYTE* out) { 90 out[FI_RGBA_RED] = r; 91 out[FI_RGBA_GREEN] = g; 92 out[FI_RGBA_BLUE] = b; 93} 94 95/** 96CMYK -> CMY -> RGB conversion from http://www.easyrgb.com/ 97 98CMYK to CMY [0-1]: C,M,Y * (1 - K) + K 99CMY to RGB [0-1]: (1 - C,M,Y) 100 101=> R,G,B = (1 - C,M,Y) * (1 - K) 102mapped to [0-MAX_VAL]: 103(MAX_VAL - C,M,Y) * (MAX_VAL - K) / MAX_VAL 104*/ 105template <class T> 106static inline void 107CMYKToRGB(T C, T M, T Y, T K, T* out) { 108 unsigned max_val = std::numeric_limits<T>::max(); 109 110 unsigned r = (max_val - C) * (max_val - K) / max_val; 111 unsigned g = (max_val - M) * (max_val - K) / max_val; 112 unsigned b = (max_val - Y) * (max_val - K) / max_val; 113 114 // clamp values to [0..max_val] 115 T red = (T)CLAMP(r, (unsigned)0, max_val); 116 T green = (T)CLAMP(g, (unsigned)0, max_val); 117 T blue = (T)CLAMP(b, (unsigned)0, max_val); 118 119 assignRGB(red, green, blue, out); 120} 121 122template <class T> 123static void 124_convertCMYKtoRGBA(unsigned width, unsigned height, BYTE* line_start, unsigned pitch, unsigned samplesperpixel) { 125 const BOOL hasBlack = (samplesperpixel > 3) ? TRUE : FALSE; 126 const T MAX_VAL = std::numeric_limits<T>::max(); 127 128 T K = 0; 129 for(unsigned y = 0; y < height; y++) { 130 T *line = (T*)line_start; 131 132 for(unsigned x = 0; x < width; x++) { 133 if(hasBlack) { 134 K = line[FI_RGBA_ALPHA]; 135 line[FI_RGBA_ALPHA] = MAX_VAL; // TODO write the first extra channel as alpha! 136 } 137 138 CMYKToRGB<T>(line[0], line[1], line[2], K, line); 139 140 line += samplesperpixel; 141 } 142 line_start += pitch; 143 } 144} 145 146BOOL 147ConvertCMYKtoRGBA(FIBITMAP* dib) { 148 if(!FreeImage_HasPixels(dib)) { 149 return FALSE; 150 } 151 152 const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); 153 const unsigned bytesperpixel = FreeImage_GetBPP(dib)/8; 154 155 unsigned channelSize = 1; 156 if (image_type == FIT_RGBA16 || image_type == FIT_RGB16) { 157 channelSize = sizeof(WORD); 158 } else if (!(image_type == FIT_BITMAP && (bytesperpixel > 2))) { 159 return FALSE; 160 } 161 162 const unsigned width = FreeImage_GetWidth(dib); 163 const unsigned height = FreeImage_GetHeight(dib); 164 BYTE *line_start = FreeImage_GetScanLine(dib, 0); 165 const unsigned pitch = FreeImage_GetPitch(dib); 166 167 unsigned samplesperpixel = FreeImage_GetLine(dib) / width / channelSize; 168 169 if(channelSize == sizeof(WORD)) { 170 _convertCMYKtoRGBA<WORD>(width, height, line_start, pitch, samplesperpixel); 171 } else { 172 _convertCMYKtoRGBA<BYTE>(width, height, line_start, pitch, samplesperpixel); 173 } 174 175 return TRUE; 176} 177 178// ---------------------------------------------------------- 179 180/** 181CIELab -> XYZ conversion from http://www.easyrgb.com/ 182*/ 183static void 184CIELabToXYZ(float L, float a, float b, float *X, float *Y, float *Z) { 185 float pow_3; 186 187 // CIELab -> XYZ conversion 188 // ------------------------ 189 float var_Y = (L + 16.F ) / 116.F; 190 float var_X = a / 500.F + var_Y; 191 float var_Z = var_Y - b / 200.F; 192 193 pow_3 = powf(var_Y, 3); 194 if(pow_3 > 0.008856F) { 195 var_Y = pow_3; 196 } else { 197 var_Y = ( var_Y - 16.F / 116.F ) / 7.787F; 198 } 199 pow_3 = powf(var_X, 3); 200 if(pow_3 > 0.008856F) { 201 var_X = pow_3; 202 } else { 203 var_X = ( var_X - 16.F / 116.F ) / 7.787F; 204 } 205 pow_3 = powf(var_Z, 3); 206 if(pow_3 > 0.008856F) { 207 var_Z = pow_3; 208 } else { 209 var_Z = ( var_Z - 16.F / 116.F ) / 7.787F; 210 } 211 212 static const float ref_X = 95.047F; 213 static const float ref_Y = 100.000F; 214 static const float ref_Z = 108.883F; 215 216 *X = ref_X * var_X; // ref_X = 95.047 (Observer= 2°, Illuminant= D65) 217 *Y = ref_Y * var_Y; // ref_Y = 100.000 218 *Z = ref_Z * var_Z; // ref_Z = 108.883 219} 220 221/** 222XYZ -> RGB conversion from http://www.easyrgb.com/ 223*/ 224static void 225XYZToRGB(float X, float Y, float Z, float *R, float *G, float *B) { 226 float var_X = X / 100; // X from 0 to 95.047 (Observer = 2°, Illuminant = D65) 227 float var_Y = Y / 100; // Y from 0 to 100.000 228 float var_Z = Z / 100; // Z from 0 to 108.883 229 230 float var_R = var_X * 3.2406F + var_Y * -1.5372F + var_Z * -0.4986F; 231 float var_G = var_X * -0.9689F + var_Y * 1.8758F + var_Z * 0.0415F; 232 float var_B = var_X * 0.0557F + var_Y * -0.2040F + var_Z * 1.0570F; 233 234 float exponent = 1.F / 2.4F; 235 236 if(var_R > 0.0031308F) { 237 var_R = 1.055F * powf(var_R, exponent) - 0.055F; 238 } else { 239 var_R = 12.92F * var_R; 240 } 241 if(var_G > 0.0031308F) { 242 var_G = 1.055F * powf(var_G, exponent) - 0.055F; 243 } else { 244 var_G = 12.92F * var_G; 245 } 246 if(var_B > 0.0031308F) { 247 var_B = 1.055F * powf(var_B, exponent) - 0.055F; 248 } else { 249 var_B = 12.92F * var_B; 250 } 251 252 *R = var_R; 253 *G = var_G; 254 *B = var_B; 255} 256 257template<class T> 258static void 259CIELabToRGB(float L, float a, float b, T *rgb) { 260 float X, Y, Z; 261 float R, G, B; 262 const float max_val = std::numeric_limits<T>::max(); 263 264 CIELabToXYZ(L, a, b, &X, &Y, &Z); 265 XYZToRGB(X, Y, Z, &R, &G, &B); 266 267 // clamp values to [0..max_val] 268 T red = (T)CLAMP(R * max_val, 0.0F, max_val); 269 T green = (T)CLAMP(G * max_val, 0.0F, max_val); 270 T blue = (T)CLAMP(B * max_val, 0.0F, max_val); 271 272 assignRGB(red, green, blue, rgb); 273} 274 275template<class T> 276static void 277_convertLABtoRGB(unsigned width, unsigned height, BYTE* line_start, unsigned pitch, unsigned samplesperpixel) { 278 const unsigned max_val = std::numeric_limits<T>::max(); 279 const float sL = 100.F / max_val; 280 const float sa = 256.F / max_val; 281 const float sb = 256.F / max_val; 282 283 for(unsigned y = 0; y < height; y++) { 284 T *line = (T*)line_start; 285 286 for(unsigned x = 0; x < width; x++) { 287 CIELabToRGB(line[0]* sL, line[1]* sa - 128.F, line[2]* sb - 128.F, line); 288 289 line += samplesperpixel; 290 } 291 line_start += pitch; 292 } 293} 294 295BOOL 296ConvertLABtoRGB(FIBITMAP* dib) { 297 if(!FreeImage_HasPixels(dib)) { 298 return FALSE; 299 } 300 301 const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); 302 const unsigned bytesperpixel = FreeImage_GetBPP(dib) / 8; 303 304 unsigned channelSize = 1; 305 if (image_type == FIT_RGBA16 || image_type == FIT_RGB16) { 306 channelSize = sizeof(WORD); 307 } else if (!(image_type == FIT_BITMAP && (bytesperpixel > 2))) { 308 return FALSE; 309 } 310 311 const unsigned width = FreeImage_GetWidth(dib); 312 const unsigned height = FreeImage_GetHeight(dib); 313 BYTE *line_start = FreeImage_GetScanLine(dib, 0); 314 const unsigned pitch = FreeImage_GetPitch(dib); 315 316 unsigned samplesperpixel = FreeImage_GetLine(dib) / width / channelSize; 317 318 if(channelSize == 1) { 319 _convertLABtoRGB<BYTE>(width, height, line_start, pitch, samplesperpixel); 320 } 321 else { 322 _convertLABtoRGB<WORD>(width, height, line_start, pitch, samplesperpixel); 323 } 324 325 return TRUE; 326} 327 328// ---------------------------------------------------------- 329 330FIBITMAP* 331RemoveAlphaChannel(FIBITMAP* src) { 332 333 if(!FreeImage_HasPixels(src)) { 334 return NULL; 335 } 336 337 const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src); 338 339 switch(image_type) { 340 case FIT_BITMAP: 341 if(FreeImage_GetBPP(src) == 32) { 342 // convert to 24-bit 343 return FreeImage_ConvertTo24Bits(src); 344 } 345 break; 346 case FIT_RGBA16: 347 // convert to RGB16 348 return FreeImage_ConvertToRGB16(src); 349 case FIT_RGBAF: 350 // convert to RGBF 351 return FreeImage_ConvertToRGBF(src); 352 default: 353 // unsupported image type 354 return NULL; 355 } 356 357 return NULL; 358} 359 360 361// ========================================================== 362 363FIBITMAP * DLL_CALLCONV 364FreeImage_ColorQuantize(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize) { 365 return FreeImage_ColorQuantizeEx(dib, quantize); 366} 367 368FIBITMAP * DLL_CALLCONV 369FreeImage_ColorQuantizeEx(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize, int PaletteSize, int ReserveSize, RGBQUAD *ReservePalette) { 370 if( PaletteSize < 2 ) PaletteSize = 2; 371 if( PaletteSize > 256 ) PaletteSize = 256; 372 if( ReserveSize < 0 ) ReserveSize = 0; 373 if( ReserveSize > PaletteSize ) ReserveSize = PaletteSize; 374 if (FreeImage_HasPixels(dib)) { 375 if (FreeImage_GetBPP(dib) == 24) { 376 switch(quantize) { 377 case FIQ_WUQUANT : 378 { 379 try { 380 WuQuantizer Q (dib); 381 FIBITMAP *dst = Q.Quantize(PaletteSize, ReserveSize, ReservePalette); 382 if(dst) { 383 // copy metadata from src to dst 384 FreeImage_CloneMetadata(dst, dib); 385 } 386 return dst; 387 } catch (const char *) { 388 return NULL; 389 } 390 } 391 case FIQ_NNQUANT : 392 { 393 // sampling factor in range 1..30. 394 // 1 => slower (but better), 30 => faster. Default value is 1 395 const int sampling = 1; 396 397 NNQuantizer Q(PaletteSize); 398 FIBITMAP *dst = Q.Quantize(dib, ReserveSize, ReservePalette, sampling); 399 if(dst) { 400 // copy metadata from src to dst 401 FreeImage_CloneMetadata(dst, dib); 402 } 403 return dst; 404 } 405 } 406 } 407 } 408 409 return NULL; 410} 411 412// ========================================================== 413 414FIBITMAP * DLL_CALLCONV 415FreeImage_ConvertFromRawBits(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) { 416 FIBITMAP *dib = FreeImage_Allocate(width, height, bpp, red_mask, green_mask, blue_mask); 417 418 if (dib != NULL) { 419 if (topdown) { 420 for (int i = height - 1; i >= 0; --i) { 421 memcpy(FreeImage_GetScanLine(dib, i), bits, FreeImage_GetLine(dib)); 422 bits += pitch; 423 } 424 } else { 425 for (int i = 0; i < height; ++i) { 426 memcpy(FreeImage_GetScanLine(dib, i), bits, FreeImage_GetLine(dib)); 427 bits += pitch; 428 } 429 } 430 } 431 432 return dib; 433} 434 435void DLL_CALLCONV 436FreeImage_ConvertToRawBits(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) { 437 if (FreeImage_HasPixels(dib) && (bits != NULL)) { 438 for (unsigned i = 0; i < FreeImage_GetHeight(dib); ++i) { 439 BYTE *scanline = FreeImage_GetScanLine(dib, topdown ? (FreeImage_GetHeight(dib) - i - 1) : i); 440 441 if ((bpp == 16) && (FreeImage_GetBPP(dib) == 16)) { 442 // convert 555 to 565 or vice versa 443 444 if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) { 445 if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) { 446 FreeImage_ConvertLine16_565_To16_555(bits, scanline, FreeImage_GetWidth(dib)); 447 } else { 448 memcpy(bits, scanline, FreeImage_GetLine(dib)); 449 } 450 } else { 451 if ((FreeImage_GetRedMask(dib) == FI16_555_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_555_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_555_BLUE_MASK)) { 452 FreeImage_ConvertLine16_555_To16_565(bits, scanline, FreeImage_GetWidth(dib)); 453 } else { 454 memcpy(bits, scanline, FreeImage_GetLine(dib)); 455 } 456 } 457 } else if (FreeImage_GetBPP(dib) != bpp) { 458 switch(FreeImage_GetBPP(dib)) { 459 case 1 : 460 switch(bpp) { 461 CONVERT(1, 8) 462 CONVERTTO16WITHPALETTE(1) 463 CONVERTWITHPALETTE(1, 24) 464 CONVERTWITHPALETTE(1, 32) 465 } 466 467 break; 468 469 case 4 : 470 switch(bpp) { 471 CONVERT(4, 8) 472 CONVERTTO16WITHPALETTE(4) 473 CONVERTWITHPALETTE(4, 24) 474 CONVERTWITHPALETTE(4, 32) 475 } 476 477 break; 478 479 case 8 : 480 switch(bpp) { 481 CONVERTTO16WITHPALETTE(8) 482 CONVERTWITHPALETTE(8, 24) 483 CONVERTWITHPALETTE(8, 32) 484 } 485 486 break; 487 488 case 24 : 489 switch(bpp) { 490 CONVERT(24, 8) 491 CONVERTTO16(24) 492 CONVERT(24, 32) 493 } 494 495 break; 496 497 case 32 : 498 switch(bpp) { 499 CONVERT(32, 8) 500 CONVERTTO16(32) 501 CONVERT(32, 24) 502 } 503 504 break; 505 } 506 } else { 507 memcpy(bits, scanline, FreeImage_GetLine(dib)); 508 } 509 510 bits += pitch; 511 } 512 } 513}