diff options
Diffstat (limited to 'plugins/AdvaImg/src/FreeImageToolkit/Background.cpp')
-rw-r--r-- | plugins/AdvaImg/src/FreeImageToolkit/Background.cpp | 895 |
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; +} + |