1997-11-25 06:05:25 +08:00
|
|
|
/* edge filter for the GIMP
|
|
|
|
* -Peter Mattis
|
|
|
|
*
|
|
|
|
* This filter performs edge detection on the input image.
|
|
|
|
* The code for this filter is based on "pgmedge", a program
|
|
|
|
* that is part of the netpbm package.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* pgmedge.c - edge-detect a portable graymap
|
|
|
|
**
|
|
|
|
** Copyright (C) 1989 by Jef Poskanzer.
|
|
|
|
**
|
|
|
|
** Permission to use, copy, modify, and distribute this software and its
|
|
|
|
** documentation for any purpose and without fee is hereby granted, provided
|
|
|
|
** that the above copyright notice appear in all copies and that both that
|
|
|
|
** copyright notice and this permission notice appear in supporting
|
|
|
|
** documentation. This software is provided "as is" without express or
|
|
|
|
** implied warranty.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ported to GIMP Plug-in API 1.0
|
|
|
|
* version 1.07
|
|
|
|
* This version requires GIMP v0.99.10 or above.
|
|
|
|
*
|
|
|
|
* This plug-in performs edge detection. The code is based on edge.c
|
|
|
|
* for GIMP 0.54 by Peter Mattis.
|
|
|
|
*
|
|
|
|
* Eiichi Takamori <taka@ma1.seikyou.ne.jp>
|
|
|
|
* http://ha1.seikyou.ne.jp/home/taka/gimp/
|
|
|
|
*
|
|
|
|
* Tips: you can enter arbitrary value into entry.
|
|
|
|
* (not bounded between 1.0 and 10.0)
|
|
|
|
*
|
|
|
|
* Changes from version 1.06 to version 1.07:
|
|
|
|
* - Added entry
|
|
|
|
* - Cleaned up code a bit
|
|
|
|
*
|
|
|
|
* Differences from Peter Mattis's original `edge' plug-in:
|
|
|
|
* - Added Wrapmode. (useful for tilable images)
|
|
|
|
* - Enhanced speed in this version.
|
|
|
|
* - It works with the alpha channel.
|
|
|
|
*/
|
|
|
|
|
2000-01-07 06:26:10 +08:00
|
|
|
#include "config.h"
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2000-01-07 06:26:10 +08:00
|
|
|
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
2000-01-12 01:41:07 +08:00
|
|
|
#include <libgimp/gimp.h>
|
|
|
|
#include <libgimp/gimpui.h>
|
|
|
|
|
1999-12-22 18:03:19 +08:00
|
|
|
#include "libgimp/stdplugins-intl.h"
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2000-05-01 05:03:44 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
#ifdef RCSID
|
2000-01-12 01:41:07 +08:00
|
|
|
static gchar rcsid[] = "$Id$";
|
1997-11-25 06:05:25 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Some useful macros */
|
|
|
|
|
|
|
|
#define TILE_CACHE_SIZE 48
|
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
WRAP,
|
|
|
|
SMEAR,
|
|
|
|
BLACK
|
|
|
|
};
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
typedef struct
|
|
|
|
{
|
2000-05-01 05:03:44 +08:00
|
|
|
gdouble amount;
|
1997-11-25 06:05:25 +08:00
|
|
|
gint wrapmode;
|
|
|
|
} EdgeVals;
|
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
typedef struct
|
|
|
|
{
|
1997-11-25 06:05:25 +08:00
|
|
|
gint run;
|
|
|
|
} EdgeInterface;
|
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
typedef struct
|
|
|
|
{
|
2000-08-22 09:26:57 +08:00
|
|
|
GimpTile *tile;
|
2001-12-29 21:26:29 +08:00
|
|
|
gint row, col; /* tile's row, col */
|
|
|
|
gint bpp;
|
|
|
|
gint tile_width, tile_height;
|
2000-08-22 09:26:57 +08:00
|
|
|
GimpDrawable *drawable;
|
2001-12-29 21:26:29 +08:00
|
|
|
gint drawable_width, drawable_height;
|
1997-11-25 06:05:25 +08:00
|
|
|
} TileBuf;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Function prototypes.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void query (void);
|
2001-12-29 21:26:29 +08:00
|
|
|
static void run (gchar *name,
|
|
|
|
gint nparams,
|
|
|
|
GimpParam *param,
|
|
|
|
gint *nreturn_vals,
|
|
|
|
GimpParam **return_vals);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2000-08-22 09:26:57 +08:00
|
|
|
static void edge (GimpDrawable *drawable);
|
|
|
|
static gint edge_dialog (GimpDrawable *drawable);
|
2000-01-13 23:39:26 +08:00
|
|
|
|
|
|
|
static long long_sqrt (long n);
|
|
|
|
|
2001-12-29 21:26:29 +08:00
|
|
|
static void init_tile_buf (TileBuf *buf,
|
2000-08-22 09:26:57 +08:00
|
|
|
GimpDrawable *drawable);
|
2001-12-29 21:26:29 +08:00
|
|
|
static void get_tile_pixel (TileBuf *buf,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
guchar *pixel,
|
|
|
|
gint wrapmode);
|
|
|
|
static void end_tile_buf (TileBuf *buf);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/***** Local vars *****/
|
|
|
|
|
2000-08-22 09:26:57 +08:00
|
|
|
GimpPlugInInfo PLUG_IN_INFO =
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2000-01-13 23:39:26 +08:00
|
|
|
NULL, /* init */
|
|
|
|
NULL, /* quit */
|
|
|
|
query, /* query */
|
|
|
|
run, /* run */
|
1997-11-25 06:05:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static EdgeVals evals =
|
|
|
|
{
|
2000-01-13 23:39:26 +08:00
|
|
|
2.0, /* amount */
|
|
|
|
SMEAR /* wrapmode */
|
1997-11-25 06:05:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static EdgeInterface eint =
|
|
|
|
{
|
2000-01-13 23:39:26 +08:00
|
|
|
FALSE /* run */
|
1997-11-25 06:05:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/***** Functions *****/
|
|
|
|
|
|
|
|
MAIN ()
|
|
|
|
|
|
|
|
static void
|
2000-05-01 05:03:44 +08:00
|
|
|
query (void)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2000-08-22 09:26:57 +08:00
|
|
|
static GimpParamDef args[] =
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2000-08-22 09:26:57 +08:00
|
|
|
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
|
|
|
|
{ GIMP_PDB_IMAGE, "image", "Input image (unused)" },
|
|
|
|
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
|
|
|
|
{ GIMP_PDB_FLOAT, "amount", "Edge detection amount" },
|
|
|
|
{ GIMP_PDB_INT32, "wrapmode", "Edge detection behavior: { WRAP (0), SMEAR (1), BLACK (2) }" }
|
1997-11-25 06:05:25 +08:00
|
|
|
};
|
2000-05-01 05:03:44 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
gchar *help_string =
|
2000-05-01 05:03:44 +08:00
|
|
|
"Perform edge detection on the contents of the specified drawable. It "
|
|
|
|
"applies, I think, convolution with 3x3 kernel. AMOUNT is an arbitrary "
|
|
|
|
"constant, WRAPMODE is like displace plug-in (useful for tilable image).";
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
gimp_install_procedure ("plug_in_edge",
|
2000-01-31 10:32:30 +08:00
|
|
|
"Perform edge detection on the contents of the specified drawable",
|
1997-11-25 06:05:25 +08:00
|
|
|
help_string,
|
|
|
|
"Peter Mattis & (ported to 1.0 by) Eiichi Takamori",
|
|
|
|
"Peter Mattis",
|
|
|
|
"1996",
|
1999-12-22 18:03:19 +08:00
|
|
|
N_("<Image>/Filters/Edge-Detect/Edge..."),
|
1997-11-25 06:05:25 +08:00
|
|
|
"RGB*, GRAY*",
|
2000-08-22 09:26:57 +08:00
|
|
|
GIMP_PLUGIN,
|
2001-12-06 10:28:58 +08:00
|
|
|
G_N_ELEMENTS (args), 0,
|
2000-05-01 05:03:44 +08:00
|
|
|
args, NULL);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2001-12-29 21:26:29 +08:00
|
|
|
run (gchar *name,
|
|
|
|
gint nparams,
|
2000-08-22 09:26:57 +08:00
|
|
|
GimpParam *param,
|
2001-12-29 21:26:29 +08:00
|
|
|
gint *nreturn_vals,
|
2000-08-22 09:26:57 +08:00
|
|
|
GimpParam **return_vals)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-12-29 21:26:29 +08:00
|
|
|
static GimpParam values[1];
|
|
|
|
GimpDrawable *drawable;
|
|
|
|
GimpRunMode run_mode;
|
|
|
|
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
run_mode = param[0].data.d_int32;
|
|
|
|
|
|
|
|
/* Get the specified drawable */
|
|
|
|
drawable = gimp_drawable_get (param[2].data.d_drawable);
|
|
|
|
|
|
|
|
*nreturn_vals = 1;
|
2001-12-29 21:26:29 +08:00
|
|
|
*return_vals = values;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-12-29 21:26:29 +08:00
|
|
|
values[0].type = GIMP_PDB_STATUS;
|
1997-11-25 06:05:25 +08:00
|
|
|
values[0].data.d_status = status;
|
|
|
|
|
|
|
|
switch (run_mode)
|
|
|
|
{
|
2000-08-22 09:26:57 +08:00
|
|
|
case GIMP_RUN_INTERACTIVE:
|
1999-12-22 18:03:19 +08:00
|
|
|
INIT_I18N_UI();
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Possibly retrieve data */
|
|
|
|
gimp_get_data ("plug_in_edge", &evals);
|
|
|
|
|
|
|
|
/* First acquire information with a dialog */
|
|
|
|
if (! edge_dialog (drawable))
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
|
2000-08-22 09:26:57 +08:00
|
|
|
case GIMP_RUN_NONINTERACTIVE:
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Make sure all the arguments are there! */
|
|
|
|
if (nparams != 5)
|
2000-08-22 09:26:57 +08:00
|
|
|
status = GIMP_PDB_CALLING_ERROR;
|
|
|
|
if (status == GIMP_PDB_SUCCESS)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2000-01-13 23:39:26 +08:00
|
|
|
evals.amount = param[3].data.d_float;
|
1997-11-25 06:05:25 +08:00
|
|
|
evals.wrapmode = param[4].data.d_int32;
|
|
|
|
}
|
1999-12-22 18:03:19 +08:00
|
|
|
INIT_I18N();
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
|
|
|
|
2000-08-22 09:26:57 +08:00
|
|
|
case GIMP_RUN_WITH_LAST_VALS:
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Possibly retrieve data */
|
|
|
|
gimp_get_data ("plug_in_edge", &evals);
|
1999-12-22 18:03:19 +08:00
|
|
|
INIT_I18N();
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1998-02-01 09:58:47 +08:00
|
|
|
/* make sure the drawable exist and is not indexed */
|
2001-06-15 04:07:38 +08:00
|
|
|
if (gimp_drawable_is_rgb (drawable->drawable_id) ||
|
|
|
|
gimp_drawable_is_gray (drawable->drawable_id))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2000-01-13 23:39:26 +08:00
|
|
|
gimp_progress_init (_("Edge Detection..."));
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* set the tile cache size */
|
|
|
|
gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
|
|
|
|
|
|
|
|
/* run the edge effect */
|
|
|
|
edge (drawable);
|
|
|
|
|
2000-08-22 09:26:57 +08:00
|
|
|
if (run_mode != GIMP_RUN_NONINTERACTIVE)
|
1997-11-25 06:05:25 +08:00
|
|
|
gimp_displays_flush ();
|
|
|
|
|
|
|
|
/* Store data */
|
2000-08-22 09:26:57 +08:00
|
|
|
if (run_mode == GIMP_RUN_INTERACTIVE)
|
1997-11-25 06:05:25 +08:00
|
|
|
gimp_set_data ("plug_in_edge", &evals, sizeof (EdgeVals));
|
|
|
|
}
|
1998-02-01 09:58:47 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* gimp_message ("edge: cannot operate on indexed color images"); */
|
2000-08-22 09:26:57 +08:00
|
|
|
status = GIMP_PDB_EXECUTION_ERROR;
|
1998-02-01 09:58:47 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
values[0].data.d_status = status;
|
|
|
|
|
|
|
|
gimp_drawable_detach (drawable);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
|
|
|
|
TileBuf Util Routines: Util routines for getting arbitrary pixel
|
|
|
|
CAUTION -- the tile is read only !!
|
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
static void
|
2001-12-29 21:26:29 +08:00
|
|
|
init_tile_buf (TileBuf *buf,
|
2000-08-22 09:26:57 +08:00
|
|
|
GimpDrawable *drawable)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
buf->tile = NULL;
|
|
|
|
buf->col = 0;
|
|
|
|
buf->row = 0;
|
2001-06-15 04:07:38 +08:00
|
|
|
if (gimp_drawable_is_rgb (drawable->drawable_id))
|
1997-11-25 06:05:25 +08:00
|
|
|
buf->bpp = 3;
|
|
|
|
else
|
|
|
|
buf->bpp = 1;
|
|
|
|
buf->tile_width = gimp_tile_width();
|
|
|
|
buf->tile_height = gimp_tile_height();
|
|
|
|
buf->drawable = drawable;
|
2001-06-15 04:07:38 +08:00
|
|
|
buf->drawable_width = gimp_drawable_width(drawable->drawable_id);
|
|
|
|
buf->drawable_height = gimp_drawable_height(drawable->drawable_id);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2000-01-13 23:39:26 +08:00
|
|
|
get_tile_pixel (TileBuf *buf,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
guchar *pixel,
|
|
|
|
gint wrapmode)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
gint b;
|
|
|
|
gint offx, offy;
|
|
|
|
gint row, col;
|
|
|
|
guchar *ptr;
|
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
if (x < 0 || x >= buf->drawable_width ||
|
|
|
|
y < 0 || y >= buf->drawable_height)
|
|
|
|
switch (wrapmode)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
case WRAP:
|
2000-01-13 23:39:26 +08:00
|
|
|
if (x < 0 || x >= buf->drawable_width)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
x %= buf->drawable_width;
|
2000-01-13 23:39:26 +08:00
|
|
|
if (x < 0)
|
1997-11-25 06:05:25 +08:00
|
|
|
x += buf->drawable_width;
|
|
|
|
}
|
2000-01-13 23:39:26 +08:00
|
|
|
if (y < 0 || y >= buf->drawable_height)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
y %= buf->drawable_height;
|
2000-01-13 23:39:26 +08:00
|
|
|
if (y < 0)
|
1997-11-25 06:05:25 +08:00
|
|
|
y += buf->drawable_height;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SMEAR:
|
2000-01-13 23:39:26 +08:00
|
|
|
if (x < 0)
|
1997-11-25 06:05:25 +08:00
|
|
|
x = 0;
|
2000-01-13 23:39:26 +08:00
|
|
|
if (x >= buf->drawable_width)
|
1997-11-25 06:05:25 +08:00
|
|
|
x = buf->drawable_width - 1;
|
2000-01-13 23:39:26 +08:00
|
|
|
if (y < 0)
|
1997-11-25 06:05:25 +08:00
|
|
|
y = 0;
|
2000-01-13 23:39:26 +08:00
|
|
|
if (y >= buf->drawable_height)
|
1997-11-25 06:05:25 +08:00
|
|
|
y = buf->drawable_height - 1;
|
|
|
|
break;
|
|
|
|
case BLACK:
|
2000-01-13 23:39:26 +08:00
|
|
|
if (x < 0 || x >= buf->drawable_width ||
|
|
|
|
y < 0 || y >= buf->drawable_height)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2000-01-13 23:39:26 +08:00
|
|
|
for (b = 0; b < buf->bpp; b++)
|
|
|
|
pixel[b] = 0;
|
1997-11-25 06:05:25 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
col = x / buf->tile_width;
|
|
|
|
offx = x % buf->tile_width;
|
|
|
|
row = y / buf->tile_height;
|
|
|
|
offy = y % buf->tile_height;
|
|
|
|
|
|
|
|
/* retrieve tile */
|
2000-01-13 23:39:26 +08:00
|
|
|
if (!buf->tile || col != buf->col || row != buf->row)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2000-01-13 23:39:26 +08:00
|
|
|
if(buf->tile)
|
|
|
|
gimp_tile_unref (buf->tile, FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
buf->col = col;
|
|
|
|
buf->row = row;
|
2000-01-13 23:39:26 +08:00
|
|
|
buf->tile = gimp_drawable_get_tile (buf->drawable, FALSE, row, col);
|
|
|
|
gimp_tile_ref (buf->tile);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* retrieve target pixel */
|
2000-01-13 23:39:26 +08:00
|
|
|
ptr = buf->tile->data + (offy * buf->tile->ewidth + offx) * buf->tile->bpp;
|
|
|
|
for(b = 0; b < buf->bpp; b++)
|
1997-11-25 06:05:25 +08:00
|
|
|
pixel[b] = ptr[b];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2000-01-13 23:39:26 +08:00
|
|
|
end_tile_buf (TileBuf *buf)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2000-01-13 23:39:26 +08:00
|
|
|
if (buf->tile)
|
|
|
|
gimp_tile_unref (buf->tile, FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
TileBuf Util Routines End
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
static long
|
2000-01-13 23:39:26 +08:00
|
|
|
long_sqrt (long n)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
#define lsqrt_max4pow (1UL << 30)
|
|
|
|
/* lsqrt_max4pow is the (machine-specific) largest power of 4 that can
|
|
|
|
* be represented in an unsigned long.
|
|
|
|
*
|
|
|
|
* Compute the integer square root of the integer argument n
|
|
|
|
* Method is to divide n by x computing the quotient x and remainder r
|
|
|
|
* Notice that the divisor x is changing as the quotient x changes
|
|
|
|
*
|
|
|
|
* Instead of shifting the dividend/remainder left, we shift the
|
|
|
|
* quotient/divisor right. The binary point starts at the extreme
|
|
|
|
* left, and shifts two bits at a time to the extreme right.
|
|
|
|
*
|
|
|
|
* The residue contains n-x^2. (Within these comments, the ^ operator
|
|
|
|
* signifies exponentiation rather than exclusive or. Also, the /
|
|
|
|
* operator returns fractions, rather than truncating, so 1/4 means
|
|
|
|
* one fourth, not zero.)
|
|
|
|
*
|
|
|
|
* Since (x + 1/2)^2 == x^2 + x + 1/4,
|
|
|
|
* n - (x + 1/2)^2 == (n - x^2) - (x + 1/4)
|
|
|
|
* Thus, we can increase x by 1/2 if we decrease (n-x^2) by (x+1/4)
|
|
|
|
*/
|
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
gulong residue; /* n - x^2 */
|
|
|
|
gulong root; /* x + 1/4 */
|
|
|
|
gulong half; /* 1/2 */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
residue = n; /* n - (x = 0)^2, with suitable alignment */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* if the correct answer fits in two bits, pull it out of a magic hat
|
|
|
|
*/
|
|
|
|
if (residue <= 12)
|
|
|
|
return (0x03FFEA94 >> (residue *= 2)) & 3;
|
|
|
|
|
|
|
|
root = lsqrt_max4pow; /* x + 1/4, shifted all the way left */
|
|
|
|
/* half = root + root; 1/2, shifted likewise */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unwind iterations corresponding to leading zero bits
|
|
|
|
*/
|
|
|
|
while (root > residue)
|
|
|
|
root >>= 2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unwind the iteration corresponding to the first one bit
|
|
|
|
* Operations have been rearranged and combined for efficiency
|
|
|
|
* Initialization of half is folded into this iteration
|
|
|
|
*/
|
|
|
|
residue -= root; /* Decrease (n-x^2) by (0+1/4) */
|
|
|
|
half = root >> 2; /* 1/4, with binary point shifted right 2 */
|
|
|
|
root += half; /* x=1. (root is now (x=1)+1/4.) */
|
|
|
|
half += half; /* 1/2, properly aligned */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Normal loop (there is at least one iteration remaining)
|
|
|
|
*/
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (root <= residue) /* Whenever we can, */
|
|
|
|
{
|
|
|
|
residue -= root; /* decrease (n-x^2) by (x+1/4) */
|
|
|
|
root += half; /* increase x by 1/2 */
|
|
|
|
}
|
|
|
|
half >>= 2; /* Shift binary point 2 places right */
|
|
|
|
root -= half; /* x{ +1/2 } +1/4 - 1/8 == x { +1/2 } 1/8 */
|
|
|
|
root >>= 1; /* 2x{ +1 } +1/4, shifted right 2 places */
|
|
|
|
}
|
|
|
|
while (half); /* When 1/2 == 0, bin. point is at far right */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* round up if (x+1/2)^2 < n
|
|
|
|
*/
|
|
|
|
if (root < residue)
|
|
|
|
++root;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Guaranteed to be correctly rounded (or truncated)
|
|
|
|
*/
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
|
|
|
/********************************************************/
|
|
|
|
/* Edge Detection main */
|
|
|
|
/********************************************************/
|
|
|
|
|
|
|
|
static void
|
2000-08-22 09:26:57 +08:00
|
|
|
edge (GimpDrawable *drawable)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* this function is too long, so I must split this into a few
|
|
|
|
* functions later ... -- taka
|
|
|
|
*/
|
2000-08-22 09:26:57 +08:00
|
|
|
GimpPixelRgn src_rgn, dest_rgn;
|
1997-11-25 06:05:25 +08:00
|
|
|
gpointer pr;
|
|
|
|
TileBuf buf;
|
|
|
|
guchar *srcrow, *src;
|
|
|
|
guchar *destrow, *dest;
|
|
|
|
guchar pix00[3], pix01[3], pix02[3];
|
|
|
|
guchar pix10[3],/*pix11[3],*/ pix12[3];
|
|
|
|
guchar pix20[3], pix21[3], pix22[3];
|
|
|
|
glong width, height;
|
|
|
|
gint alpha, has_alpha, chan;
|
|
|
|
gint x, y;
|
|
|
|
gint x1, y1, x2, y2;
|
|
|
|
glong sum1, sum2;
|
|
|
|
glong sum, scale;
|
|
|
|
gint maxval;
|
|
|
|
gint cur_progress;
|
|
|
|
gint max_progress;
|
|
|
|
gint wrapmode;
|
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
if (evals.amount < 1.0)
|
|
|
|
evals.amount = 1.0;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
init_tile_buf (&buf, drawable);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-06-15 04:07:38 +08:00
|
|
|
gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-06-15 04:07:38 +08:00
|
|
|
width = gimp_drawable_width (drawable->drawable_id);
|
|
|
|
height = gimp_drawable_height (drawable->drawable_id);
|
|
|
|
alpha = gimp_drawable_bpp (drawable->drawable_id);
|
|
|
|
has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
|
2000-01-13 23:39:26 +08:00
|
|
|
if (has_alpha)
|
|
|
|
alpha--;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
maxval = 255;
|
|
|
|
scale = (10 << 16) / evals.amount;
|
|
|
|
wrapmode = evals.wrapmode;
|
|
|
|
|
|
|
|
cur_progress = 0;
|
|
|
|
max_progress = (x2 - x1) * (y2 - y1);
|
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, x2-x1, y2-y1, FALSE, FALSE);
|
|
|
|
gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, x2-x1, y2-y1, TRUE, TRUE);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
|
1997-11-25 06:05:25 +08:00
|
|
|
pr != NULL;
|
2000-01-13 23:39:26 +08:00
|
|
|
pr = gimp_pixel_rgns_process (pr))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
srcrow = src_rgn.data;
|
|
|
|
destrow = dest_rgn.data;
|
2000-01-13 23:39:26 +08:00
|
|
|
for (y = dest_rgn.y;
|
1997-11-25 06:05:25 +08:00
|
|
|
y < (dest_rgn.y + dest_rgn.h);
|
2000-01-13 23:39:26 +08:00
|
|
|
y++, srcrow += src_rgn.rowstride, destrow += dest_rgn.rowstride)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
src = srcrow;
|
|
|
|
dest = destrow;
|
2000-01-13 23:39:26 +08:00
|
|
|
for (x = dest_rgn.x;
|
1997-11-25 06:05:25 +08:00
|
|
|
x < (dest_rgn.x + dest_rgn.w);
|
2000-01-13 23:39:26 +08:00
|
|
|
x++, src += src_rgn.bpp, dest += dest_rgn.bpp)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2000-01-13 23:39:26 +08:00
|
|
|
if(dest_rgn.x < x && x < dest_rgn.x + dest_rgn.w - 1 &&
|
|
|
|
dest_rgn.y < y && y < dest_rgn.y + dest_rgn.h - 1)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
** 3x3 kernel is inside of the tile -- do fast
|
|
|
|
** version
|
|
|
|
*/
|
2000-01-13 23:39:26 +08:00
|
|
|
for (chan = 0; chan < alpha; chan++)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* PIX(1,1) is the current pixel, so
|
|
|
|
* e.g. PIX(0,0) means 1 above and 1 left pixel.
|
|
|
|
*
|
|
|
|
* There were casting to `long' in GIMP 0.54
|
|
|
|
* edge code, but I think `guchar' should be
|
|
|
|
* extended to `int' with minus operators, so
|
|
|
|
* there's no need to cast to `long'. Both sum1
|
|
|
|
* and sum2 will be between -4*255 to 4*255
|
|
|
|
*
|
|
|
|
* -- taka
|
|
|
|
*/
|
1998-01-09 17:26:28 +08:00
|
|
|
#define PIX(X,Y) src[ (Y-1)*(int)src_rgn.rowstride + (X-1)*(int)src_rgn.bpp + chan ]
|
2000-01-13 21:44:06 +08:00
|
|
|
/* make convolution */
|
2000-01-13 23:39:26 +08:00
|
|
|
sum1 = (PIX(2,0) - PIX(0,0)) +
|
|
|
|
2 * (PIX(2,1) - PIX(0,1)) +
|
|
|
|
(PIX(2,2) - PIX(2,0));
|
|
|
|
sum2 = (PIX(0,2) - PIX(0,0)) +
|
|
|
|
2 * (PIX(1,2) - PIX(1,0)) +
|
|
|
|
(PIX(2,2) - PIX(2,0));
|
1997-11-25 06:05:25 +08:00
|
|
|
#undef PIX
|
|
|
|
/* common job ... */
|
2000-01-13 23:39:26 +08:00
|
|
|
sum = long_sqrt ((long) sum1 * sum1 + (long) sum2 * sum2);
|
1997-11-25 06:05:25 +08:00
|
|
|
sum = (sum * scale) >> 16; /* arbitrary scaling factor */
|
2000-01-13 23:39:26 +08:00
|
|
|
if (sum > maxval)
|
|
|
|
sum = maxval;
|
1997-11-25 06:05:25 +08:00
|
|
|
dest[chan] = sum;
|
|
|
|
}
|
2000-01-13 23:39:26 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1997-11-25 06:05:25 +08:00
|
|
|
/*
|
|
|
|
** The kernel is not inside of the tile -- do slow
|
|
|
|
** version
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* When the kernel intersects the boundary of the
|
|
|
|
* image, get_tile_pixel() will (should) do the
|
|
|
|
* right work with `wrapmode'.
|
|
|
|
*/
|
2000-01-13 23:39:26 +08:00
|
|
|
get_tile_pixel (&buf, x-1, y-1, pix00, wrapmode);
|
|
|
|
get_tile_pixel (&buf, x , y-1, pix10, wrapmode);
|
|
|
|
get_tile_pixel (&buf, x+1, y-1, pix20, wrapmode);
|
|
|
|
get_tile_pixel (&buf, x-1, y , pix01, wrapmode);
|
|
|
|
get_tile_pixel (&buf, x+1, y , pix21, wrapmode);
|
|
|
|
get_tile_pixel (&buf, x-1, y+1, pix02, wrapmode);
|
|
|
|
get_tile_pixel (&buf, x , y+1, pix12, wrapmode);
|
|
|
|
get_tile_pixel (&buf, x+1, y+1, pix22, wrapmode);
|
|
|
|
|
|
|
|
for (chan = 0; chan < alpha; chan++)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2000-01-13 21:44:06 +08:00
|
|
|
/* make convolution */
|
2000-01-13 23:39:26 +08:00
|
|
|
sum1 = (pix20[chan] - pix00[chan]) +
|
|
|
|
2 * (pix21[chan] - pix01[chan]) +
|
|
|
|
(pix22[chan] - pix20[chan]);
|
|
|
|
sum2 = (pix02[chan] - pix00[chan]) +
|
|
|
|
2 * (pix12[chan] - pix10[chan]) +
|
|
|
|
(pix22[chan] - pix20[chan]);
|
1997-11-25 06:05:25 +08:00
|
|
|
/* common job ... */
|
2000-01-13 23:39:26 +08:00
|
|
|
sum = long_sqrt ((long) sum1 * sum1 + (long) sum2 * sum2);
|
|
|
|
sum = (sum * scale) >> 16; /* arbitrary scaling factor */
|
1997-11-25 06:05:25 +08:00
|
|
|
if (sum > maxval) sum = maxval;
|
|
|
|
dest[chan] = sum;
|
|
|
|
}
|
|
|
|
}
|
2000-01-13 23:39:26 +08:00
|
|
|
if (has_alpha)
|
|
|
|
dest[alpha] = src[alpha];
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
cur_progress += dest_rgn.w * dest_rgn.h;
|
2000-01-13 23:39:26 +08:00
|
|
|
gimp_progress_update ((double) cur_progress / (double) max_progress);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
end_tile_buf (&buf);
|
1997-11-25 06:05:25 +08:00
|
|
|
gimp_drawable_flush (drawable);
|
2001-06-15 04:07:38 +08:00
|
|
|
gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
|
|
|
|
gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************/
|
|
|
|
/* Dialog */
|
|
|
|
/*******************************************************/
|
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
static void
|
|
|
|
edge_ok_callback (GtkWidget *widget,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
eint.run = TRUE;
|
|
|
|
|
|
|
|
gtk_widget_destroy (GTK_WIDGET (data));
|
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static gint
|
2000-08-22 09:26:57 +08:00
|
|
|
edge_dialog (GimpDrawable *drawable)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
GtkWidget *dlg;
|
|
|
|
GtkWidget *frame;
|
|
|
|
GtkWidget *table;
|
|
|
|
GtkWidget *hbox;
|
|
|
|
GtkWidget *toggle;
|
|
|
|
GtkObject *scale_data;
|
2000-05-01 05:03:44 +08:00
|
|
|
GSList *group = NULL;
|
2000-01-12 01:41:07 +08:00
|
|
|
|
2000-01-13 23:39:26 +08:00
|
|
|
gint use_wrap = (evals.wrapmode == WRAP);
|
|
|
|
gint use_smear = (evals.wrapmode == SMEAR);
|
|
|
|
gint use_black = (evals.wrapmode == BLACK);
|
2000-01-12 01:41:07 +08:00
|
|
|
|
2000-05-01 05:03:44 +08:00
|
|
|
gimp_ui_init ("edge", FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2000-01-07 06:26:10 +08:00
|
|
|
dlg = gimp_dialog_new (_("Edge Detection"), "edge",
|
2000-05-23 01:10:28 +08:00
|
|
|
gimp_standard_help_func, "filters/edge.html",
|
2000-01-07 06:26:10 +08:00
|
|
|
GTK_WIN_POS_MOUSE,
|
|
|
|
FALSE, TRUE, FALSE,
|
|
|
|
|
2001-08-04 03:52:08 +08:00
|
|
|
GTK_STOCK_CANCEL, gtk_widget_destroy,
|
2000-01-07 06:26:10 +08:00
|
|
|
NULL, 1, NULL, FALSE, TRUE,
|
2001-11-29 21:23:44 +08:00
|
|
|
GTK_STOCK_OK, edge_ok_callback,
|
|
|
|
NULL, NULL, NULL, TRUE, FALSE,
|
2000-01-07 06:26:10 +08:00
|
|
|
|
|
|
|
NULL);
|
|
|
|
|
2001-12-29 21:26:29 +08:00
|
|
|
g_signal_connect (G_OBJECT (dlg), "destroy",
|
|
|
|
G_CALLBACK (gtk_main_quit),
|
|
|
|
NULL);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* parameter settings */
|
2000-01-12 01:41:07 +08:00
|
|
|
frame = gtk_frame_new (_("Parameter Settings"));
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
2000-01-12 01:41:07 +08:00
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
|
|
|
|
|
|
|
table = gtk_table_new (2, 3, FALSE);
|
2000-01-12 01:41:07 +08:00
|
|
|
gtk_table_set_col_spacings (GTK_TABLE (table), 4);
|
|
|
|
gtk_table_set_row_spacings (GTK_TABLE (table), 4);
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (table), 4);
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_container_add (GTK_CONTAINER (frame), table);
|
|
|
|
|
2000-01-12 01:41:07 +08:00
|
|
|
/* Label, scale, entry for evals.amount */
|
2000-01-13 23:39:26 +08:00
|
|
|
scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
|
|
|
|
_("Amount:"), 100, 0,
|
|
|
|
evals.amount, 1.0, 10.0, 0.1, 1.0, 1,
|
2000-02-04 23:12:17 +08:00
|
|
|
TRUE, 0, 0,
|
2000-01-13 23:39:26 +08:00
|
|
|
NULL, NULL);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-12-29 21:26:29 +08:00
|
|
|
g_signal_connect (G_OBJECT (scale_data), "value_changed",
|
|
|
|
G_CALLBACK (gimp_double_adjustment_update),
|
|
|
|
&evals.amount);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2000-01-12 01:41:07 +08:00
|
|
|
/* Radio buttons WRAP, SMEAR, BLACK */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2000-01-12 01:41:07 +08:00
|
|
|
hbox = gtk_hbox_new (FALSE, 4);
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_table_attach (GTK_TABLE (table), hbox, 0, 3, 1, 2,
|
|
|
|
GTK_FILL, GTK_FILL, 0, 0);
|
2001-12-29 21:26:29 +08:00
|
|
|
gtk_widget_show (hbox);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-12-22 18:03:19 +08:00
|
|
|
toggle = gtk_radio_button_new_with_label (group, _("Wrap"));
|
2001-12-29 21:26:29 +08:00
|
|
|
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
|
1999-01-16 01:35:04 +08:00
|
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_wrap);
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_widget_show (toggle);
|
|
|
|
|
2001-12-29 21:26:29 +08:00
|
|
|
g_signal_connect (G_OBJECT (toggle), "toggled",
|
|
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
|
|
&use_wrap);
|
|
|
|
|
1999-12-22 18:03:19 +08:00
|
|
|
toggle = gtk_radio_button_new_with_label (group, _("Smear"));
|
2001-12-29 21:26:29 +08:00
|
|
|
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
|
1999-01-16 01:35:04 +08:00
|
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_smear);
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_widget_show (toggle);
|
|
|
|
|
2001-12-29 21:26:29 +08:00
|
|
|
g_signal_connect (G_OBJECT (toggle), "toggled",
|
|
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
|
|
&use_smear);
|
|
|
|
|
1999-12-22 18:03:19 +08:00
|
|
|
toggle = gtk_radio_button_new_with_label (group, _("Black"));
|
2001-12-29 21:26:29 +08:00
|
|
|
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
|
1999-01-16 01:35:04 +08:00
|
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_black);
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_widget_show (toggle);
|
2001-12-29 21:26:29 +08:00
|
|
|
|
|
|
|
g_signal_connect (G_OBJECT (toggle), "toggled",
|
|
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
|
|
&use_black);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
gtk_widget_show (table);
|
|
|
|
gtk_widget_show (frame);
|
|
|
|
gtk_widget_show (dlg);
|
|
|
|
|
|
|
|
gtk_main ();
|
|
|
|
gdk_flush ();
|
|
|
|
|
|
|
|
if (use_wrap)
|
|
|
|
evals.wrapmode = WRAP;
|
|
|
|
else if (use_smear)
|
|
|
|
evals.wrapmode = SMEAR;
|
|
|
|
else if (use_black)
|
|
|
|
evals.wrapmode = BLACK;
|
|
|
|
|
|
|
|
return eint.run;
|
|
|
|
}
|