cvs: gd /libgd/examples CMakeLists.txt crop.c test_crop_threshold.png /libgd/src CMakeLists.txt gd.h gd_crop.c

[email protected] ("Pierre-Alain Joye") Wed, 12 Dec 2007 15:56:03 -0000
Newsgroups php.gd.cvs
Message-ID <cvspajoye1197474963@cvsserver>
pajoye		Wed Dec 12 15:56:03 2007 UTC

  Added files:                 
    /gd/libgd/examples	crop.c test_crop_threshold.png 
    /gd/libgd/src	gd_crop.c 

  Modified files:              
    /gd/libgd/examples	CMakeLists.txt 
    /gd/libgd/src	CMakeLists.txt gd.h 
  Log:
  - #136 add crop image support
   - gdImageCrop, gdImageAutoCrop and gdImageThresholdCrop
   - add gdRect struct to define a rectangle area at position (x,y) with
     a given width and height
pajoye-20071212155603.txt (text/plain, 11.2 KB)
http://cvs.php.net/viewvc.cgi/gd/libgd/examples/CMakeLists.txt?r1=1.1&r2=1.2&diff_format=u
Index: gd/libgd/examples/CMakeLists.txt
diff -u gd/libgd/examples/CMakeLists.txt:1.1 gd/libgd/examples/CMakeLists.txt:1.2
--- gd/libgd/examples/CMakeLists.txt:1.1	Thu Oct  4 19:47:41 2007
+++ gd/libgd/examples/CMakeLists.txt	Wed Dec 12 15:56:03 2007
@@ -1,14 +1,14 @@
 
-message("inc: ${GD_INCLUDE_DIR}")
 include_directories (BEFORE ${GD_SOURCE_DIR}/src "${CMAKE_BINARY_DIR}")
 
 
 SET(TESTS_FILES
-  tiffread	
+  tiffread
+  tgaread
+  crop
 )
 
 FOREACH(test_name ${TESTS_FILES})
 	add_executable(${test_name} "${test_name}.c")
 	target_link_libraries (${test_name} ${GD_LIB})
-	ADD_TEST(${test_name} ${EXECUTABLE_OUTPUT_PATH}/${test_name})
 ENDFOREACH(test_name)
http://cvs.php.net/viewvc.cgi/gd/libgd/src/CMakeLists.txt?r1=1.11&r2=1.12&diff_format=u
Index: gd/libgd/src/CMakeLists.txt
diff -u gd/libgd/src/CMakeLists.txt:1.11 gd/libgd/src/CMakeLists.txt:1.12
--- gd/libgd/src/CMakeLists.txt:1.11	Sat Nov  3 10:40:14 2007
+++ gd/libgd/src/CMakeLists.txt	Wed Dec 12 15:56:03 2007
@@ -2,6 +2,7 @@
 SET (LIBGD_SRC_FILES
 	gd.c
 	gdfx.c
+	gd_crop.c
 	gd_transform.c
 	gd_security.c
 	gd_gd.c
http://cvs.php.net/viewvc.cgi/gd/libgd/src/gd.h?r1=1.40&r2=1.41&diff_format=u
Index: gd/libgd/src/gd.h
diff -u gd/libgd/src/gd.h:1.40 gd/libgd/src/gd.h:1.41
--- gd/libgd/src/gd.h:1.40	Mon Oct  8 07:21:40 2007
+++ gd/libgd/src/gd.h	Wed Dec 12 15:56:03 2007
@@ -130,6 +130,15 @@
 #define gdTrueColorGetGreen(c) (((c) & 0x00FF00) >> 8)
 #define gdTrueColorGetBlue(c) ((c) & 0x0000FF)
 
+enum gdCropMode {
+	GD_CROP_DEFAULT = 0,
+	GD_CROP_TRANSPARENT,
+	GD_CROP_BLACK,
+	GD_CROP_WHITE,
+	GD_CROP_SIDES
+};
+
+
 /* This function accepts truecolor pixel values only. The 
 	source color is composited with the destination color
 	based on the alpha channel value of the source color.
@@ -475,11 +484,19 @@
 			   char *string, gdFTStringExtraPtr strex);
 
 /* Point type for use in polygon drawing. */
-  typedef struct
-  {
-    int x, y;
-  }
-  gdPoint, *gdPointPtr;
+typedef struct
+{
+	int x, y;
+}
+gdPoint, *gdPointPtr;
+
+typedef struct
+{
+	int x, y;
+	int width, height;
+}
+gdRect, *gdRectPtr;
+
 
 BGD_DECLARE(void) gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c);
 BGD_DECLARE(void) gdImageOpenPolygon (gdImagePtr im, gdPointPtr p, int n, int c);
