BOOL DLL_CALLCONV FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) { char crop[64]; try { // check the src file format if(FreeImage_GetFileType(src_file) != FIF_JPEG) { throw FI_MSG_ERROR_MAGIC_NUMBER; } // normalize the rectangle if(right < left) { INPLACESWAP(left, right); } if(bottom < top) { INPLACESWAP(top, bottom); } // build the crop option sprintf(crop, "%dx%d+%d+%d", right - left, bottom - top, left, top); // setup IO FilenameIO filenameIO; memset(&filenameIO, 0, sizeof(FilenameIO)); filenameIO.src_file = src_file; filenameIO.dst_file = dst_file; // perform the transformation return LosslessTransform(&filenameIO, FIJPEG_OP_NONE, crop, FALSE); } catch(const char *text) { FreeImage_OutputMessageProc(FIF_JPEG, text); return FALSE; } }
void ImageLoader:: compatible() { // BITMAP数据是从下往上的, 需要翻转 FreeImage_FlipVertical(mDIB); // FreeImage是BGR顺序, 与NVTT要求的一致, 而PVRT需要RGB顺序, 两者都需要32bits数据 if (config.imageCompressionType != CT_DXTC) { // 将BGR改为RGB, 交换R channel和B channel const unsigned bytesperpixel = FreeImage_GetBPP(mDIB) / 8; const unsigned height = FreeImage_GetHeight(mDIB); const unsigned pitch = FreeImage_GetPitch(mDIB); const unsigned lineSize = FreeImage_GetLine(mDIB); BYTE* line = FreeImage_GetBits(mDIB); for (unsigned y = 0; y < height; ++y, line += pitch) { for (BYTE* pixel = line; pixel < line + lineSize; pixel += bytesperpixel) { INPLACESWAP(pixel[0], pixel[2]); } } } if (FreeImage_GetBPP(mDIB) != 32) { FIBITMAP* convertDIB = FreeImage_ConvertTo32Bits(mDIB); FreeImage_Unload(mDIB); mDIB = convertDIB; } }
/** Build a crop string. @param crop Output crop string @param left Specifies the left position of the cropped rectangle @param top Specifies the top position of the cropped rectangle @param right Specifies the right position of the cropped rectangle @param bottom Specifies the bottom position of the cropped rectangle @param width Image width @param height Image height @return Returns TRUE if successful, returns FALSE otherwise */ static BOOL getCropString(char* crop, int* left, int* top, int* right, int* bottom, int width, int height) { if(!left || !top || !right || !bottom) { return FALSE; } *left = CLAMP(*left, 0, width); *top = CLAMP(*top, 0, height); // negative/zero right and bottom count from the edges inwards if(*right <= 0) { *right = width + *right; } if(*bottom <= 0) { *bottom = height + *bottom; } *right = CLAMP(*right, 0, width); *bottom = CLAMP(*bottom, 0, height); // test for empty rect if(((*left - *right) == 0) || ((*top - *bottom) == 0)) { return FALSE; } // normalize the rectangle if(*right < *left) { INPLACESWAP(*left, *right); } if(*bottom < *top) { INPLACESWAP(*top, *bottom); } // test for "noop" rect if(*left == 0 && *right == width && *top == 0 && *bottom == height) { return FALSE; } // build the crop option sprintf(crop, "%dx%d+%d+%d", *right - *left, *bottom - *top, *left, *top); return TRUE; }
/** Load uncompressed image pixels for 1-, 4-, 8-, 16-, 24- and 32-bit dib @param io FreeImage IO @param handle FreeImage IO handle @param dib Image to be loaded @param height Image height @param pitch Image pitch @param bit_count Image bit-depth (1-, 4-, 8-, 16-, 24- or 32-bit) @return Returns TRUE if successful, returns FALSE otherwise */ static BOOL LoadPixelData(FreeImageIO *io, fi_handle handle, FIBITMAP *dib, int height, unsigned pitch, unsigned bit_count) { unsigned count = 0; // Load pixel data // NB: height can be < 0 for BMP data if (height > 0) { count = io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle); if(count != 1) { return FALSE; } } else { int positiveHeight = abs(height); for (int c = 0; c < positiveHeight; ++c) { count = io->read_proc((void *)FreeImage_GetScanLine(dib, positiveHeight - c - 1), pitch, 1, handle); if(count != 1) { return FALSE; } } } // swap as needed #ifdef FREEIMAGE_BIGENDIAN if (bit_count == 16) { for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y); for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { SwapShort(pixel); pixel++; } } } #endif #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB if (bit_count == 24 || bit_count == 32) { for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { BYTE *pixel = FreeImage_GetScanLine(dib, y); for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { INPLACESWAP(pixel[0], pixel[2]); pixel += (bit_count >> 3); } } } #endif return TRUE; }
static FIBITMAP * LoadOS21XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) { FIBITMAP *dib = NULL; try { BITMAPINFOOS2_1X_HEADER bios2_1x; io->read_proc(&bios2_1x, sizeof(BITMAPINFOOS2_1X_HEADER), 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapOS21XHeader(&bios2_1x); #endif // keep some general information about the bitmap int used_colors = 0; int width = bios2_1x.biWidth; int height = bios2_1x.biHeight; int bit_count = bios2_1x.biBitCount; int pitch = CalculatePitch(CalculateLine(width, bit_count)); switch (bit_count) { case 1 : case 4 : case 8 : { used_colors = CalculateUsedPaletteEntries(bit_count); // allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette dib = FreeImage_Allocate(width, height, bit_count); if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = 0; pInfoHeader->biYPelsPerMeter = 0; // load the palette RGBQUAD *pal = FreeImage_GetPalette(dib); for (int count = 0; count < used_colors; count++) { FILE_BGR bgr; io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle); pal[count].rgbRed = bgr.r; pal[count].rgbGreen = bgr.g; pal[count].rgbBlue = bgr.b; } // Skip over the optional palette // A 24 or 32 bit DIB may contain a palette for faster color reduction io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); // read the pixel data if (height > 0) { io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle); } else { for (int c = 0; c < abs(height); ++c) { io->read_proc((void *)FreeImage_GetScanLine(dib, height - c - 1), pitch, 1, handle); } } return dib; } case 16 : { dib = FreeImage_Allocate(width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK); if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = 0; pInfoHeader->biYPelsPerMeter = 0; io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle); #ifdef FREEIMAGE_BIGENDIAN for(int y = 0; y < FreeImage_GetHeight(dib); y++) { WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y); for(int x = 0; x < FreeImage_GetWidth(dib); x++) { SwapShort(pixel); pixel++; } } #endif return dib; } case 24 : case 32 : { if( bit_count == 32 ) { dib = FreeImage_Allocate(width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_Allocate(width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = 0; pInfoHeader->biYPelsPerMeter = 0; // Skip over the optional palette // A 24 or 32 bit DIB may contain a palette for faster color reduction io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle); #ifdef FREEIMAGE_BIGENDIAN for(int y = 0; y < FreeImage_GetHeight(dib); y++) { BYTE *pixel = FreeImage_GetScanLine(dib, y); for(int x = 0; x < FreeImage_GetWidth(dib); x++) { INPLACESWAP(pixel[0], pixel[2]); pixel += (bit_count>>3); } } #endif // check if the bitmap contains transparency, if so enable it in the header FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA)); return dib; } } } catch(const char *message) { if(dib) FreeImage_Unload(dib); FreeImage_OutputMessageProc(s_format_id, message); } return NULL; }
static FIBITMAP * LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) { FIBITMAP *dib = NULL; try { // load the info header BITMAPINFOHEADER bih; io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapInfoHeader(&bih); #endif // keep some general information about the bitmap int used_colors = bih.biClrUsed; int width = bih.biWidth; int height = bih.biHeight; int bit_count = bih.biBitCount; int compression = bih.biCompression; int pitch = CalculatePitch(CalculateLine(width, bit_count)); switch (bit_count) { case 1 : case 4 : case 8 : { if ((used_colors <= 0) || (used_colors > CalculateUsedPaletteEntries(bit_count))) used_colors = CalculateUsedPaletteEntries(bit_count); // allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette dib = FreeImage_Allocate(width, height, bit_count); if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = bih.biXPelsPerMeter; pInfoHeader->biYPelsPerMeter = bih.biYPelsPerMeter; // load the palette io->seek_proc(handle, sizeof(BITMAPFILEHEADER) + bih.biSize, SEEK_SET); RGBQUAD *pal = FreeImage_GetPalette(dib); for (int count = 0; count < used_colors; count++) { FILE_BGR bgr; io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle); pal[count].rgbRed = bgr.r; pal[count].rgbGreen = bgr.g; pal[count].rgbBlue = bgr.b; } // seek to the actual pixel data. // this is needed because sometimes the palette is larger than the entries it contains predicts if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); // read the pixel data switch (compression) { case BI_RGB : if (height > 0) { io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle); } else { for (int c = 0; c < abs(height); ++c) { io->read_proc((void *)FreeImage_GetScanLine(dib, height - c - 1), pitch, 1, handle); } } return dib; case BI_RLE4 : { BYTE status_byte = 0; BYTE second_byte = 0; int scanline = 0; int bits = 0; BOOL low_nibble = FALSE; for (;;) { io->read_proc(&status_byte, sizeof(BYTE), 1, handle); switch (status_byte) { case RLE_COMMAND : io->read_proc(&status_byte, sizeof(BYTE), 1, handle); switch (status_byte) { case RLE_ENDOFLINE : bits = 0; scanline++; low_nibble = FALSE; break; case RLE_ENDOFBITMAP : return (FIBITMAP *)dib; case RLE_DELTA : { // read the delta values BYTE delta_x; BYTE delta_y; io->read_proc(&delta_x, sizeof(BYTE), 1, handle); io->read_proc(&delta_y, sizeof(BYTE), 1, handle); // apply them bits += delta_x / 2; scanline += delta_y; break; } default : io->read_proc(&second_byte, sizeof(BYTE), 1, handle); BYTE *sline = FreeImage_GetScanLine(dib, scanline); for (int i = 0; i < status_byte; i++) { if (low_nibble) { *(sline + bits) |= LOWNIBBLE(second_byte); if (i != status_byte - 1) io->read_proc(&second_byte, sizeof(BYTE), 1, handle); bits++; } else { *(sline + bits) |= HINIBBLE(second_byte); } low_nibble = !low_nibble; } if (((status_byte / 2) & 1 ) == 1) io->read_proc(&second_byte, sizeof(BYTE), 1, handle); break; }; break; default : { BYTE *sline = FreeImage_GetScanLine(dib, scanline); io->read_proc(&second_byte, sizeof(BYTE), 1, handle); for (unsigned i = 0; i < status_byte; i++) { if (low_nibble) { *(sline + bits) |= LOWNIBBLE(second_byte); bits++; } else { *(sline + bits) |= HINIBBLE(second_byte); } low_nibble = !low_nibble; } } break; }; } break; } case BI_RLE8 : { BYTE status_byte = 0; BYTE second_byte = 0; int scanline = 0; int bits = 0; for (;;) { io->read_proc(&status_byte, sizeof(BYTE), 1, handle); switch (status_byte) { case RLE_COMMAND : io->read_proc(&status_byte, sizeof(BYTE), 1, handle); switch (status_byte) { case RLE_ENDOFLINE : bits = 0; scanline++; break; case RLE_ENDOFBITMAP : return (FIBITMAP *)dib; case RLE_DELTA : { // read the delta values BYTE delta_x; BYTE delta_y; io->read_proc(&delta_x, sizeof(BYTE), 1, handle); io->read_proc(&delta_y, sizeof(BYTE), 1, handle); // apply them bits += delta_x; scanline += delta_y; break; } default : io->read_proc((void *)(FreeImage_GetScanLine(dib, scanline) + bits), sizeof(BYTE) * status_byte, 1, handle); // align run length to even number of bytes if ((status_byte & 1) == 1) io->read_proc(&second_byte, sizeof(BYTE), 1, handle); bits += status_byte; break; }; break; default : BYTE *sline = FreeImage_GetScanLine(dib, scanline); io->read_proc(&second_byte, sizeof(BYTE), 1, handle); for (unsigned i = 0; i < status_byte; i++) { *(sline + bits) = second_byte; bits++; } break; }; } break; } default : throw "compression type not supported"; } break; } case 16 : { if (bih.biCompression == 3) { DWORD bitfields[3]; io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle); dib = FreeImage_Allocate(width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]); } else { dib = FreeImage_Allocate(width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK); } if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = bih.biXPelsPerMeter; pInfoHeader->biYPelsPerMeter = bih.biYPelsPerMeter; if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle); #ifdef FREEIMAGE_BIGENDIAN for(int y = 0; y < FreeImage_GetHeight(dib); y++) { WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y); for(int x = 0; x < FreeImage_GetWidth(dib); x++) { SwapShort(pixel); pixel++; } } #endif return dib; } case 24 : case 32 : { if( bit_count == 32 ) { dib = FreeImage_Allocate(width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_Allocate(width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = bih.biXPelsPerMeter; pInfoHeader->biYPelsPerMeter = bih.biYPelsPerMeter; // Skip over the optional palette // A 24 or 32 bit DIB may contain a palette for faster color reduction if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); // read in the bitmap bits io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle); #ifdef FREEIMAGE_BIGENDIAN for(int y = 0; y < FreeImage_GetHeight(dib); y++) { BYTE *pixel = FreeImage_GetScanLine(dib, y); for(int x = 0; x < FreeImage_GetWidth(dib); x++) { INPLACESWAP(pixel[0], pixel[2]); pixel += (bit_count>>3); } } #endif // check if the bitmap contains transparency, if so enable it in the header FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA)); return dib; } } } catch(const char *message) { if(dib) FreeImage_Unload(dib); FreeImage_OutputMessageProc(s_format_id, message); } return NULL; }
static HBITMAP GetDibSection(FIBITMAP *src, HDC hdc, int left, int top, int right, int bottom) { if(!src) return NULL; unsigned bpp = FreeImage_GetBPP(src); if(bpp <=4) return NULL; // normalize the rectangle if(right < left) INPLACESWAP(left, right); if(bottom < top) INPLACESWAP(top, bottom); // check the size of the sub image int src_width = FreeImage_GetWidth(src); int src_height = FreeImage_GetHeight(src); int src_pitch = FreeImage_GetPitch(src); if((left < 0) || (right > src_width) || (top < 0) || (bottom > src_height)) return NULL; // allocate the sub image int dst_width = (right - left); int dst_height = (bottom - top); BITMAPINFO *info = (BITMAPINFO *) malloc(sizeof(BITMAPINFO) + (FreeImage_GetColorsUsed(src) * sizeof(RGBQUAD))); BITMAPINFOHEADER *bih = (BITMAPINFOHEADER *)info; bih->biSize = sizeof(BITMAPINFOHEADER); bih->biWidth = dst_width; bih->biHeight = dst_height; bih->biPlanes = 1; bih->biBitCount = bpp; bih->biCompression = BI_RGB; bih->biSizeImage = 0; bih->biXPelsPerMeter = 0; bih->biYPelsPerMeter = 0; bih->biClrUsed = 0; // Always use the whole palette. bih->biClrImportant = 0; // copy the palette if(bpp < 16) memcpy(info->bmiColors, FreeImage_GetPalette(src), FreeImage_GetColorsUsed(src) * sizeof(RGBQUAD)); BYTE *dst_bits; HBITMAP hbitmap = CreateDIBSection(hdc, info, DIB_RGB_COLORS, (void **) &dst_bits, NULL, 0); free(info); if(hbitmap == NULL || dst_bits == NULL) return NULL; // get the pointers to the bits and such BYTE *src_bits = FreeImage_GetScanLine(src, src_height - top - dst_height); // calculate the number of bytes per pixel unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src); // point to x = left src_bits += (left * bytespp); int dst_line = (dst_width * bpp + 7) / 8; int dst_pitch = (dst_line + 3) & ~3; for(int y = 0; y < dst_height; y++) memcpy(dst_bits + (y * dst_pitch), src_bits + (y * src_pitch), dst_line); return hbitmap; }
FIBITMAP * DLL_CALLCONV FreeImage_RescaleRect(FIBITMAP *src, int dst_width, int dst_height, int src_left, int src_top, int src_right, int src_bottom, FREE_IMAGE_FILTER filter, unsigned flags) { FIBITMAP *dst = NULL; const int src_width = FreeImage_GetWidth(src); const int src_height = FreeImage_GetHeight(src); if (!FreeImage_HasPixels(src) || (dst_width <= 0) || (dst_height <= 0) || (src_width <= 0) || (src_height <= 0)) { return NULL; } // normalize the rectangle if (src_right < src_left) { INPLACESWAP(src_left, src_right); } if (src_bottom < src_top) { INPLACESWAP(src_top, src_bottom); } // check the size of the sub image if((src_left < 0) || (src_right > src_width) || (src_top < 0) || (src_bottom > src_height)) { return NULL; } // select the filter CGenericFilter *pFilter = NULL; switch (filter) { case FILTER_BOX: pFilter = new(std::nothrow) CBoxFilter(); break; case FILTER_BICUBIC: pFilter = new(std::nothrow) CBicubicFilter(); break; case FILTER_BILINEAR: pFilter = new(std::nothrow) CBilinearFilter(); break; case FILTER_BSPLINE: pFilter = new(std::nothrow) CBSplineFilter(); break; case FILTER_CATMULLROM: pFilter = new(std::nothrow) CCatmullRomFilter(); break; case FILTER_LANCZOS3: pFilter = new(std::nothrow) CLanczos3Filter(); break; } if (!pFilter) { return NULL; } CResizeEngine Engine(pFilter); dst = Engine.scale(src, dst_width, dst_height, src_left, src_top, src_right - src_left, src_bottom - src_top, flags); delete pFilter; if ((flags & FI_RESCALE_OMIT_METADATA) != FI_RESCALE_OMIT_METADATA) { // copy metadata from src to dst FreeImage_CloneMetadata(dst, src); } return dst; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if (handle) { FIBITMAP *dib = NULL; try { // set up the jpeglib structures struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; // step 1: allocate and initialize JPEG decompression object cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = jpeg_error_exit; jerr.output_message = jpeg_output_message; jpeg_create_decompress(&cinfo); // step 2a: specify data source (eg, a handle) jpeg_freeimage_src(&cinfo, handle, io); // step 2b: save special markers for later reading jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF); for(int m = 0; m < 16; m++) { jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF); } // step 3: read handle parameters with jpeg_read_header() jpeg_read_header(&cinfo, TRUE); // step 4: set parameters for decompression unsigned int scale_denom = 1; // fraction by which to scale image int requested_size = flags >> 16; // requested user size in pixels if(requested_size > 0) { // the JPEG codec can perform x2, x4 or x8 scaling on loading // try to find the more appropriate scaling according to user's need double scale = MAX((double)cinfo.image_width, (double)cinfo.image_height) / (double)requested_size; if(scale >= 8) { scale_denom = 8; } else if(scale >= 4) { scale_denom = 4; } else if(scale >= 2) { scale_denom = 2; } } cinfo.scale_num = 1; cinfo.scale_denom = scale_denom; if ((flags & JPEG_ACCURATE) != JPEG_ACCURATE) { cinfo.dct_method = JDCT_IFAST; cinfo.do_fancy_upsampling = FALSE; } // step 5a: start decompressor and calculate output width and height jpeg_start_decompress(&cinfo); // step 5b: allocate dib and init header if((cinfo.num_components == 4) && (cinfo.out_color_space == JCS_CMYK)) { // CMYK image if((flags & JPEG_CMYK) == JPEG_CMYK) { // load as CMYK dib = FreeImage_Allocate(cinfo.output_width, cinfo.output_height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); if(!dib) return NULL; FreeImage_GetICCProfile(dib)->flags |= FIICC_COLOR_IS_CMYK; } else { // load as CMYK and convert to RGB dib = FreeImage_Allocate(cinfo.output_width, cinfo.output_height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); if(!dib) return NULL; } } else { // RGB or greyscale image dib = FreeImage_Allocate(cinfo.output_width, cinfo.output_height, 8 * cinfo.num_components, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); if(!dib) return NULL; if (cinfo.num_components == 1) { // build a greyscale palette RGBQUAD *colors = FreeImage_GetPalette(dib); for (int i = 0; i < 256; i++) { colors[i].rgbRed = (BYTE)i; colors[i].rgbGreen = (BYTE)i; colors[i].rgbBlue = (BYTE)i; } } } if(scale_denom != 1) { // store original size info if a scaling was requested store_size_info(dib, cinfo.image_width, cinfo.image_height); } // step 5c: handle metrices if (cinfo.density_unit == 1) { // dots/inch FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)cinfo.X_density) / 0.0254000 + 0.5)); FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)cinfo.Y_density) / 0.0254000 + 0.5)); } else if (cinfo.density_unit == 2) { // dots/cm FreeImage_SetDotsPerMeterX(dib, (unsigned) (cinfo.X_density * 100)); FreeImage_SetDotsPerMeterY(dib, (unsigned) (cinfo.Y_density * 100)); } // step 6a: while (scan lines remain to be read) jpeg_read_scanlines(...); if((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) != JPEG_CMYK)) { // convert from CMYK to RGB JSAMPARRAY buffer; // output row buffer unsigned row_stride; // physical row width in output buffer // JSAMPLEs per row in output buffer row_stride = cinfo.output_width * cinfo.output_components; // make a one-row-high sample array that will go away when done with image buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); while (cinfo.output_scanline < cinfo.output_height) { JSAMPROW src = buffer[0]; JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1); jpeg_read_scanlines(&cinfo, buffer, 1); for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { WORD K = (WORD)src[3]; dst[FI_RGBA_RED] = (BYTE)((K * src[0]) / 255); dst[FI_RGBA_GREEN] = (BYTE)((K * src[1]) / 255); dst[FI_RGBA_BLUE] = (BYTE)((K * src[2]) / 255); src += 4; dst += 3; } } } else { // normal case (RGB or greyscale image) while (cinfo.output_scanline < cinfo.output_height) { JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1); jpeg_read_scanlines(&cinfo, &dst, 1); } // step 6b: swap red and blue components (see LibJPEG/jmorecfg.h: #define RGB_RED, ...) // The default behavior of the JPEG library is kept "as is" because LibTIFF uses // LibJPEG "as is". #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR if(cinfo.num_components == 3) { for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { BYTE *target = FreeImage_GetScanLine(dib, y); for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { INPLACESWAP(target[0], target[2]); target += 3; } } } #endif } // step 7: read special markers read_markers(&cinfo, dib); // step 8: finish decompression jpeg_finish_decompress(&cinfo); // step 9: release JPEG decompression object jpeg_destroy_decompress(&cinfo); // check for automatic Exif rotation if((flags & JPEG_EXIFROTATE) == JPEG_EXIFROTATE) { rotate_exif(&dib); } // everything went well. return the loaded dib return (FIBITMAP *)dib; } catch (...) { if(NULL != dib) { FreeImage_Unload(dib); } } } return NULL; }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { if ((dib) && (handle)) { try { // Check dib format const char *sError = "only 24-bit highcolor or 8-bit greyscale/palette bitmaps can be saved as JPEG"; FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib); WORD bpp = (WORD)FreeImage_GetBPP(dib); if ((bpp != 24) && (bpp != 8)) throw sError; if(bpp == 8) { // allow grey, reverse grey and palette if ((color_type != FIC_MINISBLACK) && (color_type != FIC_MINISWHITE) && (color_type != FIC_PALETTE)) throw sError; } struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; // Step 1: allocate and initialize JPEG compression object cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = jpeg_error_exit; jerr.output_message = jpeg_output_message; // Now we can initialize the JPEG compression object jpeg_create_compress(&cinfo); // Step 2: specify data destination (eg, a file) jpeg_freeimage_dst(&cinfo, handle, io); // Step 3: set parameters for compression cinfo.image_width = FreeImage_GetWidth(dib); cinfo.image_height = FreeImage_GetHeight(dib); switch(color_type) { case FIC_MINISBLACK : case FIC_MINISWHITE : cinfo.in_color_space = JCS_GRAYSCALE; cinfo.input_components = 1; break; default : cinfo.in_color_space = JCS_RGB; cinfo.input_components = 3; break; } jpeg_set_defaults(&cinfo); // progressive-JPEG support if((flags & JPEG_PROGRESSIVE) == JPEG_PROGRESSIVE) { jpeg_simple_progression(&cinfo); } // Set JFIF density parameters from the DIB data cinfo.X_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterX(dib)); cinfo.Y_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterY(dib)); cinfo.density_unit = 1; // dots / inch // set subsampling options if required if(cinfo.in_color_space == JCS_RGB) { if((flags & JPEG_SUBSAMPLING_411) == JPEG_SUBSAMPLING_411) { // 4:1:1 (4x1 1x1 1x1) - CrH 25% - CbH 25% - CrV 100% - CbV 100% // the horizontal color resolution is quartered cinfo.comp_info[0].h_samp_factor = 4; // Y cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } else if((flags & JPEG_SUBSAMPLING_420) == JPEG_SUBSAMPLING_420) { // 4:2:0 (2x2 1x1 1x1) - CrH 50% - CbH 50% - CrV 50% - CbV 50% // the chrominance resolution in both the horizontal and vertical directions is cut in half cinfo.comp_info[0].h_samp_factor = 2; // Y cinfo.comp_info[0].v_samp_factor = 2; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } else if((flags & JPEG_SUBSAMPLING_422) == JPEG_SUBSAMPLING_422){ //2x1 (low) // 4:2:2 (2x1 1x1 1x1) - CrH 50% - CbH 50% - CrV 100% - CbV 100% // half of the horizontal resolution in the chrominance is dropped (Cb & Cr), // while the full resolution is retained in the vertical direction, with respect to the luminance cinfo.comp_info[0].h_samp_factor = 2; // Y cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } else if((flags & JPEG_SUBSAMPLING_444) == JPEG_SUBSAMPLING_444){ //1x1 (no subsampling) // 4:4:4 (1x1 1x1 1x1) - CrH 100% - CbH 100% - CrV 100% - CbV 100% // the resolution of chrominance information (Cb & Cr) is preserved // at the same rate as the luminance (Y) information cinfo.comp_info[0].h_samp_factor = 1; // Y cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } } // Step 4: set quality // the first 7 bits are reserved for low level quality settings // the other bits are high level (i.e. enum-ish) int quality; if ((flags & JPEG_QUALITYBAD) == JPEG_QUALITYBAD) { quality = 10; } else if ((flags & JPEG_QUALITYAVERAGE) == JPEG_QUALITYAVERAGE) { quality = 25; } else if ((flags & JPEG_QUALITYNORMAL) == JPEG_QUALITYNORMAL) { quality = 50; } else if ((flags & JPEG_QUALITYGOOD) == JPEG_QUALITYGOOD) { quality = 75; } else if ((flags & JPEG_QUALITYSUPERB) == JPEG_QUALITYSUPERB) { quality = 100; } else { if ((flags & 0x7F) == 0) { quality = 75; } else { quality = flags & 0x7F; } } jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */ // Step 5: Start compressor jpeg_start_compress(&cinfo, TRUE); // Step 6: Write special markers write_markers(&cinfo, dib); // Step 7: while (scan lines remain to be written) if(color_type == FIC_RGB) { // 24-bit RGB image : need to swap red and blue channels unsigned pitch = FreeImage_GetPitch(dib); BYTE *target = (BYTE*)malloc(pitch * sizeof(BYTE)); if (target == NULL) { throw FI_MSG_ERROR_MEMORY; } while (cinfo.next_scanline < cinfo.image_height) { // get a copy of the scanline memcpy(target, FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1), pitch); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // swap R and B channels BYTE *target_p = target; for(unsigned x = 0; x < cinfo.image_width; x++) { INPLACESWAP(target_p[0], target_p[2]); target_p += 3; } #endif // write the scanline jpeg_write_scanlines(&cinfo, &target, 1); } free(target); } else if(color_type == FIC_MINISBLACK) { // 8-bit standard greyscale images while (cinfo.next_scanline < cinfo.image_height) { JSAMPROW b = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); jpeg_write_scanlines(&cinfo, &b, 1); } } else if(color_type == FIC_PALETTE) { // 8-bit palettized images are converted to 24-bit images RGBQUAD *palette = FreeImage_GetPalette(dib); BYTE *target = (BYTE*)malloc(cinfo.image_width * 3); if (target == NULL) { throw FI_MSG_ERROR_MEMORY; } while (cinfo.next_scanline < cinfo.image_height) { BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); FreeImage_ConvertLine8To24(target, source, cinfo.image_width, palette); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // swap R and B channels BYTE *target_p = target; for(unsigned x = 0; x < cinfo.image_width; x++) { INPLACESWAP(target_p[0], target_p[2]); target_p += 3; } #endif jpeg_write_scanlines(&cinfo, &target, 1); } free(target); } else if(color_type == FIC_MINISWHITE) { // reverse 8-bit greyscale image, so reverse grey value on the fly unsigned i; BYTE reverse[256]; BYTE *target = (BYTE *)malloc(cinfo.image_width); if (target == NULL) { throw FI_MSG_ERROR_MEMORY; } for(i = 0; i < 256; i++) { reverse[i] = (BYTE)(255 - i); } while(cinfo.next_scanline < cinfo.image_height) { BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); for(i = 0; i < cinfo.image_width; i++) { target[i] = reverse[ source[i] ]; } jpeg_write_scanlines(&cinfo, &target, 1); } free(target); } // Step 8: Finish compression jpeg_finish_compress(&cinfo); // Step 9: release JPEG compression object jpeg_destroy_compress(&cinfo); return TRUE; } catch (const char *text) { FreeImage_OutputMessageProc(s_format_id, text); return FALSE; } catch (FREE_IMAGE_FORMAT) { return FALSE; } } return FALSE; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if (handle != NULL) { FIBITMAP *dib = NULL; DWORD type, size; io->read_proc(&type, 4, 1, handle); #ifndef FREEIMAGE_BIGENDIAN SwapLong(&type); #endif if(type != ID_FORM) return NULL; io->read_proc(&size, 4, 1, handle); #ifndef FREEIMAGE_BIGENDIAN SwapLong(&size); #endif io->read_proc(&type, 4, 1, handle); #ifndef FREEIMAGE_BIGENDIAN SwapLong(&type); #endif if((type != ID_ILBM) && (type != ID_PBM)) return NULL; size -= 4; unsigned width = 0, height = 0, planes = 0, depth = 0, comp = 0; while (size) { DWORD ch_type,ch_size; io->read_proc(&ch_type, 4, 1, handle); #ifndef FREEIMAGE_BIGENDIAN SwapLong(&ch_type); #endif io->read_proc(&ch_size,4,1,handle ); #ifndef FREEIMAGE_BIGENDIAN SwapLong(&ch_size); #endif unsigned ch_end = io->tell_proc(handle) + ch_size; if (ch_type == ID_BMHD) { // Bitmap Header if (dib) FreeImage_Unload(dib); BMHD bmhd; io->read_proc(&bmhd, sizeof(bmhd), 1, handle); #ifndef FREEIMAGE_BIGENDIAN SwapHeader(&bmhd); #endif width = bmhd.w; height = bmhd.h; planes = bmhd.nPlanes; comp = bmhd.compression; if(bmhd.masking & 1) planes++; // there is a mask ( 'stencil' ) if (planes > 8 && planes != 24) return NULL; depth = planes > 8 ? 24 : 8; if( depth == 24 ) { dib = FreeImage_Allocate(width, height, depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_Allocate(width, height, depth); } } else if (ch_type == ID_CMAP) { // Palette (Color Map) if (!dib) return NULL; RGBQUAD *pal = FreeImage_GetPalette(dib); for (unsigned k = 0; k < ch_size / 3; k++) { io->read_proc(&pal[k].rgbRed, 1, 1, handle ); io->read_proc(&pal[k].rgbGreen, 1, 1, handle ); io->read_proc(&pal[k].rgbBlue, 1, 1, handle ); } } else if (ch_type == ID_BODY) { if (!dib) return NULL; if (type == ID_PBM) { // NON INTERLACED (LBM) unsigned line = FreeImage_GetLine(dib) + 1 & ~1; for (unsigned i = 0; i < FreeImage_GetHeight(dib); i++) { BYTE *bits = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - i - 1); if (comp == 1) { // use RLE compression DWORD number_of_bytes_written = 0; BYTE rle_count; BYTE byte; while (number_of_bytes_written < line) { io->read_proc(&rle_count, 1, 1, handle); if (rle_count < 128) { for (int k = 0; k < rle_count + 1; k++) { io->read_proc(&byte, 1, 1, handle); bits[number_of_bytes_written++] += byte; } } else if (rle_count > 128) { io->read_proc(&byte, 1, 1, handle); for (int k = 0; k < 257 - rle_count; k++) { bits[number_of_bytes_written++] += byte; } } } } else { // don't use compression io->read_proc(bits, line, 1, handle); } } return dib; } else { // INTERLACED (ILBM) unsigned pixel_size = depth/8; unsigned n_width=(width+15)&~15; unsigned plane_size = n_width/8; unsigned src_size = plane_size * planes; BYTE *src = (BYTE*)malloc(src_size); BYTE *dest = FreeImage_GetBits(dib); dest += FreeImage_GetPitch(dib) * height; for (unsigned y = 0; y < height; y++) { dest -= FreeImage_GetPitch(dib); // read all planes in one hit, // 'coz PSP compresses across planes... if (comp) { // unpacker algorithm for(unsigned x = 0; x < src_size;) { // read the next source byte into t signed char t = 0; io->read_proc(&t, 1, 1, handle); if (t >= 0) { // t = [0..127] => copy the next t+1 bytes literally unsigned size_to_read = t + 1; if((size_to_read + x) > src_size) { // sanity check for buffer overruns size_to_read = src_size - x; io->read_proc(src + x, size_to_read, 1, handle); x += (t + 1); } else { io->read_proc(src + x, size_to_read, 1, handle); x += size_to_read; } } else if (t != -128) { // t = [-1..-127] => replicate the next byte -t+1 times BYTE b = 0; io->read_proc(&b, 1, 1, handle); unsigned size_to_copy = (unsigned)(-(int)t + 1); if((size_to_copy + x) > src_size) { // sanity check for buffer overruns size_to_copy = src_size - x; memset(src + x, b, size_to_copy); x += (unsigned)(-(int)t + 1); } else { memset(src + x, b, size_to_copy); x += size_to_copy; } } // t = -128 => noop } } else { io->read_proc(src, src_size, 1, handle); } // lazy planar->chunky... for (unsigned x = 0; x < width; x++) { for (unsigned n = 0; n < planes; n++) { BYTE bit = (BYTE)( src[n * plane_size + (x / 8)] >> ((x^7) & 7) ); dest[x * pixel_size + (n / 8)] |= (bit & 1) << (n & 7); } } #ifndef FREEIMAGE_BIGENDIAN if (depth == 24) { for (unsigned x = 0; x < width; ++x){ INPLACESWAP(dest[x * 3], dest[x * 3 + 2]); } } #endif } free(src); return dib; } } // Every odd-length chunk is followed by a 0 pad byte. This pad // byte is not counted in ch_size. if (ch_size & 1) { ch_size++; ch_end++; } io->seek_proc(handle, ch_end - io->tell_proc(handle), SEEK_CUR); size -= ch_size + 8; } if (dib) FreeImage_Unload(dib); }