Alien-FreeImage
view release on metacpan or search on metacpan
src/Source/FreeImage/PluginJPEG.cpp view on Meta::CPAN
Validate(FreeImageIO *io, fi_handle handle) {
BYTE jpeg_signature[] = { 0xFF, 0xD8 };
BYTE signature[2] = { 0, 0 };
io->read_proc(signature, 1, sizeof(jpeg_signature), handle);
return (memcmp(jpeg_signature, signature, sizeof(jpeg_signature)) == 0);
}
static BOOL DLL_CALLCONV
SupportsExportDepth(int depth) {
return (
(depth == 8) ||
(depth == 24)
);
}
static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type) {
return (type == FIT_BITMAP) ? TRUE : FALSE;
}
static BOOL DLL_CALLCONV
SupportsICCProfiles() {
return TRUE;
}
static BOOL DLL_CALLCONV
SupportsNoPixels() {
return TRUE;
}
// ----------------------------------------------------------
static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
if (handle) {
FIBITMAP *dib = NULL;
BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
// set up the jpeglib structures
struct jpeg_decompress_struct cinfo;
ErrorManager fi_error_mgr;
try {
// step 1: allocate and initialize JPEG decompression object
// we set up the normal JPEG error routines, then override error_exit & output_message
cinfo.err = jpeg_std_error(&fi_error_mgr.pub);
fi_error_mgr.pub.error_exit = jpeg_error_exit;
fi_error_mgr.pub.output_message = jpeg_output_message;
// establish the setjmp return context for jpeg_error_exit to use
if (setjmp(fi_error_mgr.setjmp_buffer)) {
// If we get here, the JPEG code has signaled an error.
// We need to clean up the JPEG object, close the input file, and return.
jpeg_destroy_decompress(&cinfo);
throw (const char*)NULL;
}
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;
}
if ((flags & JPEG_GREYSCALE) == JPEG_GREYSCALE) {
// force loading as a 8-bit greyscale image
cinfo.out_color_space = JCS_GRAYSCALE;
}
// step 5a: start decompressor and calculate output width and height
jpeg_start_decompress(&cinfo);
// step 5b: allocate dib and init header
if((cinfo.output_components == 4) && (cinfo.out_color_space == JCS_CMYK)) {
// CMYK image
if((flags & JPEG_CMYK) == JPEG_CMYK) {
// load as CMYK
dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
if(!dib) throw FI_MSG_ERROR_DIB_MEMORY;
FreeImage_GetICCProfile(dib)->flags |= FIICC_COLOR_IS_CMYK;
} else {
// load as CMYK and convert to RGB
dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
if(!dib) throw FI_MSG_ERROR_DIB_MEMORY;
}
} else {
// RGB or greyscale image
dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 8 * cinfo.output_components, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
if(!dib) throw FI_MSG_ERROR_DIB_MEMORY;
if (cinfo.output_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 6: read special markers
read_markers(&cinfo, dib);
// --- header only mode => clean-up and return
if (header_only) {
// release JPEG decompression object
jpeg_destroy_decompress(&cinfo);
// return header data
return dib;
}
// step 7a: 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);
src/Source/FreeImage/PluginJPEG.cpp view on Meta::CPAN
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 7b: 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
SwapRedBlue32(dib);
#endif
}
// step 8: finish decompression
jpeg_finish_decompress(&cinfo);
// step 9: release JPEG decompression object
jpeg_destroy_decompress(&cinfo);
// check for automatic Exif rotation
if(!header_only && ((flags & JPEG_EXIFROTATE) == JPEG_EXIFROTATE)) {
RotateExif(&dib);
}
// everything went well. return the loaded dib
return dib;
} catch (const char *text) {
jpeg_destroy_decompress(&cinfo);
if(NULL != dib) {
FreeImage_Unload(dib);
}
if(NULL != text) {
FreeImage_OutputMessageProc(s_format_id, text);
}
}
}
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;
ErrorManager fi_error_mgr;
// Step 1: allocate and initialize JPEG compression object
// we set up the normal JPEG error routines, then override error_exit & output_message
cinfo.err = jpeg_std_error(&fi_error_mgr.pub);
fi_error_mgr.pub.error_exit = jpeg_error_exit;
fi_error_mgr.pub.output_message = jpeg_output_message;
// establish the setjmp return context for jpeg_error_exit to use
if (setjmp(fi_error_mgr.setjmp_buffer)) {
// If we get here, the JPEG code has signaled an error.
// We need to clean up the JPEG object, close the input file, and return.
jpeg_destroy_compress(&cinfo);
throw (const char*)NULL;
}
// 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);
}
// compute optimal Huffman coding tables for the image
if((flags & JPEG_OPTIMIZE) == JPEG_OPTIMIZE) {
cinfo.optimize_coding = TRUE;
}
// 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
// thumbnail support (JFIF 1.02 extension markers)
if(FreeImage_GetThumbnail(dib) != NULL) {
cinfo.write_JFIF_header = 1; //<### force it, though when color is CMYK it will be incorrect
cinfo.JFIF_minor_version = 2;
}
// baseline JPEG support
if ((flags & JPEG_BASELINE) == JPEG_BASELINE) {
cinfo.write_JFIF_header = 0; // No marker for non-JFIF colorspaces
cinfo.write_Adobe_marker = 0; // write no Adobe marker by default
}
// set subsampling options if required
src/Source/FreeImage/PluginJPEG.cpp view on Meta::CPAN
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
if ((flags & JPEG_BASELINE) != JPEG_BASELINE) {
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) {
if(text) {
FreeImage_OutputMessageProc(s_format_id, text);
}
return FALSE;
}
}
return FALSE;
}
// ==========================================================
// Init
// ==========================================================
void DLL_CALLCONV
InitJPEG(Plugin *plugin, int format_id) {
s_format_id = format_id;
plugin->format_proc = Format;
plugin->description_proc = Description;
plugin->extension_proc = Extension;
plugin->regexpr_proc = RegExpr;
plugin->open_proc = NULL;
plugin->close_proc = NULL;
plugin->pagecount_proc = NULL;
plugin->pagecapability_proc = NULL;
plugin->load_proc = Load;
plugin->save_proc = Save;
plugin->validate_proc = Validate;
plugin->mime_proc = MimeType;
plugin->supports_export_bpp_proc = SupportsExportDepth;
plugin->supports_export_type_proc = SupportsExportType;
( run in 0.535 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )