summaryrefslogtreecommitdiff
path: root/plugins/AdvaImg/src/FreeImageToolkit
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/AdvaImg/src/FreeImageToolkit')
-rw-r--r--plugins/AdvaImg/src/FreeImageToolkit/Background.cpp12
-rw-r--r--plugins/AdvaImg/src/FreeImageToolkit/CopyPaste.cpp116
-rw-r--r--plugins/AdvaImg/src/FreeImageToolkit/JPEGTransform.cpp1230
-rw-r--r--plugins/AdvaImg/src/FreeImageToolkit/Rescale.cpp41
-rw-r--r--plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp4114
-rw-r--r--plugins/AdvaImg/src/FreeImageToolkit/Resize.h392
6 files changed, 3080 insertions, 2825 deletions
diff --git a/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp b/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp
index 08cdd4473b..06b31aa332 100644
--- a/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp
+++ b/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp
@@ -215,14 +215,13 @@ static BOOL
FillBackgroundBitmap(FIBITMAP *dib, const RGBQUAD *color, int options) {
if ((!dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
- return FALSE;
+ return FALSE;;
}
if (!color) {
return FALSE;
}
- RGBQUAD blend;
const RGBQUAD *color_intl = color;
unsigned bpp = FreeImage_GetBPP(dib);
unsigned width = FreeImage_GetWidth(dib);
@@ -251,7 +250,7 @@ FillBackgroundBitmap(FIBITMAP *dib, const RGBQUAD *color, int options) {
// release, just assume to have an unicolor background and fill
// all with an 'alpha-blended' color.
if (color->rgbReserved < 255) {
-
+
// If we will draw on an unicolor background, it's
// faster to draw opaque with an alpha blended color.
// So, first get the color from the first pixel in the
@@ -259,24 +258,25 @@ FillBackgroundBitmap(FIBITMAP *dib, const RGBQUAD *color, int options) {
RGBQUAD bgcolor;
if (bpp == 8) {
bgcolor = FreeImage_GetPalette(dib)[*src_bits];
- } else {
+ } else {
bgcolor.rgbBlue = src_bits[FI_RGBA_BLUE];
bgcolor.rgbGreen = src_bits[FI_RGBA_GREEN];
bgcolor.rgbRed = src_bits[FI_RGBA_RED];
bgcolor.rgbReserved = 0xFF;
}
+ RGBQUAD blend;
GetAlphaBlendedColor(&bgcolor, color_intl, &blend);
color_intl = &blend;
}
}
-
+
int index = (bpp <= 8) ? GetPaletteIndex(dib, color_intl, options, &color_type) : 0;
if (index == -1) {
// No palette index found for a palletized
// image. This should never happen...
return FALSE;
}
-
+
// first, build the first scanline (line 0)
switch (bpp) {
case 1: {
diff --git a/plugins/AdvaImg/src/FreeImageToolkit/CopyPaste.cpp b/plugins/AdvaImg/src/FreeImageToolkit/CopyPaste.cpp
index e4b8155739..d05a5dfdc8 100644
--- a/plugins/AdvaImg/src/FreeImageToolkit/CopyPaste.cpp
+++ b/plugins/AdvaImg/src/FreeImageToolkit/CopyPaste.cpp
@@ -6,6 +6,7 @@
// - Hervé Drolon (drolon@infonie.fr)
// - Manfred Tausch (manfred.tausch@t-online.de)
// - Riley McNiff (rmcniff@marexgroup.com)
+// - Carsten Klein (cklein05@users.sourceforge.net)
//
// This file is part of FreeImage 3
//
@@ -92,7 +93,6 @@ Combine1(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned
static BOOL
Combine4(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
-
int swapTable[16];
BOOL bOddStart, bOddEnd;
@@ -744,4 +744,118 @@ FreeImage_Paste(FIBITMAP *dst, FIBITMAP *src, int left, int top, int alpha) {
return bResult;
}
+// ----------------------------------------------------------
+
+/** @brief Creates a dynamic read/write view into a FreeImage bitmap.
+
+ A dynamic view is a FreeImage bitmap with its own width and height, that,
+ however, shares its bits with another FreeImage bitmap. Typically, views
+ are used to define one or more rectangular sub-images of an existing
+ bitmap. All FreeImage operations, like saving, displaying and all the
+ toolkit functions, when applied to the view, only affect the view's
+ rectangular area.
+
+ Although the view's backing image's bits not need to be copied around,
+ which makes the view much faster than similar solutions using
+ FreeImage_Copy, a view uses some private memory that needs to be freed by
+ calling FreeImage_Unload on the view's handle to prevent memory leaks.
+
+ Only the backing image's pixels are shared by the view. For all other image
+ data, notably for the resolution, background color, color palette,
+ transparency table and for the ICC profile, the view gets a private copy
+ of the data. By default, the backing image's metadata is NOT copied to
+ the view.
+
+ As with all FreeImage functions that take a rectangle region, top and left
+ positions are included, whereas right and bottom positions are excluded
+ from the rectangle area.
+
+ Since the memory block shared by the backing image and the view must start
+ at a byte boundary, the value of parameter left must be a multiple of 8
+ for 1-bit images and a multiple of 2 for 4-bit images.
+
+ @param dib The FreeImage bitmap on which to create the view.
+ @param left The left position of the view's area.
+ @param top The top position of the view's area.
+ @param right The right position of the view's area.
+ @param bottom The bottom position of the view's area.
+ @return Returns a handle to the newly created view or NULL if the view
+ was not created.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_CreateView(FIBITMAP *dib, unsigned left, unsigned top, unsigned right, unsigned bottom) {
+ if (!FreeImage_HasPixels(dib)) {
+ return NULL;
+ }
+
+ // normalize the rectangle
+ if (right < left) {
+ INPLACESWAP(left, right);
+ }
+ if (bottom < top) {
+ INPLACESWAP(top, bottom);
+ }
+
+ // check the size of the sub image
+ unsigned width = FreeImage_GetWidth(dib);
+ unsigned height = FreeImage_GetHeight(dib);
+ if (left < 0 || right > width || top < 0 || bottom > height) {
+ return NULL;
+ }
+
+ unsigned bpp = FreeImage_GetBPP(dib);
+ BYTE *bits = FreeImage_GetScanLine(dib, height - bottom);
+ switch (bpp) {
+ case 1:
+ if (left % 8 != 0) {
+ // view can only start at a byte boundary
+ return NULL;
+ }
+ bits += (left / 8);
+ break;
+ case 4:
+ if (left % 2 != 0) {
+ // view can only start at a byte boundary
+ return NULL;
+ }
+ bits += (left / 2);
+ break;
+ default:
+ bits += left * (bpp / 8);
+ break;
+ }
+
+ FIBITMAP *dst = FreeImage_AllocateHeaderForBits(bits, FreeImage_GetPitch(dib), FreeImage_GetImageType(dib),
+ right - left, bottom - top,
+ bpp,
+ FreeImage_GetRedMask(dib), FreeImage_GetGreenMask(dib), FreeImage_GetBlueMask(dib));
+
+ if (dst == NULL) {
+ return NULL;
+ }
+ // copy some basic image properties needed for displaying and saving
+
+ // resolution
+ FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(dib));
+ FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(dib));
+
+ // background color
+ RGBQUAD bkcolor;
+ if (FreeImage_GetBackgroundColor(dib, &bkcolor)) {
+ FreeImage_SetBackgroundColor(dst, &bkcolor);
+ }
+
+ // palette
+ memcpy(FreeImage_GetPalette(dst), FreeImage_GetPalette(dib), FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD));
+
+ // transparency table
+ FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+
+ // ICC profile
+ FIICCPROFILE *src_profile = FreeImage_GetICCProfile(dib);
+ FIICCPROFILE *dst_profile = FreeImage_CreateICCProfile(dst, src_profile->data, src_profile->size);
+ dst_profile->flags = src_profile->flags;
+
+ return dst;
+}
diff --git a/plugins/AdvaImg/src/FreeImageToolkit/JPEGTransform.cpp b/plugins/AdvaImg/src/FreeImageToolkit/JPEGTransform.cpp
index 132fef7e85..6f9ba8e1f2 100644
--- a/plugins/AdvaImg/src/FreeImageToolkit/JPEGTransform.cpp
+++ b/plugins/AdvaImg/src/FreeImageToolkit/JPEGTransform.cpp
@@ -1,623 +1,623 @@
-// ==========================================================
-// JPEG lossless transformations
-//
-// Design and implementation by
-// - Petr Pytelka (pyta@lightcomp.com)
-// - Hervé Drolon (drolon@infonie.fr)
+// ==========================================================
+// JPEG lossless transformations
+//
+// Design and implementation by
+// - Petr Pytelka (pyta@lightcomp.com)
+// - Hervé Drolon (drolon@infonie.fr)
// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
-//
-// 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!
-// ==========================================================
-
-extern "C" {
-#define XMD_H
-#undef FAR
-#include <setjmp.h>
-
-#include "../LibJPEG/jinclude.h"
-#include "../LibJPEG/jpeglib.h"
-#include "../LibJPEG/jerror.h"
-#include "../LibJPEG/transupp.h"
-}
-
-#include "FreeImage.h"
-#include "Utilities.h"
-#include "FreeImageIO.h"
-
+//
+// 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!
+// ==========================================================
+
+extern "C" {
+#define XMD_H
+#undef FAR
+#include <setjmp.h>
+
+#include "../LibJPEG/jinclude.h"
+#include "../LibJPEG/jpeglib.h"
+#include "../LibJPEG/jerror.h"
+#include "../LibJPEG/transupp.h"
+}
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageIO.h"
+
// ----------------------------------------------------------
// Source manager & Destination manager setup
// (see PluginJPEG.cpp)
// ----------------------------------------------------------
-
+
void jpeg_freeimage_src(j_decompress_ptr cinfo, fi_handle infile, FreeImageIO *io);
void jpeg_freeimage_dst(j_compress_ptr cinfo, fi_handle outfile, FreeImageIO *io);
-
-// ----------------------------------------------------------
-// Error handling
+
+// ----------------------------------------------------------
+// Error handling
// (see also PluginJPEG.cpp)
-// ----------------------------------------------------------
-
-/**
- Receives control for a fatal error. Information sufficient to
- generate the error message has been stored in cinfo->err; call
- output_message to display it. Control must NOT return to the caller;
- generally this routine will exit() or longjmp() somewhere.
-*/
-METHODDEF(void)
-ls_jpeg_error_exit (j_common_ptr cinfo) {
- // always display the message
- (*cinfo->err->output_message)(cinfo);
-
- // allow JPEG with a premature end of file
- if((cinfo)->err->msg_parm.i[0] != 13) {
-
- // let the memory manager delete any temp files before we die
- jpeg_destroy(cinfo);
-
- throw FIF_JPEG;
- }
-}
-
-/**
- Actual output of any JPEG message. Note that this method does not know
- how to generate a message, only where to send it.
-*/
-METHODDEF(void)
-ls_jpeg_output_message (j_common_ptr cinfo) {
- char buffer[JMSG_LENGTH_MAX];
-
- // create the message
- (*cinfo->err->format_message)(cinfo, buffer);
- // send it to user's message proc
- FreeImage_OutputMessageProc(FIF_JPEG, buffer);
-}
-
-// ----------------------------------------------------------
-// Main program
-// ----------------------------------------------------------
-
-/**
-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;
-}
-
-static BOOL
-JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
- const BOOL onlyReturnCropRect = (dst_io == NULL) || (dst_handle == NULL);
- const long stream_start = onlyReturnCropRect ? 0 : dst_io->tell_proc(dst_handle);
- BOOL swappedDim = FALSE;
- BOOL trimH = FALSE;
- BOOL trimV = FALSE;
-
- // Set up the jpeglib structures
- jpeg_decompress_struct srcinfo;
- jpeg_compress_struct dstinfo;
- jpeg_error_mgr jsrcerr, jdsterr;
- jvirt_barray_ptr *src_coef_arrays = NULL;
- jvirt_barray_ptr *dst_coef_arrays = NULL;
- // Support for copying optional markers from source to destination file
- JCOPY_OPTION copyoption;
- // Image transformation options
- jpeg_transform_info transfoptions;
-
- // Initialize structures
- memset(&srcinfo, 0, sizeof(srcinfo));
- memset(&jsrcerr, 0, sizeof(jsrcerr));
- memset(&jdsterr, 0, sizeof(jdsterr));
- memset(&dstinfo, 0, sizeof(dstinfo));
- memset(&transfoptions, 0, sizeof(transfoptions));
-
- // Copy all extra markers from source file
- copyoption = JCOPYOPT_ALL;
-
- // Set up default JPEG parameters
- transfoptions.force_grayscale = FALSE;
- transfoptions.crop = FALSE;
-
- // Select the transform option
- switch(operation) {
- case FIJPEG_OP_FLIP_H: // horizontal flip
- transfoptions.transform = JXFORM_FLIP_H;
- trimH = TRUE;
- break;
- case FIJPEG_OP_FLIP_V: // vertical flip
- transfoptions.transform = JXFORM_FLIP_V;
- trimV = TRUE;
- break;
- case FIJPEG_OP_TRANSPOSE: // transpose across UL-to-LR axis
- transfoptions.transform = JXFORM_TRANSPOSE;
- swappedDim = TRUE;
- break;
- case FIJPEG_OP_TRANSVERSE: // transpose across UR-to-LL axis
- transfoptions.transform = JXFORM_TRANSVERSE;
- trimH = TRUE;
- trimV = TRUE;
- swappedDim = TRUE;
- break;
- case FIJPEG_OP_ROTATE_90: // 90-degree clockwise rotation
- transfoptions.transform = JXFORM_ROT_90;
- trimH = TRUE;
- swappedDim = TRUE;
- break;
- case FIJPEG_OP_ROTATE_180: // 180-degree rotation
- trimH = TRUE;
- trimV = TRUE;
- transfoptions.transform = JXFORM_ROT_180;
- break;
- case FIJPEG_OP_ROTATE_270: // 270-degree clockwise (or 90 ccw)
- transfoptions.transform = JXFORM_ROT_270;
- trimV = TRUE;
- swappedDim = TRUE;
- break;
- default:
- case FIJPEG_OP_NONE: // no transformation
- transfoptions.transform = JXFORM_NONE;
- break;
- }
- // (perfect == TRUE) ==> fail if there is non-transformable edge blocks
- transfoptions.perfect = (perfect == TRUE) ? TRUE : FALSE;
- // Drop non-transformable edge blocks: trim off any partial edge MCUs that the transform can't handle.
- transfoptions.trim = TRUE;
-
- try {
-
- // Initialize the JPEG decompression object with default error handling
- srcinfo.err = jpeg_std_error(&jsrcerr);
- srcinfo.err->error_exit = ls_jpeg_error_exit;
- srcinfo.err->output_message = ls_jpeg_output_message;
- jpeg_create_decompress(&srcinfo);
-
- // Initialize the JPEG compression object with default error handling
- dstinfo.err = jpeg_std_error(&jdsterr);
- dstinfo.err->error_exit = ls_jpeg_error_exit;
- dstinfo.err->output_message = ls_jpeg_output_message;
- jpeg_create_compress(&dstinfo);
-
- // Specify data source for decompression
- jpeg_freeimage_src(&srcinfo, src_handle, src_io);
-
- // Enable saving of extra markers that we want to copy
- jcopy_markers_setup(&srcinfo, copyoption);
-
- // Read the file header
- jpeg_read_header(&srcinfo, TRUE);
-
- // crop option
- char crop[64];
- const BOOL hasCrop = getCropString(crop, left, top, right, bottom, swappedDim ? srcinfo.image_height : srcinfo.image_width, swappedDim ? srcinfo.image_width : srcinfo.image_height);
-
- if(hasCrop) {
- if(!jtransform_parse_crop_spec(&transfoptions, crop)) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop);
- throw(1);
- }
- }
-
- // Any space needed by a transform option must be requested before
- // jpeg_read_coefficients so that memory allocation will be done right
-
- // Prepare transformation workspace
- // Fails right away if perfect flag is TRUE and transformation is not perfect
- if( !jtransform_request_workspace(&srcinfo, &transfoptions) ) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Transformation is not perfect");
- throw(1);
- }
-
- if(left || top) {
- // compute left and top offsets, it's a bit tricky, taking into account both
- // transform, which might have trimed the image,
- // and crop itself, which is adjusted to lie on a iMCU boundary
-
- const int fullWidth = swappedDim ? srcinfo.image_height : srcinfo.image_width;
- const int fullHeight = swappedDim ? srcinfo.image_width : srcinfo.image_height;
-
- int transformedFullWidth = fullWidth;
- int transformedFullHeight = fullHeight;
-
- if(trimH && transformedFullWidth/transfoptions.iMCU_sample_width > 0) {
- transformedFullWidth = (transformedFullWidth/transfoptions.iMCU_sample_width) * transfoptions.iMCU_sample_width;
- }
- if(trimV && transformedFullHeight/transfoptions.iMCU_sample_height > 0) {
- transformedFullHeight = (transformedFullHeight/transfoptions.iMCU_sample_height) * transfoptions.iMCU_sample_height;
- }
-
- const int trimmedWidth = fullWidth - transformedFullWidth;
- const int trimmedHeight = fullHeight - transformedFullHeight;
-
- if(left) {
- *left = trimmedWidth + transfoptions.x_crop_offset * transfoptions.iMCU_sample_width;
- }
- if(top) {
- *top = trimmedHeight + transfoptions.y_crop_offset * transfoptions.iMCU_sample_height;
- }
- }
-
- if(right) {
- *right = (left ? *left : 0) + transfoptions.output_width;
- }
- if(bottom) {
- *bottom = (top ? *top : 0) + transfoptions.output_height;
- }
-
- // if only the crop rect is requested, we are done
-
- if(onlyReturnCropRect) {
- jpeg_destroy_compress(&dstinfo);
- jpeg_destroy_decompress(&srcinfo);
- return TRUE;
- }
-
- // Read source file as DCT coefficients
- src_coef_arrays = jpeg_read_coefficients(&srcinfo);
-
- // Initialize destination compression parameters from source values
- jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
-
- // Adjust destination parameters if required by transform options;
- // also find out which set of coefficient arrays will hold the output
- dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
-
- // Note: we assume that jpeg_read_coefficients consumed all input
- // until JPEG_REACHED_EOI, and that jpeg_finish_decompress will
- // only consume more while (! cinfo->inputctl->eoi_reached).
- // We cannot call jpeg_finish_decompress here since we still need the
- // virtual arrays allocated from the source object for processing.
-
- if(src_handle == dst_handle) {
- dst_io->seek_proc(dst_handle, stream_start, SEEK_SET);
- }
-
- // Specify data destination for compression
- jpeg_freeimage_dst(&dstinfo, dst_handle, dst_io);
-
- // Start compressor (note no image data is actually written here)
- jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
-
- // Copy to the output file any extra markers that we want to preserve
- jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);
-
- // Execute image transformation, if any
- jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
-
- // Finish compression and release memory
- jpeg_finish_compress(&dstinfo);
- jpeg_destroy_compress(&dstinfo);
- jpeg_finish_decompress(&srcinfo);
- jpeg_destroy_decompress(&srcinfo);
-
- }
- catch(...) {
- jpeg_destroy_compress(&dstinfo);
- jpeg_destroy_decompress(&srcinfo);
- return FALSE;
- }
-
- return TRUE;
-}
-
-// ----------------------------------------------------------
-// FreeImage interface
-// ----------------------------------------------------------
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
- return JPEGTransformFromHandle(src_io, src_handle, dst_io, dst_handle, operation, left, top, right, bottom, perfect);
-}
-
-static void
-closeStdIO(fi_handle src_handle, fi_handle dst_handle) {
- if(src_handle) {
- fclose((FILE*)src_handle);
- }
- if(dst_handle) {
- fclose((FILE*)dst_handle);
- }
-}
-
-static BOOL
-openStdIO(const char* src_file, const char* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
- *src_handle = NULL;
- *dst_handle = NULL;
-
- FreeImageIO io;
- SetDefaultIO (&io);
-
- const BOOL isSameFile = (dst_file && (strcmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
-
- FILE* srcp = NULL;
- FILE* dstp = NULL;
-
- if(isSameFile) {
- srcp = fopen(src_file, "r+b");
- dstp = srcp;
- }
- else {
- srcp = fopen(src_file, "rb");
- if(dst_file) {
- dstp = fopen(dst_file, "wb");
- }
- }
-
- if(!srcp || (dst_file && !dstp)) {
- if(!srcp) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for reading", src_file);
- } else {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for writing", dst_file);
- }
- closeStdIO(srcp, dstp);
- return FALSE;
- }
-
- if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
- FreeImage_OutputMessageProc(FIF_JPEG, " Source file \"%s\" is not jpeg", src_file);
- closeStdIO(srcp, dstp);
- return FALSE;
- }
-
- *dst_io = io;
- *src_handle = srcp;
- *dst_handle = dstp;
-
- return TRUE;
-}
-
-static BOOL
-openStdIOU(const wchar_t* src_file, const wchar_t* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
-#ifdef _WIN32
-
- *src_handle = NULL;
- *dst_handle = NULL;
-
- FreeImageIO io;
- SetDefaultIO (&io);
-
- const BOOL isSameFile = (dst_file && (wcscmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
-
- FILE* srcp = NULL;
- FILE* dstp = NULL;
-
- if(isSameFile) {
- srcp = _wfopen(src_file, L"r+b");
- dstp = srcp;
- } else {
- srcp = _wfopen(src_file, L"rb");
- if(dst_file) {
- dstp = _wfopen(dst_file, L"wb");
- }
- }
-
- if(!srcp || (dst_file && !dstp)) {
- if(!srcp) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open source file for reading");
- } else {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open destination file for writing");
- }
- closeStdIO(srcp, dstp);
- return FALSE;
- }
-
- if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
- FreeImage_OutputMessageProc(FIF_JPEG, " Source file is not jpeg");
- closeStdIO(srcp, dstp);
- return FALSE;
- }
-
- *dst_io = io;
- *src_handle = srcp;
- *dst_handle = dstp;
-
- return TRUE;
-
-#else
- return FALSE;
-#endif // _WIN32
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransformCombined(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransformCombinedU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-// --------------------------------------------------------------------------
-
-static BOOL
-getMemIO(FIMEMORY* src_stream, FIMEMORY* dst_stream, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
- *src_handle = NULL;
- *dst_handle = NULL;
-
- FreeImageIO io;
- SetMemoryIO (&io);
-
- if(dst_stream) {
- FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(dst_stream->data);
- if(mem_header->delete_me != TRUE) {
- // do not save in a user buffer
- FreeImage_OutputMessageProc(FIF_JPEG, "Destination memory buffer is read only");
- return FALSE;
- }
- }
-
- *dst_io = io;
- *src_handle = src_stream;
- *dst_handle = dst_stream;
-
- return TRUE;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransformCombinedFromMemory(FIMEMORY* src_stream, FIMEMORY* dst_stream, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!getMemIO(src_stream, dst_stream, &io, &src, &dst)) {
- return FALSE;
- }
-
- return FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
-}
-
+// ----------------------------------------------------------
+
+/**
+ Receives control for a fatal error. Information sufficient to
+ generate the error message has been stored in cinfo->err; call
+ output_message to display it. Control must NOT return to the caller;
+ generally this routine will exit() or longjmp() somewhere.
+*/
+METHODDEF(void)
+ls_jpeg_error_exit (j_common_ptr cinfo) {
+ // always display the message
+ (*cinfo->err->output_message)(cinfo);
+
+ // allow JPEG with a premature end of file
+ if((cinfo)->err->msg_parm.i[0] != 13) {
+
+ // let the memory manager delete any temp files before we die
+ jpeg_destroy(cinfo);
+
+ throw FIF_JPEG;
+ }
+}
+
+/**
+ Actual output of any JPEG message. Note that this method does not know
+ how to generate a message, only where to send it.
+*/
+METHODDEF(void)
+ls_jpeg_output_message (j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+
+ // create the message
+ (*cinfo->err->format_message)(cinfo, buffer);
+ // send it to user's message proc
+ FreeImage_OutputMessageProc(FIF_JPEG, buffer);
+}
+
+// ----------------------------------------------------------
+// Main program
+// ----------------------------------------------------------
+
+/**
+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;
+}
+
+static BOOL
+JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
+ const BOOL onlyReturnCropRect = (dst_io == NULL) || (dst_handle == NULL);
+ const long stream_start = onlyReturnCropRect ? 0 : dst_io->tell_proc(dst_handle);
+ BOOL swappedDim = FALSE;
+ BOOL trimH = FALSE;
+ BOOL trimV = FALSE;
+
+ // Set up the jpeglib structures
+ jpeg_decompress_struct srcinfo;
+ jpeg_compress_struct dstinfo;
+ jpeg_error_mgr jsrcerr, jdsterr;
+ jvirt_barray_ptr *src_coef_arrays = NULL;
+ jvirt_barray_ptr *dst_coef_arrays = NULL;
+ // Support for copying optional markers from source to destination file
+ JCOPY_OPTION copyoption;
+ // Image transformation options
+ jpeg_transform_info transfoptions;
+
+ // Initialize structures
+ memset(&srcinfo, 0, sizeof(srcinfo));
+ memset(&jsrcerr, 0, sizeof(jsrcerr));
+ memset(&jdsterr, 0, sizeof(jdsterr));
+ memset(&dstinfo, 0, sizeof(dstinfo));
+ memset(&transfoptions, 0, sizeof(transfoptions));
+
+ // Copy all extra markers from source file
+ copyoption = JCOPYOPT_ALL;
+
+ // Set up default JPEG parameters
+ transfoptions.force_grayscale = FALSE;
+ transfoptions.crop = FALSE;
+
+ // Select the transform option
+ switch(operation) {
+ case FIJPEG_OP_FLIP_H: // horizontal flip
+ transfoptions.transform = JXFORM_FLIP_H;
+ trimH = TRUE;
+ break;
+ case FIJPEG_OP_FLIP_V: // vertical flip
+ transfoptions.transform = JXFORM_FLIP_V;
+ trimV = TRUE;
+ break;
+ case FIJPEG_OP_TRANSPOSE: // transpose across UL-to-LR axis
+ transfoptions.transform = JXFORM_TRANSPOSE;
+ swappedDim = TRUE;
+ break;
+ case FIJPEG_OP_TRANSVERSE: // transpose across UR-to-LL axis
+ transfoptions.transform = JXFORM_TRANSVERSE;
+ trimH = TRUE;
+ trimV = TRUE;
+ swappedDim = TRUE;
+ break;
+ case FIJPEG_OP_ROTATE_90: // 90-degree clockwise rotation
+ transfoptions.transform = JXFORM_ROT_90;
+ trimH = TRUE;
+ swappedDim = TRUE;
+ break;
+ case FIJPEG_OP_ROTATE_180: // 180-degree rotation
+ trimH = TRUE;
+ trimV = TRUE;
+ transfoptions.transform = JXFORM_ROT_180;
+ break;
+ case FIJPEG_OP_ROTATE_270: // 270-degree clockwise (or 90 ccw)
+ transfoptions.transform = JXFORM_ROT_270;
+ trimV = TRUE;
+ swappedDim = TRUE;
+ break;
+ default:
+ case FIJPEG_OP_NONE: // no transformation
+ transfoptions.transform = JXFORM_NONE;
+ break;
+ }
+ // (perfect == TRUE) ==> fail if there is non-transformable edge blocks
+ transfoptions.perfect = (perfect == TRUE) ? TRUE : FALSE;
+ // Drop non-transformable edge blocks: trim off any partial edge MCUs that the transform can't handle.
+ transfoptions.trim = TRUE;
+
+ try {
+
+ // Initialize the JPEG decompression object with default error handling
+ srcinfo.err = jpeg_std_error(&jsrcerr);
+ srcinfo.err->error_exit = ls_jpeg_error_exit;
+ srcinfo.err->output_message = ls_jpeg_output_message;
+ jpeg_create_decompress(&srcinfo);
+
+ // Initialize the JPEG compression object with default error handling
+ dstinfo.err = jpeg_std_error(&jdsterr);
+ dstinfo.err->error_exit = ls_jpeg_error_exit;
+ dstinfo.err->output_message = ls_jpeg_output_message;
+ jpeg_create_compress(&dstinfo);
+
+ // Specify data source for decompression
+ jpeg_freeimage_src(&srcinfo, src_handle, src_io);
+
+ // Enable saving of extra markers that we want to copy
+ jcopy_markers_setup(&srcinfo, copyoption);
+
+ // Read the file header
+ jpeg_read_header(&srcinfo, TRUE);
+
+ // crop option
+ char crop[64];
+ const BOOL hasCrop = getCropString(crop, left, top, right, bottom, swappedDim ? srcinfo.image_height : srcinfo.image_width, swappedDim ? srcinfo.image_width : srcinfo.image_height);
+
+ if(hasCrop) {
+ if(!jtransform_parse_crop_spec(&transfoptions, crop)) {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop);
+ throw(1);
+ }
+ }
+
+ // Any space needed by a transform option must be requested before
+ // jpeg_read_coefficients so that memory allocation will be done right
+
+ // Prepare transformation workspace
+ // Fails right away if perfect flag is TRUE and transformation is not perfect
+ if( !jtransform_request_workspace(&srcinfo, &transfoptions) ) {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Transformation is not perfect");
+ throw(1);
+ }
+
+ if(left || top) {
+ // compute left and top offsets, it's a bit tricky, taking into account both
+ // transform, which might have trimed the image,
+ // and crop itself, which is adjusted to lie on a iMCU boundary
+
+ const int fullWidth = swappedDim ? srcinfo.image_height : srcinfo.image_width;
+ const int fullHeight = swappedDim ? srcinfo.image_width : srcinfo.image_height;
+
+ int transformedFullWidth = fullWidth;
+ int transformedFullHeight = fullHeight;
+
+ if(trimH && transformedFullWidth/transfoptions.iMCU_sample_width > 0) {
+ transformedFullWidth = (transformedFullWidth/transfoptions.iMCU_sample_width) * transfoptions.iMCU_sample_width;
+ }
+ if(trimV && transformedFullHeight/transfoptions.iMCU_sample_height > 0) {
+ transformedFullHeight = (transformedFullHeight/transfoptions.iMCU_sample_height) * transfoptions.iMCU_sample_height;
+ }
+
+ const int trimmedWidth = fullWidth - transformedFullWidth;
+ const int trimmedHeight = fullHeight - transformedFullHeight;
+
+ if(left) {
+ *left = trimmedWidth + transfoptions.x_crop_offset * transfoptions.iMCU_sample_width;
+ }
+ if(top) {
+ *top = trimmedHeight + transfoptions.y_crop_offset * transfoptions.iMCU_sample_height;
+ }
+ }
+
+ if(right) {
+ *right = (left ? *left : 0) + transfoptions.output_width;
+ }
+ if(bottom) {
+ *bottom = (top ? *top : 0) + transfoptions.output_height;
+ }
+
+ // if only the crop rect is requested, we are done
+
+ if(onlyReturnCropRect) {
+ jpeg_destroy_compress(&dstinfo);
+ jpeg_destroy_decompress(&srcinfo);
+ return TRUE;
+ }
+
+ // Read source file as DCT coefficients
+ src_coef_arrays = jpeg_read_coefficients(&srcinfo);
+
+ // Initialize destination compression parameters from source values
+ jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
+
+ // Adjust destination parameters if required by transform options;
+ // also find out which set of coefficient arrays will hold the output
+ dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
+
+ // Note: we assume that jpeg_read_coefficients consumed all input
+ // until JPEG_REACHED_EOI, and that jpeg_finish_decompress will
+ // only consume more while (! cinfo->inputctl->eoi_reached).
+ // We cannot call jpeg_finish_decompress here since we still need the
+ // virtual arrays allocated from the source object for processing.
+
+ if(src_handle == dst_handle) {
+ dst_io->seek_proc(dst_handle, stream_start, SEEK_SET);
+ }
+
+ // Specify data destination for compression
+ jpeg_freeimage_dst(&dstinfo, dst_handle, dst_io);
+
+ // Start compressor (note no image data is actually written here)
+ jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
+
+ // Copy to the output file any extra markers that we want to preserve
+ jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);
+
+ // Execute image transformation, if any
+ jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
+
+ // Finish compression and release memory
+ jpeg_finish_compress(&dstinfo);
+ jpeg_destroy_compress(&dstinfo);
+ jpeg_finish_decompress(&srcinfo);
+ jpeg_destroy_decompress(&srcinfo);
+
+ }
+ catch(...) {
+ jpeg_destroy_compress(&dstinfo);
+ jpeg_destroy_decompress(&srcinfo);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+// FreeImage interface
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
+ return JPEGTransformFromHandle(src_io, src_handle, dst_io, dst_handle, operation, left, top, right, bottom, perfect);
+}
+
+static void
+closeStdIO(fi_handle src_handle, fi_handle dst_handle) {
+ if(src_handle) {
+ fclose((FILE*)src_handle);
+ }
+ if(dst_handle && (dst_handle != src_handle)) {
+ fclose((FILE*)dst_handle);
+ }
+}
+
+static BOOL
+openStdIO(const char* src_file, const char* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
+ *src_handle = NULL;
+ *dst_handle = NULL;
+
+ FreeImageIO io;
+ SetDefaultIO (&io);
+
+ const BOOL isSameFile = (dst_file && (strcmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
+
+ FILE* srcp = NULL;
+ FILE* dstp = NULL;
+
+ if(isSameFile) {
+ srcp = fopen(src_file, "r+b");
+ dstp = srcp;
+ }
+ else {
+ srcp = fopen(src_file, "rb");
+ if(dst_file) {
+ dstp = fopen(dst_file, "wb");
+ }
+ }
+
+ if(!srcp || (dst_file && !dstp)) {
+ if(!srcp) {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for reading", src_file);
+ } else {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for writing", dst_file);
+ }
+ closeStdIO(srcp, dstp);
+ return FALSE;
+ }
+
+ if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
+ FreeImage_OutputMessageProc(FIF_JPEG, " Source file \"%s\" is not jpeg", src_file);
+ closeStdIO(srcp, dstp);
+ return FALSE;
+ }
+
+ *dst_io = io;
+ *src_handle = srcp;
+ *dst_handle = dstp;
+
+ return TRUE;
+}
+
+static BOOL
+openStdIOU(const wchar_t* src_file, const wchar_t* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
+#ifdef _WIN32
+
+ *src_handle = NULL;
+ *dst_handle = NULL;
+
+ FreeImageIO io;
+ SetDefaultIO (&io);
+
+ const BOOL isSameFile = (dst_file && (wcscmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
+
+ FILE* srcp = NULL;
+ FILE* dstp = NULL;
+
+ if(isSameFile) {
+ srcp = _wfopen(src_file, L"r+b");
+ dstp = srcp;
+ } else {
+ srcp = _wfopen(src_file, L"rb");
+ if(dst_file) {
+ dstp = _wfopen(dst_file, L"wb");
+ }
+ }
+
+ if(!srcp || (dst_file && !dstp)) {
+ if(!srcp) {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open source file for reading");
+ } else {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open destination file for writing");
+ }
+ closeStdIO(srcp, dstp);
+ return FALSE;
+ }
+
+ if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
+ FreeImage_OutputMessageProc(FIF_JPEG, " Source file is not jpeg");
+ closeStdIO(srcp, dstp);
+ return FALSE;
+ }
+
+ *dst_io = io;
+ *src_handle = srcp;
+ *dst_handle = dstp;
+
+ return TRUE;
+
+#else
+ return FALSE;
+#endif // _WIN32
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransformCombined(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransformCombinedU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+// --------------------------------------------------------------------------
+
+static BOOL
+getMemIO(FIMEMORY* src_stream, FIMEMORY* dst_stream, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
+ *src_handle = NULL;
+ *dst_handle = NULL;
+
+ FreeImageIO io;
+ SetMemoryIO (&io);
+
+ if(dst_stream) {
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(dst_stream->data);
+ if(mem_header->delete_me != TRUE) {
+ // do not save in a user buffer
+ FreeImage_OutputMessageProc(FIF_JPEG, "Destination memory buffer is read only");
+ return FALSE;
+ }
+ }
+
+ *dst_io = io;
+ *src_handle = src_stream;
+ *dst_handle = dst_stream;
+
+ return TRUE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransformCombinedFromMemory(FIMEMORY* src_stream, FIMEMORY* dst_stream, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!getMemIO(src_stream, dst_stream, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ return FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
+}
+
diff --git a/plugins/AdvaImg/src/FreeImageToolkit/Rescale.cpp b/plugins/AdvaImg/src/FreeImageToolkit/Rescale.cpp
index 0c8bbc2787..4f885c29a5 100644
--- a/plugins/AdvaImg/src/FreeImageToolkit/Rescale.cpp
+++ b/plugins/AdvaImg/src/FreeImageToolkit/Rescale.cpp
@@ -22,11 +22,27 @@
#include "Resize.h"
-FIBITMAP * DLL_CALLCONV
-FreeImage_Rescale(FIBITMAP *src, int dst_width, int dst_height, FREE_IMAGE_FILTER filter) {
+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;
- if (!FreeImage_HasPixels(src) || (dst_width <= 0) || (dst_height <= 0) || (FreeImage_GetWidth(src) <= 0) || (FreeImage_GetHeight(src) <= 0)) {
+ 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;
}
@@ -59,18 +75,25 @@ FreeImage_Rescale(FIBITMAP *src, int dst_width, int dst_height, FREE_IMAGE_FILTE
CResizeEngine Engine(pFilter);
- dst = Engine.scale(src, dst_width, dst_height, 0, 0,
- FreeImage_GetWidth(src), FreeImage_GetHeight(src));
+ dst = Engine.scale(src, dst_width, dst_height, src_left, src_top,
+ src_right - src_left, src_bottom - src_top, flags);
delete pFilter;
- // copy metadata from src to dst
- FreeImage_CloneMetadata(dst, src);
-
+ if ((flags & FI_RESCALE_OMIT_METADATA) != FI_RESCALE_OMIT_METADATA) {
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+ }
+
return dst;
}
FIBITMAP * DLL_CALLCONV
+FreeImage_Rescale(FIBITMAP *src, int dst_width, int dst_height, FREE_IMAGE_FILTER filter) {
+ return FreeImage_RescaleRect(src, dst_width, dst_height, 0, 0, FreeImage_GetWidth(src), FreeImage_GetHeight(src), filter, FI_RESCALE_DEFAULT);
+}
+
+FIBITMAP * DLL_CALLCONV
FreeImage_MakeThumbnail(FIBITMAP *dib, int max_pixel_size, BOOL convert) {
FIBITMAP *thumbnail = NULL;
int new_width, new_height;
@@ -164,6 +187,6 @@ FreeImage_MakeThumbnail(FIBITMAP *dib, int max_pixel_size, BOOL convert) {
// copy metadata from src to dst
FreeImage_CloneMetadata(thumbnail, dib);
-
+
return thumbnail;
}
diff --git a/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp b/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp
index 283a91e830..dbc738ffd9 100644
--- a/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp
+++ b/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp
@@ -1,1998 +1,2116 @@
-// ==========================================================
-// Upsampling / downsampling classes
-//
-// Design and implementation by
-// - Hervé Drolon (drolon@infonie.fr)
-// - Detlev Vendt (detlev.vendt@brillit.de)
-// - Carsten Klein (cklein05@users.sourceforge.net)
-//
-// 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 "Resize.h"
-
-/**
-Returns the color type of a bitmap. In contrast to FreeImage_GetColorType,
-this function optionally supports a boolean OUT parameter, that receives TRUE,
-if the specified bitmap is greyscale, that is, it consists of grey colors only.
-Although it returns the same value as returned by FreeImage_GetColorType for all
-image types, this extended function primarily is intended for palletized images,
-since the boolean pointed to by 'bIsGreyscale' remains unchanged for RGB(A/F)
-images. However, the outgoing boolean is properly maintained for palletized images,
-as well as for any non-RGB image type, like FIT_UINTxx and FIT_DOUBLE, for example.
-@param dib A pointer to a FreeImage bitmap to calculate the extended color type for
-@param bIsGreyscale A pointer to a boolean, that receives TRUE, if the specified bitmap
-is greyscale, that is, it consists of grey colors only. This parameter can be NULL.
-@return the color type of the specified bitmap
-*/
-static FREE_IMAGE_COLOR_TYPE
-GetExtendedColorType(FIBITMAP *dib, BOOL *bIsGreyscale) {
- const unsigned bpp = FreeImage_GetBPP(dib);
- const unsigned size = CalculateUsedPaletteEntries(bpp);
- const RGBQUAD * const pal = FreeImage_GetPalette(dib);
- FREE_IMAGE_COLOR_TYPE color_type = FIC_MINISBLACK;
- BOOL bIsGrey = TRUE;
-
- switch (bpp) {
- case 1:
- {
- for (unsigned i = 0; i < size; i++) {
- if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
- color_type = FIC_PALETTE;
- bIsGrey = FALSE;
- break;
- }
- }
- if (bIsGrey) {
- if (pal[0].rgbBlue == 255 && pal[1].rgbBlue == 0) {
- color_type = FIC_MINISWHITE;
- } else if (pal[0].rgbBlue != 0 || pal[1].rgbBlue != 255) {
- color_type = FIC_PALETTE;
- }
- }
- break;
- }
-
- case 4:
- case 8:
- {
- for (unsigned i = 0; i < size; i++) {
- if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
- color_type = FIC_PALETTE;
- bIsGrey = FALSE;
- break;
- }
- if (color_type != FIC_PALETTE && pal[i].rgbBlue != i) {
- if ((size - i - 1) != pal[i].rgbBlue) {
- color_type = FIC_PALETTE;
- if (!bIsGreyscale) {
- // exit loop if we're not setting
- // bIsGreyscale parameter
- break;
- }
- } else {
- color_type = FIC_MINISWHITE;
- }
- }
- }
- break;
- }
-
- default:
- {
- color_type = FreeImage_GetColorType(dib);
- bIsGrey = (color_type == FIC_MINISBLACK) ? TRUE : FALSE;
- break;
- }
-
- }
- if (bIsGreyscale) {
- *bIsGreyscale = bIsGrey;
- }
-
- return color_type;
-}
-
-/**
-Returns a pointer to an RGBA palette, created from the specified bitmap.
-The RGBA palette is a copy of the specified bitmap's palette, that, additionally
-contains the bitmap's transparency information in the rgbReserved member
-of the palette's RGBQUAD elements.
-@param dib A pointer to a FreeImage bitmap to create the RGBA palette from.
-@param buffer A pointer to the buffer to store the RGBA palette.
-@return A pointer to the newly created RGBA palette or NULL, if the specified
-bitmap is no palletized standard bitmap. If non-NULL, the returned value is
-actually the pointer passed in parameter 'buffer'.
-*/
-static inline RGBQUAD *
-GetRGBAPalette(FIBITMAP *dib, RGBQUAD * const buffer) {
- // clone the palette
- const unsigned ncolors = FreeImage_GetColorsUsed(dib);
- if (ncolors == 0) {
- return NULL;
- }
- memcpy(buffer, FreeImage_GetPalette(dib), ncolors * sizeof(RGBQUAD));
- // merge the transparency table
- const unsigned ntransp = MIN(ncolors, FreeImage_GetTransparencyCount(dib));
- const BYTE * const tt = FreeImage_GetTransparencyTable(dib);
- for (unsigned i = 0; i < ntransp; i++) {
- buffer[i].rgbReserved = tt[i];
- }
- for (unsigned i = ntransp; i < ncolors; i++) {
- buffer[i].rgbReserved = 255;
- }
- return buffer;
-}
-
-// --------------------------------------------------------------------------
-
-CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) {
- double dWidth;
- double dFScale;
- const double dFilterWidth = pFilter->GetWidth();
-
- // scale factor
- const double dScale = double(uDstSize) / double(uSrcSize);
-
- if(dScale < 1.0) {
- // minification
- dWidth = dFilterWidth / dScale;
- dFScale = dScale;
- } else {
- // magnification
- dWidth = dFilterWidth;
- dFScale = 1.0;
- }
-
- // allocate a new line contributions structure
- //
- // window size is the number of sampled pixels
- m_WindowSize = 2 * (int)ceil(dWidth) + 1;
- // length of dst line (no. of rows / cols)
- m_LineLength = uDstSize;
-
- // allocate list of contributions
- m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
- for(unsigned u = 0; u < m_LineLength; u++) {
- // allocate contributions for every pixel
- m_WeightTable[u].Weights = (double*)malloc(m_WindowSize * sizeof(double));
- }
-
- // offset for discrete to continuous coordinate conversion
- const double dOffset = (0.5 / dScale);
-
- for(unsigned u = 0; u < m_LineLength; u++) {
- // scan through line of contributions
-
- // inverse mapping (discrete dst 'u' to continous src 'dCenter')
- const double dCenter = (double)u / dScale + dOffset;
-
- // find the significant edge points that affect the pixel
- const int iLeft = MAX(0, (int)(dCenter - dWidth + 0.5));
- const int iRight = MIN((int)(dCenter + dWidth + 0.5), int(uSrcSize));
-
- m_WeightTable[u].Left = iLeft;
- m_WeightTable[u].Right = iRight;
-
- double dTotalWeight = 0; // sum of weights (initialized to zero)
- for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
- // calculate weights
- const double weight = dFScale * pFilter->Filter(dFScale * ((double)iSrc + 0.5 - dCenter));
- // assert((iSrc-iLeft) < m_WindowSize);
- m_WeightTable[u].Weights[iSrc-iLeft] = weight;
- dTotalWeight += weight;
- }
- if((dTotalWeight > 0) && (dTotalWeight != 1)) {
- // normalize weight of neighbouring points
- for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
- // normalize point
- m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight;
- }
- }
-
- // simplify the filter, discarding null weights at the right
- {
- int iTrailing = iRight - iLeft - 1;
- while(m_WeightTable[u].Weights[iTrailing] == 0) {
- m_WeightTable[u].Right--;
- iTrailing--;
- if(m_WeightTable[u].Right == m_WeightTable[u].Left) {
- break;
- }
- }
-
- }
-
- } // next dst pixel
-}
-
-CWeightsTable::~CWeightsTable() {
- for(unsigned u = 0; u < m_LineLength; u++) {
- // free contributions for every pixel
- free(m_WeightTable[u].Weights);
- }
- // free list of pixels contributions
- free(m_WeightTable);
-}
-
-// --------------------------------------------------------------------------
-
-FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height) {
-
- const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
- const unsigned src_bpp = FreeImage_GetBPP(src);
-
- // determine the image's color type
- BOOL bIsGreyscale = FALSE;
- FREE_IMAGE_COLOR_TYPE color_type;
- if (src_bpp <= 8) {
- color_type = GetExtendedColorType(src, &bIsGreyscale);
- } else {
- color_type = FIC_RGB;
- }
-
- // determine the required bit depth of the destination image
- unsigned dst_bpp;
- if (color_type == FIC_PALETTE && !bIsGreyscale) {
- // non greyscale FIC_PALETTE images require a high-color destination
- // image (24- or 32-bits depending on the image's transparent state)
- dst_bpp = FreeImage_IsTransparent(src) ? 32 : 24;
- } else if (src_bpp <= 8) {
- // greyscale images require an 8-bit destination image
- // (or a 32-bit image if the image is transparent)
- dst_bpp = FreeImage_IsTransparent(src) ? 32 : 8;
- if (dst_bpp == 32) {
- // additionally, for transparent images we always need a
- // palette including transparency information (an RGBA palette)
- // so, set color_type accordingly.
- color_type = FIC_PALETTE;
- }
- } else if (src_bpp == 16 && image_type == FIT_BITMAP) {
- // 16-bit 555 and 565 RGB images require a high-color destination image
- // (fixed to 24 bits, since 16-bit RGBs don't support transparency in FreeImage)
- dst_bpp = 24;
- } else {
- // bit depth remains unchanged for all other images
- dst_bpp = src_bpp;
- }
-
- // early exit if destination size is equal to source size
- if ((src_width == dst_width) && (src_height == dst_height)) {
- FIBITMAP *out = src;
- FIBITMAP *tmp = src;
- if ((src_width != FreeImage_GetWidth(src)) || (src_height != FreeImage_GetHeight(src))) {
- out = FreeImage_Copy(tmp, src_left, src_top, src_left + src_width, src_top + src_height);
- tmp = out;
- }
- if (src_bpp != dst_bpp) {
- switch (dst_bpp) {
- case 8:
- out = FreeImage_ConvertToGreyscale(tmp);
- if (tmp != src) {
- FreeImage_Unload(tmp);
- }
- break;
-
- case 24:
- out = FreeImage_ConvertTo24Bits(tmp);
- if (tmp != src) {
- FreeImage_Unload(tmp);
- }
- break;
-
- case 32:
- out = FreeImage_ConvertTo32Bits(tmp);
- if (tmp != src) {
- FreeImage_Unload(tmp);
- }
- break;
- }
- }
-
- return (out != src) ? out : FreeImage_Clone(src);
- }
-
- RGBQUAD pal_buffer[256];
- RGBQUAD *src_pal = NULL;
-
- // provide the source image's palette to the rescaler for
- // FIC_PALETTE type images (this includes palletized greyscale
- // images with an unordered palette as well as transparent images)
- if (color_type == FIC_PALETTE) {
- if (dst_bpp == 32) {
- // a 32 bit destination image signals transparency, so
- // create an RGBA palette from the source palette
- src_pal = GetRGBAPalette(src, pal_buffer);
- } else {
- src_pal = FreeImage_GetPalette(src);
- }
- }
-
- // allocate the dst image
- FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, dst_bpp, 0, 0, 0);
- if (!dst) {
- return NULL;
- }
-
- if (dst_bpp == 8) {
- RGBQUAD * const dst_pal = FreeImage_GetPalette(dst);
- if (color_type == FIC_MINISWHITE) {
- // build an inverted greyscale palette
- CREATE_GREYSCALE_PALETTE_REVERSE(dst_pal, 256);
- }
- /*
- else {
- // build a default greyscale palette
- // Currently, FreeImage_AllocateT already creates a default
- // greyscale palette for 8 bpp images, so we can skip this here.
- CREATE_GREYSCALE_PALETTE(dst_pal, 256);
- }
- */
- }
-
- // calculate x and y offsets; since FreeImage uses bottom-up bitmaps, the
- // value of src_offset_y is measured from the bottom of the image
- unsigned src_offset_x = src_left;
- unsigned src_offset_y;
- if (src_top > 0) {
- src_offset_y = FreeImage_GetHeight(src) - src_height - src_top;
- } else {
- src_offset_y = 0;
- }
-
- /*
- Decide which filtering order (xy or yx) is faster for this mapping.
- --- The theory ---
- Try to minimize calculations by counting the number of convolution multiplies
- if(dst_width*src_height <= src_width*dst_height) {
- // xy filtering
- } else {
- // yx filtering
- }
- --- The practice ---
- Try to minimize calculations by counting the number of vertical convolutions (the most time consuming task)
- if(dst_width*dst_height <= src_width*dst_height) {
- // xy filtering
- } else {
- // yx filtering
- }
- */
-
- if (dst_width <= src_width) {
- // xy filtering
- // -------------
-
- FIBITMAP *tmp = NULL;
-
- if (src_width != dst_width) {
- // source and destination widths are different so, we must
- // filter horizontally
- if (src_height != dst_height) {
- // source and destination heights are also different so, we need
- // a temporary image
- tmp = FreeImage_AllocateT(image_type, dst_width, src_height, dst_bpp, 0, 0, 0);
- if (!tmp) {
- FreeImage_Unload(dst);
- return NULL;
- }
- } else {
- // source and destination heights are equal so, we can directly
- // scale into destination image (second filter method will not
- // be invoked)
- tmp = dst;
- }
-
- // scale source image horizontally into temporary (or destination) image
- horizontalFilter(src, src_height, src_width, src_offset_x, src_offset_y, src_pal, tmp, dst_width);
-
- // set x and y offsets to zero for the second filter method
- // invocation (the temporary image only contains the portion of
- // the image to be rescaled with no offsets)
- src_offset_x = 0;
- src_offset_y = 0;
-
- // also ensure, that the second filter method gets no source
- // palette (the temporary image is palletized only, if it is
- // greyscale; in that case, it is an 8-bit image with a linear
- // palette so, the source palette is not needed or will even be
- // mismatching, if the source palette is unordered)
- src_pal = NULL;
- } else {
- // source and destination widths are equal so, just copy the
- // image pointer
- tmp = src;
- }
-
- if (src_height != dst_height) {
- // source and destination heights are different so, scale
- // temporary (or source) image vertically into destination image
- verticalFilter(tmp, dst_width, src_height, src_offset_x, src_offset_y, src_pal, dst, dst_height);
- }
-
- // free temporary image, if not pointing to either src or dst
- if (tmp != src && tmp != dst) {
- FreeImage_Unload(tmp);
- }
-
- } else {
- // yx filtering
- // -------------
-
- // Remark:
- // The yx filtering branch could be more optimized by taking into,
- // account that (src_width != dst_width) is always true, which
- // follows from the above condition, which selects filtering order.
- // Since (dst_width <= src_width) == TRUE selects xy filtering,
- // both widths must be different when performing yx filtering.
- // However, to make the code more robust, not depending on that
- // condition and more symmetric to the xy filtering case, these
- // (src_width != dst_width) conditions are still in place.
-
- FIBITMAP *tmp = NULL;
-
- if (src_height != dst_height) {
- // source and destination heights are different so, we must
- // filter vertically
- if (src_width != dst_width) {
- // source and destination widths are also different so, we need
- // a temporary image
- tmp = FreeImage_AllocateT(image_type, src_width, dst_height, dst_bpp, 0, 0, 0);
- if (!tmp) {
- FreeImage_Unload(dst);
- return NULL;
- }
- } else {
- // source and destination widths are equal so, we can directly
- // scale into destination image (second filter method will not
- // be invoked)
- tmp = dst;
- }
-
- // scale source image vertically into temporary (or destination) image
- verticalFilter(src, src_width, src_height, src_offset_x, src_offset_y, src_pal, tmp, dst_height);
-
- // set x and y offsets to zero for the second filter method
- // invocation (the temporary image only contains the portion of
- // the image to be rescaled with no offsets)
- src_offset_x = 0;
- src_offset_y = 0;
-
- // also ensure, that the second filter method gets no source
- // palette (the temporary image is palletized only, if it is
- // greyscale; in that case, it is an 8-bit image with a linear
- // palette so, the source palette is not needed or will even be
- // mismatching, if the source palette is unordered)
- src_pal = NULL;
-
- } else {
- // source and destination heights are equal so, just copy the
- // image pointer
- tmp = src;
- }
-
- if (src_width != dst_width) {
- // source and destination heights are different so, scale
- // temporary (or source) image horizontally into destination image
- horizontalFilter(tmp, dst_height, src_width, src_offset_x, src_offset_y, src_pal, dst, dst_width);
- }
-
- // free temporary image, if not pointing to either src or dst
- if (tmp != src && tmp != dst) {
- FreeImage_Unload(tmp);
- }
- }
-
- return dst;
-}
-
-void CResizeEngine::horizontalFilter(FIBITMAP *const src, unsigned height, unsigned src_width, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_width) {
-
- // allocate and calculate the contributions
- CWeightsTable weightsTable(m_pFilter, dst_width, src_width);
-
- // step through rows
- switch(FreeImage_GetImageType(src)) {
- case FIT_BITMAP:
- {
- switch(FreeImage_GetBPP(src)) {
- case 1:
- {
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // transparently convert the 1-bit non-transparent greyscale
- // image to 8 bpp
- src_offset_x >>= 3;
- if (src_pal) {
- // we have got a palette
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double value = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
- value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- }
- }
- } else {
- // we do not have a palette
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double value = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
- value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
- }
- value *= 0xFF;
-
- // clamp and place result in destination pixel
- dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 1-bit image
- // to 24 bpp; we always have got a palette here
- src_offset_x >>= 3;
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i - iLeft);
- const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 1-bit image
- // to 32 bpp; we always have got a palette here
- src_offset_x >>= 3;
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i - iLeft);
- const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += 4;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 4:
- {
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // transparently convert the non-transparent 4-bit greyscale image
- // to 8 bpp; we always have got a palette for 4-bit images
- src_offset_x >>= 1;
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double value = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
- value += (weightsTable.getWeight(x, i - iLeft)
- * (double)*(BYTE *)&src_pal[pixel]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 4-bit image
- // to 24 bpp; we always have got a palette for 4-bit images
- src_offset_x >>= 1;
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i - iLeft);
- const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 4-bit image
- // to 32 bpp; we always have got a palette for 4-bit images
- src_offset_x >>= 1;
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i - iLeft);
- const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += 4;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 8:
- {
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // scale the 8-bit non-transparent greyscale image
- // into an 8 bpp destination image
- if (src_pal) {
- // we have got a palette
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE * const pixel = src_bits + iLeft;
- double value = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- value += (weightsTable.getWeight(x, i)
- * (double)*(BYTE *)&src_pal[pixel[i]]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- }
- }
- } else {
- // we do not have a palette
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE * const pixel = src_bits + iLeft;
- double value = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- value += (weightsTable.getWeight(x, i) * (double)pixel[i]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 8-bit image
- // to 24 bpp; we always have got a palette here
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE * const pixel = src_bits + iLeft;
- double r = 0, g = 0, b = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- const BYTE *const entry = (BYTE *)&src_pal[pixel[i]];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 8-bit image
- // to 32 bpp; we always have got a palette here
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE * const pixel = src_bits + iLeft;
- double r = 0, g = 0, b = 0, a = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- const BYTE * const entry = (BYTE *)&src_pal[pixel[i]];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += 4;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 16:
- {
- // transparently convert the 16-bit non-transparent image
- // to 24 bpp
- if (IS_FORMAT_RGB565(src)) {
- // image has 565 format
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const WORD *pixel = src_bits + iLeft;
- double r = 0, g = 0, b = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
- g += (weight * (double)((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
- b += (weight * (double)((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
- pixel++;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- } else {
- // image has 555 format
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const WORD *pixel = src_bits + iLeft;
- double r = 0, g = 0, b = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
- g += (weight * (double)((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
- b += (weight * (double)((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
- pixel++;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // scale the 24-bit non-transparent image
- // into a 24 bpp destination image
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 3;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE * pixel = src_bits + iLeft * 3;
- double r = 0, g = 0, b = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)pixel[FI_RGBA_RED]);
- g += (weight * (double)pixel[FI_RGBA_GREEN]);
- b += (weight * (double)pixel[FI_RGBA_BLUE]);
- pixel += 3;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- }
- break;
-
- case 32:
- {
- // scale the 32-bit transparent image
- // into a 32 bpp destination image
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 4;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE *pixel = src_bits + iLeft * 4;
- double r = 0, g = 0, b = 0, a = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)pixel[FI_RGBA_RED]);
- g += (weight * (double)pixel[FI_RGBA_GREEN]);
- b += (weight * (double)pixel[FI_RGBA_BLUE]);
- a += (weight * (double)pixel[FI_RGBA_ALPHA]);
- pixel += 4;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += 4;
- }
- }
- }
- break;
- }
- }
- break;
-
- case FIT_UINT16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
- WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const WORD *pixel = src_bits + iLeft * wordspp;
- double value = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- value += (weight * (double)pixel[0]);
- pixel++;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
- dst_bits += wordspp;
- }
- }
- }
- break;
-
- case FIT_RGB16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
- WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const WORD *pixel = src_bits + iLeft * wordspp;
- double r = 0, g = 0, b = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)pixel[0]);
- g += (weight * (double)pixel[1]);
- b += (weight * (double)pixel[2]);
- pixel += wordspp;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
- dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
- dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
- dst_bits += wordspp;
- }
- }
- }
- break;
-
- case FIT_RGBA16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
- WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const WORD *pixel = src_bits + iLeft * wordspp;
- double r = 0, g = 0, b = 0, a = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)pixel[0]);
- g += (weight * (double)pixel[1]);
- b += (weight * (double)pixel[2]);
- a += (weight * (double)pixel[3]);
- pixel += wordspp;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
- dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
- dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
- dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
- dst_bits += wordspp;
- }
- }
- }
- break;
-
- case FIT_FLOAT:
- case FIT_RGBF:
- case FIT_RGBAF:
- {
- // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
- const unsigned floatspp = (FreeImage_GetLine(src) / src_width) / sizeof(float);
-
- for(unsigned y = 0; y < height; y++) {
- // scale each row
- const float *src_bits = (float*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(float);
- float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
-
- for(unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
-
- for(unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i-iLeft);
-
- unsigned index = i * floatspp; // pixel index
- for (unsigned j = 0; j < floatspp; j++) {
- value[j] += (weight * (double)src_bits[index++]);
- }
- }
-
- // place result in destination pixel
- for (unsigned j = 0; j < floatspp; j++) {
- dst_bits[j] = (float)value[j];
- }
-
- dst_bits += floatspp;
- }
- }
- }
- break;
- }
-}
-
-/// Performs vertical image filtering
-void CResizeEngine::verticalFilter(FIBITMAP *const src, unsigned width, unsigned src_height, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_height) {
-
- // allocate and calculate the contributions
- CWeightsTable weightsTable(m_pFilter, dst_height, src_height);
-
- // step through columns
- switch(FreeImage_GetImageType(src)) {
- case FIT_BITMAP:
- {
- const unsigned dst_pitch = FreeImage_GetPitch(dst);
- BYTE * const dst_base = FreeImage_GetBits(dst);
-
- switch(FreeImage_GetBPP(src)) {
- case 1:
- {
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const BYTE * const src_base = FreeImage_GetBits(src)
- + src_offset_y * src_pitch + (src_offset_x >> 3);
-
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // transparently convert the 1-bit non-transparent greyscale
- // image to 8 bpp
- if (src_pal) {
- // we have got a palette
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x;
- const unsigned index = x >> 3;
- const unsigned mask = 0x80 >> (x & 0x07);
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const unsigned pixel = (*src_bits & mask) != 0;
- value += (weightsTable.getWeight(y, i)
- * (double)*(BYTE *)&src_pal[pixel]);
- src_bits += src_pitch;
- }
- value *= 0xFF;
-
- // clamp and place result in destination pixel
- *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- } else {
- // we do not have a palette
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x;
- const unsigned index = x >> 3;
- const unsigned mask = 0x80 >> (x & 0x07);
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- value += (weightsTable.getWeight(y, i)
- * (double)((*src_bits & mask) != 0));
- src_bits += src_pitch;
- }
- value *= 0xFF;
-
- // clamp and place result in destination pixel
- *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 1-bit image
- // to 24 bpp; we always have got a palette here
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 3;
- const unsigned index = x >> 3;
- const unsigned mask = 0x80 >> (x & 0x07);
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const unsigned pixel = (*src_bits & mask) != 0;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 1-bit image
- // to 32 bpp; we always have got a palette here
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 4;
- const unsigned index = x >> 3;
- const unsigned mask = 0x80 >> (x & 0x07);
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const unsigned pixel = (*src_bits & mask) != 0;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 4:
- {
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 1);
-
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // transparently convert the non-transparent 4-bit greyscale image
- // to 8 bpp; we always have got a palette for 4-bit images
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x;
- const unsigned index = x >> 1;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
- value += (weightsTable.getWeight(y, i)
- * (double)*(BYTE *)&src_pal[pixel]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 4-bit image
- // to 24 bpp; we always have got a palette for 4-bit images
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 3;
- const unsigned index = x >> 1;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
- const BYTE *const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 4-bit image
- // to 32 bpp; we always have got a palette for 4-bit images
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 4;
- const unsigned index = x >> 1;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
- const BYTE *const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 8:
- {
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
-
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // scale the 8-bit non-transparent greyscale image
- // into an 8 bpp destination image
- if (src_pal) {
- // we have got a palette
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + x;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- value += (weightsTable.getWeight(y, i)
- * (double)*(BYTE *)&src_pal[*src_bits]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- } else {
- // we do not have a palette
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + x;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- value += (weightsTable.getWeight(y, i)
- * (double)*src_bits);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 8-bit image
- // to 24 bpp; we always have got a palette here
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 3;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + x;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 8-bit image
- // to 32 bpp; we always have got a palette here
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 4;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + x;
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 16:
- {
- // transparently convert the 16-bit non-transparent image
- // to 24 bpp
- const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
- const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
-
- if (IS_FORMAT_RGB565(src)) {
- // image has 565 format
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 3;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const WORD *src_bits = src_base + iLeft * src_pitch + x;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)((*src_bits & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
- g += (weight * (double)((*src_bits & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
- b += (weight * (double)((*src_bits & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- } else {
- // image has 555 format
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 3;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const WORD *src_bits = src_base + iLeft * src_pitch + x;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)((*src_bits & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
- g += (weight * (double)((*src_bits & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
- b += (weight * (double)((*src_bits & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // scale the 24-bit transparent image
- // into a 24 bpp destination image
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 3;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * 3;
- BYTE *dst_bits = dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)src_bits[FI_RGBA_RED]);
- g += (weight * (double)src_bits[FI_RGBA_GREEN]);
- b += (weight * (double)src_bits[FI_RGBA_BLUE]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case 32:
- {
- // scale the 32-bit transparent image
- // into a 32 bpp destination image
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 4;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * 4;
- BYTE *dst_bits = dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)src_bits[FI_RGBA_RED]);
- g += (weight * (double)src_bits[FI_RGBA_GREEN]);
- b += (weight * (double)src_bits[FI_RGBA_BLUE]);
- a += (weight * (double)src_bits[FI_RGBA_ALPHA]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int) (a + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
- }
- }
- break;
-
- case FIT_UINT16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
-
- const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
- WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
-
- const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
- const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * wordspp; // pixel index
- WORD *dst_bits = dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const WORD *src_bits = src_base + iLeft * src_pitch + index;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- value += (weight * (double)src_bits[0]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
-
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case FIT_RGB16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
-
- const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
- WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
-
- const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
- const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * wordspp; // pixel index
- WORD *dst_bits = dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const WORD *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)src_bits[0]);
- g += (weight * (double)src_bits[1]);
- b += (weight * (double)src_bits[2]);
-
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
- dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
- dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
-
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case FIT_RGBA16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
-
- const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
- WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
-
- const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
- const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * wordspp; // pixel index
- WORD *dst_bits = dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const WORD *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)src_bits[0]);
- g += (weight * (double)src_bits[1]);
- b += (weight * (double)src_bits[2]);
- a += (weight * (double)src_bits[3]);
-
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
- dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
- dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
- dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
-
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case FIT_FLOAT:
- case FIT_RGBF:
- case FIT_RGBAF:
- {
- // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
- const unsigned floatspp = (FreeImage_GetLine(src) / width) / sizeof(float);
-
- const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float);
- float *const dst_base = (float *)FreeImage_GetBits(dst);
-
- const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float);
- const float *const src_base = (float *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * floatspp;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * floatspp; // pixel index
- float *dst_bits = (float *)dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
- const float *src_bits = src_base + iLeft * src_pitch + index;
- double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i - iLeft);
- for (unsigned j = 0; j < floatspp; j++) {
- value[j] += (weight * (double)src_bits[j]);
- }
- src_bits += src_pitch;
- }
-
- // place result in destination pixel
- for (unsigned j = 0; j < floatspp; j++) {
- dst_bits[j] = (float)value[j];
- }
- dst_bits += dst_pitch;
- }
- }
- }
- break;
- }
-}
+// ==========================================================
+// Upsampling / downsampling classes
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Carsten Klein (cklein05@users.sourceforge.net)
+//
+// 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 "Resize.h"
+
+/**
+Returns the color type of a bitmap. In contrast to FreeImage_GetColorType,
+this function optionally supports a boolean OUT parameter, that receives TRUE,
+if the specified bitmap is greyscale, that is, it consists of grey colors only.
+Although it returns the same value as returned by FreeImage_GetColorType for all
+image types, this extended function primarily is intended for palletized images,
+since the boolean pointed to by 'bIsGreyscale' remains unchanged for RGB(A/F)
+images. However, the outgoing boolean is properly maintained for palletized images,
+as well as for any non-RGB image type, like FIT_UINTxx and FIT_DOUBLE, for example.
+@param dib A pointer to a FreeImage bitmap to calculate the extended color type for
+@param bIsGreyscale A pointer to a boolean, that receives TRUE, if the specified bitmap
+is greyscale, that is, it consists of grey colors only. This parameter can be NULL.
+@return the color type of the specified bitmap
+*/
+static FREE_IMAGE_COLOR_TYPE
+GetExtendedColorType(FIBITMAP *dib, BOOL *bIsGreyscale) {
+ const unsigned bpp = FreeImage_GetBPP(dib);
+ const unsigned size = CalculateUsedPaletteEntries(bpp);
+ const RGBQUAD * const pal = FreeImage_GetPalette(dib);
+ FREE_IMAGE_COLOR_TYPE color_type = FIC_MINISBLACK;
+ BOOL bIsGrey = TRUE;
+
+ switch (bpp) {
+ case 1:
+ {
+ for (unsigned i = 0; i < size; i++) {
+ if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
+ color_type = FIC_PALETTE;
+ bIsGrey = FALSE;
+ break;
+ }
+ }
+ if (bIsGrey) {
+ if (pal[0].rgbBlue == 255 && pal[1].rgbBlue == 0) {
+ color_type = FIC_MINISWHITE;
+ } else if (pal[0].rgbBlue != 0 || pal[1].rgbBlue != 255) {
+ color_type = FIC_PALETTE;
+ }
+ }
+ break;
+ }
+
+ case 4:
+ case 8:
+ {
+ for (unsigned i = 0; i < size; i++) {
+ if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
+ color_type = FIC_PALETTE;
+ bIsGrey = FALSE;
+ break;
+ }
+ if (color_type != FIC_PALETTE && pal[i].rgbBlue != i) {
+ if ((size - i - 1) != pal[i].rgbBlue) {
+ color_type = FIC_PALETTE;
+ if (!bIsGreyscale) {
+ // exit loop if we're not setting
+ // bIsGreyscale parameter
+ break;
+ }
+ } else {
+ color_type = FIC_MINISWHITE;
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ color_type = FreeImage_GetColorType(dib);
+ bIsGrey = (color_type == FIC_MINISBLACK) ? TRUE : FALSE;
+ break;
+ }
+
+ }
+ if (bIsGreyscale) {
+ *bIsGreyscale = bIsGrey;
+ }
+
+ return color_type;
+}
+
+/**
+Returns a pointer to an RGBA palette, created from the specified bitmap.
+The RGBA palette is a copy of the specified bitmap's palette, that, additionally
+contains the bitmap's transparency information in the rgbReserved member
+of the palette's RGBQUAD elements.
+@param dib A pointer to a FreeImage bitmap to create the RGBA palette from.
+@param buffer A pointer to the buffer to store the RGBA palette.
+@return A pointer to the newly created RGBA palette or NULL, if the specified
+bitmap is no palletized standard bitmap. If non-NULL, the returned value is
+actually the pointer passed in parameter 'buffer'.
+*/
+static inline RGBQUAD *
+GetRGBAPalette(FIBITMAP *dib, RGBQUAD * const buffer) {
+ // clone the palette
+ const unsigned ncolors = FreeImage_GetColorsUsed(dib);
+ if (ncolors == 0) {
+ return NULL;
+ }
+ memcpy(buffer, FreeImage_GetPalette(dib), ncolors * sizeof(RGBQUAD));
+ // merge the transparency table
+ const unsigned ntransp = MIN(ncolors, FreeImage_GetTransparencyCount(dib));
+ const BYTE * const tt = FreeImage_GetTransparencyTable(dib);
+ for (unsigned i = 0; i < ntransp; i++) {
+ buffer[i].rgbReserved = tt[i];
+ }
+ for (unsigned i = ntransp; i < ncolors; i++) {
+ buffer[i].rgbReserved = 255;
+ }
+ return buffer;
+}
+
+// --------------------------------------------------------------------------
+
+CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) {
+ double dWidth;
+ double dFScale;
+ const double dFilterWidth = pFilter->GetWidth();
+
+ // scale factor
+ const double dScale = double(uDstSize) / double(uSrcSize);
+
+ if(dScale < 1.0) {
+ // minification
+ dWidth = dFilterWidth / dScale;
+ dFScale = dScale;
+ } else {
+ // magnification
+ dWidth = dFilterWidth;
+ dFScale = 1.0;
+ }
+
+ // allocate a new line contributions structure
+ //
+ // window size is the number of sampled pixels
+ m_WindowSize = 2 * (int)ceil(dWidth) + 1;
+ // length of dst line (no. of rows / cols)
+ m_LineLength = uDstSize;
+
+ // allocate list of contributions
+ m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
+ for(unsigned u = 0; u < m_LineLength; u++) {
+ // allocate contributions for every pixel
+ m_WeightTable[u].Weights = (double*)malloc(m_WindowSize * sizeof(double));
+ }
+
+ // offset for discrete to continuous coordinate conversion
+ const double dOffset = (0.5 / dScale);
+
+ for(unsigned u = 0; u < m_LineLength; u++) {
+ // scan through line of contributions
+
+ // inverse mapping (discrete dst 'u' to continous src 'dCenter')
+ const double dCenter = (double)u / dScale + dOffset;
+
+ // find the significant edge points that affect the pixel
+ const int iLeft = MAX(0, (int)(dCenter - dWidth + 0.5));
+ const int iRight = MIN((int)(dCenter + dWidth + 0.5), int(uSrcSize));
+
+ m_WeightTable[u].Left = iLeft;
+ m_WeightTable[u].Right = iRight;
+
+ double dTotalWeight = 0; // sum of weights (initialized to zero)
+ for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
+ // calculate weights
+ const double weight = dFScale * pFilter->Filter(dFScale * ((double)iSrc + 0.5 - dCenter));
+ // assert((iSrc-iLeft) < m_WindowSize);
+ m_WeightTable[u].Weights[iSrc-iLeft] = weight;
+ dTotalWeight += weight;
+ }
+ if((dTotalWeight > 0) && (dTotalWeight != 1)) {
+ // normalize weight of neighbouring points
+ for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
+ // normalize point
+ m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight;
+ }
+ }
+
+ // simplify the filter, discarding null weights at the right
+ {
+ int iTrailing = iRight - iLeft - 1;
+ while(m_WeightTable[u].Weights[iTrailing] == 0) {
+ m_WeightTable[u].Right--;
+ iTrailing--;
+ if(m_WeightTable[u].Right == m_WeightTable[u].Left) {
+ break;
+ }
+ }
+
+ }
+
+ } // next dst pixel
+}
+
+CWeightsTable::~CWeightsTable() {
+ for(unsigned u = 0; u < m_LineLength; u++) {
+ // free contributions for every pixel
+ free(m_WeightTable[u].Weights);
+ }
+ // free list of pixels contributions
+ free(m_WeightTable);
+}
+
+// --------------------------------------------------------------------------
+
+FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height, unsigned flags) {
+
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+ const unsigned src_bpp = FreeImage_GetBPP(src);
+
+ // determine the image's color type
+ BOOL bIsGreyscale = FALSE;
+ FREE_IMAGE_COLOR_TYPE color_type;
+ if (src_bpp <= 8) {
+ color_type = GetExtendedColorType(src, &bIsGreyscale);
+ } else {
+ color_type = FIC_RGB;
+ }
+
+ // determine the required bit depth of the destination image
+ unsigned dst_bpp;
+ unsigned dst_bpp_s1 = 0;
+ if (color_type == FIC_PALETTE && !bIsGreyscale) {
+ // non greyscale FIC_PALETTE images require a high-color destination
+ // image (24- or 32-bits depending on the image's transparent state)
+ dst_bpp = FreeImage_IsTransparent(src) ? 32 : 24;
+ } else if (src_bpp <= 8) {
+ // greyscale images require an 8-bit destination image
+ // (or a 32-bit image if the image is transparent);
+ // however, if flag FI_RESCALE_TRUE_COLOR is set, we will return
+ // a true color (24 bpp) image
+ if (FreeImage_IsTransparent(src)) {
+ dst_bpp = 32;
+ // additionally, for transparent images we always need a
+ // palette including transparency information (an RGBA palette)
+ // so, set color_type accordingly
+ color_type = FIC_PALETTE;
+ } else {
+ dst_bpp = ((flags & FI_RESCALE_TRUE_COLOR) == FI_RESCALE_TRUE_COLOR) ? 24 : 8;
+ // in any case, we use a fast 8-bit temporary image for the
+ // first filter operation (stage 1, either horizontal or
+ // vertical) and implicitly convert to 24 bpp (if requested
+ // by flag FI_RESCALE_TRUE_COLOR) during the second filter
+ // operation
+ dst_bpp_s1 = 8;
+ }
+ } else if (src_bpp == 16 && image_type == FIT_BITMAP) {
+ // 16-bit 555 and 565 RGB images require a high-color destination
+ // image (fixed to 24 bits, since 16-bit RGBs don't support
+ // transparency in FreeImage)
+ dst_bpp = 24;
+ } else {
+ // bit depth remains unchanged for all other images
+ dst_bpp = src_bpp;
+ }
+
+ // make 'stage 1' bpp a copy of the destination bpp if it
+ // was not explicitly set
+ if (dst_bpp_s1 == 0) {
+ dst_bpp_s1 = dst_bpp;
+ }
+
+ // early exit if destination size is equal to source size
+ if ((src_width == dst_width) && (src_height == dst_height)) {
+ FIBITMAP *out = src;
+ FIBITMAP *tmp = src;
+ if ((src_width != FreeImage_GetWidth(src)) || (src_height != FreeImage_GetHeight(src))) {
+ out = FreeImage_Copy(tmp, src_left, src_top, src_left + src_width, src_top + src_height);
+ tmp = out;
+ }
+ if (src_bpp != dst_bpp) {
+ switch (dst_bpp) {
+ case 8:
+ out = FreeImage_ConvertToGreyscale(tmp);
+ break;
+ case 24:
+ out = FreeImage_ConvertTo24Bits(tmp);
+ break;
+ case 32:
+ out = FreeImage_ConvertTo32Bits(tmp);
+ break;
+ default:
+ break;
+ }
+ if (tmp != src) {
+ FreeImage_Unload(tmp);
+ tmp = NULL;
+ }
+ }
+
+ return (out != src) ? out : FreeImage_Clone(src);
+ }
+
+ RGBQUAD pal_buffer[256];
+ RGBQUAD *src_pal = NULL;
+
+ // provide the source image's palette to the rescaler for
+ // FIC_PALETTE type images (this includes palletized greyscale
+ // images with an unordered palette as well as transparent images)
+ if (color_type == FIC_PALETTE) {
+ if (dst_bpp == 32) {
+ // a 32-bit destination image signals transparency, so
+ // create an RGBA palette from the source palette
+ src_pal = GetRGBAPalette(src, pal_buffer);
+ } else {
+ src_pal = FreeImage_GetPalette(src);
+ }
+ }
+
+ // allocate the dst image
+ FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, dst_bpp, 0, 0, 0);
+ if (!dst) {
+ return NULL;
+ }
+
+ if (dst_bpp == 8) {
+ RGBQUAD * const dst_pal = FreeImage_GetPalette(dst);
+ if (color_type == FIC_MINISWHITE) {
+ // build an inverted greyscale palette
+ CREATE_GREYSCALE_PALETTE_REVERSE(dst_pal, 256);
+ }
+ /*
+ else {
+ // build a default greyscale palette
+ // Currently, FreeImage_AllocateT already creates a default
+ // greyscale palette for 8 bpp images, so we can skip this here.
+ CREATE_GREYSCALE_PALETTE(dst_pal, 256);
+ }
+ */
+ }
+
+ // calculate x and y offsets; since FreeImage uses bottom-up bitmaps, the
+ // value of src_offset_y is measured from the bottom of the image
+ unsigned src_offset_x = src_left;
+ unsigned src_offset_y = FreeImage_GetHeight(src) - src_height - src_top;
+
+ /*
+ Decide which filtering order (xy or yx) is faster for this mapping.
+ --- The theory ---
+ Try to minimize calculations by counting the number of convolution multiplies
+ if(dst_width*src_height <= src_width*dst_height) {
+ // xy filtering
+ } else {
+ // yx filtering
+ }
+ --- The practice ---
+ Try to minimize calculations by counting the number of vertical convolutions (the most time consuming task)
+ if(dst_width*dst_height <= src_width*dst_height) {
+ // xy filtering
+ } else {
+ // yx filtering
+ }
+ */
+
+ if (dst_width <= src_width) {
+ // xy filtering
+ // -------------
+
+ FIBITMAP *tmp = NULL;
+
+ if (src_width != dst_width) {
+ // source and destination widths are different so, we must
+ // filter horizontally
+ if (src_height != dst_height) {
+ // source and destination heights are also different so, we need
+ // a temporary image
+ tmp = FreeImage_AllocateT(image_type, dst_width, src_height, dst_bpp_s1, 0, 0, 0);
+ if (!tmp) {
+ FreeImage_Unload(dst);
+ return NULL;
+ }
+ } else {
+ // source and destination heights are equal so, we can directly
+ // scale into destination image (second filter method will not
+ // be invoked)
+ tmp = dst;
+ }
+
+ // scale source image horizontally into temporary (or destination) image
+ horizontalFilter(src, src_height, src_width, src_offset_x, src_offset_y, src_pal, tmp, dst_width);
+
+ // set x and y offsets to zero for the second filter method
+ // invocation (the temporary image only contains the portion of
+ // the image to be rescaled with no offsets)
+ src_offset_x = 0;
+ src_offset_y = 0;
+
+ // also ensure, that the second filter method gets no source
+ // palette (the temporary image is palletized only, if it is
+ // greyscale; in that case, it is an 8-bit image with a linear
+ // palette so, the source palette is not needed or will even be
+ // mismatching, if the source palette is unordered)
+ src_pal = NULL;
+ } else {
+ // source and destination widths are equal so, just copy the
+ // image pointer
+ tmp = src;
+ }
+
+ if (src_height != dst_height) {
+ // source and destination heights are different so, scale
+ // temporary (or source) image vertically into destination image
+ verticalFilter(tmp, dst_width, src_height, src_offset_x, src_offset_y, src_pal, dst, dst_height);
+ }
+
+ // free temporary image, if not pointing to either src or dst
+ if (tmp != src && tmp != dst) {
+ FreeImage_Unload(tmp);
+ }
+
+ } else {
+ // yx filtering
+ // -------------
+
+ // Remark:
+ // The yx filtering branch could be more optimized by taking into,
+ // account that (src_width != dst_width) is always true, which
+ // follows from the above condition, which selects filtering order.
+ // Since (dst_width <= src_width) == TRUE selects xy filtering,
+ // both widths must be different when performing yx filtering.
+ // However, to make the code more robust, not depending on that
+ // condition and more symmetric to the xy filtering case, these
+ // (src_width != dst_width) conditions are still in place.
+
+ FIBITMAP *tmp = NULL;
+
+ if (src_height != dst_height) {
+ // source and destination heights are different so, we must
+ // filter vertically
+ if (src_width != dst_width) {
+ // source and destination widths are also different so, we need
+ // a temporary image
+ tmp = FreeImage_AllocateT(image_type, src_width, dst_height, dst_bpp_s1, 0, 0, 0);
+ if (!tmp) {
+ FreeImage_Unload(dst);
+ return NULL;
+ }
+ } else {
+ // source and destination widths are equal so, we can directly
+ // scale into destination image (second filter method will not
+ // be invoked)
+ tmp = dst;
+ }
+
+ // scale source image vertically into temporary (or destination) image
+ verticalFilter(src, src_width, src_height, src_offset_x, src_offset_y, src_pal, tmp, dst_height);
+
+ // set x and y offsets to zero for the second filter method
+ // invocation (the temporary image only contains the portion of
+ // the image to be rescaled with no offsets)
+ src_offset_x = 0;
+ src_offset_y = 0;
+
+ // also ensure, that the second filter method gets no source
+ // palette (the temporary image is palletized only, if it is
+ // greyscale; in that case, it is an 8-bit image with a linear
+ // palette so, the source palette is not needed or will even be
+ // mismatching, if the source palette is unordered)
+ src_pal = NULL;
+
+ } else {
+ // source and destination heights are equal so, just copy the
+ // image pointer
+ tmp = src;
+ }
+
+ if (src_width != dst_width) {
+ // source and destination heights are different so, scale
+ // temporary (or source) image horizontally into destination image
+ horizontalFilter(tmp, dst_height, src_width, src_offset_x, src_offset_y, src_pal, dst, dst_width);
+ }
+
+ // free temporary image, if not pointing to either src or dst
+ if (tmp != src && tmp != dst) {
+ FreeImage_Unload(tmp);
+ }
+ }
+
+ return dst;
+}
+
+void CResizeEngine::horizontalFilter(FIBITMAP *const src, unsigned height, unsigned src_width, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_width) {
+
+ // allocate and calculate the contributions
+ CWeightsTable weightsTable(m_pFilter, dst_width, src_width);
+
+ // step through rows
+ switch(FreeImage_GetImageType(src)) {
+ case FIT_BITMAP:
+ {
+ switch(FreeImage_GetBPP(src)) {
+ case 1:
+ {
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // transparently convert the 1-bit non-transparent greyscale image to 8 bpp
+ src_offset_x >>= 3;
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double value = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+ value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double value = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+ value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
+ }
+ value *= 0xFF;
+
+ // clamp and place result in destination pixel
+ dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 1-bit image to 24 bpp
+ src_offset_x >>= 3;
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i - iLeft);
+ const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double value = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+ value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
+ }
+ value *= 0xFF;
+
+ // clamp and place result in destination pixel
+ const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_RED] = bval;
+ dst_bits[FI_RGBA_GREEN] = bval;
+ dst_bits[FI_RGBA_BLUE] = bval;
+ dst_bits += 3;
+ }
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 1-bit image to 32 bpp;
+ // we always have got a palette here
+ src_offset_x >>= 3;
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i - iLeft);
+ const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += 4;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 4:
+ {
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // transparently convert the non-transparent 4-bit greyscale image to 8 bpp;
+ // we always have got a palette for 4-bit images
+ src_offset_x >>= 1;
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double value = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
+ value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 4-bit image to 24 bpp;
+ // we always have got a palette for 4-bit images
+ src_offset_x >>= 1;
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i - iLeft);
+ const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 4-bit image to 32 bpp;
+ // we always have got a palette for 4-bit images
+ src_offset_x >>= 1;
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i - iLeft);
+ const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += 4;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 8:
+ {
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // scale the 8-bit non-transparent greyscale image
+ // into an 8 bpp destination image
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * const pixel = src_bits + iLeft;
+ double value = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(x, i) * (double)*(BYTE *)&src_pal[pixel[i]]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * const pixel = src_bits + iLeft;
+ double value = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(x, i) * (double)pixel[i]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 8-bit image to 24 bpp
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * const pixel = src_bits + iLeft;
+ double r = 0, g = 0, b = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ const BYTE *const entry = (BYTE *)&src_pal[pixel[i]];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * const pixel = src_bits + iLeft;
+ double value = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ value += (weight * (double)pixel[i]);
+ }
+
+ // clamp and place result in destination pixel
+ const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_RED] = bval;
+ dst_bits[FI_RGBA_GREEN] = bval;
+ dst_bits[FI_RGBA_BLUE] = bval;
+ dst_bits += 3;
+ }
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 8-bit image to 32 bpp;
+ // we always have got a palette here
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * const pixel = src_bits + iLeft;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ const BYTE * const entry = (BYTE *)&src_pal[pixel[i]];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += 4;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 16:
+ {
+ // transparently convert the 16-bit non-transparent image to 24 bpp
+ if (IS_FORMAT_RGB565(src)) {
+ // image has 565 format
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const WORD *pixel = src_bits + iLeft;
+ double r = 0, g = 0, b = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
+ g += (weight * (double)((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
+ b += (weight * (double)((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
+ pixel++;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ } else {
+ // image has 555 format
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const WORD *pixel = src_bits + iLeft;
+ double r = 0, g = 0, b = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
+ g += (weight * (double)((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
+ b += (weight * (double)((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
+ pixel++;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // scale the 24-bit non-transparent image into a 24 bpp destination image
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 3;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * pixel = src_bits + iLeft * 3;
+ double r = 0, g = 0, b = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)pixel[FI_RGBA_RED]);
+ g += (weight * (double)pixel[FI_RGBA_GREEN]);
+ b += (weight * (double)pixel[FI_RGBA_BLUE]);
+ pixel += 3;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // scale the 32-bit transparent image into a 32 bpp destination image
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 4;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE *pixel = src_bits + iLeft * 4;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)pixel[FI_RGBA_RED]);
+ g += (weight * (double)pixel[FI_RGBA_GREEN]);
+ b += (weight * (double)pixel[FI_RGBA_BLUE]);
+ a += (weight * (double)pixel[FI_RGBA_ALPHA]);
+ pixel += 4;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += 4;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case FIT_UINT16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+ WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const WORD *pixel = src_bits + iLeft * wordspp;
+ double value = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ value += (weight * (double)pixel[0]);
+ pixel++;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
+ dst_bits += wordspp;
+ }
+ }
+ }
+ break;
+
+ case FIT_RGB16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+ WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const WORD *pixel = src_bits + iLeft * wordspp;
+ double r = 0, g = 0, b = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)pixel[0]);
+ g += (weight * (double)pixel[1]);
+ b += (weight * (double)pixel[2]);
+ pixel += wordspp;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+ dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+ dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+ dst_bits += wordspp;
+ }
+ }
+ }
+ break;
+
+ case FIT_RGBA16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+ WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const WORD *pixel = src_bits + iLeft * wordspp;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)pixel[0]);
+ g += (weight * (double)pixel[1]);
+ b += (weight * (double)pixel[2]);
+ a += (weight * (double)pixel[3]);
+ pixel += wordspp;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+ dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+ dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+ dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
+ dst_bits += wordspp;
+ }
+ }
+ }
+ break;
+
+ case FIT_FLOAT:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ {
+ // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
+ const unsigned floatspp = (FreeImage_GetLine(src) / src_width) / sizeof(float);
+
+ for(unsigned y = 0; y < height; y++) {
+ // scale each row
+ const float *src_bits = (float*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(float);
+ float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
+
+ for(unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
+
+ for(unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i-iLeft);
+
+ unsigned index = i * floatspp; // pixel index
+ for (unsigned j = 0; j < floatspp; j++) {
+ value[j] += (weight * (double)src_bits[index++]);
+ }
+ }
+
+ // place result in destination pixel
+ for (unsigned j = 0; j < floatspp; j++) {
+ dst_bits[j] = (float)value[j];
+ }
+
+ dst_bits += floatspp;
+ }
+ }
+ }
+ break;
+ }
+}
+
+/// Performs vertical image filtering
+void CResizeEngine::verticalFilter(FIBITMAP *const src, unsigned width, unsigned src_height, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_height) {
+
+ // allocate and calculate the contributions
+ CWeightsTable weightsTable(m_pFilter, dst_height, src_height);
+
+ // step through columns
+ switch(FreeImage_GetImageType(src)) {
+ case FIT_BITMAP:
+ {
+ const unsigned dst_pitch = FreeImage_GetPitch(dst);
+ BYTE * const dst_base = FreeImage_GetBits(dst);
+
+ switch(FreeImage_GetBPP(src)) {
+ case 1:
+ {
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const BYTE * const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 3);
+
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // transparently convert the 1-bit non-transparent greyscale image to 8 bpp
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x;
+ const unsigned index = x >> 3;
+ const unsigned mask = 0x80 >> (x & 0x07);
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = (*src_bits & mask) != 0;
+ value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
+ src_bits += src_pitch;
+ }
+ value *= 0xFF;
+
+ // clamp and place result in destination pixel
+ *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x;
+ const unsigned index = x >> 3;
+ const unsigned mask = 0x80 >> (x & 0x07);
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
+ src_bits += src_pitch;
+ }
+ value *= 0xFF;
+
+ // clamp and place result in destination pixel
+ *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 1-bit image to 24 bpp
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+ const unsigned index = x >> 3;
+ const unsigned mask = 0x80 >> (x & 0x07);
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const unsigned pixel = (*src_bits & mask) != 0;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+ const unsigned index = x >> 3;
+ const unsigned mask = 0x80 >> (x & 0x07);
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
+ src_bits += src_pitch;
+ }
+ value *= 0xFF;
+
+ // clamp and place result in destination pixel
+ const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_RED] = bval;
+ dst_bits[FI_RGBA_GREEN] = bval;
+ dst_bits[FI_RGBA_BLUE] = bval;
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 1-bit image to 32 bpp;
+ // we always have got a palette here
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 4;
+ const unsigned index = x >> 3;
+ const unsigned mask = 0x80 >> (x & 0x07);
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const unsigned pixel = (*src_bits & mask) != 0;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 4:
+ {
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 1);
+
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // transparently convert the non-transparent 4-bit greyscale image to 8 bpp;
+ // we always have got a palette for 4-bit images
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x;
+ const unsigned index = x >> 1;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
+ value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 4-bit image to 24 bpp;
+ // we always have got a palette for 4-bit images
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+ const unsigned index = x >> 1;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
+ const BYTE *const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 4-bit image to 32 bpp;
+ // we always have got a palette for 4-bit images
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 4;
+ const unsigned index = x >> 1;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
+ const BYTE *const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 8:
+ {
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
+
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // scale the 8-bit non-transparent greyscale image into an 8 bpp destination image
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[*src_bits]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(y, i) * (double)*src_bits);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 8-bit image to 24 bpp
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(y, i) * (double)*src_bits);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_RED] = bval;
+ dst_bits[FI_RGBA_GREEN] = bval;
+ dst_bits[FI_RGBA_BLUE] = bval;
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 8-bit image to 32 bpp;
+ // we always have got a palette here
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 4;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 16:
+ {
+ // transparently convert the 16-bit non-transparent image to 24 bpp
+ const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+ const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
+
+ if (IS_FORMAT_RGB565(src)) {
+ // image has 565 format
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const WORD *src_bits = src_base + iLeft * src_pitch + x;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)((*src_bits & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
+ g += (weight * (double)((*src_bits & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
+ b += (weight * (double)((*src_bits & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ } else {
+ // image has 555 format
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const WORD *src_bits = src_base + iLeft * src_pitch + x;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)((*src_bits & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
+ g += (weight * (double)((*src_bits & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
+ b += (weight * (double)((*src_bits & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // scale the 24-bit transparent image into a 24 bpp destination image
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 3;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * 3;
+ BYTE *dst_bits = dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)src_bits[FI_RGBA_RED]);
+ g += (weight * (double)src_bits[FI_RGBA_GREEN]);
+ b += (weight * (double)src_bits[FI_RGBA_BLUE]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // scale the 32-bit transparent image into a 32 bpp destination image
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 4;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * 4;
+ BYTE *dst_bits = dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)src_bits[FI_RGBA_RED]);
+ g += (weight * (double)src_bits[FI_RGBA_GREEN]);
+ b += (weight * (double)src_bits[FI_RGBA_BLUE]);
+ a += (weight * (double)src_bits[FI_RGBA_ALPHA]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int) (a + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case FIT_UINT16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
+
+ const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
+ WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
+
+ const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+ const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * wordspp; // pixel index
+ WORD *dst_bits = dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const WORD *src_bits = src_base + iLeft * src_pitch + index;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ value += (weight * (double)src_bits[0]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
+
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case FIT_RGB16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
+
+ const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
+ WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
+
+ const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+ const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * wordspp; // pixel index
+ WORD *dst_bits = dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const WORD *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)src_bits[0]);
+ g += (weight * (double)src_bits[1]);
+ b += (weight * (double)src_bits[2]);
+
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+ dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+ dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case FIT_RGBA16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
+
+ const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
+ WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
+
+ const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+ const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * wordspp; // pixel index
+ WORD *dst_bits = dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const WORD *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)src_bits[0]);
+ g += (weight * (double)src_bits[1]);
+ b += (weight * (double)src_bits[2]);
+ a += (weight * (double)src_bits[3]);
+
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+ dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+ dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+ dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
+
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case FIT_FLOAT:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ {
+ // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
+ const unsigned floatspp = (FreeImage_GetLine(src) / width) / sizeof(float);
+
+ const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float);
+ float *const dst_base = (float *)FreeImage_GetBits(dst);
+
+ const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float);
+ const float *const src_base = (float *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * floatspp;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * floatspp; // pixel index
+ float *dst_bits = (float *)dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
+ const float *src_bits = src_base + iLeft * src_pitch + index;
+ double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i - iLeft);
+ for (unsigned j = 0; j < floatspp; j++) {
+ value[j] += (weight * (double)src_bits[j]);
+ }
+ src_bits += src_pitch;
+ }
+
+ // place result in destination pixel
+ for (unsigned j = 0; j < floatspp; j++) {
+ dst_bits[j] = (float)value[j];
+ }
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+ }
+}
diff --git a/plugins/AdvaImg/src/FreeImageToolkit/Resize.h b/plugins/AdvaImg/src/FreeImageToolkit/Resize.h
index ca382efa60..ce1d7328d7 100644
--- a/plugins/AdvaImg/src/FreeImageToolkit/Resize.h
+++ b/plugins/AdvaImg/src/FreeImageToolkit/Resize.h
@@ -1,196 +1,196 @@
-// ==========================================================
-// Upsampling / downsampling classes
-//
-// Design and implementation by
-// - Hervé Drolon (drolon@infonie.fr)
-// - Detlev Vendt (detlev.vendt@brillit.de)
-// - Carsten Klein (cklein05@users.sourceforge.net)
-//
-// 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!
-// ==========================================================
-
-#ifndef _RESIZE_H_
-#define _RESIZE_H_
-
-#include "FreeImage.h"
-#include "Utilities.h"
-#include "Filters.h"
-
-/**
- Filter weights table.<br>
- This class stores contribution information for an entire line (row or column).
-*/
-class CWeightsTable
-{
-/**
- Sampled filter weight table.<br>
- Contribution information for a single pixel
-*/
-typedef struct {
- /// Normalized weights of neighboring pixels
- double *Weights;
- /// Bounds of source pixels window
- unsigned Left, Right;
-} Contribution;
-
-private:
- /// Row (or column) of contribution weights
- Contribution *m_WeightTable;
- /// Filter window size (of affecting source pixels)
- unsigned m_WindowSize;
- /// Length of line (no. of rows / cols)
- unsigned m_LineLength;
-
-public:
- /**
- Constructor<br>
- Allocate and compute the weights table
- @param pFilter Filter used for upsampling or downsampling
- @param uDstSize Length (in pixels) of the destination line buffer
- @param uSrcSize Length (in pixels) of the source line buffer
- */
- CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize);
-
- /**
- Destructor<br>
- Destroy the weights table
- */
- ~CWeightsTable();
-
- /** Retrieve a filter weight, given source and destination positions
- @param dst_pos Pixel position in destination line buffer
- @param src_pos Pixel position in source line buffer
- @return Returns the filter weight
- */
- double getWeight(unsigned dst_pos, unsigned src_pos) {
- return m_WeightTable[dst_pos].Weights[src_pos];
- }
-
- /** Retrieve left boundary of source line buffer
- @param dst_pos Pixel position in destination line buffer
- @return Returns the left boundary of source line buffer
- */
- unsigned getLeftBoundary(unsigned dst_pos) {
- return m_WeightTable[dst_pos].Left;
- }
-
- /** Retrieve right boundary of source line buffer
- @param dst_pos Pixel position in destination line buffer
- @return Returns the right boundary of source line buffer
- */
- unsigned getRightBoundary(unsigned dst_pos) {
- return m_WeightTable[dst_pos].Right;
- }
-};
-
-// ---------------------------------------------
-
-/**
- CResizeEngine<br>
- This class performs filtered zoom. It scales an image to the desired dimensions with
- any of the CGenericFilter derived filter class.<br>
- It works with FIT_BITMAP buffers, WORD buffers (FIT_UINT16, FIT_RGB16, FIT_RGBA16)
- and float buffers (FIT_FLOAT, FIT_RGBF, FIT_RGBAF).<br><br>
-
- <b>References</b> : <br>
- [1] Paul Heckbert, C code to zoom raster images up or down, with nice filtering.
- UC Berkeley, August 1989. [online] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/Web/People/ph/heckbert.html
- [2] Eran Yariv, Two Pass Scaling using Filters. The Code Project, December 1999.
- [online] http://www.codeproject.com/bitmap/2_pass_scaling.asp
-
-*/
-class CResizeEngine
-{
-private:
- /// Pointer to the FIR / IIR filter
- CGenericFilter* m_pFilter;
-
-public:
-
- /**
- Constructor
- @param filter FIR /IIR filter to be used
- */
- CResizeEngine(CGenericFilter* filter):m_pFilter(filter) {}
-
- /// Destructor
- virtual ~CResizeEngine() {}
-
- /** Scale an image to the desired dimensions.
-
- Method CResizeEngine::scale, as well as the two filtering methods
- CResizeEngine::horizontalFilter and CResizeEngine::verticalFilter take
- four additional parameters, that define a rectangle in the source
- image to be rescaled.
-
- These are src_left, src_top, src_width and src_height and should work
- like these of function FreeImage_Copy. However, src_left and src_top are
- actually named src_offset_x and src_offset_y in the filtering methods.
-
- Additionally, since src_height and dst_height are always the same for
- method horizontalFilter as src_width and dst_width are always the same
- for verticalFilter, these have been stripped down to a single parameter
- height and width for horizontalFilter and verticalFilter respectively.
-
- Currently, method scale is called with the actual size of the source
- image. However, in a future version, we could provide a new function
- called FreeImage_RescaleRect that rescales only part of an image.
-
- @param src Pointer to the source image
- @param dst_width Destination image width
- @param dst_height Destination image height
- @param src_left Left boundary of the source rectangle to be scaled
- @param src_top Top boundary of the source rectangle to be scaled
- @param src_width Width of the source rectangle to be scaled
- @param src_height Height of the source rectangle to be scaled
- @return Returns the scaled image if successful, returns NULL otherwise
- */
- FIBITMAP* scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height);
-
-private:
-
- /**
- Performs horizontal image filtering
-
- @param src Source image
- @param height Source / Destination image height
- @param src_width Source image width
- @param src_offset_x
- @param src_offset_y
- @param src_pal
- @param dst Destination image
- @param dst_width Destination image width
- */
- void horizontalFilter(FIBITMAP * const src, const unsigned height, const unsigned src_width,
- const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal,
- FIBITMAP * const dst, const unsigned dst_width);
-
- /**
- Performs vertical image filtering
- @param src Source image
- @param width Source / Destination image width
- @param src_height Source image height
- @param src_offset_x
- @param src_offset_y
- @param src_pal
- @param dst Destination image
- @param dst_height Destination image height
- */
- void verticalFilter(FIBITMAP * const src, const unsigned width, const unsigned src_height,
- const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal,
- FIBITMAP * const dst, const unsigned dst_height);
-};
-
-#endif // _RESIZE_H_
+// ==========================================================
+// Upsampling / downsampling classes
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Carsten Klein (cklein05@users.sourceforge.net)
+//
+// 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!
+// ==========================================================
+
+#ifndef _RESIZE_H_
+#define _RESIZE_H_
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "Filters.h"
+
+/**
+ Filter weights table.<br>
+ This class stores contribution information for an entire line (row or column).
+*/
+class CWeightsTable
+{
+/**
+ Sampled filter weight table.<br>
+ Contribution information for a single pixel
+*/
+typedef struct {
+ /// Normalized weights of neighboring pixels
+ double *Weights;
+ /// Bounds of source pixels window
+ unsigned Left, Right;
+} Contribution;
+
+private:
+ /// Row (or column) of contribution weights
+ Contribution *m_WeightTable;
+ /// Filter window size (of affecting source pixels)
+ unsigned m_WindowSize;
+ /// Length of line (no. of rows / cols)
+ unsigned m_LineLength;
+
+public:
+ /**
+ Constructor<br>
+ Allocate and compute the weights table
+ @param pFilter Filter used for upsampling or downsampling
+ @param uDstSize Length (in pixels) of the destination line buffer
+ @param uSrcSize Length (in pixels) of the source line buffer
+ */
+ CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize);
+
+ /**
+ Destructor<br>
+ Destroy the weights table
+ */
+ ~CWeightsTable();
+
+ /** Retrieve a filter weight, given source and destination positions
+ @param dst_pos Pixel position in destination line buffer
+ @param src_pos Pixel position in source line buffer
+ @return Returns the filter weight
+ */
+ double getWeight(unsigned dst_pos, unsigned src_pos) {
+ return m_WeightTable[dst_pos].Weights[src_pos];
+ }
+
+ /** Retrieve left boundary of source line buffer
+ @param dst_pos Pixel position in destination line buffer
+ @return Returns the left boundary of source line buffer
+ */
+ unsigned getLeftBoundary(unsigned dst_pos) {
+ return m_WeightTable[dst_pos].Left;
+ }
+
+ /** Retrieve right boundary of source line buffer
+ @param dst_pos Pixel position in destination line buffer
+ @return Returns the right boundary of source line buffer
+ */
+ unsigned getRightBoundary(unsigned dst_pos) {
+ return m_WeightTable[dst_pos].Right;
+ }
+};
+
+// ---------------------------------------------
+
+/**
+ CResizeEngine<br>
+ This class performs filtered zoom. It scales an image to the desired dimensions with
+ any of the CGenericFilter derived filter class.<br>
+ It works with FIT_BITMAP buffers, WORD buffers (FIT_UINT16, FIT_RGB16, FIT_RGBA16)
+ and float buffers (FIT_FLOAT, FIT_RGBF, FIT_RGBAF).<br><br>
+
+ <b>References</b> : <br>
+ [1] Paul Heckbert, C code to zoom raster images up or down, with nice filtering.
+ UC Berkeley, August 1989. [online] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/Web/People/ph/heckbert.html
+ [2] Eran Yariv, Two Pass Scaling using Filters. The Code Project, December 1999.
+ [online] http://www.codeproject.com/bitmap/2_pass_scaling.asp
+
+*/
+class CResizeEngine
+{
+private:
+ /// Pointer to the FIR / IIR filter
+ CGenericFilter* m_pFilter;
+
+public:
+
+ /**
+ Constructor
+ @param filter FIR /IIR filter to be used
+ */
+ CResizeEngine(CGenericFilter* filter):m_pFilter(filter) {}
+
+ /// Destructor
+ virtual ~CResizeEngine() {}
+
+ /** Scale an image to the desired dimensions.
+
+ Method CResizeEngine::scale, as well as the two filtering methods
+ CResizeEngine::horizontalFilter and CResizeEngine::verticalFilter take
+ four additional parameters, that define a rectangle in the source
+ image to be rescaled.
+
+ These are src_left, src_top, src_width and src_height and should work
+ like these of function FreeImage_Copy. However, src_left and src_top are
+ actually named src_offset_x and src_offset_y in the filtering methods.
+
+ Additionally, since src_height and dst_height are always the same for
+ method horizontalFilter as src_width and dst_width are always the same
+ for verticalFilter, these have been stripped down to a single parameter
+ height and width for horizontalFilter and verticalFilter respectively.
+
+ Currently, method scale is called with the actual size of the source
+ image. However, in a future version, we could provide a new function
+ called FreeImage_RescaleRect that rescales only part of an image.
+
+ @param src Pointer to the source image
+ @param dst_width Destination image width
+ @param dst_height Destination image height
+ @param src_left Left boundary of the source rectangle to be scaled
+ @param src_top Top boundary of the source rectangle to be scaled
+ @param src_width Width of the source rectangle to be scaled
+ @param src_height Height of the source rectangle to be scaled
+ @return Returns the scaled image if successful, returns NULL otherwise
+ */
+ FIBITMAP* scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height, unsigned flags);
+
+private:
+
+ /**
+ Performs horizontal image filtering
+
+ @param src Source image
+ @param height Source / Destination image height
+ @param src_width Source image width
+ @param src_offset_x
+ @param src_offset_y
+ @param src_pal
+ @param dst Destination image
+ @param dst_width Destination image width
+ */
+ void horizontalFilter(FIBITMAP * const src, const unsigned height, const unsigned src_width,
+ const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal,
+ FIBITMAP * const dst, const unsigned dst_width);
+
+ /**
+ Performs vertical image filtering
+ @param src Source image
+ @param width Source / Destination image width
+ @param src_height Source image height
+ @param src_offset_x
+ @param src_offset_y
+ @param src_pal
+ @param dst Destination image
+ @param dst_height Destination image height
+ */
+ void verticalFilter(FIBITMAP * const src, const unsigned width, const unsigned src_height,
+ const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal,
+ FIBITMAP * const dst, const unsigned dst_height);
+};
+
+#endif // _RESIZE_H_