| // ========================================================== |
| // 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; |
| } |
| |