@@ -731,6 +748,12 @@
 BGD_DECLARE(void) gdImageAlphaBlending (gdImagePtr im, int alphaBlendingArg);
 BGD_DECLARE(void) gdImageSaveAlpha (gdImagePtr im, int saveAlphaArg);
 
+
+BGD_DECLARE(gdImagePtr) gdImageCrop(gdImagePtr src, const gdRect *crop);
+BGD_DECLARE(gdImagePtr) gdImageAutoCrop(gdImagePtr im, const unsigned int mode);
+BGD_DECLARE(gdImagePtr) gdImageThresholdCrop(gdImagePtr im, const unsigned int color, const int threshold);
+
+
 /* Macros to access information about images. */
 
 /* Returns nonzero if the image is a truecolor image,

http://cvs.php.net/viewvc.cgi/gd/libgd/examples/crop.c?view=markup&rev=1.1
Index: gd/libgd/examples/crop.c
+++ gd/libgd/examples/crop.c
/* $Id: crop.c,v 1.1 2007/12/12 15:56:03 pajoye Exp $ */
/*
 * You can fetch a set of samples TIFF images here:
 * ftp://ftp.remotesensing.org/pub/libtiff/
 * (pics-x.y.z.tar.gz)
 */

#include "gd.h"
#include <stdio.h>
#include <stdlib.h>

void save_png(gdImagePtr im, const char *filename)
{
	FILE *fp;
	fp = fopen(filename, "wb");
	if (!fp) {
		fprintf(stderr, "Can't save png image %s\n", filename);
		return;
	}
	gdImagePng(im, fp);
	fclose(fp);
}

gdImagePtr read_png(const char *filename)
{
	FILE * fp;
	gdImagePtr im;

	fp = fopen(filename, "rb");
	if (!fp) {
		fprintf(stderr, "Can't read png image %s\n", filename);
		return NULL;
	}
	im = gdImageCreateFromPng(fp);
	fclose(fp);
	return im;
}

int main()
{
 	gdImagePtr im, im2;
	FILE *fp;
	char path[2048];
	char dst[2048];

	im = gdImageCreateTrueColor(400, 400);

	if (!im) {
		fprintf(stderr, "Can't create 400x400 TC image\n", path);
		return 1;
	}

	gdImageFilledRectangle(im, 19, 29, 390, 390, 0xFFFFFF);
	gdImageRectangle(im, 19, 29, 390, 390, 0xFF0000);
	save_png(im, "a1.png");

	im2 = gdImageAutoCrop(im, GD_CROP_SIDES);
	if (im2) {
		save_png(im2, "a2.png");
		gdImageDestroy(im2);
	}
	gdImageDestroy(im);

	im = read_png("test_crop_threshold.png");
	if (!im) {
		return 1;
	}

	im2 = gdImageThresholdCrop(im, 0xFFFFFF, 120);
	if (im2) {
		save_png(im2, "a3.png");
		gdImageDestroy(im2);
	}
	gdImageDestroy(im);

	im = read_png("test_crop_threshold.png");
	if (!im) {
		return 1;
	}

	im2 = gdImageThresholdCrop(im, 0xFFFFFF, 70);
	if (im2) {
		save_png(im2, "a4.png");
		gdImageDestroy(im2);
	}

	gdImageDestroy(im);
}

