Blame src/gd_crop.c

Packit ed3af9
/**
Packit ed3af9
 * File: Cropping
Packit ed3af9
 *
Packit ed3af9
 * Crop an image
Packit ed3af9
 *
Packit ed3af9
 * Some functions to crop images, automatically (auto detection of the border
Packit ed3af9
 * color), using a given color (with or without tolerance) or using a given
Packit ed3af9
 * rectangle.
Packit ed3af9
 * 
Packit ed3af9
 * Example:
Packit ed3af9
 *   (start code)
Packit ed3af9
 *   im2 = gdImageAutoCrop(im, GD_CROP_SIDES);
Packit ed3af9
 *   if (im2) {
Packit ed3af9
 *       gdImageDestroy(im); // unless you need the original image subsequently
Packit ed3af9
 *       // do something with the cropped image
Packit ed3af9
 *   }
Packit ed3af9
 *   gdImageDestroy(im2);
Packit ed3af9
 *   (end code)
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
Packit ed3af9
#ifdef HAVE_CONFIG_H
Packit ed3af9
#include "config.h"
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
#include <stdlib.h>
Packit ed3af9
#include "gd.h"
Packit ed3af9
#include "gd_color.h"
Packit ed3af9
Packit ed3af9
static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color);
Packit ed3af9
Packit ed3af9
/**
Packit ed3af9
 * Function: gdImageCrop
Packit ed3af9
 *
Packit ed3af9
 * Crop an image to a given rectangle
Packit ed3af9
 *
Packit ed3af9
 * Parameters:
Packit ed3af9
 *   src  - The image.
Packit ed3af9
 *   crop - The cropping rectangle, see <gdRect>.
Packit ed3af9
 *
Packit ed3af9
 * Returns:
Packit ed3af9
 *   The newly created cropped image, or NULL on failure.
Packit ed3af9
 *
Packit ed3af9
 * See also:
Packit ed3af9
 *   - <gdImageCropAuto>
Packit ed3af9
 *   - <gdImageCropThreshold>
Packit ed3af9
 */
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCrop(gdImagePtr src, const gdRect *crop)
Packit ed3af9
{
Packit ed3af9
	gdImagePtr dst;
Packit ed3af9
Packit ed3af9
	if (gdImageTrueColor(src)) {
Packit ed3af9
		dst = gdImageCreateTrueColor(crop->width, crop->height);
Packit ed3af9
	} else {
Packit ed3af9
		dst = gdImageCreate(crop->width, crop->height);
Packit ed3af9
	}
Packit ed3af9
	if (!dst) return NULL;
Packit ed3af9
	gdImageCopy(dst, src, 0, 0, crop->x, crop->y, crop->width, crop->height);
Packit ed3af9
Packit ed3af9
	return dst;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/**
Packit ed3af9
 * Function: gdImageCropAuto
Packit ed3af9
 *
Packit ed3af9
 * Crop an image automatically
Packit ed3af9
 *
Packit ed3af9
 * This function detects the cropping area according to the given _mode_.
Packit ed3af9
 *
Packit ed3af9
 * Parameters:
Packit ed3af9
 *   im   - The image.
Packit ed3af9
 *   mode - The cropping mode, see <gdCropMode>.
Packit ed3af9
 *
Packit ed3af9
 * Returns:
Packit ed3af9
 *   The newly created cropped image, or NULL on failure.
Packit ed3af9
 *
Packit ed3af9
 * See also:
Packit ed3af9
 *   - <gdImageCrop>
Packit ed3af9
 *   - <gdImageCropThreshold>
Packit ed3af9
 */
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCropAuto(gdImagePtr im, const unsigned int mode)
Packit ed3af9
{
Packit ed3af9
	const int width = gdImageSX(im);
Packit ed3af9
	const int height = gdImageSY(im);
Packit ed3af9
Packit ed3af9
	int x,y;
Packit ed3af9
	int color, match;
Packit ed3af9
	gdRect crop;
Packit ed3af9
Packit ed3af9
	crop.x = 0;
Packit ed3af9
	crop.y = 0;
Packit ed3af9
	crop.width = 0;
Packit ed3af9
	crop.height = 0;
Packit ed3af9
Packit ed3af9
	switch (mode) {
Packit ed3af9
	case GD_CROP_TRANSPARENT:
Packit ed3af9
		color = gdImageGetTransparent(im);
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case GD_CROP_BLACK:
Packit ed3af9
		color = gdImageColorClosestAlpha(im, 0, 0, 0, 0);
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case GD_CROP_WHITE:
Packit ed3af9
		color = gdImageColorClosestAlpha(im, 255, 255, 255, 0);
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case GD_CROP_SIDES:
Packit ed3af9
		gdGuessBackgroundColorFromCorners(im, &color;;
Packit ed3af9
		break;
Packit ed3af9
Packit ed3af9
	case GD_CROP_DEFAULT:
Packit ed3af9
	default:
Packit ed3af9
		color = gdImageGetTransparent(im);
Packit ed3af9
		break;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* TODO: Add gdImageGetRowPtr and works with ptr at the row level
Packit ed3af9
	 * for the true color and palette images
Packit ed3af9
	 * new formats will simply work with ptr
Packit ed3af9
	 */
Packit ed3af9
	match = 1;
Packit ed3af9
	for (y = 0; match && y < height; y++) {
Packit ed3af9
		for (x = 0; match && x < width; x++) {
Packit ed3af9
			match = (color == gdImageGetPixel(im, x,y));
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Nothing to do > bye
Packit ed3af9
	 * Duplicate the image?
Packit ed3af9
	 */
Packit ed3af9
	if (y == height - 1) {
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	crop.y = y -1;
Packit ed3af9
	match = 1;
Packit ed3af9
	for (y = height - 1; match && y >= 0; y--) {
Packit ed3af9
		for (x = 0; match && x < width; x++) {
Packit ed3af9
			match = (color == gdImageGetPixel(im, x,y));
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (y == 0) {
Packit ed3af9
		crop.height = height - crop.y + 1;
Packit ed3af9
	} else {
Packit ed3af9
		crop.height = y - crop.y + 2;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	match = 1;
Packit ed3af9
	for (x = 0; match && x < width; x++) {
Packit ed3af9
		for (y = 0; match && y < crop.y + crop.height - 1; y++) {
Packit ed3af9
			match = (color == gdImageGetPixel(im, x,y));
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	crop.x = x - 1;
Packit ed3af9
Packit ed3af9
	match = 1;
Packit ed3af9
	for (x = width - 1; match && x >= 0; x--) {
Packit ed3af9
		for (y = 0; match &&  y < crop.y + crop.height - 1; y++) {
Packit ed3af9
			match = (color == gdImageGetPixel(im, x,y));
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	crop.width = x - crop.x + 2;
Packit ed3af9
Packit ed3af9
	return gdImageCrop(im, &crop;;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/**
Packit ed3af9
 * Function: gdImageCropThreshold
Packit ed3af9
 *
Packit ed3af9
 * Crop an image using a given color
Packit ed3af9
 *
Packit ed3af9
 * The _threshold_ defines the tolerance to be used while comparing the image
Packit ed3af9
 * color and the color to crop. The method used to calculate the color
Packit ed3af9
 * difference is based on the color distance in the RGB(A) cube.
Packit ed3af9
 *
Packit ed3af9
 * Parameters:
Packit ed3af9
 *   im        - The image.
Packit ed3af9
 *   color     - The crop color.
Packit ed3af9
 *   threshold - The crop threshold.
Packit ed3af9
 * 
Packit ed3af9
 * Returns:
Packit ed3af9
 *   The newly created cropped image, or NULL on failure.
Packit ed3af9
 *
Packit ed3af9
 * See also:
Packit ed3af9
 *   - <gdImageCrop>
Packit ed3af9
 *   - <gdImageCropAuto>
Packit ed3af9
 */
Packit ed3af9
BGD_DECLARE(gdImagePtr) gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold)
Packit ed3af9
{
Packit ed3af9
	const int width = gdImageSX(im);
Packit ed3af9
	const int height = gdImageSY(im);
Packit ed3af9
Packit ed3af9
	int x,y;
Packit ed3af9
	int match;
Packit ed3af9
	gdRect crop;
Packit ed3af9
Packit ed3af9
	crop.x = 0;
Packit ed3af9
	crop.y = 0;
Packit ed3af9
	crop.width = 0;
Packit ed3af9
	crop.height = 0;
Packit ed3af9
Packit ed3af9
	/* Pierre: crop everything sounds bad */
Packit ed3af9
	if (threshold > 100.0) {
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (!gdImageTrueColor(im) && color >= gdImageColorsTotal(im)) {
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* TODO: Add gdImageGetRowPtr and works with ptr at the row level
Packit ed3af9
	 * for the true color and palette images
Packit ed3af9
	 * new formats will simply work with ptr
Packit ed3af9
	 */
Packit ed3af9
	match = 1;
Packit ed3af9
	for (y = 0; match && y < height; y++) {
Packit ed3af9
		for (x = 0; match && x < width; x++) {
Packit ed3af9
			match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Pierre
Packit ed3af9
	 * Nothing to do > bye
Packit ed3af9
	 * Duplicate the image?
Packit ed3af9
	 */
Packit ed3af9
	if (y == height - 1) {
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	crop.y = y -1;
Packit ed3af9
	match = 1;
Packit ed3af9
	for (y = height - 1; match && y >= 0; y--) {
Packit ed3af9
		for (x = 0; match && x < width; x++) {
Packit ed3af9
			match = (gdColorMatch(im, color, gdImageGetPixel(im, x, y), threshold)) > 0;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (y == 0) {
Packit ed3af9
		crop.height = height - crop.y + 1;
Packit ed3af9
	} else {
Packit ed3af9
		crop.height = y - crop.y + 2;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	match = 1;
Packit ed3af9
	for (x = 0; match && x < width; x++) {
Packit ed3af9
		for (y = 0; match && y < crop.y + crop.height - 1; y++) {
Packit ed3af9
			match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	crop.x = x - 1;
Packit ed3af9
Packit ed3af9
	match = 1;
Packit ed3af9
	for (x = width - 1; match && x >= 0; x--) {
Packit ed3af9
		for (y = 0; match &&  y < crop.y + crop.height - 1; y++) {
Packit ed3af9
			match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	crop.width = x - crop.x + 2;
Packit ed3af9
Packit ed3af9
	return gdImageCrop(im, &crop;;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/* This algorithm comes from pnmcrop (http://netpbm.sourceforge.net/)
Packit ed3af9
 * Three steps:
Packit ed3af9
 *  - if 3 corners are equal.
Packit ed3af9
 *  - if two are equal.
Packit ed3af9
 *  - Last solution: average the colors
Packit ed3af9
 */
Packit ed3af9
static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color)
Packit ed3af9
{
Packit ed3af9
	const int tl = gdImageGetPixel(im, 0, 0);
Packit ed3af9
	const int tr = gdImageGetPixel(im, gdImageSX(im) - 1, 0);
Packit ed3af9
	const int bl = gdImageGetPixel(im, 0, gdImageSY(im) -1);
Packit ed3af9
	const int br = gdImageGetPixel(im, gdImageSX(im) - 1, gdImageSY(im) -1);
Packit ed3af9
Packit ed3af9
	if (tr == bl && tr == br) {
Packit ed3af9
		*color = tr;
Packit ed3af9
		return 3;
Packit ed3af9
	} else if (tl == bl && tl == br) {
Packit ed3af9
		*color = tl;
Packit ed3af9
		return 3;
Packit ed3af9
	} else if (tl == tr &&  tl == br) {
Packit ed3af9
		*color = tl;
Packit ed3af9
		return 3;
Packit ed3af9
	} else if (tl == tr &&  tl == bl) {
Packit ed3af9
		*color = tl;
Packit ed3af9
		return 3;
Packit ed3af9
	} else if (tl == tr  || tl == bl || tl == br) {
Packit ed3af9
		*color = tl;
Packit ed3af9
		return 2;
Packit ed3af9
	} else if (tr == bl) {
Packit ed3af9
		*color = tr;
Packit ed3af9
		return 2;
Packit ed3af9
	} else if (br == bl) {
Packit ed3af9
		*color = bl;
Packit ed3af9
		return 2;
Packit ed3af9
	} else {
Packit ed3af9
		register int r,b,g,a;
Packit ed3af9
Packit ed3af9
		r = (int)(0.5f + (gdImageRed(im, tl) + gdImageRed(im, tr) + gdImageRed(im, bl) + gdImageRed(im, br)) / 4);
Packit ed3af9
		g = (int)(0.5f + (gdImageGreen(im, tl) + gdImageGreen(im, tr) + gdImageGreen(im, bl) + gdImageGreen(im, br)) / 4);
Packit ed3af9
		b = (int)(0.5f + (gdImageBlue(im, tl) + gdImageBlue(im, tr) + gdImageBlue(im, bl) + gdImageBlue(im, br)) / 4);
Packit ed3af9
		a = (int)(0.5f + (gdImageAlpha(im, tl) + gdImageAlpha(im, tr) + gdImageAlpha(im, bl) + gdImageAlpha(im, br)) / 4);
Packit ed3af9
		*color = gdImageColorClosestAlpha(im, r, g, b, a);
Packit ed3af9
		return 0;
Packit ed3af9
	}
Packit ed3af9
}