mirror of https://github.com/GNOME/gimp.git
939 lines
23 KiB
C
939 lines
23 KiB
C
/*
|
|
fuse - associative image reconstruction
|
|
Copyright (C) 1997 Scott Draves <spot@cs.cmu.edu>
|
|
|
|
The GIMP -- an image manipulation program
|
|
Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
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.
|
|
*/
|
|
|
|
|
|
/*
|
|
|
|
revision history
|
|
|
|
Fri Nov 28 1997 - added template image
|
|
|
|
Sun Nov 16 1997 - listbox to select multiple inputs
|
|
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
|
|
#include "gtk/gtk.h"
|
|
#include "libgimp/gimp.h"
|
|
#include "libgimp/gimpmenu.h"
|
|
#include "gck/gck.h"
|
|
|
|
|
|
static void query(void);
|
|
static void run(char *name,
|
|
int nparams,
|
|
GParam * param,
|
|
int *nreturn_vals,
|
|
GParam ** return_vals);
|
|
static gint dialog();
|
|
|
|
static void doit(GDrawable *);
|
|
|
|
typedef unsigned long pixel;
|
|
|
|
typedef struct {
|
|
pixel *pixels;
|
|
int width, height, stride;
|
|
} image;
|
|
|
|
|
|
GPlugInInfo PLUG_IN_INFO =
|
|
{
|
|
NULL, /* init_proc */
|
|
NULL, /* quit_proc */
|
|
query, /* query_proc */
|
|
run, /* run_proc */
|
|
};
|
|
|
|
int run_flag = 0;
|
|
|
|
|
|
GtkWidget *dlg;
|
|
GckListBox *listbox;
|
|
|
|
|
|
#define preview_size 150
|
|
GtkWidget *preview;
|
|
|
|
GDrawable *drawable;
|
|
|
|
#define max_inputs 100
|
|
|
|
struct {
|
|
int ninputs;
|
|
gint32 input_image_ids[max_inputs];
|
|
int tile_size;
|
|
int overlap;
|
|
int order;
|
|
int acceleration;
|
|
int search_time;
|
|
int use_template;
|
|
double template_weight; /* in [0,10] */
|
|
} config = {
|
|
0,
|
|
{0},
|
|
25,
|
|
8,
|
|
0,
|
|
0,
|
|
10,
|
|
0,
|
|
0.5,
|
|
};
|
|
|
|
|
|
|
|
MAIN();
|
|
|
|
static void query()
|
|
{
|
|
static GParamDef args[] =
|
|
{
|
|
{PARAM_INT32, "run_mode", "Interactive, non-interactive"},
|
|
{PARAM_IMAGE, "image", "Input image (unused)"},
|
|
{PARAM_DRAWABLE, "drawable", "Input drawable"},
|
|
};
|
|
static GParamDef *return_vals = NULL;
|
|
static int nargs = sizeof(args) / sizeof(args[0]);
|
|
static int nreturn_vals = 0;
|
|
|
|
gimp_install_procedure("plug_in_fuse",
|
|
"associative image reconstruction",
|
|
"uhm, image dissociation",
|
|
"Scott Draves",
|
|
"Scott Draves",
|
|
"Nov 1997",
|
|
"<Image>/Filters/Combine/Fuse",
|
|
"RGB*",
|
|
PROC_PLUG_IN,
|
|
nargs, nreturn_vals,
|
|
args, return_vals);
|
|
}
|
|
|
|
static void
|
|
run(char *name, int n_params, GParam * param, int *nreturn_vals,
|
|
GParam ** return_vals)
|
|
{
|
|
static GParam values[1];
|
|
GRunModeType run_mode;
|
|
GStatusType status = STATUS_SUCCESS;
|
|
|
|
*nreturn_vals = 1;
|
|
*return_vals = values;
|
|
|
|
srandom(time(0));
|
|
|
|
run_mode = param[0].data.d_int32;
|
|
|
|
if (run_mode == RUN_NONINTERACTIVE) {
|
|
status = STATUS_CALLING_ERROR;
|
|
} else {
|
|
gimp_get_data("plug_in_fuse", &config);
|
|
|
|
drawable = gimp_drawable_get(param[2].data.d_drawable);
|
|
|
|
if (run_mode == RUN_INTERACTIVE) {
|
|
if (!dialog()) {
|
|
status = STATUS_EXECUTION_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
|
|
if (gimp_drawable_color(drawable->id)) {
|
|
;
|
|
} else {
|
|
status = STATUS_EXECUTION_ERROR;
|
|
}
|
|
gimp_drawable_detach(drawable);
|
|
}
|
|
|
|
values[0].type = PARAM_STATUS;
|
|
values[0].data.d_status = status;
|
|
}
|
|
|
|
|
|
double
|
|
compare(image *in, image *out) {
|
|
int x, y;
|
|
double r = 0.0;
|
|
double t = 0.0;
|
|
for (y = 0; y < in->height; y++) {
|
|
pixel *p = in->pixels + y * in->stride;
|
|
pixel *q = out->pixels + y * out->stride;
|
|
for (x = 0; x < in->width; x++) {
|
|
double rr, z;
|
|
guchar *i = (guchar *) (&p[x]);
|
|
guchar *o = (guchar *) (&q[x]);
|
|
int d;
|
|
if (0 == i[3] || 0 == o[3])
|
|
continue;
|
|
d = i[0] - (int) o[0];
|
|
rr = d * d;
|
|
d = i[1] - (int) o[1];
|
|
rr += d * d;
|
|
d = i[2] - (int) o[2];
|
|
rr += d * d;
|
|
z = i[3] * (double) o[3];
|
|
r += rr * z;
|
|
t += z * z; /* z * 255? */
|
|
}
|
|
}
|
|
|
|
if (t < 0.5)
|
|
return -1.0;
|
|
|
|
return r/t;
|
|
}
|
|
|
|
/* use bbox of src */
|
|
void
|
|
blit(image *dst, image *src) {
|
|
int x, y;
|
|
for (y = 0; y < src->height; y++) {
|
|
pixel *p = dst->pixels + y * dst->stride;
|
|
pixel *q = src->pixels + y * src->stride;
|
|
for (x = 0; x < src->width; x++) {
|
|
guchar *o = (guchar *) (&p[x]);
|
|
guchar *i = (guchar *) (&q[x]);
|
|
int s = i[3];
|
|
if (255 == s)
|
|
p[x] = q[x];
|
|
else {
|
|
o[0] = (o[0] * (255 - s) + i[0] * s) >> 8;
|
|
o[1] = (o[1] * (255 - s) + i[1] * s) >> 8;
|
|
o[2] = (o[2] * (255 - s) + i[2] * s) >> 8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* find tile somewhere in IN that matches OUT and TEMPLATE */
|
|
void
|
|
source_match(image *in, image *out, image *template) {
|
|
int i;
|
|
double best_d = HUGE;
|
|
image best_tile;
|
|
double w = exp(-0.3 * (10-config.template_weight));
|
|
|
|
if (0 == out->width || 0 == out->height)
|
|
return;
|
|
|
|
for (i = 0; i < 1+(config.search_time<<8); i++) {
|
|
image tile;
|
|
double d;
|
|
|
|
tile.pixels =
|
|
in->pixels + (random() % (in->width - out->width)) +
|
|
in->stride * (random() % (in->height - out->height));
|
|
|
|
tile.stride = in->stride;
|
|
tile.width = out->width;
|
|
tile.height = out->height;
|
|
|
|
d = compare(&tile, out);
|
|
if (d < 0.0) {
|
|
/* should really only happen with null overlap */
|
|
best_tile = tile;
|
|
break;
|
|
}
|
|
if (template) {
|
|
double e = compare(&tile, template);
|
|
if (e < 0.0)
|
|
printf("e less than zero\n");
|
|
else
|
|
d = (d * (1.0 - w) + e * w);
|
|
}
|
|
|
|
if (d < best_d) {
|
|
best_d = d;
|
|
best_tile = tile;
|
|
}
|
|
}
|
|
blit(out, &best_tile);
|
|
}
|
|
|
|
|
|
void
|
|
do_fuse(int nin, image *ins, image *out, image *template) {
|
|
int i, parity = 0;
|
|
double r, rad;
|
|
double bo;
|
|
int *prmute;
|
|
|
|
bo = config.tile_size - config.overlap;
|
|
|
|
for (i = 0; i < nin; i++)
|
|
if (config.tile_size >= ins[i].width ||
|
|
config.tile_size >= ins[i].height) {
|
|
printf("input smaller than tile - bailing\n");
|
|
return;
|
|
}
|
|
|
|
rad = sqrt(out->width * out->width +
|
|
out->height * out->height) / 2;
|
|
|
|
prmute = malloc(sizeof(int) * (1 + rad * 2.0 * M_PI / bo));
|
|
|
|
for (r = 0; r < rad; r += bo) {
|
|
int n = 1 + r * 2.0 * M_PI / bo;
|
|
for (i = 0; i < n; i++)
|
|
prmute[i] = i;
|
|
for (i = 0; i < 2*n; i++) {
|
|
int i1 = random() % n;
|
|
int i2 = random() % n;
|
|
int t = prmute[i1];
|
|
prmute[i1] = prmute[i2];
|
|
prmute[i2] = t;
|
|
}
|
|
parity++;
|
|
for (i = 0; i < n; i++) {
|
|
image tile, template_tile;
|
|
int x, y, w, h;
|
|
double theta = 2.0 * M_PI * prmute[i] / n;
|
|
if (parity&1)
|
|
theta += M_PI / n;
|
|
w = config.tile_size;
|
|
h = config.tile_size;
|
|
x = (out->width - w) / 2 + r * cos(theta);
|
|
y = (out->height - h) / 2 + r * sin(theta);
|
|
|
|
/* clip */
|
|
if (x < 0) {w += x; x = 0;}
|
|
if (y < 0) {h += y; y = 0;}
|
|
if (x > out->width - w) w = out->width - x;
|
|
if (y > out->height - h) h = out->height - y;
|
|
|
|
if (w <= 0 || h <= 0)
|
|
continue;
|
|
|
|
tile.pixels = out->pixels + y * out->stride + x;
|
|
tile.stride = out->stride;
|
|
tile.width = w;
|
|
tile.height = h;
|
|
|
|
if (template) {
|
|
template_tile.pixels = template->pixels + y * template->stride + x;
|
|
template_tile.stride = template->stride;
|
|
template_tile.width = w;
|
|
template_tile.height = h;
|
|
}
|
|
|
|
source_match(ins + (random()%nin), &tile,
|
|
template ? &template_tile : NULL);
|
|
if ((parity < 4) || ((i&7) == 0)) {
|
|
int xx, yy;
|
|
int cx, cy;
|
|
guchar *buf = (guchar *) malloc(3 * preview_size);
|
|
int p2 = preview_size/2;
|
|
if (r < p2) {
|
|
cx = out->width/2;
|
|
cy = out->height/2;
|
|
} else {
|
|
cx = x;
|
|
cy = y;
|
|
}
|
|
for (yy = cy-p2; yy < cy+p2; yy++) {
|
|
for (xx = cx-p2; xx < cx+p2; xx++) {
|
|
guchar *p = &buf[3*(xx-(cx-p2))];
|
|
guchar *q = (guchar *)(out->pixels+yy*out->stride+xx);
|
|
if ((xx < out->width) &&
|
|
(xx >= 0) &&
|
|
(yy < out->height) &&
|
|
(yy >= 0)) {
|
|
p[0] = q[0];
|
|
p[1] = q[1];
|
|
p[2] = q[2];
|
|
} else {
|
|
p[0] = p[1] = p[2] = 0;
|
|
}
|
|
}
|
|
gtk_preview_draw_row(GTK_PREVIEW (preview),
|
|
buf, 0, (yy-(cy-p2)), preview_size);
|
|
}
|
|
free(buf);
|
|
gtk_widget_draw (preview, NULL);
|
|
}
|
|
gimp_progress_update((1-i/(double)n) * r * r / (rad * rad) +
|
|
i/(double)n * (r + bo) * (r + bo) / (rad * rad));
|
|
}
|
|
}
|
|
free(prmute);
|
|
}
|
|
|
|
static void
|
|
doit(GDrawable * target_drawable) {
|
|
image *in, out, template;
|
|
gint bytes;
|
|
GPixelRgn pr;
|
|
GDrawable *input_drawable;
|
|
int i;
|
|
|
|
if (0 == config.ninputs) {
|
|
printf("fuse needs at least one input\n");
|
|
return;
|
|
}
|
|
|
|
in = (image *) malloc(sizeof(image) * config.ninputs);
|
|
|
|
for (i = 0; i < config.ninputs; i++) {
|
|
image *inp = &in[i];
|
|
|
|
inp->width = gimp_image_width(config.input_image_ids[i]);
|
|
inp->height = gimp_image_height(config.input_image_ids[i]);
|
|
inp->stride = inp->width;
|
|
|
|
/* allocate and initialize input buffer */
|
|
|
|
inp->pixels = (pixel *) malloc(inp->width * inp->height * 4);
|
|
if (inp->pixels == NULL) {
|
|
fprintf(stderr, "cannot malloc %d bytes for input.\n", inp->width * inp->height * 4);
|
|
return;
|
|
}
|
|
|
|
{
|
|
int nlayers;
|
|
gint32 *layer_ids;
|
|
layer_ids = gimp_image_get_layers (config.input_image_ids[i], &nlayers);
|
|
if ((NULL == layer_ids) || (nlayers <= 0)) {
|
|
fprintf(stderr, "fuse: error getting layer IDs\n");
|
|
return;
|
|
}
|
|
input_drawable = gimp_drawable_get(layer_ids[nlayers-1]);
|
|
bytes = input_drawable->bpp;
|
|
|
|
gimp_pixel_rgn_init(&pr, input_drawable,
|
|
0, 0, inp->width, inp->height, FALSE, FALSE);
|
|
if (4 == bytes) {
|
|
printf("4 channel image, using alpha\n");
|
|
gimp_pixel_rgn_get_rect(&pr, (guchar *)inp->pixels, 0, 0, inp->width, inp->height);
|
|
} else if (3 == bytes) {
|
|
int i;
|
|
guchar *tb = malloc (inp->width * bytes);
|
|
for (i = 0; i < inp->height; i++) {
|
|
int j;
|
|
gimp_pixel_rgn_get_rect(&pr, tb, 0, i, inp->width, 1);
|
|
for (j = 0; j < inp->width; j++) {
|
|
guchar *d = (guchar *) (inp->pixels + (inp->width * i) + j);
|
|
guchar *s = tb + j * bytes;
|
|
d[0] = s[0];
|
|
d[1] = s[1];
|
|
d[2] = s[2];
|
|
d[3] = 255;
|
|
}
|
|
}
|
|
free(tb);
|
|
} else if (1 == bytes) {
|
|
int i;
|
|
guchar *tb = malloc (inp->width * bytes);
|
|
for (i = 0; i < inp->height; i++) {
|
|
int j;
|
|
gimp_pixel_rgn_get_rect(&pr, tb, 0, i, inp->width, 1);
|
|
for (j = 0; j < inp->width; j++) {
|
|
guchar *d = (guchar *) (inp->pixels + (inp->width * i) + j);
|
|
guchar *s = tb + j * bytes;
|
|
d[0] = d[1] = d[2] = s[0];
|
|
d[3] = 255;
|
|
}
|
|
}
|
|
free(tb);
|
|
} else {
|
|
printf("cannot handle image with %d bytes per pixel\n", bytes);
|
|
}
|
|
|
|
|
|
if (nlayers > 1) {
|
|
input_drawable = gimp_drawable_get(layer_ids[0]);
|
|
bytes = input_drawable->bpp;
|
|
|
|
gimp_pixel_rgn_init(&pr, input_drawable,
|
|
0, 0, inp->width, inp->height, FALSE, FALSE);
|
|
printf("looking for alpha\n");
|
|
if (1 == bytes) {
|
|
int i;
|
|
guchar *tb = malloc (inp->width * bytes);
|
|
printf("got alpha\n");
|
|
for (i = 0; i < inp->height; i++) {
|
|
int j;
|
|
gimp_pixel_rgn_get_rect(&pr, tb, 0, i, inp->width, 1);
|
|
for (j = 0; j < inp->width; j++) {
|
|
guchar *d = (guchar *) (inp->pixels + (inp->width * i) + j);
|
|
guchar *s = tb + j * bytes;
|
|
d[3] = s[0];
|
|
}
|
|
}
|
|
free(tb);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
out.width = target_drawable->width;
|
|
out.height = target_drawable->height;
|
|
out.stride = out.width;
|
|
|
|
out.pixels = (pixel *) malloc(out.width * out.height * 4);
|
|
if (out.pixels == NULL) {
|
|
fprintf(stderr, "cannot malloc %d bytes for output.\n",
|
|
out.width * out.height * 4);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (config.use_template) {
|
|
template.width = target_drawable->width;
|
|
template.height = target_drawable->height;
|
|
template.stride = template.width;
|
|
template.pixels = (pixel *) malloc(template.width * template.height * 4);
|
|
if (template.pixels == NULL) {
|
|
fprintf(stderr, "cannot malloc %d bytes for output.\n",
|
|
template.width * template.height * 4);
|
|
return;
|
|
}
|
|
|
|
bytes = target_drawable->bpp;
|
|
|
|
gimp_pixel_rgn_init(&pr, target_drawable,
|
|
0, 0, template.width, template.height, FALSE, FALSE);
|
|
if (4 == bytes) {
|
|
printf("4 channel image, using alpha\n");
|
|
gimp_pixel_rgn_get_rect(&pr, (guchar *)template.pixels, 0, 0, template.width, template.height);
|
|
} else if (3 == bytes) {
|
|
int i;
|
|
guchar *tb = malloc (template.width * bytes);
|
|
for (i = 0; i < template.height; i++) {
|
|
int j;
|
|
gimp_pixel_rgn_get_rect(&pr, tb, 0, i, template.width, 1);
|
|
for (j = 0; j < template.width; j++) {
|
|
guchar *d = (guchar *) (template.pixels + (template.width * i) + j);
|
|
guchar *s = tb + j * bytes;
|
|
d[0] = s[0];
|
|
d[1] = s[1];
|
|
d[2] = s[2];
|
|
d[3] = 255;
|
|
}
|
|
}
|
|
free(tb);
|
|
}
|
|
}
|
|
|
|
do_fuse(config.ninputs, in, &out,
|
|
config.use_template ? &template : NULL);
|
|
|
|
gimp_pixel_rgn_init(&pr, target_drawable,
|
|
0, 0, out.width, out.height, FALSE, FALSE);
|
|
|
|
bytes = target_drawable->bpp;
|
|
|
|
|
|
if (4 == bytes) {
|
|
gimp_pixel_rgn_set_rect(&pr, (guchar *) out.pixels, 0, 0, out.width, out.height);
|
|
} else {
|
|
int i;
|
|
guchar *tb = malloc (out.width * bytes);
|
|
for (i = 0; i < out.height; i++) {
|
|
int j;
|
|
for (j = 0; j < out.width; j++) {
|
|
char *s = (char *) (out.pixels + (out.width * i) + j);
|
|
char *d = tb + j * bytes;
|
|
d[0] = s[0];
|
|
d[1] = s[1];
|
|
d[2] = s[2];
|
|
}
|
|
gimp_pixel_rgn_set_rect(&pr, tb, 0, i, out.width, 1);
|
|
}
|
|
free(tb);
|
|
}
|
|
gimp_drawable_flush(target_drawable);
|
|
/*
|
|
gimp_drawable_merge_shadow(target_drawable->id, TRUE);
|
|
*/
|
|
gimp_drawable_update(target_drawable->id, 0, 0,
|
|
target_drawable->width, target_drawable->height);
|
|
|
|
|
|
|
|
for (i = 0; i < config.ninputs; i++)
|
|
free(in[i].pixels);
|
|
free(in);
|
|
|
|
free(out.pixels);
|
|
if (config.use_template)
|
|
free(template.pixels);
|
|
}
|
|
|
|
|
|
static void close_callback(GtkWidget * widget, gpointer data)
|
|
{
|
|
gtk_main_quit();
|
|
}
|
|
|
|
static void ok_callback(GtkWidget * widget, gpointer data)
|
|
{
|
|
GList *sel;
|
|
int n;
|
|
run_flag = 1;
|
|
|
|
if (gimp_drawable_color(drawable->id)) {
|
|
|
|
sel = gck_listbox_get_current_selection(listbox);
|
|
|
|
n = 0;
|
|
while (sel != NULL && n < max_inputs) {
|
|
|
|
config.input_image_ids[n] =
|
|
((GckListBoxItem *) sel->data)->user_data;
|
|
sel = sel->next;
|
|
n++;
|
|
}
|
|
config.ninputs = n;
|
|
|
|
gimp_progress_init("Fusing...");
|
|
gimp_tile_cache_ntiles(2 * (drawable->width / gimp_tile_width() + 1));
|
|
|
|
doit(drawable);
|
|
|
|
gtk_widget_destroy(dlg);
|
|
|
|
gimp_displays_flush();
|
|
gimp_set_data("plug_in_fuse", &config, sizeof(config));
|
|
}
|
|
}
|
|
|
|
static gint
|
|
not_indexed_constrain (gint32 image_id, gint32 drawable_id, gpointer data) {
|
|
|
|
if ((gimp_image_width (image_id) < config.tile_size)
|
|
&& (gimp_image_width (image_id) < config.tile_size))
|
|
return 0;
|
|
|
|
return INDEXED != gimp_image_base_type (image_id);
|
|
}
|
|
|
|
static void
|
|
overlap_callback (GtkWidget *widget, gpointer data)
|
|
{
|
|
config.overlap = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
|
|
if (config.overlap > config.tile_size)
|
|
config.overlap = config.tile_size;
|
|
}
|
|
|
|
static void
|
|
tile_size_callback (GtkWidget *widget, gpointer data)
|
|
{
|
|
config.tile_size = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
|
|
if (config.tile_size < 0)
|
|
config.tile_size = 1;
|
|
}
|
|
|
|
|
|
static void
|
|
search_time_callback (GtkWidget *widget, gpointer data)
|
|
{
|
|
config.search_time = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
|
|
if (config.search_time < 0)
|
|
config.search_time = 0;
|
|
}
|
|
|
|
|
|
static void
|
|
toggle_callback (GtkWidget *widget, gpointer data)
|
|
{
|
|
int *toggle_val;
|
|
|
|
toggle_val = (int *) data;
|
|
|
|
if (GTK_TOGGLE_BUTTON (widget)->active)
|
|
*toggle_val = TRUE;
|
|
else
|
|
*toggle_val = FALSE;
|
|
}
|
|
|
|
static void
|
|
scale_callback (GtkAdjustment *adjustment, gpointer data)
|
|
{
|
|
* (double *) data = adjustment->value;
|
|
}
|
|
|
|
static char*
|
|
gimp_base_name (char *str)
|
|
{
|
|
char *t;
|
|
|
|
t = strrchr (str, '/');
|
|
if (!t)
|
|
return str;
|
|
return t+1;
|
|
}
|
|
|
|
static gint dialog() {
|
|
GtkWidget *frame;
|
|
GtkWidget *button;
|
|
GtkWidget *table;
|
|
GtkWidget *box;
|
|
GtkWidget *w;
|
|
gchar **argv;
|
|
gint argc;
|
|
int row;
|
|
guchar *color_cube;
|
|
|
|
argc = 1;
|
|
argv = g_new(gchar *, 1);
|
|
argv[0] = g_strdup("fuse");
|
|
|
|
gtk_init(&argc, &argv);
|
|
gtk_rc_parse (gimp_gtkrc ());
|
|
|
|
gtk_preview_set_gamma (gimp_gamma ());
|
|
gtk_preview_set_install_cmap (gimp_install_cmap ());
|
|
color_cube = gimp_color_cube ();
|
|
gtk_preview_set_color_cube (color_cube[0], color_cube[1],
|
|
color_cube[2], color_cube[3]);
|
|
|
|
gtk_widget_set_default_visual (gtk_preview_get_visual ());
|
|
gtk_widget_set_default_colormap (gtk_preview_get_cmap ());
|
|
|
|
dlg = gtk_dialog_new();
|
|
gtk_window_set_title(GTK_WINDOW(dlg), "Fuse");
|
|
gtk_window_position(GTK_WINDOW(dlg), GTK_WIN_POS_MOUSE);
|
|
gtk_signal_connect(GTK_OBJECT(dlg), "destroy",
|
|
(GtkSignalFunc) close_callback, NULL);
|
|
|
|
button = gtk_button_new_with_label("Ok");
|
|
GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
|
|
gtk_signal_connect(GTK_OBJECT(button), "clicked",
|
|
(GtkSignalFunc) ok_callback,
|
|
dlg);
|
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), button, TRUE, TRUE, 0);
|
|
gtk_widget_grab_default(button);
|
|
gtk_widget_show(button);
|
|
|
|
button = gtk_button_new_with_label("Cancel");
|
|
GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
|
|
gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
|
|
(GtkSignalFunc) gtk_widget_destroy,
|
|
GTK_OBJECT(dlg));
|
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), button, TRUE, TRUE, 0);
|
|
gtk_widget_show(button);
|
|
|
|
box = GTK_DIALOG(dlg)->vbox;
|
|
|
|
|
|
{
|
|
gint32 *images;
|
|
int i, k, nimages;
|
|
|
|
frame = gtk_frame_new("Source Images");
|
|
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
|
|
gtk_container_border_width(GTK_CONTAINER(frame), 10);
|
|
gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 10);
|
|
gtk_widget_show(frame);
|
|
|
|
listbox = gck_listbox_new(frame, TRUE, TRUE, 10, 150, 150,
|
|
GTK_SELECTION_MULTIPLE, NULL,
|
|
NULL /* event_handler */);
|
|
|
|
|
|
images = gimp_query_images (&nimages);
|
|
for (i = 0, k = 0; i < nimages; i++) {
|
|
if (not_indexed_constrain(images[i], 0, 0)) {
|
|
int j;
|
|
GckListBoxItem item;
|
|
char *filename;
|
|
filename = gimp_image_get_filename (images[i]);
|
|
item.label = g_new (char, strlen (filename) + 16);
|
|
sprintf (item.label, "%s-%d", gimp_base_name (filename), images[i]);
|
|
g_free (filename);
|
|
|
|
item.widget = NULL;
|
|
item.user_data = (gpointer) images[i];
|
|
item.listbox = NULL;
|
|
gck_listbox_insert_item(listbox, &item, k);
|
|
g_free(item.label);
|
|
for (j = 0; j < config.ninputs; j++)
|
|
if (images[i] == config.input_image_ids[j])
|
|
(void) gck_listbox_select_item_by_position(listbox, k);
|
|
k++;
|
|
}
|
|
}
|
|
}
|
|
|
|
frame = gtk_frame_new("Parameters");
|
|
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
|
|
gtk_container_border_width(GTK_CONTAINER(frame), 10);
|
|
gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 10);
|
|
gtk_widget_show(frame);
|
|
|
|
box = gtk_vbox_new (FALSE, 5);
|
|
gtk_container_add(GTK_CONTAINER(frame), box);
|
|
gtk_widget_show (box);
|
|
|
|
{
|
|
GtkWidget *label;
|
|
GtkWidget *entry;
|
|
GtkWidget *hbox;
|
|
char buffer[20];
|
|
hbox = gtk_hbox_new (FALSE, 5);
|
|
gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
|
|
|
|
label = gtk_label_new ("Tile Size: ");
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
entry = gtk_entry_new ();
|
|
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
|
|
sprintf (buffer, "%d", config.tile_size);
|
|
gtk_entry_set_text (GTK_ENTRY (entry), buffer);
|
|
gtk_signal_connect (GTK_OBJECT (entry), "changed",
|
|
(GtkSignalFunc) tile_size_callback,
|
|
NULL);
|
|
gtk_widget_show (entry);
|
|
|
|
gtk_widget_show (hbox);
|
|
}
|
|
|
|
|
|
{
|
|
GtkWidget *label;
|
|
GtkWidget *entry;
|
|
GtkWidget *hbox;
|
|
char buffer[20];
|
|
hbox = gtk_hbox_new (FALSE, 5);
|
|
gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
|
|
|
|
label = gtk_label_new ("Overlap: ");
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
entry = gtk_entry_new ();
|
|
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
|
|
sprintf (buffer, "%d", config.overlap);
|
|
gtk_entry_set_text (GTK_ENTRY (entry), buffer);
|
|
gtk_signal_connect (GTK_OBJECT (entry), "changed",
|
|
(GtkSignalFunc) overlap_callback,
|
|
NULL);
|
|
gtk_widget_show (entry);
|
|
|
|
gtk_widget_show (hbox);
|
|
}
|
|
|
|
|
|
{
|
|
GtkWidget *label;
|
|
GtkWidget *entry;
|
|
GtkWidget *hbox;
|
|
char buffer[20];
|
|
hbox = gtk_hbox_new (FALSE, 5);
|
|
gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
|
|
|
|
label = gtk_label_new ("Search Time: ");
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
entry = gtk_entry_new ();
|
|
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
|
|
sprintf (buffer, "%d", config.search_time);
|
|
gtk_entry_set_text (GTK_ENTRY (entry), buffer);
|
|
gtk_signal_connect (GTK_OBJECT (entry), "changed",
|
|
(GtkSignalFunc) search_time_callback,
|
|
NULL);
|
|
gtk_widget_show (entry);
|
|
|
|
gtk_widget_show (hbox);
|
|
}
|
|
|
|
{
|
|
GtkWidget *toggle;
|
|
toggle = gtk_check_button_new_with_label ("use target as template");
|
|
gtk_box_pack_start(GTK_BOX(box), toggle, FALSE, FALSE, 0);
|
|
gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
|
|
(GtkSignalFunc) toggle_callback,
|
|
&config.use_template);
|
|
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), (config.use_template));
|
|
gtk_widget_show (toggle);
|
|
}
|
|
|
|
{
|
|
GtkWidget *label;
|
|
GtkWidget *scale;
|
|
GtkObject *scale_data;
|
|
GtkWidget *hbox;
|
|
char buffer[20];
|
|
hbox = gtk_hbox_new (FALSE, 5);
|
|
gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
|
|
|
|
label = gtk_label_new ("Template weight: ");
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
scale_data = gtk_adjustment_new (config.template_weight, 0.0, 10.0,
|
|
0.01, 0.01, 0.0);
|
|
scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_data));
|
|
gtk_widget_set_usize (scale, 150, 0);
|
|
gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
|
|
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
|
|
gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
|
|
gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
|
|
(GtkSignalFunc) scale_callback,
|
|
&config.template_weight);
|
|
gtk_widget_show (scale);
|
|
|
|
gtk_widget_show (hbox);
|
|
}
|
|
|
|
box = GTK_DIALOG(dlg)->vbox;
|
|
|
|
{
|
|
frame = gtk_frame_new("Preview");
|
|
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
|
|
gtk_container_border_width(GTK_CONTAINER(frame), 10);
|
|
gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 10);
|
|
gtk_widget_show(frame);
|
|
|
|
|
|
preview = gtk_preview_new (GTK_PREVIEW_COLOR);
|
|
gtk_preview_size (GTK_PREVIEW (preview), preview_size, preview_size);
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), preview);
|
|
|
|
gtk_widget_show (preview);
|
|
}
|
|
|
|
gtk_widget_show(dlg);
|
|
gtk_main();
|
|
gdk_flush();
|
|
|
|
return run_flag;
|
|
}
|