http://cvs.php.net/viewvc.cgi/gd/libgd/src/gd_crop.c?view=markup&rev=1.1
Index: gd/libgd/src/gd_crop.c
+++ gd/libgd/src/gd_crop.c
/* Crop support
 * manual crop using a gdRect or automatic crop using a background
 * color (automatic detections or using either the transparent color,
 * black or white).
 * An alternative method allows to crop using a given color and a
 * threshold. It works relatively well but it can be improved.
 * Maybe L*a*b* and Delta-E will give better results (and a better
 * granularity).
 */

#include <gd.h>

static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color);
static int gdColorMatch(gdImagePtr im, int col1, int col2, int threshold);

BGD_DECLARE(gdImagePtr) gdImageCrop(gdImagePtr src, const gdRect *crop)
{
	gdImagePtr dst;

	dst = gdImageCreateTrueColor(crop->width, crop->height);
	gdImageCopy(dst, src, 0, 0, crop->x, crop->y, crop->width, crop->height);

	return dst;
}

BGD_DECLARE(gdImagePtr) gdImageAutoCrop(gdImagePtr im, const unsigned int mode)
{
	const int width = gdImageSX(im);
	const int height = gdImageSY(im);

	int x,y;
	int color, corners, match;
	gdRect crop;

	crop.x = 0;
	crop.y = 0;
	crop.width = 0;
	crop.height = 0;

	switch (mode) {
		case GD_CROP_TRANSPARENT:
			color = gdImageGetTransparent(im);
			break;

		case GD_CROP_BLACK:
			color = gdImageColorClosestAlpha(im, 0, 0, 0, 0);
			break;

		case GD_CROP_WHITE:
			color = gdImageColorClosestAlpha(im, 255, 255, 255, 0);
			break;

		case GD_CROP_SIDES:
			corners = gdGuessBackgroundColorFromCorners(im, &color);
			break;

		case GD_CROP_DEFAULT:
		default:
			color = gdImageGetTransparent(im);
			break;
	}

	/* TODO: Add gdImageGetRowPtr and works with ptr at the row level
	 * for the true color and palette images
	 * new formats will simply work with ptr
	 */
	match = 1;
	for (y = 0; match && y < height; y++) {
		for (x = 0; match && x < width; x++) {
			match = (color == gdImageGetPixel(im, x,y));
		}
	}

	/* Nothing to do > bye */
	if (y == height - 1) {
		return;
	}

	crop.y = y -1;
	match = 1;
	for (y = height - 1; match && y >= 0; y--) {
		for (x = 0; match && x < width; x++) {
			match = (color == gdImageGetPixel(im, x,y));
		}
	}

	if (y == 0) {
		crop.height = height - crop.y + 1;
	} else {
		crop.height = y - crop.y + 2;
	}

	match = 1;
	for (x = 0; match && x < width; x++) {
		for (y = 0; match && y < crop.y + crop.height - 1; y++) {
			match = (color == gdImageGetPixel(im, x,y));
		}
	}
	crop.x = x - 1;

	match = 1;
	for (x = width - 1; match && x >= 0; x--) {
		for (y = 0; match &&  y < crop.y + crop.height - 1; y++) {
			match = (color == gdImageGetPixel(im, x,y));
		}
	}
	crop.width = x - crop.x + 2;

	return gdImageCrop(im, &crop);
}

BGD_DECLARE(gdImagePtr) gdImageThresholdCrop(gdImagePtr im, const unsigned int color, const int threshold)
{
	const int width = gdImageSX(im);
	const int height = gdImageSY(im);

	int x,y;
	int corners, match;
	gdRect crop;

	crop.x = 0;
	crop.y = 0;
	crop.width = 0;
	crop.height = 0;

	if (threshold >= 255) {
		return;
	}

	/* TODO: Add gdImageGetRowPtr and works with ptr at the row level
	 * for the true color and palette images
	 * new formats will simply work with ptr
	 */
	match = 1;
	for (y = 0; match && y < height; y++) {
		for (x = 0; match && x < width; x++) {
			match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
		}
	}

	/* Nothing to do > bye */
	if (y == height - 1) {
		return;
	}

	crop.y = y -1;
	match = 1;
	for (y = height - 1; match && y >= 0; y--) {
		for (x = 0; match && x < width; x++) {
			match = (gdColorMatch(im, color, gdImageGetPixel(im, x, y), threshold)) > 0;
		}
	}

	if (y == 0) {
		crop.height = height - crop.y + 1;
	} else {
		crop.height = y - crop.y + 2;
	}

	match = 1;
	for (x = 0; match && x < width; x++) {
		for (y = 0; match && y < crop.y + crop.height - 1; y++) {
			match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
		}
	}
	crop.x = x - 1;

	match = 1;
	for (x = width - 1; match && x >= 0; x--) {
		for (y = 0; match &&  y < crop.y + crop.height - 1; y++) {
			match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
		}
	}
	crop.width = x - crop.x + 2;

	return gdImageCrop(im, &crop);
}

