gimp/plug-ins/stereogram.c

587 lines
14 KiB
C

/* Stereogram 0.5 --- image filter plug-in for The Gimp
* Copyright (C) 1997 Francisco Bustamante
* pointnclick@geocities.com
* http://www.geocities.com/SiliconValley/Lakes/4813/index.html
*
* 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.
*/
/* This plug-in based on information from the Stereogram FAQ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#define SIRDS 1
#define SIS 2
#define TILE_CACHE_SIZE 16
typedef struct {
gint run;
} StereogramInterface;
static StereogramInterface sint = {
FALSE
};
static void query (void);
static void run (char *name,
int nparams,
GParam *param,
int *nreturn_vals,
GParam **return_vals);
static gint create_stereogram(GDrawable *heightdrawable,
guchar *pixels,
GDrawable *patterndrawable,
int type);
static gint stereogram_dialog(void);
static void stereogram_close_callback(GtkWidget *widget,
gpointer data);
static void stereogram_ok_callback(GtkWidget *widget,
gpointer data);
static void stereogram_drawable_callback (gint32 id,
gpointer data);
static void stereogram_toggle_update(GtkWidget *widget,
gpointer data);
static gint stereogram_constrain (gint32 image_id,
gint32 drawable_id,
gpointer data);
typedef struct {
int t; /* Stereogram type */
gint32 patID; /* Pattern Drawable ID*/
} StereogramVals;
static StereogramVals svals =
{
SIRDS, /* type */
-1 /* patID */
};
GPlugInInfo PLUG_IN_INFO =
{
NULL, /*init_proc*/
NULL, /*quit_proc*/
query, /*query_proc*/
run, /*run_proc*/
};
MAIN()
static void
query(void)
{
static GParamDef args[] =
{
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
{ PARAM_IMAGE, "image", "Input Image" },
{ PARAM_DRAWABLE, "heightmap", "Height Map" },
{ PARAM_INT32, "type", "Type of stereogram" },
{ PARAM_DRAWABLE, "pat", "Pattern to use for stereogram" },
};
static GParamDef *return_vals = NULL;
static int nargs = sizeof(args) / sizeof(args[0]);
static int nreturn_vals = 0;
gimp_install_procedure("plug_in_stereogram",
"Creates SIS (Single Image Stereograms) and SIRDS (Single Image Random Dot Stereogram)",
"Based on the Stereogram FAQ",
"Francisco Bustamante",
"Francisco Bustamante",
"1997",
"<Image>/Filters/Render/Stereogram",
"GRAY",
PROC_PLUG_IN,
nargs, nreturn_vals,
args, return_vals);
} /*query*/
static void
run (char *name,
int nparams,
GParam *param,
int *nreturn_vals,
GParam **return_vals)
{
GRunModeType run_mode;
static GParam values[1];
GDrawable *heightdrawable;
GDrawable *patterndrawable;
GDrawable *destdrawable;
GStatusType status = STATUS_SUCCESS;
GPixelRgn destrgn;
guchar *dest_pixels;
static gint32 DestImageID;
gint32 layerID;
run_mode = param[0].data.d_int32;
*nreturn_vals = 1;
*return_vals = values;
values[0].type = PARAM_STATUS;
values[0].data.d_status = status;
switch(run_mode) {
case RUN_INTERACTIVE :
/*Retrieve data */
gimp_get_data("plug_in_stereogram", &svals);
if(!stereogram_dialog())
return;
break;
case RUN_NONINTERACTIVE :
if((nparams < 4) || ((nparams != 5) && (param[3].data.d_int32 == SIS)))
status = STATUS_CALLING_ERROR;
if (status == STATUS_SUCCESS) {
svals.t = param[3].data.d_int32;
if(nparams == 5)
svals.patID = param[4].data.d_drawable;
}
case RUN_WITH_LAST_VALS :
/* Retrieve data */
gimp_get_data("plug_in_stereogram", &svals);
break;
default:
break;
}
heightdrawable = gimp_drawable_get(param[2].data.d_drawable);
DestImageID = gimp_image_new(heightdrawable->width, heightdrawable->height, RGB);
layerID = gimp_layer_new(DestImageID, "Background", heightdrawable->width,
heightdrawable->height,
RGB, 100, NORMAL_MODE);
gimp_image_add_layer(DestImageID, layerID, 0);
destdrawable = gimp_drawable_get(layerID);
dest_pixels = (guchar *) g_malloc((destdrawable->width) * (destdrawable->height) * 3);
if(status == STATUS_SUCCESS) {
if(gimp_drawable_gray(param[2].data.d_drawable)) {
gimp_progress_init("Stereogramming...");
gimp_tile_cache_ntiles(TILE_CACHE_SIZE);
patterndrawable = NULL;
if(svals.patID != -1)
patterndrawable = gimp_drawable_get(svals.patID);
create_stereogram(heightdrawable, dest_pixels,
patterndrawable, svals.t);
if(patterndrawable)
gimp_drawable_detach(patterndrawable);
if(run_mode == RUN_INTERACTIVE)
gimp_set_data("plug_in_stereogram", &svals, sizeof(StereogramVals));
}
}
gimp_pixel_rgn_init(&destrgn, destdrawable, 0, 0,
destdrawable->width,
destdrawable->height,
FALSE, FALSE);
gimp_pixel_rgn_set_rect(&destrgn, dest_pixels, 0, 0,
destdrawable->width,
destdrawable->height);
gimp_drawable_detach(destdrawable);
gimp_drawable_detach(heightdrawable);
gimp_display_new(DestImageID);
values[0].data.d_status = status;
}
guchar red[6] = {0, 170, 20, 60, 135, 255};
guchar green[6] = {0, 32, 56, 153, 23, 255};
guchar blue[6] = {0, 200, 140, 40, 100, 255};
static void
set_to_zeros(gint *buf, gint len)
{
gint i;
for( i = 0; i < len; i ++) {
buf[i] = -1;
}
}
static void
pixel_set(gint * buf, gint position, gint value)
{
buf[position] = value;
}
static gint
is_pixel_set(gint *buf, gint position)
{
if(buf[position] != -1)
return TRUE;
else
return FALSE;
}
static void
put_pixel_in_blank(guchar *pattern, gint pat_width,
gint pat_height, guchar *dest,
gint offset, gint dest_line, gint *set)
{
guchar *temp;
guchar *temp_pat;
gint pat_x, pat_y;
gint tempx;
tempx = offset;
while((set[tempx] == -1) && (tempx > 0))
tempx--;
if(tempx == 0) {
set[tempx] = 0;
pat_x = set[tempx];
pat_y = dest_line % pat_height;
temp = dest + tempx * 3;
temp_pat = pattern + (pat_y * pat_width * 3) + (pat_x * 3);
*temp++ = *temp_pat++;
*temp++ = *temp_pat++;
*temp = *temp_pat;
tempx++;
}
tempx ++;
while(tempx <= offset) {
set[tempx] = ((set[tempx - 1] + 1) % pat_width);
pat_x = set[tempx];
pat_y = dest_line % pat_height;
temp = dest + tempx * 3;
temp_pat = pattern + (pat_y * pat_width * 3) + (pat_x * 3);
*temp++ = *temp_pat++;
*temp++ = *temp_pat++;
*temp = *temp_pat;
tempx++;
}
}
void
line_fill(guchar *pattern, gint pat_width,
gint pat_height, guchar *dest,
gint dest_line, gint *set,
gint line_width)
{
gint cur_x;
for(cur_x = 0; cur_x < line_width; cur_x ++) {
if(set[cur_x] == -1) {
put_pixel_in_blank(pattern, pat_width,
pat_height, dest,
cur_x, dest_line, set);
}
}
}
static gint
create_stereogram(GDrawable *heightdrawable,
guchar *pixels,
GDrawable *patterndrawable,
int type)
{
int E;
int dist_to_screen;
GPixelRgn patrgn;
GPixelRgn hgtrgn;
gint src_height, src_width;
gint pat_height, pat_width;
gint dest_height, dest_width;
guchar *pattern = NULL;
guchar *final_line;
guchar *temp;
guchar *source_line;
guchar *temp_source;
gint cur_progress = 0, max_progress = heightdrawable->height;
gint cur_x = 0, cur_y = 0, curpat_y = 0;
gint separation;
int i, left, right;
gint *set;
dest_width = src_width = heightdrawable->width;
dest_height = src_height = heightdrawable->height;
source_line = (guchar *) g_malloc(heightdrawable->width);
gimp_pixel_rgn_init(&hgtrgn, heightdrawable, 0, 0,
src_width,
src_height,
FALSE, FALSE);
set = (gint *) g_malloc(dest_width * sizeof(gint));
if(svals.t == SIS) {
pat_width = patterndrawable->width;
pat_height = patterndrawable->height;
gimp_pixel_rgn_init(&patrgn, patterndrawable, 0, 0,
pat_width,
pat_height,
FALSE, FALSE);
pattern = (guchar *) g_malloc(pat_height * pat_width * 3);
gimp_pixel_rgn_get_rect(&patrgn, pattern, 0, 0, pat_width, pat_height);
}
else {
pat_width = 80;
pat_height = 80;
pattern = (guchar *) g_malloc(pat_height * pat_width * 3);
for(cur_y = 0; cur_y < pat_height; cur_y++) {
for(cur_x = 0; cur_x < pat_width; cur_x++) {
temp = pattern + ((cur_y * pat_width) + cur_x) * 3;
i = rand() % 6;
*temp++ = red[i];
*temp++ = green[i];
*temp = blue[i];
}
}
}
E = 192;
dist_to_screen = 900;
curpat_y = 0;
for(cur_y = 0; cur_y < src_height; cur_y++) {
set_to_zeros(set, dest_width);
gimp_pixel_rgn_get_row(&hgtrgn, source_line, 0, cur_y, src_width);
final_line = pixels + (cur_y * dest_width * 3);
for(cur_x = 0; cur_x < src_width; cur_x++) {
temp_source = source_line + cur_x;
separation = ((800 - *temp_source) * E) / ( dist_to_screen + (800 - *temp_source));
left = cur_x - (separation / 2);
right = left + separation;
if((right < dest_width) && (left >= 0)) {
if(is_pixel_set(set, left) == FALSE) {
put_pixel_in_blank(pattern, pat_width,
pat_height, final_line,
left, cur_y, set);
}
temp = final_line + left * 3;
*(final_line + (right * 3)) = *temp;
*(final_line + (right * 3 + 1)) = *++temp;
*(final_line + (right * 3 + 2)) = *++temp;
pixel_set(set, right, set[left]);
}
}
line_fill(pattern, pat_width,
pat_height, final_line,
cur_y, set,
src_width);
cur_progress++;
if((cur_progress % 10) == 0) {
gimp_progress_update((gdouble) cur_progress / (gdouble) max_progress);
}
}
if(pattern)
g_free(pattern);
return 1;
} /*create_stereogram*/
static gint
stereogram_dialog(void)
{
GtkWidget *dlg;
GtkWidget *label;
GtkWidget *optionmenu;
GtkWidget *menu;
GtkWidget *button;
GtkWidget *table;
GtkWidget *frame;
GtkWidget *toggle;
GtkWidget *toggle_hbox;
GSList *group = NULL;
gchar **argv;
gint argc;
gint sis = (svals.t == SIS);
gint sirds = (svals.t == SIRDS);
argc = 1;
argv = g_new(gchar *, 1);
argv[0] = g_strdup("stereogram");
gtk_init(&argc, &argv);
dlg = gtk_dialog_new();
gtk_window_set_title(GTK_WINDOW(dlg), "Stereogram");
gtk_window_position(GTK_WINDOW(dlg), GTK_WIN_POS_MOUSE);
gtk_signal_connect(GTK_OBJECT(dlg), "destroy",
(GtkSignalFunc) stereogram_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) stereogram_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) stereogram_close_callback,
GTK_OBJECT(dlg));
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), button, TRUE, TRUE, 0);
gtk_widget_show(button);
frame = gtk_frame_new("Stereogram Options");
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(GTK_DIALOG(dlg)->vbox), frame, TRUE, TRUE, 0);
table = gtk_table_new(3, 8, FALSE);
gtk_container_border_width(GTK_CONTAINER(table), 5);
gtk_container_add(GTK_CONTAINER(frame), table);
gtk_table_set_row_spacing (GTK_TABLE (table), 0, 10);
gtk_table_set_row_spacing (GTK_TABLE (table), 1, 10);
gtk_table_set_col_spacing (GTK_TABLE (table), 0, 10);
gtk_table_set_col_spacing (GTK_TABLE (table), 1, 10);
label = gtk_label_new("Stereogram Pattern: ");
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
optionmenu = gtk_option_menu_new();
menu = gimp_drawable_menu_new(stereogram_constrain,
stereogram_drawable_callback,
NULL, svals.patID);
gtk_option_menu_set_menu(GTK_OPTION_MENU(optionmenu), menu);
gtk_table_attach(GTK_TABLE(table), optionmenu, 1, 3, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show(optionmenu);
toggle_hbox = gtk_hbox_new(FALSE, 10);
gtk_container_border_width(GTK_CONTAINER(toggle_hbox), 5);
gtk_table_attach(GTK_TABLE(table), toggle_hbox, 0, 3, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
label = gtk_label_new("Stereogram type: ");
gtk_box_pack_start(GTK_BOX(toggle_hbox), label, FALSE, FALSE, 0);
gtk_widget_show(label);
toggle = gtk_radio_button_new_with_label(group, "SIS");
group = gtk_radio_button_group(GTK_RADIO_BUTTON(toggle));
gtk_box_pack_start(GTK_BOX(toggle_hbox), toggle, FALSE, FALSE, 0);
gtk_signal_connect(GTK_OBJECT(toggle), "toggled",
(GtkSignalFunc) stereogram_toggle_update,
&sis);
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), sis);
gtk_widget_show(toggle);
toggle = gtk_radio_button_new_with_label(group, "SIRDS");
group = gtk_radio_button_group(GTK_RADIO_BUTTON(toggle));
gtk_box_pack_start(GTK_BOX(toggle_hbox), toggle, FALSE, FALSE, 0);
gtk_signal_connect(GTK_OBJECT(toggle), "toggled",
(GtkSignalFunc) stereogram_toggle_update,
&sirds);
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), sirds);
gtk_widget_show(toggle);
gtk_widget_show(toggle_hbox);
gtk_widget_show(table);
gtk_widget_show(frame);
gtk_widget_show(dlg);
gtk_main();
gdk_flush();
if(sis)
svals.t = SIS;
else
svals.t = SIRDS;
return sint.run;
}
static void
stereogram_close_callback(GtkWidget *widget,
gpointer data)
{
gtk_main_quit();
}
static void
stereogram_ok_callback(GtkWidget *widget,
gpointer data)
{
sint.run = TRUE;
gtk_widget_destroy(GTK_WIDGET(data));
}
static void
stereogram_toggle_update(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 gint
stereogram_constrain (gint32 image_id,
gint32 drawable_id,
gpointer data)
{
if (drawable_id == -1)
return TRUE;
return (gimp_drawable_color (drawable_id));
}
static void
stereogram_drawable_callback (gint32 id,
gpointer data)
{
svals.patID = id;
}