1998-11-06 08:51:39 +08:00
|
|
|
/*********************************************************************************/
|
|
|
|
/* LIC 0.14 -- image filter plug-in for The Gimp program */
|
|
|
|
/* Copyright (C) 1996 Tom Bech */
|
|
|
|
/*===============================================================================*/
|
|
|
|
/* E-mail: tomb@gimp.org */
|
|
|
|
/* You can contact the original The Gimp authors at gimp@xcf.berkeley.edu */
|
|
|
|
/*===============================================================================*/
|
|
|
|
/* This program is free software; you can redistribute it and/or modify it under */
|
|
|
|
/* the terms of the GNU General Public License as published by the Free Software */
|
|
|
|
/* Foundation; either version 2 of the License, or (at your option) any later */
|
|
|
|
/* version. */
|
|
|
|
/*===============================================================================*/
|
|
|
|
/* This program is distributed in the hope that it will be useful, but WITHOUT */
|
|
|
|
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */
|
|
|
|
/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.*/
|
|
|
|
/*===============================================================================*/
|
|
|
|
/* You should have received a copy of the GNU General Public License along with */
|
|
|
|
/* this program; if not, write to the Free Software Foundation, Inc., 675 Mass */
|
|
|
|
/* Ave, Cambridge, MA 02139, USA. */
|
|
|
|
/*===============================================================================*/
|
|
|
|
/* In other words, you can't sue me for whatever happens while using this ;) */
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* Changes (post 0.10): */
|
|
|
|
/* -> 0.11: Fixed a bug in the convolution kernels (Tom). */
|
|
|
|
/* -> 0.12: Added Quartic's bilinear interpolation stuff (Tom). */
|
|
|
|
/* -> 0.13 Changed some UI stuff causing trouble with the 0.60 release, added */
|
|
|
|
/* the (GIMP) tags and changed random() calls to rand() (Tom) */
|
|
|
|
/* -> 0.14 Ported to 0.99.11 (Tom) */
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* This plug-in implements the Line Integral Convolution (LIC) as described in */
|
|
|
|
/* Cabral et al. "Imaging vector fields using line integral convolution" in the */
|
|
|
|
/* Proceedings of ACM SIGGRAPH 93. Publ. by ACM, New York, NY, USA. p. 263-270. */
|
|
|
|
/* Some of the code is based on code by Steinar Haugen (thanks!), the Perlin */
|
|
|
|
/* noise function is practically ripped as is :) */
|
|
|
|
/*********************************************************************************/
|
2000-01-26 01:46:56 +08:00
|
|
|
|
app/appenv.h New file. Includes <math.h>. Move G_PI, RINT(), ROUND() etc
1999-09-01 Tor Lillqvist <tml@iki.fi>
* app/appenv.h
* libgimp/gimpmath.h: New file. Includes <math.h>. Move G_PI,
RINT(), ROUND() etc from app/appenv.h here, so plug-ins can
use them, too. Remove some commented-out old stuff in appenv.h.
* libgimp/gimp.h: Include gimpmath.h.
* libgimp/gimp.c (gimp_main): Win32: Don't install signal
handlers, we can't do anything useful in the handler ourselves
anyway (it would be nice to print out a backtrace, but that seems
pretty hard to do, even if not impossible). Let Windows inform the
user about the crash. If the plug-in was compiled with MSVC, and
the user also has it, she is offered a chance to start the
debugger automatically anyway.
* app/*several*.c: Include gimpmath.h for G_PI etc. Don't include
<math.h>, as gimpmath.h includes it.
* plug-ins/*/*many*.c: Include config.h. Don't include <math.h>.
Remove all the duplicated definitions of G_PI and rint(). Use
RINT() instead of rint().
* app/app_procs.[ch]: app_exit() takes a gboolean.
* app/batch.c
* app/commands.c
* app/interface.c: Call app_exit() with FALSE or TRUE.
* app/main.c (on_error): Call gimp_fatal_error. (main): Don't
install any signal handler on Win32 here, either.
* app/errors.c (gimp_fatal_error, gimp_terminate): Win32: Format
the message and call MessageBox with it. g_on_error_query doesn't
do anything useful on Win32, and printf'ing a message to stdout or
stderr doesn't do anything, either, in a windowing application.
1999-09-02 04:30:56 +08:00
|
|
|
#include "config.h"
|
1998-11-06 08:51:39 +08:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <signal.h>
|
2000-01-07 18:39:33 +08:00
|
|
|
|
1998-11-06 08:51:39 +08:00
|
|
|
#include <gdk/gdk.h>
|
|
|
|
#include <gtk/gtk.h>
|
2000-01-07 18:39:33 +08:00
|
|
|
|
1998-11-06 08:51:39 +08:00
|
|
|
#include <libgimp/gimp.h>
|
|
|
|
#include <libgimp/gimpui.h>
|
|
|
|
|
2000-01-07 18:39:33 +08:00
|
|
|
#include "libgimp/stdplugins-intl.h"
|
1999-09-26 13:16:19 +08:00
|
|
|
|
1998-11-06 08:51:39 +08:00
|
|
|
/************/
|
|
|
|
/* Typedefs */
|
|
|
|
/************/
|
|
|
|
|
|
|
|
#define CHECKBOUNDS(x,y) (x>=0 && y>=0 && x<width && y<height)
|
|
|
|
|
|
|
|
#define EPSILON 1.0e-5
|
|
|
|
|
|
|
|
#define numx 40 /* Pseudo-random vector grid size */
|
|
|
|
#define numy 40
|
|
|
|
|
|
|
|
#define stepx 0.5
|
|
|
|
#define stepy 0.5
|
|
|
|
|
|
|
|
#define TILE_CACHE_SIZE 16
|
|
|
|
|
|
|
|
/***********************/
|
|
|
|
/* Some useful structs */
|
|
|
|
/***********************/
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
gdouble r,g,b,a;
|
|
|
|
} rgbpixel;
|
|
|
|
|
|
|
|
/*****************************/
|
|
|
|
/* Global variables and such */
|
|
|
|
/*****************************/
|
|
|
|
|
|
|
|
rgbpixel white = {1.0,1.0,1.0};
|
|
|
|
rgbpixel black = {0.0,0.0,0.0};
|
|
|
|
rgbpixel background;
|
|
|
|
|
|
|
|
gdouble G[numx][numy][2];
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
gdouble filtlen;
|
|
|
|
gdouble noisemag;
|
|
|
|
gdouble intsteps;
|
|
|
|
gdouble minv;
|
|
|
|
gdouble maxv;
|
1998-11-06 08:51:39 +08:00
|
|
|
gboolean create_new_image;
|
2000-01-26 01:46:56 +08:00
|
|
|
gint effect_channel;
|
|
|
|
gint effect_operator;
|
|
|
|
gint effect_convolve;
|
|
|
|
gint32 effect_image_id;
|
1998-11-06 08:51:39 +08:00
|
|
|
} LicValues;
|
|
|
|
|
|
|
|
LicValues licvals;
|
|
|
|
|
|
|
|
gdouble l=10.0,dx=2.0,dy=2.0,minv=-2.5,maxv=2.5,isteps=20.0;
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
GDrawable *input_drawable;
|
|
|
|
GDrawable *output_drawable;
|
|
|
|
GPixelRgn source_region;
|
|
|
|
GPixelRgn dest_region;
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
gint imgtype,width,height,in_channels,out_channels;
|
|
|
|
gint border_x1,border_y1,border_x2,border_y2;
|
|
|
|
glong maxcounter;
|
1998-11-06 08:51:39 +08:00
|
|
|
guchar *scalarfield;
|
|
|
|
|
|
|
|
GtkWidget *dialog;
|
|
|
|
|
|
|
|
/************************/
|
|
|
|
/* Convenience routines */
|
|
|
|
/************************/
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
rgb_add (rgbpixel *a,
|
|
|
|
rgbpixel *b)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
a->r=a->r+b->r;
|
|
|
|
a->g=a->g+b->g;
|
|
|
|
a->b=a->b+b->b;
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
rgb_mul (rgbpixel *a,
|
|
|
|
gdouble b)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
a->r=a->r*b;
|
|
|
|
a->g=a->g*b;
|
|
|
|
a->b=a->b*b;
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
rgb_clamp (rgbpixel *a)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
if (a->r>1.0)
|
|
|
|
a->r=1.0;
|
|
|
|
|
|
|
|
if (a->g>1.0)
|
|
|
|
a->g=1.0;
|
|
|
|
|
|
|
|
if (a->b>1.0)
|
|
|
|
a->b=1.0;
|
|
|
|
|
|
|
|
if (a->r<0.0)
|
|
|
|
a->r=0.0;
|
|
|
|
|
|
|
|
if (a->g<0.0)
|
|
|
|
a->g=0.0;
|
|
|
|
|
|
|
|
if (a->b<0.0)
|
|
|
|
a->b=0.0;
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static rgbpixel
|
|
|
|
peek (gint x,
|
|
|
|
gint y)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
static guchar data[4];
|
|
|
|
rgbpixel color;
|
|
|
|
|
|
|
|
gimp_pixel_rgn_get_pixel(&source_region,data,x,y);
|
|
|
|
|
|
|
|
color.r=(gdouble)(data[0])/255.0;
|
|
|
|
color.g=(gdouble)(data[1])/255.0;
|
|
|
|
color.b=(gdouble)(data[2])/255.0;
|
|
|
|
|
|
|
|
if (input_drawable->bpp==4)
|
|
|
|
{
|
|
|
|
if (in_channels==4)
|
|
|
|
color.a=(gdouble)(data[3])/255.0;
|
|
|
|
else
|
|
|
|
color.a=1.0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
color.a=1.0;
|
|
|
|
|
|
|
|
return(color);
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
poke (gint x,
|
|
|
|
gint y,
|
|
|
|
rgbpixel *color)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
static guchar data[4];
|
|
|
|
|
|
|
|
data[0]=(guchar)(color->r*255.0);
|
|
|
|
data[1]=(guchar)(color->g*255.0);
|
|
|
|
data[2]=(guchar)(color->b*255.0);
|
|
|
|
data[3]=(guchar)(color->a*255.0);
|
|
|
|
|
|
|
|
gimp_pixel_rgn_set_pixel(&dest_region,data,x,y);
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************/
|
|
|
|
/* Allocate memory for temporary images */
|
|
|
|
/****************************************/
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static gint
|
|
|
|
image_setup (GDrawable *drawable,
|
|
|
|
gint interactive)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
/* Get some useful info on the input drawable */
|
|
|
|
/* ========================================== */
|
|
|
|
|
|
|
|
input_drawable=drawable;
|
|
|
|
output_drawable=drawable;
|
|
|
|
|
|
|
|
gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2);
|
|
|
|
|
|
|
|
width=input_drawable->width;
|
|
|
|
height=input_drawable->height;
|
|
|
|
|
|
|
|
gimp_pixel_rgn_init (&source_region, input_drawable, 0, 0, width, height, FALSE, FALSE);
|
|
|
|
|
|
|
|
maxcounter=(glong)width*(glong)height;
|
|
|
|
|
|
|
|
/* Assume at least RGB */
|
|
|
|
/* =================== */
|
|
|
|
|
|
|
|
in_channels=3;
|
|
|
|
if (gimp_drawable_has_alpha(input_drawable->id)==TRUE)
|
|
|
|
in_channels++;
|
|
|
|
|
|
|
|
if (interactive==TRUE)
|
|
|
|
{
|
|
|
|
/* Allocate memory for temp. images */
|
|
|
|
/* ================================ */
|
|
|
|
}
|
|
|
|
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static guchar
|
|
|
|
peekmap (guchar *image,
|
|
|
|
gint x,
|
|
|
|
gint y)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
glong index;
|
|
|
|
|
|
|
|
index=(glong)x+(glong)width*(glong)y;
|
|
|
|
|
|
|
|
return(image[index]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************/
|
|
|
|
/* Main part */
|
|
|
|
/*************/
|
|
|
|
|
|
|
|
/***************************************************/
|
|
|
|
/* Compute the derivative in the x and y direction */
|
|
|
|
/* We use these convolution kernels: */
|
|
|
|
/* |1 0 -1| | 1 2 1| */
|
|
|
|
/* DX: |2 0 -2| DY: | 0 0 0| */
|
|
|
|
/* |1 0 -1| | -1 -2 -1| */
|
|
|
|
/* (It's a varation of the Sobel kernels, really) */
|
|
|
|
/***************************************************/
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static gint
|
|
|
|
gradx (guchar *image,
|
|
|
|
gint x,
|
|
|
|
gint y)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gint val=0;
|
|
|
|
|
|
|
|
if (CHECKBOUNDS(x-1,y-1)==TRUE)
|
|
|
|
val=val+(gint)peekmap(image,x-1,y-1);
|
|
|
|
if (CHECKBOUNDS(x+1,y-1)==TRUE)
|
|
|
|
val=val-(gint)peekmap(image,x+1,y-1);
|
|
|
|
|
|
|
|
if (CHECKBOUNDS(x-1,y)==TRUE)
|
|
|
|
val=val+2*(gint)peekmap(image,x-1,y);
|
|
|
|
if (CHECKBOUNDS(x+1,y)==TRUE)
|
|
|
|
val=val-2*(gint)peekmap(image,x+1,y);
|
|
|
|
|
|
|
|
if (CHECKBOUNDS(x-1,y+1)==TRUE)
|
|
|
|
val=val+(gint)peekmap(image,x-1,y+1);
|
|
|
|
if (CHECKBOUNDS(x+1,y+1)==TRUE)
|
|
|
|
val=val-(gint)peekmap(image,x+1,y+1);
|
|
|
|
|
|
|
|
return(val);
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static gint
|
|
|
|
grady (guchar *image,
|
|
|
|
gint x,
|
|
|
|
gint y)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gint val=0;
|
|
|
|
|
|
|
|
if (CHECKBOUNDS(x-1,y-1)==TRUE)
|
|
|
|
val=val+(gint)peekmap(image,x-1,y-1);
|
|
|
|
if (CHECKBOUNDS(x,y-1)==TRUE)
|
|
|
|
val=val+2*(gint)peekmap(image,x,y-1);
|
|
|
|
if (CHECKBOUNDS(x+1,y-1)==TRUE)
|
|
|
|
val=val+(gint)peekmap(image,x+1,y-1);
|
|
|
|
|
|
|
|
if (CHECKBOUNDS(x-1,y+1)==TRUE)
|
|
|
|
val=val-(gint)peekmap(image,x-1,y+1);
|
|
|
|
if (CHECKBOUNDS(x,y+1)==TRUE)
|
|
|
|
val=val-2*(gint)peekmap(image,x,y+1);
|
|
|
|
if (CHECKBOUNDS(x+1,y+1)==TRUE)
|
|
|
|
val=val-(gint)peekmap(image,x+1,y+1);
|
|
|
|
|
|
|
|
return(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************/
|
|
|
|
/* A nice 2nd order cubic spline :) */
|
|
|
|
/************************************/
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static gdouble
|
|
|
|
cubic (gdouble t)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gdouble at=fabs(t);
|
|
|
|
|
|
|
|
if (at<1.0)
|
|
|
|
return(2.0*at*at*at-3.0*at*at+1.0);
|
|
|
|
|
|
|
|
return(0.0);
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static gdouble
|
|
|
|
omega (gdouble u,
|
|
|
|
gdouble v,
|
|
|
|
gint i,
|
|
|
|
gint j)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
while (i<0)
|
|
|
|
i+=numx;
|
|
|
|
|
|
|
|
while (j<0)
|
|
|
|
j+=numy;
|
|
|
|
|
|
|
|
i%=numx;
|
|
|
|
j%=numy;
|
|
|
|
|
|
|
|
return(cubic(u)*cubic(v)*(G[i][j][0]*u+G[i][j][1]*v));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************/
|
|
|
|
/* The noise function (2D variant of Perlins noise function) */
|
|
|
|
/*************************************************************/
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static gdouble
|
|
|
|
noise (gdouble x,
|
|
|
|
gdouble y)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gint i,sti=(gint)floor(x/dx);
|
|
|
|
gint j,stj=(gint)floor(y/dy);
|
|
|
|
|
|
|
|
gdouble sum=0.0;
|
|
|
|
|
|
|
|
/* Calculate the gdouble sum */
|
|
|
|
/* ======================== */
|
|
|
|
|
|
|
|
for (i=sti; i<=sti+1; i++)
|
|
|
|
{
|
|
|
|
for (j=stj; j<=stj+1; j++)
|
|
|
|
sum+=omega((x-(gdouble)i*dx)/dx,(y-(gdouble)j*dy)/dy,i,j);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(sum);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************/
|
|
|
|
/* Generates pseudo-random vectors with length 1 */
|
|
|
|
/*************************************************/
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
generatevectors (void)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gdouble alpha;
|
|
|
|
gint i,j;
|
|
|
|
|
|
|
|
for (i=0; i<numx; i++)
|
|
|
|
{
|
|
|
|
for (j=0; j<numy; j++)
|
|
|
|
{
|
app/appenv.h New file. Includes <math.h>. Move G_PI, RINT(), ROUND() etc
1999-09-01 Tor Lillqvist <tml@iki.fi>
* app/appenv.h
* libgimp/gimpmath.h: New file. Includes <math.h>. Move G_PI,
RINT(), ROUND() etc from app/appenv.h here, so plug-ins can
use them, too. Remove some commented-out old stuff in appenv.h.
* libgimp/gimp.h: Include gimpmath.h.
* libgimp/gimp.c (gimp_main): Win32: Don't install signal
handlers, we can't do anything useful in the handler ourselves
anyway (it would be nice to print out a backtrace, but that seems
pretty hard to do, even if not impossible). Let Windows inform the
user about the crash. If the plug-in was compiled with MSVC, and
the user also has it, she is offered a chance to start the
debugger automatically anyway.
* app/*several*.c: Include gimpmath.h for G_PI etc. Don't include
<math.h>, as gimpmath.h includes it.
* plug-ins/*/*many*.c: Include config.h. Don't include <math.h>.
Remove all the duplicated definitions of G_PI and rint(). Use
RINT() instead of rint().
* app/app_procs.[ch]: app_exit() takes a gboolean.
* app/batch.c
* app/commands.c
* app/interface.c: Call app_exit() with FALSE or TRUE.
* app/main.c (on_error): Call gimp_fatal_error. (main): Don't
install any signal handler on Win32 here, either.
* app/errors.c (gimp_fatal_error, gimp_terminate): Win32: Format
the message and call MessageBox with it. g_on_error_query doesn't
do anything useful on Win32, and printf'ing a message to stdout or
stderr doesn't do anything, either, in a windowing application.
1999-09-02 04:30:56 +08:00
|
|
|
alpha = (gdouble)(rand()%1000)*(G_PI/500.0);
|
1998-11-06 08:51:39 +08:00
|
|
|
G[i][j][0] = cos(alpha);
|
|
|
|
G[i][j][1] = sin(alpha);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A simple triangle filter */
|
|
|
|
/* ======================== */
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static gdouble
|
|
|
|
filter (gdouble u)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gdouble f=1.0-fabs(u)/l;
|
|
|
|
|
|
|
|
if (f<0.0)
|
|
|
|
f=0.0;
|
|
|
|
|
|
|
|
return(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************/
|
|
|
|
/* Compute the Line Integral Convolution (LIC) at x,y */
|
|
|
|
/******************************************************/
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static gdouble
|
|
|
|
lic_noise (gint x,
|
|
|
|
gint y,
|
|
|
|
gdouble vx,
|
|
|
|
gdouble vy)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gdouble i=0.0;
|
|
|
|
gdouble f1=0.0,f2=0.0;
|
|
|
|
gdouble u,step=2.0*l/isteps;
|
|
|
|
gdouble xx=(gdouble)x,yy=(gdouble)y;
|
|
|
|
gdouble c,s;
|
|
|
|
|
|
|
|
/* Get vector at x,y */
|
|
|
|
/* ================= */
|
|
|
|
|
|
|
|
c = vx;
|
|
|
|
s = vy;
|
|
|
|
|
|
|
|
/* Calculate integral numerically */
|
|
|
|
/* ============================== */
|
|
|
|
|
|
|
|
f1 = filter(-l)*noise(xx+l*c,yy+l*s);
|
|
|
|
|
|
|
|
for (u=-l+step; u<=l; u+=step)
|
|
|
|
{
|
|
|
|
f2 = filter(u)*noise(xx-u*c,yy-u*s);
|
|
|
|
i+=(f1+f2)*0.5*step;
|
|
|
|
f1=f2;
|
|
|
|
}
|
|
|
|
|
|
|
|
i=(i-minv)/(maxv-minv);
|
|
|
|
|
|
|
|
if (i<0.0)
|
|
|
|
i=0.0;
|
|
|
|
|
|
|
|
if (i>1.0)
|
|
|
|
i=1.0;
|
|
|
|
|
|
|
|
i=(i/2.0)+0.5;
|
|
|
|
|
|
|
|
return(i);
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static rgbpixel
|
|
|
|
bilinear (gdouble x,
|
|
|
|
gdouble y,
|
|
|
|
rgbpixel *p)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gdouble m0, m1;
|
|
|
|
gdouble ix, iy;
|
|
|
|
rgbpixel v;
|
|
|
|
|
|
|
|
x = fmod(x, 1.0);
|
|
|
|
y = fmod(y, 1.0);
|
|
|
|
|
|
|
|
if (x < 0)
|
|
|
|
x += 1.0;
|
|
|
|
|
|
|
|
if (y < 0)
|
|
|
|
y += 1.0;
|
|
|
|
|
|
|
|
ix = 1.0 - x;
|
|
|
|
iy = 1.0 - y;
|
|
|
|
|
|
|
|
/* Red */
|
|
|
|
/* === */
|
|
|
|
|
|
|
|
m0 = ix * p[0].r + x * p[1].r;
|
|
|
|
m1 = ix * p[2].r + x * p[3].r;
|
|
|
|
|
|
|
|
v.r = iy * m0 + y * m1;
|
|
|
|
|
|
|
|
/* Green */
|
|
|
|
/* ===== */
|
|
|
|
|
|
|
|
m0 = ix * p[0].g + x * p[1].g;
|
|
|
|
m1 = ix * p[2].g + x * p[3].g;
|
|
|
|
|
|
|
|
v.g = iy * m0 + y * m1;
|
|
|
|
|
|
|
|
/* Blue */
|
|
|
|
/* ==== */
|
|
|
|
|
|
|
|
m0 = ix * p[0].b + x * p[1].b;
|
|
|
|
m1 = ix * p[2].b + x * p[3].b;
|
|
|
|
|
|
|
|
v.b = iy * m0 + y * m1;
|
|
|
|
|
|
|
|
return(v);
|
|
|
|
} /* bilinear */
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
getpixel (rgbpixel *p,
|
|
|
|
gdouble u,
|
|
|
|
gdouble v)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
register gint x1, y1, x2, y2;
|
|
|
|
static rgbpixel pp[4];
|
|
|
|
|
|
|
|
x1 = (gint)u;
|
|
|
|
y1 = (gint)v;
|
|
|
|
|
|
|
|
if (x1 < 0)
|
|
|
|
x1 = width - (-x1 % width);
|
|
|
|
else
|
|
|
|
x1 = x1 % width;
|
|
|
|
|
|
|
|
if (y1 < 0)
|
|
|
|
y1 = height - (-y1 % height);
|
|
|
|
else
|
|
|
|
y1 = y1 % height;
|
|
|
|
|
|
|
|
x2 = (x1 + 1) % width;
|
|
|
|
y2 = (y1 + 1) % height;
|
|
|
|
|
|
|
|
pp[0] = peek(x1, y1);
|
|
|
|
pp[1] = peek(x2, y1);
|
|
|
|
pp[2] = peek(x1, y2);
|
|
|
|
pp[3] = peek(x2, y2);
|
|
|
|
|
|
|
|
*p=bilinear(u,v,pp);
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
lic_image (gint x,
|
|
|
|
gint y,
|
|
|
|
gdouble vx,
|
|
|
|
gdouble vy,
|
|
|
|
rgbpixel *color)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gdouble u,step=2.0*l/isteps;
|
|
|
|
gdouble xx=(gdouble)x,yy=(gdouble)y;
|
|
|
|
gdouble c,s;
|
|
|
|
rgbpixel col,col1,col2,col3;
|
|
|
|
|
|
|
|
/* Get vector at x,y */
|
|
|
|
/* ================= */
|
|
|
|
|
|
|
|
c = vx;
|
|
|
|
s = vy;
|
|
|
|
|
|
|
|
/* Calculate integral numerically */
|
|
|
|
/* ============================== */
|
|
|
|
|
|
|
|
col=black;
|
|
|
|
getpixel(&col1,xx+l*c,yy+l*s);
|
|
|
|
rgb_mul(&col1,filter(-l));
|
|
|
|
|
|
|
|
for (u=-l+step; u<=l; u+=step)
|
|
|
|
{
|
|
|
|
getpixel(&col2,xx-u*c,yy-u*s);
|
|
|
|
rgb_mul(&col2,filter(u));
|
|
|
|
|
|
|
|
col3=col1;
|
|
|
|
rgb_add(&col3,&col2);
|
|
|
|
rgb_mul(&col3,0.5*step);
|
|
|
|
rgb_add(&col,&col3);
|
|
|
|
|
|
|
|
col1=col2;
|
|
|
|
}
|
|
|
|
|
|
|
|
rgb_mul(&col,1.0/l);
|
|
|
|
rgb_clamp(&col);
|
|
|
|
|
|
|
|
*color=col;
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static gdouble
|
|
|
|
maximum (gdouble a,
|
|
|
|
gdouble b,
|
|
|
|
gdouble c)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gdouble max=a;
|
|
|
|
|
|
|
|
if (b>max)
|
|
|
|
max=b;
|
|
|
|
if (c>max)
|
|
|
|
max=c;
|
|
|
|
|
|
|
|
return(max);
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static gdouble
|
|
|
|
minimum (gdouble a,
|
|
|
|
gdouble b,
|
|
|
|
gdouble c)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gdouble min=a;
|
|
|
|
|
|
|
|
if (b<min)
|
|
|
|
min=b;
|
|
|
|
if (c<min)
|
|
|
|
min=c;
|
|
|
|
|
|
|
|
return(min);
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
get_hue (rgbpixel *col,
|
|
|
|
gdouble *hue)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gdouble max,min,delta;
|
|
|
|
|
|
|
|
max=maximum(col->r,col->g,col->b);
|
|
|
|
min=minimum(col->r,col->g,col->b);
|
|
|
|
|
|
|
|
if (max==min) *hue=-1.0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delta=max-min;
|
|
|
|
if (col->r==max)
|
|
|
|
*hue=(col->g-col->b)/delta;
|
|
|
|
else if (col->g==max)
|
|
|
|
*hue=2.0+(col->b-col->r)/delta;
|
|
|
|
else if (col->b==max)
|
|
|
|
*hue=4.0+(col->r-col->g)/delta;
|
|
|
|
|
|
|
|
*hue=*hue*60.0;
|
|
|
|
if (*hue<0.0)
|
|
|
|
*hue=*hue+360.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
get_saturation (rgbpixel *col,
|
|
|
|
gdouble *sat)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gdouble max,min,l;
|
|
|
|
|
|
|
|
max=maximum(col->r,col->g,col->b);
|
|
|
|
min=minimum(col->r,col->g,col->b);
|
|
|
|
|
|
|
|
if (max==min)
|
|
|
|
*sat=0.0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
l=(max+min)/2.0;
|
|
|
|
if (l<=0.5)
|
|
|
|
*sat=(max-min)/(max+min);
|
|
|
|
else
|
|
|
|
*sat=(max-min)/(2.0-max-min);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
get_brightness (rgbpixel *col,
|
|
|
|
gdouble *bri)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gdouble max,min;
|
|
|
|
|
|
|
|
max=maximum(col->r,col->g,col->b);
|
|
|
|
min=minimum(col->r,col->g,col->b);
|
|
|
|
|
|
|
|
*bri=(max+min)/2.0;
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
rgb_to_hue (GDrawable *image,
|
|
|
|
guchar **map)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
guchar *themap,data[4];
|
|
|
|
gint w,h,x,y;
|
|
|
|
rgbpixel color;
|
|
|
|
gdouble val;
|
|
|
|
glong maxc,index=0;
|
|
|
|
GPixelRgn region;
|
|
|
|
|
|
|
|
w=image->width;
|
|
|
|
h=image->height;
|
|
|
|
maxc=(glong)w*(glong)h;
|
|
|
|
|
|
|
|
/* gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2); */
|
|
|
|
|
|
|
|
gimp_pixel_rgn_init (®ion, image, 0, 0, w, h, FALSE, FALSE);
|
|
|
|
|
|
|
|
themap=(guchar *)malloc((size_t)maxc*sizeof(guchar));
|
|
|
|
|
|
|
|
for (y=0;y<h;y++)
|
|
|
|
{
|
|
|
|
for (x=0;x<w;x++)
|
|
|
|
{
|
|
|
|
gimp_pixel_rgn_get_pixel(®ion,data,x,y);
|
|
|
|
|
|
|
|
color.r=data[0];
|
|
|
|
color.g=data[1];
|
|
|
|
color.b=data[2];
|
|
|
|
|
|
|
|
get_hue(&color,&val);
|
|
|
|
|
|
|
|
themap[index++]=(guchar)(val*255.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*map=themap;
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
rgb_to_saturation (GDrawable *image,
|
|
|
|
guchar **map)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
guchar *themap,data[4];
|
|
|
|
gint w,h,x,y;
|
|
|
|
rgbpixel color;
|
|
|
|
gdouble val;
|
|
|
|
glong maxc,index=0;
|
|
|
|
GPixelRgn region;
|
|
|
|
|
|
|
|
w=image->width;
|
|
|
|
h=image->height;
|
|
|
|
maxc=(glong)w*(glong)h;
|
|
|
|
|
|
|
|
/* gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2); */
|
|
|
|
|
|
|
|
gimp_pixel_rgn_init (®ion, image, 0, 0, w, h, FALSE, FALSE);
|
|
|
|
|
|
|
|
themap=(guchar *)malloc((size_t)maxc*sizeof(guchar));
|
|
|
|
|
|
|
|
for (y=0;y<h;y++)
|
|
|
|
{
|
|
|
|
for (x=0;x<w;x++)
|
|
|
|
{
|
|
|
|
gimp_pixel_rgn_get_pixel(®ion,data,x,y);
|
|
|
|
|
|
|
|
color.r=data[0];
|
|
|
|
color.g=data[1];
|
|
|
|
color.b=data[2];
|
|
|
|
|
|
|
|
get_saturation(&color,&val);
|
|
|
|
|
|
|
|
themap[index++]=(guchar)(val*255.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*map=themap;
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
rgb_to_brightness (GDrawable *image,
|
|
|
|
guchar **map)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
guchar *themap,data[4];
|
|
|
|
gint w,h,x,y;
|
|
|
|
rgbpixel color;
|
|
|
|
gdouble val;
|
|
|
|
glong maxc,index=0;
|
|
|
|
GPixelRgn region;
|
|
|
|
|
|
|
|
w=image->width;
|
|
|
|
h=image->height;
|
|
|
|
maxc=(glong)w*(glong)h;
|
|
|
|
|
|
|
|
/* gimp_drawable_mask_bounds (drawable->id, &border_x1, &border_y1, &border_x2, &border_y2); */
|
|
|
|
|
|
|
|
gimp_pixel_rgn_init (®ion, image, 0, 0, w, h, FALSE, FALSE);
|
|
|
|
|
|
|
|
themap=(guchar *)malloc((size_t)maxc*sizeof(guchar));
|
|
|
|
|
|
|
|
for (y=0;y<h;y++)
|
|
|
|
{
|
|
|
|
for (x=0;x<w;x++)
|
|
|
|
{
|
|
|
|
gimp_pixel_rgn_get_pixel(®ion,data,x,y);
|
|
|
|
|
|
|
|
color.r=data[0];
|
|
|
|
color.g=data[1];
|
|
|
|
color.b=data[2];
|
|
|
|
|
|
|
|
get_brightness(&color,&val);
|
|
|
|
|
|
|
|
themap[index++]=(guchar)(val*255.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*map=themap;
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
compute_lic_derivative (void)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gint xcount,ycount;
|
|
|
|
glong counter=0;
|
|
|
|
rgbpixel color;
|
|
|
|
gdouble vx,vy,tmp;
|
|
|
|
|
|
|
|
for (ycount=0;ycount<height;ycount++)
|
|
|
|
{
|
|
|
|
for (xcount=0;xcount<width;xcount++)
|
|
|
|
{
|
|
|
|
/* Get direction vector at (x,y) and normalize it */
|
|
|
|
/* ============================================== */
|
|
|
|
|
|
|
|
vx=gradx(scalarfield,xcount,ycount);
|
|
|
|
vy=grady(scalarfield,xcount,ycount);
|
|
|
|
|
|
|
|
tmp=sqrt(vx*vx+vy*vy);
|
|
|
|
if (tmp!=0.0)
|
|
|
|
{
|
|
|
|
tmp=1.0/tmp;
|
|
|
|
vx*=tmp; vy*=tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convolve with the LIC at (x,y) */
|
|
|
|
/* ============================== */
|
|
|
|
|
|
|
|
if (licvals.effect_convolve==0)
|
|
|
|
{
|
|
|
|
color=peek(xcount,ycount);
|
|
|
|
tmp=lic_noise(xcount,ycount,vx,vy);
|
|
|
|
rgb_mul(&color,tmp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lic_image(xcount,ycount,vx,vy,&color);
|
|
|
|
|
|
|
|
poke(xcount,ycount,&color);
|
|
|
|
|
|
|
|
counter++;
|
|
|
|
|
|
|
|
if ((counter % width)==0)
|
|
|
|
gimp_progress_update((gfloat)counter/(gfloat)maxcounter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
compute_lic_gradient (void)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gint xcount,ycount;
|
|
|
|
glong counter=0;
|
|
|
|
rgbpixel color;
|
|
|
|
gdouble vx,vy,tmp;
|
|
|
|
|
|
|
|
for (ycount=0;ycount<height;ycount++)
|
|
|
|
{
|
|
|
|
for (xcount=0;xcount<width;xcount++)
|
|
|
|
{
|
|
|
|
/* Get derivative at (x,y), rotate it 90 degrees and normalize it */
|
|
|
|
/* ============================================================== */
|
|
|
|
|
|
|
|
vx=gradx(scalarfield,xcount,ycount);
|
|
|
|
vy=grady(scalarfield,xcount,ycount);
|
|
|
|
|
|
|
|
vx=-1.0*vx; tmp=vy; vy=vx; vx=tmp;
|
|
|
|
|
|
|
|
tmp=sqrt(vx*vx+vy*vy);
|
|
|
|
if (tmp!=0.0)
|
|
|
|
{
|
|
|
|
tmp=1.0/tmp;
|
|
|
|
vx*=tmp; vy*=tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convolve with the LIC at (x,y) */
|
|
|
|
/* ============================== */
|
|
|
|
|
|
|
|
if (licvals.effect_convolve==0)
|
|
|
|
{
|
|
|
|
color=peek(xcount,ycount);
|
|
|
|
tmp=lic_noise(xcount,ycount,vx,vy);
|
|
|
|
rgb_mul(&color,tmp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lic_image(xcount,ycount,vx,vy,&color);
|
|
|
|
|
|
|
|
poke(xcount,ycount,&color);
|
|
|
|
|
|
|
|
counter++;
|
|
|
|
|
|
|
|
if ((counter % width)==0)
|
|
|
|
gimp_progress_update((gfloat)counter/(gfloat)maxcounter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
compute_image (void)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gint32 new_image_id=-1,new_layer_id=-1;
|
|
|
|
GDrawable *effect;
|
|
|
|
|
|
|
|
if (licvals.create_new_image==TRUE)
|
|
|
|
{
|
|
|
|
/* Create a new image */
|
|
|
|
/* ================== */
|
|
|
|
|
2000-01-26 03:11:57 +08:00
|
|
|
new_image_id = gimp_image_new (width, height, RGB);
|
|
|
|
gimp_image_undo_disable (new_image_id);
|
|
|
|
|
1998-11-06 08:51:39 +08:00
|
|
|
/* Create a "normal" layer */
|
|
|
|
/* ======================= */
|
|
|
|
|
2000-01-26 03:11:57 +08:00
|
|
|
new_layer_id = gimp_layer_new (new_image_id, _("Background"),
|
|
|
|
width, height, RGB_IMAGE, 100.0, NORMAL_MODE);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 03:11:57 +08:00
|
|
|
gimp_image_add_layer (new_image_id, new_layer_id, 0);
|
|
|
|
output_drawable = gimp_drawable_get (new_layer_id);
|
1998-11-06 08:51:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
gimp_pixel_rgn_init (&dest_region, output_drawable, 0,0, width,height, TRUE,TRUE);
|
|
|
|
|
1999-09-26 13:16:19 +08:00
|
|
|
gimp_progress_init( _("Van Gogh (LIC)"));
|
1998-11-06 08:51:39 +08:00
|
|
|
|
|
|
|
if (licvals.effect_convolve==0)
|
|
|
|
generatevectors();
|
|
|
|
|
|
|
|
l=(gdouble)licvals.filtlen;
|
|
|
|
dx=dy=(gdouble)licvals.noisemag;
|
|
|
|
minv=((gdouble)licvals.minv)/10.0;
|
|
|
|
maxv=((gdouble)licvals.maxv)/10.0;
|
|
|
|
isteps=(gdouble)licvals.intsteps;
|
|
|
|
|
|
|
|
effect=gimp_drawable_get(licvals.effect_image_id);
|
|
|
|
|
|
|
|
switch (licvals.effect_channel)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
rgb_to_hue(effect,&scalarfield);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
rgb_to_saturation(effect,&scalarfield);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
rgb_to_brightness(effect,&scalarfield);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scalarfield==NULL)
|
|
|
|
{
|
1999-12-29 04:04:19 +08:00
|
|
|
printf( "LIC: Couldn't allocate temporary buffer - out of memory!\n");
|
1998-11-06 08:51:39 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (licvals.effect_operator==0)
|
|
|
|
compute_lic_derivative();
|
|
|
|
else
|
|
|
|
compute_lic_gradient();
|
|
|
|
|
|
|
|
free(scalarfield);
|
|
|
|
|
|
|
|
/* Update image */
|
|
|
|
/* ============ */
|
|
|
|
|
|
|
|
gimp_drawable_flush(output_drawable);
|
|
|
|
gimp_drawable_merge_shadow(output_drawable->id, TRUE);
|
|
|
|
gimp_drawable_update(output_drawable->id, 0,0, width,height);
|
|
|
|
|
2000-01-26 03:11:57 +08:00
|
|
|
if (new_image_id != -1)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
2000-01-26 03:11:57 +08:00
|
|
|
gimp_display_new (new_image_id);
|
|
|
|
gimp_displays_flush ();
|
|
|
|
gimp_drawable_detach (output_drawable);
|
|
|
|
gimp_image_undo_enable (new_image_id);
|
1998-11-06 08:51:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************/
|
|
|
|
/* Below is only UI stuff */
|
|
|
|
/**************************/
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
ok_button_clicked (GtkWidget *widget,
|
|
|
|
gpointer data)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
2000-01-26 01:46:56 +08:00
|
|
|
gtk_widget_hide (GTK_WIDGET (data));
|
|
|
|
gdk_flush ();
|
|
|
|
compute_image ();
|
|
|
|
gtk_main_quit ();
|
1998-11-06 08:51:39 +08:00
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static gint
|
|
|
|
effect_image_constrain (gint32 image_id,
|
|
|
|
gint32 drawable_id,
|
|
|
|
gpointer data)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
if (drawable_id == -1)
|
|
|
|
return(TRUE);
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
return gimp_drawable_is_rgb (drawable_id);
|
1998-11-06 08:51:39 +08:00
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
effect_image_callback (gint32 id,
|
|
|
|
gpointer data)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
2000-01-26 01:46:56 +08:00
|
|
|
licvals.effect_image_id = id;
|
1998-11-06 08:51:39 +08:00
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
create_main_dialog (void)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
2000-01-26 01:46:56 +08:00
|
|
|
GtkWidget *vbox;
|
|
|
|
GtkWidget *hbox;
|
|
|
|
GtkWidget *vbox2;
|
1998-11-06 08:51:39 +08:00
|
|
|
GtkWidget *frame;
|
2000-01-26 01:46:56 +08:00
|
|
|
GtkWidget *table;
|
1998-11-06 08:51:39 +08:00
|
|
|
GtkWidget *option_menu;
|
|
|
|
GtkWidget *menu;
|
|
|
|
GtkWidget *button;
|
|
|
|
GtkObject *scale_data;
|
|
|
|
|
2000-01-07 18:39:33 +08:00
|
|
|
dialog = gimp_dialog_new (_("Van Gogh (LIC)"), "lic",
|
|
|
|
gimp_plugin_help_func, "filters/lic.html",
|
|
|
|
GTK_WIN_POS_MOUSE,
|
|
|
|
FALSE, TRUE, FALSE,
|
|
|
|
|
|
|
|
_("OK"), ok_button_clicked,
|
|
|
|
NULL, NULL, NULL, TRUE, FALSE,
|
|
|
|
_("Cancel"), gtk_main_quit,
|
|
|
|
NULL, NULL, NULL, FALSE, TRUE,
|
|
|
|
|
|
|
|
NULL);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
vbox = gtk_vbox_new (FALSE, 4);
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
|
|
|
|
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
|
|
|
|
gtk_widget_show (vbox);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
hbox = gtk_hbox_new (FALSE, 6);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
|
|
gtk_widget_show (hbox);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
frame = gtk_frame_new (_("Options"));
|
|
|
|
gtk_container_add (GTK_CONTAINER (hbox), frame);
|
|
|
|
gtk_widget_show (frame);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
vbox2 = gtk_vbox_new (FALSE, 1);
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox2), 2);
|
|
|
|
gtk_container_add (GTK_CONTAINER (frame), vbox2);
|
|
|
|
gtk_widget_show (vbox2);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
button = gtk_check_button_new_with_label( _("Create\nNew Image"));
|
|
|
|
gtk_label_set_justify (GTK_LABEL (GTK_BIN (button)->child), GTK_JUSTIFY_LEFT);
|
|
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
|
|
|
|
licvals.create_new_image == TRUE);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
|
1998-11-06 08:51:39 +08:00
|
|
|
gtk_widget_show(button);
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
frame = gimp_radio_group_new2 (TRUE, _("Effect Channel"),
|
|
|
|
gimp_radio_button_update,
|
|
|
|
&licvals.effect_channel,
|
|
|
|
(gpointer) licvals.effect_channel,
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
_("Hue"), (gpointer) 0, NULL,
|
|
|
|
_("Saturation"), (gpointer) 1, NULL,
|
|
|
|
_("Brightness"), (gpointer) 2, NULL,
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
NULL);
|
|
|
|
gtk_container_add (GTK_CONTAINER (hbox), frame);
|
|
|
|
gtk_widget_show (frame);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
frame = gimp_radio_group_new2 (TRUE, _("Effect Operator"),
|
|
|
|
gimp_radio_button_update,
|
|
|
|
&licvals.effect_operator,
|
|
|
|
(gpointer) licvals.effect_operator,
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
_("Derivative"), (gpointer) 0, NULL,
|
|
|
|
_("Gradient"), (gpointer) 1, NULL,
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
NULL);
|
|
|
|
gtk_container_add (GTK_CONTAINER (hbox), frame);
|
|
|
|
gtk_widget_show (frame);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
frame = gimp_radio_group_new2 (TRUE, _("Convolve"),
|
|
|
|
gimp_radio_button_update,
|
|
|
|
&licvals.effect_convolve,
|
|
|
|
(gpointer) licvals.effect_convolve,
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
_("With White Noise"), (gpointer) 0, NULL,
|
|
|
|
_("With Source Image"), (gpointer) 1, NULL,
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
NULL);
|
|
|
|
gtk_container_add (GTK_CONTAINER (hbox), frame);
|
|
|
|
gtk_widget_show (frame);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
frame = gtk_frame_new (_("Parameters"));
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
gtk_widget_show (frame);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
table = gtk_table_new (6, 3, FALSE);
|
|
|
|
gtk_table_set_col_spacings (GTK_TABLE (table), 4);
|
|
|
|
gtk_table_set_row_spacings (GTK_TABLE (table), 2);
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (table), 4);
|
|
|
|
gtk_container_add (GTK_CONTAINER (frame), table);
|
|
|
|
gtk_widget_show (table);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
|
|
|
/* Effect image menu */
|
2000-01-26 01:46:56 +08:00
|
|
|
option_menu = gtk_option_menu_new ();
|
1998-11-06 08:51:39 +08:00
|
|
|
menu = gimp_drawable_menu_new(effect_image_constrain,
|
|
|
|
effect_image_callback,
|
|
|
|
NULL,
|
|
|
|
licvals.effect_image_id);
|
2000-01-26 01:46:56 +08:00
|
|
|
gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
|
2000-01-31 11:13:02 +08:00
|
|
|
gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
|
2000-01-26 01:46:56 +08:00
|
|
|
_("Effect Image:"), 1.0, 0.5,
|
2000-01-31 11:13:02 +08:00
|
|
|
option_menu, 2, TRUE);
|
2000-01-26 01:46:56 +08:00
|
|
|
|
|
|
|
scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
|
|
|
|
_("Filter Length"), 0, 0,
|
|
|
|
licvals.filtlen, 0, 64, 1.0, 8.0, 1,
|
|
|
|
NULL, NULL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT(scale_data), "value_changed",
|
|
|
|
GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
|
|
|
|
&licvals.filtlen);
|
|
|
|
|
|
|
|
scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
|
|
|
|
_("Noise Magnitude:"), 0, 0,
|
|
|
|
licvals.noisemag, 1, 5, 0.1, 1.0, 1,
|
|
|
|
NULL, NULL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT(scale_data), "value_changed",
|
|
|
|
GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
|
|
|
|
&licvals.noisemag);
|
|
|
|
|
|
|
|
scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
|
|
|
|
_("Integration Steps:"), 0, 0,
|
|
|
|
licvals.intsteps, 1, 40, 1.0, 5.0, 1,
|
|
|
|
NULL, NULL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT(scale_data), "value_changed",
|
|
|
|
GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
|
|
|
|
&licvals.intsteps);
|
|
|
|
|
|
|
|
scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
|
|
|
|
_("Minimum Value:"), 0, 0,
|
|
|
|
licvals.minv, -100, 0, 1, 10, 1,
|
|
|
|
NULL, NULL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT(scale_data), "value_changed",
|
|
|
|
GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
|
|
|
|
&licvals.minv);
|
|
|
|
|
|
|
|
scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 5,
|
|
|
|
_("Maximum Value:"), 0, 0,
|
|
|
|
licvals.maxv, 0, 100, 1, 10, 1,
|
|
|
|
NULL, NULL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT(scale_data), "value_changed",
|
|
|
|
GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
|
|
|
|
&licvals.maxv);
|
|
|
|
|
|
|
|
gtk_widget_show (dialog);
|
1998-11-06 08:51:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************/
|
|
|
|
/* Implementation */
|
|
|
|
/******************/
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void lic_interactive (GDrawable *drawable);
|
|
|
|
/*
|
|
|
|
static void lic_noninteractive (GDrawable *drawable);
|
|
|
|
*/
|
1998-11-06 08:51:39 +08:00
|
|
|
|
|
|
|
/*************************************/
|
|
|
|
/* Set parameters to standard values */
|
|
|
|
/*************************************/
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
set_default_settings (void)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
licvals.filtlen=5;
|
|
|
|
licvals.noisemag=2;
|
|
|
|
licvals.intsteps=25;
|
|
|
|
licvals.minv=-25;
|
|
|
|
licvals.maxv=25;
|
|
|
|
licvals.create_new_image=TRUE;
|
|
|
|
licvals.effect_channel=2;
|
|
|
|
licvals.effect_operator=1;
|
|
|
|
licvals.effect_convolve=1;
|
|
|
|
licvals.effect_image_id=0;
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
query (void)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
static GParamDef args[] =
|
2000-01-26 01:46:56 +08:00
|
|
|
{
|
|
|
|
{ PARAM_INT32, "run_mode", "Interactive" },
|
|
|
|
{ PARAM_IMAGE, "image", "Input image" },
|
|
|
|
{ PARAM_DRAWABLE, "drawable", "Input drawable" },
|
|
|
|
};
|
1998-11-06 08:51:39 +08:00
|
|
|
|
|
|
|
static GParamDef *return_vals = NULL;
|
|
|
|
static gint nargs = sizeof (args) / sizeof (args[0]);
|
|
|
|
static gint nreturn_vals = 0;
|
|
|
|
|
1999-09-26 13:16:19 +08:00
|
|
|
INIT_I18N();
|
|
|
|
|
1998-11-06 08:51:39 +08:00
|
|
|
gimp_install_procedure ("plug_in_lic",
|
2000-01-31 10:32:30 +08:00
|
|
|
"Creates a Van Gogh effect (Line Integral Convolution)",
|
1998-11-06 08:51:39 +08:00
|
|
|
"No help yet",
|
|
|
|
"Tom Bech & Federico Mena Quintero",
|
|
|
|
"Tom Bech & Federico Mena Quintero",
|
|
|
|
"Version 0.14, September 24 1997",
|
1999-11-24 04:29:20 +08:00
|
|
|
N_("<Image>/Filters/Map/Van Gogh (LIC)..."),
|
1998-11-06 08:51:39 +08:00
|
|
|
"RGB",
|
|
|
|
PROC_PLUG_IN,
|
|
|
|
nargs, nreturn_vals,
|
|
|
|
args, return_vals);
|
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
run (gchar *name,
|
|
|
|
gint nparams,
|
|
|
|
GParam *param,
|
|
|
|
gint *nreturn_vals,
|
|
|
|
GParam **return_vals)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
static GParam values[1];
|
|
|
|
GDrawable *drawable;
|
|
|
|
GRunModeType run_mode;
|
|
|
|
GStatusType status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
run_mode = param[0].data.d_int32;
|
|
|
|
|
1999-09-26 13:16:19 +08:00
|
|
|
if (run_mode == RUN_INTERACTIVE) {
|
|
|
|
INIT_I18N_UI();
|
|
|
|
} else {
|
|
|
|
INIT_I18N();
|
|
|
|
}
|
|
|
|
|
1998-11-06 08:51:39 +08:00
|
|
|
*nreturn_vals = 1;
|
|
|
|
*return_vals = values;
|
|
|
|
|
|
|
|
values[0].type = PARAM_STATUS;
|
|
|
|
values[0].data.d_status = status;
|
|
|
|
|
|
|
|
/* Set default values */
|
|
|
|
/* ================== */
|
|
|
|
|
|
|
|
set_default_settings();
|
|
|
|
|
|
|
|
/* Possibly retrieve data */
|
|
|
|
/* ====================== */
|
|
|
|
|
|
|
|
gimp_get_data ("plug_in_lic", &licvals);
|
|
|
|
|
|
|
|
/* Get the specified drawable */
|
|
|
|
/* ========================== */
|
|
|
|
|
|
|
|
drawable = gimp_drawable_get (param[2].data.d_drawable);
|
|
|
|
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
|
|
{
|
|
|
|
/* Make sure that the drawable is RGBA or RGB color */
|
|
|
|
/* ================================================ */
|
|
|
|
|
1999-10-25 04:49:09 +08:00
|
|
|
if (gimp_drawable_is_rgb(drawable->id))
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
/* Set the tile cache size */
|
|
|
|
/* ======================= */
|
|
|
|
|
|
|
|
gimp_tile_cache_ntiles(TILE_CACHE_SIZE);
|
|
|
|
|
|
|
|
switch (run_mode)
|
|
|
|
{
|
|
|
|
case RUN_INTERACTIVE:
|
|
|
|
lic_interactive(drawable);
|
|
|
|
gimp_set_data("plug_in_lic", &licvals, sizeof(LicValues));
|
|
|
|
break;
|
|
|
|
case RUN_WITH_LAST_VALS:
|
|
|
|
image_setup(drawable,FALSE);
|
|
|
|
compute_image();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
status = STATUS_EXECUTION_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
values[0].data.d_status = status;
|
|
|
|
gimp_drawable_detach (drawable);
|
|
|
|
}
|
|
|
|
|
|
|
|
GPlugInInfo PLUG_IN_INFO =
|
|
|
|
{
|
2000-01-26 01:46:56 +08:00
|
|
|
NULL, /* init_proc */
|
|
|
|
NULL, /* quit_proc */
|
|
|
|
query, /* query_proc */
|
|
|
|
run, /* run_proc */
|
1998-11-06 08:51:39 +08:00
|
|
|
};
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
static void
|
|
|
|
lic_interactive (GDrawable *drawable)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
|
|
|
gchar **argv;
|
|
|
|
gint argc;
|
|
|
|
|
|
|
|
argc = 1;
|
|
|
|
argv = g_new (gchar *, 1);
|
|
|
|
argv[0] = g_strdup ("lic");
|
|
|
|
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
gtk_rc_parse(gimp_gtkrc());
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
gdk_set_use_xshm (gimp_use_xshm());
|
1998-11-06 08:51:39 +08:00
|
|
|
|
|
|
|
/* Create application window */
|
|
|
|
/* ========================= */
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
create_main_dialog ();
|
1998-11-06 08:51:39 +08:00
|
|
|
|
|
|
|
/* Prepare images */
|
|
|
|
/* ============== */
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
image_setup (drawable, TRUE);
|
1998-11-06 08:51:39 +08:00
|
|
|
|
|
|
|
/* Gtk main event loop */
|
|
|
|
/* =================== */
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
gtk_main ();
|
|
|
|
gdk_flush ();
|
1998-11-06 08:51:39 +08:00
|
|
|
}
|
|
|
|
|
2000-01-26 01:46:56 +08:00
|
|
|
/*
|
|
|
|
static void
|
|
|
|
lic_noninteractive (GDrawable *drawable)
|
1998-11-06 08:51:39 +08:00
|
|
|
{
|
2000-01-26 01:46:56 +08:00
|
|
|
g_message ("Noninteractive not yet implemented! Sorry.\n");
|
1998-11-06 08:51:39 +08:00
|
|
|
}
|
2000-01-26 01:46:56 +08:00
|
|
|
*/
|
1998-11-06 08:51:39 +08:00
|
|
|
|
1999-04-23 14:35:53 +08:00
|
|
|
MAIN()
|