/* This algorithm comes from pnmcrop (http://netpbm.sourceforge.net/)
 * Three steps:
 *  - if 3 corners are equal.
 *  - if two are equal.
 *  - Last solution: average the colors
 */
static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color)
{
	const int tl = gdImageGetPixel(im, 0, 0);
	const int tr = gdImageGetPixel(im, gdImageSX(im) - 1, 0);
	const int bl = gdImageGetPixel(im, 0, gdImageSY(im) -1);
	const int br = gdImageGetPixel(im, gdImageSX(im) - 1, gdImageSY(im) -1);

	if (tr == bl && tr == br) {
		*color = tr;
		return 3;
	} else if (tl == bl && tl == br) {
		*color = tl;
		return 3;
	} else if (tl == tr &&  tl == br) {
		*color = tl;
		return 3;
	} else if (tl == tr &&  tl == bl) {
		*color = tl;
		return 3;
	} else if (tl == tr  || tl == bl || tl == br) {
		*color = tl;
		return 2;
	} else if (tr == bl || tr == bl) {
		*color = tr;
		return 2;
	} else if (br == bl) {
		*color = bl;
		return 2;
	} else {
		int r,b,g,a;

		r = (0.5f + (gdImageRed(im, tl) + gdImageRed(im, tr) + gdImageRed(im, bl) + gdImageRed(im, br)) / 4);
		g = (0.5f + (gdImageGreen(im, tl) + gdImageGreen(im, tr) + gdImageGreen(im, bl) + gdImageGreen(im, br)) / 4);
		b = (0.5f + (gdImageBlue(im, tl) + gdImageBlue(im, tr) + gdImageBlue(im, bl) + gdImageBlue(im, br)) / 4);
		a = (0.5f + (gdImageAlpha(im, tl) + gdImageAlpha(im, tr) + gdImageAlpha(im, bl) + gdImageAlpha(im, br)) / 4);
		*color = gdImageColorClosestAlpha(im, r, g, b, a);
		return 0;
	}
}

static int gdColorMatch(gdImagePtr im, int col1, int col2, int threshold)
{
	int diff, max = 0;


	/* alternative method would be to take the distance in the rgb cube
	 * between the desired color and the current pixel:
	 * (r2 - r1)^2 + (g2 -b1)^2 + (g1 -g2)^2
	 *
	 * but I did not see a difference in my results, that's why I kept
	 * this faster implementation.
	 */
	diff = abs(gdImageRed(im, col2) - gdImageRed(im, col1));
	if (diff > max) {
		max = diff;
	}

	diff = abs(gdImageGreen(im, col2) - gdImageGreen(im, col1));
	if (diff > max) {
		max = diff;
	}

	diff = abs(gdImageBlue(im, col2) - gdImageBlue(im, col1));
	if (diff > max) {
		max = diff;
	}
/* do we need alpha here? We may detect full transparency and consider
 * them as background
 */
/*
	diff = abs(gdImageAlpha(im, col2) - gdImageAlpha(im, col1));
	if (diff > max) {
		max = diff;
	}
*/
	return (max < threshold);
}

/*
 * To be implemented when we have more image formats.
 * Buffer like gray8 gray16 or rgb8 will require some tweak
 * and can be done in this function (called from the autocrop
 * function. (Pierre)
 */
#if 0
static int colors_equal (const int col1, const in col2)
{

}
#endif