summaryrefslogtreecommitdiff
path: root/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/AdvaImg/src/FreeImageToolkit/Background.cpp')
-rw-r--r--plugins/AdvaImg/src/FreeImageToolkit/Background.cpp895
1 files changed, 895 insertions, 0 deletions
diff --git a/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp b/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp
new file mode 100644
index 0000000000..06b31aa332
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp
@@ -0,0 +1,895 @@
+// ==========================================================
+// Background filling routines
+//
+// Design and implementation by
+// - Carsten Klein (c.klein@datagis.com)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+/** @brief Determines, whether a palletized image is visually greyscale or not.
+
+ Unlike with FreeImage_GetColorType, which returns either FIC_MINISBLACK or
+ FIC_MINISWHITE for a greyscale image with a linear ramp palette, the return
+ value of this function does not depend on the palette's order, but only on the
+ palette's individual colors.
+ @param dib The image to be tested.
+ @return Returns TRUE if the palette of the image specified contains only
+ greyscales, FALSE otherwise.
+ */
+static BOOL
+IsVisualGreyscaleImage(FIBITMAP *dib) {
+
+ switch (FreeImage_GetBPP(dib)) {
+ case 1:
+ case 4:
+ case 8: {
+ unsigned ncolors = FreeImage_GetColorsUsed(dib);
+ RGBQUAD *rgb = FreeImage_GetPalette(dib);
+ for (unsigned i = 0; i< ncolors; i++) {
+ if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ default: {
+ return (FreeImage_GetColorType(dib) == FIC_MINISBLACK);
+ }
+ }
+}
+
+/** @brief Looks up a specified color in a FIBITMAP's palette and returns the color's
+ palette index or -1 if the color was not found.
+
+ Unlike with FreeImage_GetColorType, which returns either FIC_MINISBLACK or
+ FIC_MINISWHITE for a greyscale image with a linear ramp palette, the return
+ value of this function does not depend on the palette's order, but only on the
+ palette's individual colors.
+ @param dib The image, whose palette should be searched through.
+ @param color The color to be searched in the palette.
+ @param options Options that affect the color search process.
+ @param color_type A pointer, that optionally specifies the image's color type as
+ returned by FreeImage_GetColorType. If invalid or NULL, this function determines the
+ color type with FreeImage_GetColorType.
+ @return Returns the specified color's palette index, the color's rgbReserved member
+ if option FI_COLOR_ALPHA_IS_INDEX was specified or -1, if the color was not found
+ in the image's palette or if the specified image is non-palletized.
+ */
+static int
+GetPaletteIndex(FIBITMAP *dib, const RGBQUAD *color, int options, FREE_IMAGE_COLOR_TYPE *color_type) {
+
+ int result = -1;
+
+ if ((!dib) || (!color)) {
+ return result;
+ }
+
+ int bpp = FreeImage_GetBPP(dib);
+
+ // First check trivial case: return color->rgbReserved if only
+ // FI_COLOR_ALPHA_IS_INDEX is set.
+ if ((options & FI_COLOR_ALPHA_IS_INDEX) == FI_COLOR_ALPHA_IS_INDEX) {
+ if (bpp == 1) {
+ return color->rgbReserved & 0x01;
+ } else if (bpp == 4) {
+ return color->rgbReserved & 0x0F;
+ }
+ return color->rgbReserved;
+ }
+
+ if (bpp == 8) {
+ FREE_IMAGE_COLOR_TYPE ct =
+ (color_type == NULL || *color_type < 0) ?
+ FreeImage_GetColorType(dib) : *color_type;
+ if (ct == FIC_MINISBLACK) {
+ return GREY(color->rgbRed, color->rgbGreen, color->rgbBlue);
+ }
+ if (ct == FIC_MINISWHITE) {
+ return 255 - GREY(color->rgbRed, color->rgbGreen, color->rgbBlue);
+ }
+ } else if (bpp > 8) {
+ // for palettized images only
+ return result;
+ }
+
+ if (options & FI_COLOR_FIND_EQUAL_COLOR) {
+
+ // Option FI_COLOR_ALPHA_IS_INDEX is implicit here so, set
+ // index to color->rgbReserved
+ result = color->rgbReserved;
+ if (bpp == 1) {
+ result &= 0x01;
+ } else if (bpp == 4) {
+ result &= 0x0F;
+ }
+
+ unsigned ucolor;
+ if (!IsVisualGreyscaleImage(dib)) {
+ ucolor = (*((unsigned *)color)) & 0xFFFFFF;
+ } else {
+ ucolor = GREY(color->rgbRed, color->rgbGreen, color->rgbBlue) * 0x010101;
+ //ucolor = (ucolor | (ucolor << 8) | (ucolor << 16));
+ }
+ unsigned ncolors = FreeImage_GetColorsUsed(dib);
+ unsigned *palette = (unsigned *)FreeImage_GetPalette(dib);
+ for (unsigned i = 0; i < ncolors; i++) {
+ if ((palette[i] & 0xFFFFFF) == ucolor) {
+ result = i;
+ break;
+ }
+ }
+ } else {
+ unsigned minimum = UINT_MAX;
+ unsigned ncolors = FreeImage_GetColorsUsed(dib);
+ BYTE *palette = (BYTE *)FreeImage_GetPalette(dib);
+ BYTE red, green, blue;
+ if (!IsVisualGreyscaleImage(dib)) {
+ red = color->rgbRed;
+ green = color->rgbGreen;
+ blue = color->rgbBlue;
+ } else {
+ red = GREY(color->rgbRed, color->rgbGreen, color->rgbBlue);
+ green = blue = red;
+ }
+ for (unsigned i = 0; i < ncolors; i++) {
+ unsigned m = abs(palette[FI_RGBA_BLUE] - blue)
+ + abs(palette[FI_RGBA_GREEN] - green)
+ + abs(palette[FI_RGBA_RED] - red);
+ if (m < minimum) {
+ minimum = m;
+ result = i;
+ if (m == 0) {
+ break;
+ }
+ }
+ palette += sizeof(RGBQUAD);
+ }
+ }
+ return result;
+}
+
+/** @brief Blends an alpha-transparent foreground color over an opaque background
+ color.
+
+ This function blends the alpha-transparent foreground color fgcolor over the
+ background color bgcolor. The background color is considered fully opaque,
+ whatever it's alpha value contains, whereas the foreground color is considered
+ to be a real RGBA color with an alpha value, which is used for the blend
+ operation. The resulting color is returned through the blended parameter.
+ @param bgcolor The background color for the blend operation.
+ @param fgcolor The foreground color for the blend operation. This color's alpha
+ value, stored in the rgbReserved member, is the alpha value used for the blend
+ operation.
+ @param blended This out parameter takes the blended color and so, returns it to
+ the caller. This color's alpha value will be 0xFF (255) so, the blended color
+ itself has no transparency. The this argument is not changed, if the function
+ fails.
+ @return Returns TRUE on success, FALSE otherwise. This function fails if any of
+ the color arguments is a null pointer.
+ */
+static BOOL
+GetAlphaBlendedColor(const RGBQUAD *bgcolor, const RGBQUAD *fgcolor, RGBQUAD *blended) {
+
+ if ((!bgcolor) || (!fgcolor) || (!blended)) {
+ return FALSE;
+ }
+
+ BYTE alpha = fgcolor->rgbReserved;
+ BYTE not_alpha = ~alpha;
+
+ blended->rgbRed = (BYTE)( ((WORD)fgcolor->rgbRed * alpha + not_alpha * (WORD)bgcolor->rgbRed) >> 8 );
+ blended->rgbGreen = (BYTE)( ((WORD)fgcolor->rgbGreen * alpha + not_alpha * (WORD)bgcolor->rgbGreen) >> 8) ;
+ blended->rgbBlue = (BYTE)( ((WORD)fgcolor->rgbRed * alpha + not_alpha * (WORD)bgcolor->rgbBlue) >> 8 );
+ blended->rgbReserved = 0xFF;
+
+ return TRUE;
+}
+
+/** @brief Fills a FIT_BITMAP image with the specified color.
+
+ This function does the dirty work for FreeImage_FillBackground for FIT_BITMAP
+ images.
+ @param dib The image to be filled.
+ @param color The color, the specified image should be filled with.
+ @param options Options that affect the color search process for palletized images.
+ @return Returns TRUE on success, FALSE otherwise. This function fails if any of
+ the dib and color is NULL or the provided image is not a FIT_BITMAP image.
+ */
+static BOOL
+FillBackgroundBitmap(FIBITMAP *dib, const RGBQUAD *color, int options) {
+
+ if ((!dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+ return FALSE;;
+ }
+
+ if (!color) {
+ return FALSE;
+ }
+
+ const RGBQUAD *color_intl = color;
+ unsigned bpp = FreeImage_GetBPP(dib);
+ unsigned width = FreeImage_GetWidth(dib);
+ unsigned height = FreeImage_GetHeight(dib);
+
+ FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+
+ // get a pointer to the first scanline (bottom line)
+ BYTE *src_bits = FreeImage_GetScanLine(dib, 0);
+ BYTE *dst_bits = src_bits;
+
+ BOOL supports_alpha = ((bpp >= 24) || ((bpp == 8) && (color_type != FIC_PALETTE)));
+
+ // Check for RGBA case if bitmap supports alpha
+ // blending (8-bit greyscale, 24- or 32-bit images)
+ if (supports_alpha && (options & FI_COLOR_IS_RGBA_COLOR)) {
+
+ if (color->rgbReserved == 0) {
+ // the fill color is fully transparent; we are done
+ return TRUE;
+ }
+
+ // Only if the fill color is NOT fully opaque, draw it with
+ // the (much) slower FreeImage_DrawLine function and return.
+ // Since we do not have the FreeImage_DrawLine function in this
+ // 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
+ // image (bottom-left pixel).
+ RGBQUAD bgcolor;
+ if (bpp == 8) {
+ bgcolor = FreeImage_GetPalette(dib)[*src_bits];
+ } 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: {
+ unsigned bytes = (width / 8);
+ memset(dst_bits, ((index == 1) ? 0xFF : 0x00), bytes);
+ //int n = width % 8;
+ int n = width & 7;
+ if (n) {
+ if (index == 1) {
+ // set n leftmost bits
+ dst_bits[bytes] |= (0xFF << (8 - n));
+ } else {
+ // clear n leftmost bits
+ dst_bits[bytes] &= (0xFF >> n);
+ }
+ }
+ break;
+ }
+ case 4: {
+ unsigned bytes = (width / 2);
+ memset(dst_bits, (index | (index << 4)), bytes);
+ //if (bytes % 2) {
+ if (bytes & 1) {
+ dst_bits[bytes] &= 0x0F;
+ dst_bits[bytes] |= (index << 4);
+ }
+ break;
+ }
+ case 8: {
+ memset(dst_bits, index, FreeImage_GetLine(dib));
+ break;
+ }
+ case 16: {
+ WORD wcolor = RGBQUAD_TO_WORD(dib, color_intl);
+ for (unsigned x = 0; x < width; x++) {
+ ((WORD *)dst_bits)[x] = wcolor;
+ }
+ break;
+ }
+ case 24: {
+ RGBTRIPLE rgbt = *((RGBTRIPLE *)color_intl);
+ for (unsigned x = 0; x < width; x++) {
+ ((RGBTRIPLE *)dst_bits)[x] = rgbt;
+ }
+ break;
+ }
+ case 32: {
+ RGBQUAD rgbq;
+ rgbq.rgbBlue = ((RGBTRIPLE *)color_intl)->rgbtBlue;
+ rgbq.rgbGreen = ((RGBTRIPLE *)color_intl)->rgbtGreen;
+ rgbq.rgbRed = ((RGBTRIPLE *)color_intl)->rgbtRed;
+ rgbq.rgbReserved = 0xFF;
+ for (unsigned x = 0; x < width; x++) {
+ ((RGBQUAD *)dst_bits)[x] = rgbq;
+ }
+ break;
+ }
+ default:
+ return FALSE;
+ }
+
+ // Then, copy the first scanline into all following scanlines.
+ // 'src_bits' is a pointer to the first scanline and is already
+ // set up correctly.
+ if (src_bits) {
+ unsigned pitch = FreeImage_GetPitch(dib);
+ unsigned bytes = FreeImage_GetLine(dib);
+ dst_bits = src_bits + pitch;
+ for (unsigned y = 1; y < height; y++) {
+ memcpy(dst_bits, src_bits, bytes);
+ dst_bits += pitch;
+ }
+ }
+ return TRUE;
+}
+
+/** @brief Fills an image with the specified color.
+
+ This function sets all pixels of an image to the color provided through the color
+ parameter. Since this should work for all image types supported by FreeImage, the
+ pointer color must point to a memory location, which is at least as large as the
+ image's color value, if this size is greater than 4 bytes. As the color is specified
+ by an RGBQUAD structure for all images of type FIT_BITMAP (including all palletized
+ images), the smallest possible size of this memory is the size of the RGBQUAD structure,
+ which uses 4 bytes.
+
+ So, color must point to a double, if the image to be filled is of type FIT_DOUBLE and
+ point to a RGBF structure if the image is of type FIT_RGBF and so on.
+
+ However, the fill color is always specified through a RGBQUAD structure for all images
+ of type FIT_BITMAP. So, for 32- and 24-bit images, the red, green and blue members of
+ the RGBQUAD structure are directly used for the image's red, green and blue channel
+ respectively. Although alpha transparent RGBQUAD colors are supported, the alpha channel
+ of a 32-bit image never gets modified by this function. A fill color with an alpha value
+ smaller than 255 gets blended with the image's actual background color, which is determined
+ from the image's bottom-left pixel. So, currently using alpha enabled colors, assumes the
+ image to be unicolor before the fill operation. However, the RGBQUAD's rgbReserved member is
+ only taken into account, if option FI_COLOR_IS_RGBA_COLOR has been specified.
+
+ For 16-bit images, the red-, green- and blue components of the specified color are
+ transparently translated into either the 16-bit 555 or 565 representation. This depends
+ on the image's actual red- green- and blue masks.
+
+ Special attention must be payed for palletized images. Generally, the RGB color specified
+ is looked up in the image's palette. The found palette index is then used to fill the image.
+ There are some option flags, that affect this lookup process:
+
+ no option specified (0x00) Uses the color, that is nearest to the specified color.
+ This is the default behavior and should always find a
+ color in the palette. However, the visual result may
+ far from what was expected and mainly depends on the
+ image's palette.
+
+ FI_COLOR_FIND_EQUAL_COLOR (0x02) Searches the image's palette for the specified color
+ but only uses the returned palette index, if the specified
+ color exactly matches the palette entry. Of course,
+ depending on the image's actual palette entries, this
+ operation may fail. In this case, the function falls back
+ to option FI_COLOR_ALPHA_IS_INDEX and uses the RGBQUAD's
+ rgbReserved member (or its low nibble for 4-bit images
+ or its least significant bit (LSB) for 1-bit images) as
+ the palette index used for the fill operation.
+
+ FI_COLOR_ALPHA_IS_INDEX (0x04) Does not perform any color lookup from the palette, but
+ uses the RGBQUAD's alpha channel member rgbReserved as
+ the palette index to be used for the fill operation.
+ However, for 4-bit images, only the low nibble of the
+ rgbReserved member are used and for 1-bit images, only
+ the least significant bit (LSB) is used.
+
+ This function fails if any of dib and color is NULL.
+
+ @param dib The image to be filled.
+ @param color A pointer to the color value to be used for filling the image. The
+ memory pointed to by this pointer is always assumed to be at least as large as the
+ image's color value, but never smaller than the size of an RGBQUAD structure.
+ @param options Options that affect the color search process for palletized images.
+ @return Returns TRUE on success, FALSE otherwise. This function fails if any of
+ dib and color is NULL.
+ */
+BOOL DLL_CALLCONV
+FreeImage_FillBackground(FIBITMAP *dib, const void *color, int options) {
+
+ if (!FreeImage_HasPixels(dib)) {
+ return FALSE;
+ }
+
+ if (!color) {
+ return FALSE;
+ }
+
+ // handle FIT_BITMAP images with FreeImage_FillBackground()
+ if (FreeImage_GetImageType(dib) == FIT_BITMAP) {
+ return FillBackgroundBitmap(dib, (RGBQUAD *)color, options);
+ }
+
+ // first, construct the first scanline (bottom line)
+ unsigned bytespp = (FreeImage_GetBPP(dib) / 8);
+ BYTE *src_bits = FreeImage_GetScanLine(dib, 0);
+ BYTE *dst_bits = src_bits;
+ for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ memcpy(dst_bits, color, bytespp);
+ dst_bits += bytespp;
+ }
+
+ // then, copy the first scanline into all following scanlines
+ unsigned height = FreeImage_GetHeight(dib);
+ unsigned pitch = FreeImage_GetPitch(dib);
+ unsigned bytes = FreeImage_GetLine(dib);
+ dst_bits = src_bits + pitch;
+ for (unsigned y = 1; y < height; y++) {
+ memcpy(dst_bits, src_bits, bytes);
+ dst_bits += pitch;
+ }
+ return TRUE;
+}
+
+/** @brief Allocates a new image of the specified type, width, height and bit depth and
+ optionally fills it with the specified color.
+
+ This function is an extension to FreeImage_AllocateT, which additionally supports specifying
+ a palette to be set for the newly create image, as well as specifying a background color,
+ the newly created image should initially be filled with.
+
+ Basically, this function internally relies on function FreeImage_AllocateT, followed by a
+ call to FreeImage_FillBackground. This is why both parameters color and options behave the
+ same as it is documented for function FreeImage_FillBackground. So, please refer to the
+ documentation of FreeImage_FillBackground to learn more about parameters color and options.
+
+ The palette specified through parameter palette is only copied to the newly created
+ image, if its image type is FIT_BITMAP and the desired bit depth is smaller than or equal
+ to 8 bits per pixel. In other words, the palette parameter is only taken into account for
+ palletized images. However, if the preceding conditions match and if palette is not NULL,
+ the memory pointed to by the palette pointer is assumed to be at least as large as size
+ of a fully populated palette for the desired bit depth. So, for an 8-bit image, this size
+ is 256 x sizeof(RGBQUAD), for an 4-bit image it is 16 x sizeof(RGBQUAD) and it is
+ 2 x sizeof(RGBQUAD) for a 1-bit image. In other words, this function does not support
+ partial palettes.
+
+ However, specifying a palette is not necessarily needed, even for palletized images. This
+ function is capable of implicitly creating a palette, if parameter palette is NULL. If the
+ specified background color is a greyscale value (red = green = blue) or if option
+ FI_COLOR_ALPHA_IS_INDEX is specified, a greyscale palette is created. For a 1-bit image, only
+ if the specified background color is either black or white, a monochrome palette, consisting
+ of black and white only is created. In any case, the darker colors are stored at the smaller
+ palette indices.
+
+ If the specified background color is not a greyscale value, or is neither black nor white
+ for a 1-bit image, solely this single color is injected into the otherwise black-initialized
+ palette. For this operation, option FI_COLOR_ALPHA_IS_INDEX is implicit, so the specified
+ color is applied to the palette entry, specified by the background color's rgbReserved
+ member. The image is then filled with this palette index.
+
+ This function returns a newly created image as function FreeImage_AllocateT does, if both
+ parameters color and palette are NULL. If only color is NULL, the palette pointed to by
+ parameter palette is initially set for the new image, if a palletized image of type
+ FIT_BITMAP is created. However, in the latter case, this function returns an image, whose
+ pixels are all initialized with zeros so, the image will be filled with the color of the
+ first palette entry.
+
+ @param type Specifies the image type of the new image.
+ @param width The desired width in pixels of the new image.
+ @param height The desired height in pixels of the new image.
+ @param bpp The desired bit depth of the new image.
+ @param color A pointer to the color value to be used for filling the image. The
+ memory pointed to by this pointer is always assumed to be at least as large as the
+ image's color value but never smaller than the size of an RGBQUAD structure.
+ @param options Options that affect the color search process for palletized images.
+ @param red_mask Specifies the bits used to store the red components of a pixel.
+ @param green_mask Specifies the bits used to store the green components of a pixel.
+ @param blue_mask Specifies the bits used to store the blue components of a pixel.
+ @return Returns a pointer to a newly allocated image on success, NULL otherwise.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateExT(FREE_IMAGE_TYPE type, int width, int height, int bpp, const void *color, int options, const RGBQUAD *palette, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+
+ FIBITMAP *bitmap = FreeImage_AllocateT(type, width, height, bpp, red_mask, green_mask, blue_mask);
+
+ if (!color) {
+ if ((palette) && (type == FIT_BITMAP) && (bpp <= 8)) {
+ memcpy(FreeImage_GetPalette(bitmap), palette, FreeImage_GetColorsUsed(bitmap) * sizeof(RGBQUAD));
+ }
+ return bitmap;
+ }
+
+ if (bitmap != NULL) {
+
+ // Only fill the new bitmap if the specified color
+ // differs from "black", that is not all bytes of the
+ // color are equal to zero.
+ switch (bpp) {
+ case 1: {
+ // although 1-bit implies FIT_BITMAP, better get an unsigned
+ // color and palette
+ unsigned *urgb = (unsigned *)color;
+ unsigned *upal = (unsigned *)FreeImage_GetPalette(bitmap);
+ RGBQUAD rgbq = RGBQUAD();
+
+ if (palette != NULL) {
+ // clone the specified palette
+ memcpy(FreeImage_GetPalette(bitmap), palette, 2 * sizeof(RGBQUAD));
+ } else if (options & FI_COLOR_ALPHA_IS_INDEX) {
+ CREATE_GREYSCALE_PALETTE(upal, 2);
+ } else {
+ // check, whether the specified color is either black or white
+ if ((*urgb & 0xFFFFFF) == 0x000000) {
+ // in any case build a FIC_MINISBLACK palette
+ CREATE_GREYSCALE_PALETTE(upal, 2);
+ color = &rgbq;
+ } else if ((*urgb & 0xFFFFFF) == 0xFFFFFF) {
+ // in any case build a FIC_MINISBLACK palette
+ CREATE_GREYSCALE_PALETTE(upal, 2);
+ rgbq.rgbReserved = 1;
+ color = &rgbq;
+ } else {
+ // Otherwise inject the specified color into the so far
+ // black-only palette. We use color->rgbReserved as the
+ // desired palette index.
+ BYTE index = ((RGBQUAD *)color)->rgbReserved & 0x01;
+ upal[index] = *urgb & 0x00FFFFFF;
+ }
+ options |= FI_COLOR_ALPHA_IS_INDEX;
+ }
+ // and defer to FreeImage_FillBackground
+ FreeImage_FillBackground(bitmap, color, options);
+ break;
+ }
+ case 4: {
+ // 4-bit implies FIT_BITMAP so, get a RGBQUAD color
+ RGBQUAD *rgb = (RGBQUAD *)color;
+ RGBQUAD *pal = FreeImage_GetPalette(bitmap);
+ RGBQUAD rgbq = RGBQUAD();
+
+ if (palette != NULL) {
+ // clone the specified palette
+ memcpy(pal, palette, 16 * sizeof(RGBQUAD));
+ } else if (options & FI_COLOR_ALPHA_IS_INDEX) {
+ CREATE_GREYSCALE_PALETTE(pal, 16);
+ } else {
+ // check, whether the specified color is a grey one
+ if ((rgb->rgbRed == rgb->rgbGreen) && (rgb->rgbRed == rgb->rgbBlue)) {
+ // if so, build a greyscale palette
+ CREATE_GREYSCALE_PALETTE(pal, 16);
+ rgbq.rgbReserved = rgb->rgbRed >> 4;
+ color = &rgbq;
+ } else {
+ // Otherwise inject the specified color into the so far
+ // black-only palette. We use color->rgbReserved as the
+ // desired palette index.
+ BYTE index = (rgb->rgbReserved & 0x0F);
+ ((unsigned *)pal)[index] = *((unsigned *)rgb) & 0x00FFFFFF;
+ }
+ options |= FI_COLOR_ALPHA_IS_INDEX;
+ }
+ // and defer to FreeImage_FillBackground
+ FreeImage_FillBackground(bitmap, color, options);
+ break;
+ }
+ case 8: {
+ // 8-bit implies FIT_BITMAP so, get a RGBQUAD color
+ RGBQUAD *rgb = (RGBQUAD *)color;
+ RGBQUAD *pal = FreeImage_GetPalette(bitmap);
+ RGBQUAD rgbq;
+
+ if (palette != NULL) {
+ // clone the specified palette
+ memcpy(pal, palette, 256 * sizeof(RGBQUAD));
+ } else if (options & FI_COLOR_ALPHA_IS_INDEX) {
+ CREATE_GREYSCALE_PALETTE(pal, 256);
+ } else {
+ // check, whether the specified color is a grey one
+ if ((rgb->rgbRed == rgb->rgbGreen) && (rgb->rgbRed == rgb->rgbBlue)) {
+ // if so, build a greyscale palette
+ CREATE_GREYSCALE_PALETTE(pal, 256);
+ rgbq.rgbReserved = rgb->rgbRed;
+ color = &rgbq;
+ } else {
+ // Otherwise inject the specified color into the so far
+ // black-only palette. We use color->rgbReserved as the
+ // desired palette index.
+ BYTE index = rgb->rgbReserved;
+ ((unsigned *)pal)[index] = *((unsigned *)rgb) & 0x00FFFFFF;
+ }
+ options |= FI_COLOR_ALPHA_IS_INDEX;
+ }
+ // and defer to FreeImage_FillBackground
+ FreeImage_FillBackground(bitmap, color, options);
+ break;
+ }
+ case 16: {
+ WORD wcolor = (type == FIT_BITMAP) ?
+ RGBQUAD_TO_WORD(bitmap, ((RGBQUAD *)color)) : *((WORD *)color);
+ if (wcolor != 0) {
+ FreeImage_FillBackground(bitmap, color, options);
+ }
+ break;
+ }
+ default: {
+ int bytespp = bpp / 8;
+ for (int i = 0; i < bytespp; i++) {
+ if (((BYTE *)color)[i] != 0) {
+ FreeImage_FillBackground(bitmap, color, options);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return bitmap;
+}
+
+/** @brief Allocates a new image of the specified width, height and bit depth and optionally
+ fills it with the specified color.
+
+ This function is an extension to FreeImage_Allocate, which additionally supports specifying
+ a palette to be set for the newly create image, as well as specifying a background color,
+ the newly created image should initially be filled with.
+
+ Basically, this function internally relies on function FreeImage_Allocate, followed by a
+ call to FreeImage_FillBackground. This is why both parameters color and options behave the
+ same as it is documented for function FreeImage_FillBackground. So, please refer to the
+ documentation of FreeImage_FillBackground to learn more about parameters color and options.
+
+ The palette specified through parameter palette is only copied to the newly created
+ image, if the desired bit depth is smaller than or equal to 8 bits per pixel. In other words,
+ the palette parameter is only taken into account for palletized images. However, if the
+ image to be created is a palletized image and if palette is not NULL, the memory pointed to
+ by the palette pointer is assumed to be at least as large as size of a fully populated
+ palette for the desired bit depth. So, for an 8-bit image, this size is 256 x sizeof(RGBQUAD),
+ for an 4-bit image it is 16 x sizeof(RGBQUAD) and it is 2 x sizeof(RGBQUAD) for a 1-bit
+ image. In other words, this function does not support partial palettes.
+
+ However, specifying a palette is not necessarily needed, even for palletized images. This
+ function is capable of implicitly creating a palette, if parameter palette is NULL. If the
+ specified background color is a greyscale value (red = green = blue) or if option
+ FI_COLOR_ALPHA_IS_INDEX is specified, a greyscale palette is created. For a 1-bit image, only
+ if the specified background color is either black or white, a monochrome palette, consisting
+ of black and white only is created. In any case, the darker colors are stored at the smaller
+ palette indices.
+
+ If the specified background color is not a greyscale value, or is neither black nor white
+ for a 1-bit image, solely this single color is injected into the otherwise black-initialized
+ palette. For this operation, option FI_COLOR_ALPHA_IS_INDEX is implicit, so the specified
+ color is applied to the palette entry, specified by the background color's rgbReserved
+ member. The image is then filled with this palette index.
+
+ This function returns a newly created image as function FreeImage_Allocate does, if both
+ parameters color and palette are NULL. If only color is NULL, the palette pointed to by
+ parameter palette is initially set for the new image, if a palletized image of type
+ FIT_BITMAP is created. However, in the latter case, this function returns an image, whose
+ pixels are all initialized with zeros so, the image will be filled with the color of the
+ first palette entry.
+
+ @param width The desired width in pixels of the new image.
+ @param height The desired height in pixels of the new image.
+ @param bpp The desired bit depth of the new image.
+ @param color A pointer to an RGBQUAD structure, that provides the color to be used for
+ filling the image.
+ @param options Options that affect the color search process for palletized images.
+ @param red_mask Specifies the bits used to store the red components of a pixel.
+ @param green_mask Specifies the bits used to store the green components of a pixel.
+ @param blue_mask Specifies the bits used to store the blue components of a pixel.
+ @return Returns a pointer to a newly allocated image on success, NULL otherwise.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateEx(int width, int height, int bpp, const RGBQUAD *color, int options, const RGBQUAD *palette, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+ return FreeImage_AllocateExT(FIT_BITMAP, width, height, bpp, ((void *)color), options, palette, red_mask, green_mask, blue_mask);
+}
+
+/** @brief Enlarges or shrinks an image selectively per side and fills newly added areas
+ with the specified background color.
+
+ This function enlarges or shrinks an image selectively per side. The main purpose of this
+ function is to add borders to an image. To add a border to any of the image's sides, a
+ positive integer value must be passed in any of the parameters left, top, right or bottom.
+ This value represents the border's width in pixels. Newly created parts of the image (the
+ border areas) are filled with the specified color. Specifying a negative integer value for
+ a certain side, will shrink or crop the image on this side. Consequently, specifying zero
+ for a certain side will not change the image's extension on that side.
+
+ So, calling this function with all parameters left, top, right and bottom set to zero, is
+ effectively the same as calling function FreeImage_Clone; setting all parameters left, top,
+ right and bottom to value equal to or smaller than zero, my easily be substituted by a call
+ to function FreeImage_Copy. Both these cases produce a new image, which is guaranteed not to
+ be larger than the input image. Thus, since the specified color is not needed in these cases,
+ the pointer color may be NULL.
+
+ Both parameters color and options work according to function FreeImage_FillBackground. So,
+ please refer to the documentation of FreeImage_FillBackground to learn more about parameters
+ color and options. For palletized images, the palette of the input image src is
+ transparently copied to the newly created enlarged or shrunken image, so any color
+ look-ups are performed on this palette.
+
+ Here are some examples, that illustrate, how to use the parameters left, top, right and
+ bottom:
+
+ // create a white color
+ RGBQUAD c;
+ c.rgbRed = 0xFF;
+ c.rgbGreen = 0xFF;
+ c.rgbBlue = 0xFF;
+ c.rgbReserved = 0x00;
+
+ // add a white, symmetric 10 pixel wide border to the image
+ dib2 = FreeImage_EnlargeCanvas(dib, 10, 10, 10, 10, &c, FI_COLOR_IS_RGB_COLOR);
+
+ // add white, 20 pixel wide stripes to the top and bottom side of the image
+ dib3 = FreeImage_EnlargeCanvas(dib, 0, 20, 0, 20, &c, FI_COLOR_IS_RGB_COLOR);
+
+ // add white, 30 pixel wide stripes to the right side of the image and
+ // cut off the 40 leftmost pixel columns
+ dib3 = FreeImage_EnlargeCanvas(dib, -40, 0, 30, 0, &c, FI_COLOR_IS_RGB_COLOR);
+
+ This function fails if either the input image is NULL or the pointer to the color is
+ NULL, while at least on of left, top, right and bottom is greater than zero. This
+ function also returns NULL, if the new image's size will be negative in either x- or
+ y-direction.
+
+ @param dib The image to be enlarged or shrunken.
+ @param left The number of pixels, the image should be enlarged on its left side. Negative
+ values shrink the image on its left side.
+ @param top The number of pixels, the image should be enlarged on its top side. Negative
+ values shrink the image on its top side.
+ @param right The number of pixels, the image should be enlarged on its right side. Negative
+ values shrink the image on its right side.
+ @param bottom The number of pixels, the image should be enlarged on its bottom side. Negative
+ values shrink the image on its bottom side.
+ @param color The color, the enlarged sides of the image should be filled with.
+ @param options Options that affect the color search process for palletized images.
+ @return Returns a pointer to a newly allocated enlarged or shrunken image on success,
+ NULL otherwise. This function fails if either the input image is NULL or the pointer to the
+ color is NULL, while at least on of left, top, right and bottom is greater than zero. This
+ function also returns NULL, if the new image's size will be negative in either x- or
+ y-direction.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_EnlargeCanvas(FIBITMAP *src, int left, int top, int right, int bottom, const void *color, int options) {
+
+ if(!FreeImage_HasPixels(src)) return NULL;
+
+ // Just return a clone of the image, if left, top, right and bottom are
+ // all zero.
+ if ((left == 0) && (right == 0) && (top == 0) && (bottom == 0)) {
+ return FreeImage_Clone(src);
+ }
+
+ int width = FreeImage_GetWidth(src);
+ int height = FreeImage_GetHeight(src);
+
+ // Relay on FreeImage_Copy, if all parameters left, top, right and
+ // bottom are smaller than or equal zero. The color pointer may be
+ // NULL in this case.
+ if ((left <= 0) && (right <= 0) && (top <= 0) && (bottom <= 0)) {
+ return FreeImage_Copy(src, -left, -top, width + right, height + bottom);
+ }
+
+ // From here, we need a valid color, since the image will be enlarged on
+ // at least one side. So, fail if we don't have a valid color pointer.
+ if (!color) {
+ return NULL;
+ }
+
+ if (((left < 0) && (-left >= width)) || ((right < 0) && (-right >= width)) ||
+ ((top < 0) && (-top >= height)) || ((bottom < 0) && (-bottom >= height))) {
+ return NULL;
+ }
+
+ unsigned newWidth = width + left + right;
+ unsigned newHeight = height + top + bottom;
+
+ FREE_IMAGE_TYPE type = FreeImage_GetImageType(src);
+ unsigned bpp = FreeImage_GetBPP(src);
+
+ FIBITMAP *dst = FreeImage_AllocateExT(
+ type, newWidth, newHeight, bpp, color, options,
+ FreeImage_GetPalette(src),
+ FreeImage_GetRedMask(src),
+ FreeImage_GetGreenMask(src),
+ FreeImage_GetBlueMask(src));
+
+ if (!dst) {
+ return NULL;
+ }
+
+ if ((type == FIT_BITMAP) && (bpp <= 4)) {
+ FIBITMAP *copy = FreeImage_Copy(src,
+ ((left >= 0) ? 0 : -left),
+ ((top >= 0) ? 0 : -top),
+ ((width+right)>width)?width:(width+right),
+ ((height+bottom)>height)?height:(height+bottom));
+
+ if (!copy) {
+ FreeImage_Unload(dst);
+ return NULL;
+ }
+
+ if (!FreeImage_Paste(dst, copy,
+ ((left <= 0) ? 0 : left),
+ ((top <= 0) ? 0 : top), 256)) {
+ FreeImage_Unload(copy);
+ FreeImage_Unload(dst);
+ return NULL;
+ }
+
+ FreeImage_Unload(copy);
+
+ } else {
+
+ int bytespp = bpp / 8;
+ BYTE *srcPtr = FreeImage_GetScanLine(src, height - 1 - ((top >= 0) ? 0 : -top));
+ BYTE *dstPtr = FreeImage_GetScanLine(dst, newHeight - 1 - ((top <= 0) ? 0 : top));
+
+ unsigned srcPitch = FreeImage_GetPitch(src);
+ unsigned dstPitch = FreeImage_GetPitch(dst);
+
+ int lineWidth = bytespp * (width + MIN(0, left) + MIN(0, right));
+ int lines = height + MIN(0, top) + MIN(0, bottom);
+
+ if (left <= 0) {
+ srcPtr += (-left * bytespp);
+ } else {
+ dstPtr += (left * bytespp);
+ }
+
+ for (int i = 0; i < lines; i++) {
+ memcpy(dstPtr, srcPtr, lineWidth);
+ srcPtr -= srcPitch;
+ dstPtr -= dstPitch;
+ }
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ // copy transparency table
+ FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(src), FreeImage_GetTransparencyCount(src));
+
+ // copy background color
+ RGBQUAD bkcolor;
+ if( FreeImage_GetBackgroundColor(src, &bkcolor) ) {
+ FreeImage_SetBackgroundColor(dst, &bkcolor);
+ }
+
+ // clone resolution
+ FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(src));
+ FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(src));
+
+ // clone ICC profile
+ FIICCPROFILE *src_profile = FreeImage_GetICCProfile(src);
+ FIICCPROFILE *dst_profile = FreeImage_CreateICCProfile(dst, src_profile->data, src_profile->size);
+ dst_profile->flags = src_profile->flags;
+
+ return dst;
+}
+