You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
518 lines
12 KiB
518 lines
12 KiB
#include <gtk/gtk.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include "wetpix.h"
|
|
#include "wetpaint.h"
|
|
#include "wettexture.h"
|
|
#include "wetphysics.h"
|
|
WetPack *pack; /* The global wet pack */
|
|
|
|
double lastx, lasty;
|
|
double dist;
|
|
double spacing = 2;
|
|
|
|
WetPix paint = { 707, 0, 707, 0, 707, 0, 240, 0 };
|
|
|
|
/* colors from Curtis et al, Siggraph 97 */
|
|
|
|
WetPix paintbox[] = {
|
|
{496, 0, 16992, 0, 3808, 0, 0, 0},
|
|
{16992, 9744, 21712, 6400, 25024, 3296, 0, 0},
|
|
{6512, 6512, 6512, 4880, 11312, 0, 0, 0},
|
|
{16002, 0, 2848, 0, 16992, 0, 0, 0},
|
|
{22672, 0, 5328, 2272, 4288, 2640, 0, 0},
|
|
{8000, 0, 16992, 0, 28352, 0, 0, 0},
|
|
{5696, 5696, 12416, 2496, 28352, 0, 0, 0},
|
|
{0, 0, 5136, 0, 28352, 0, 0, 0},
|
|
{2320, 1760, 7344, 4656, 28352, 0, 0, 0},
|
|
{8000, 0, 3312, 0, 5504, 0, 0, 0},
|
|
{13680, 0, 16992, 0, 3312, 0, 0, 0},
|
|
{5264, 5136, 1056, 544, 6448, 6304, 0, 0},
|
|
{11440, 11440, 11440, 11440, 11440, 11440, 0, 0},
|
|
{11312, 0, 11312, 0, 11312, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0}
|
|
};
|
|
|
|
int n_paints = 15;
|
|
|
|
char *paintstr = "select paint";
|
|
|
|
GtkWidget *paintname;
|
|
|
|
char *paintnames[] = {
|
|
"Quinacridone Rose",
|
|
"Indian Red",
|
|
"Cadmium Yellow",
|
|
"Hookers Green",
|
|
"Cerulean Blue",
|
|
"Burnt Umber",
|
|
"Cadmium Red",
|
|
"Brilliant Orange",
|
|
"Hansa Yellow",
|
|
"Phthalo Green",
|
|
"French Ultramarine",
|
|
"Interference Lilac",
|
|
"Titanium White",
|
|
"Ivory Black",
|
|
"Pure Water"
|
|
};
|
|
|
|
GtkWidget *autodryb;
|
|
int timo = 0;
|
|
int adsorb_cnt;
|
|
|
|
GtkObject *brushsize_adjust;
|
|
|
|
GtkObject *wetness_adjust;
|
|
|
|
GtkObject *strength_adjust;
|
|
|
|
static void stop_drying(void)
|
|
{
|
|
timo = 0;
|
|
gtk_label_set_text(GTK_LABEL(paintname), paintstr);
|
|
}
|
|
|
|
static double strength_func(double strength, double pressure)
|
|
{
|
|
return strength * (strength + pressure) * 0.5;
|
|
}
|
|
|
|
static gint wet_button_press(GtkWidget * widget, GdkEventButton * event)
|
|
{
|
|
#define noVERBOSE
|
|
#ifdef VERBOSE
|
|
g_print("button press %f %f %f\n", event->x, event->y,
|
|
event->pressure);
|
|
|
|
#endif
|
|
wet_dab(pack->layers[1],
|
|
&paint,
|
|
event->x,
|
|
event->y,
|
|
((GtkAdjustment *) brushsize_adjust)->value *
|
|
event->pressure, 0.75 + 0.25 * event->pressure,
|
|
strength_func(((GtkAdjustment *) strength_adjust)->value,
|
|
event->pressure));
|
|
|
|
lastx = event->x;
|
|
lasty = event->y;
|
|
dist = 0;
|
|
|
|
stop_drying();
|
|
|
|
gtk_widget_queue_draw(widget);
|
|
return TRUE;
|
|
}
|
|
|
|
static gint wet_motion(GtkWidget * widget, GdkEventMotion * event)
|
|
{
|
|
double delta;
|
|
#ifdef VERBOSE
|
|
g_print("motion %f %f %f %d\n", event->x, event->y,
|
|
event->pressure, event->state);
|
|
|
|
#endif
|
|
stop_drying();
|
|
|
|
if (!(event->state & 256))
|
|
return TRUE;
|
|
|
|
delta = sqrt((event->x - lastx) * (event->x - lastx) +
|
|
(event->y - lasty) * (event->y - lasty));
|
|
|
|
dist += delta;
|
|
|
|
if (dist >= spacing) {
|
|
/* todo: interpolate position and pressure of the dab */
|
|
wet_dab(pack->layers[1],
|
|
&paint,
|
|
event->x,
|
|
event->y,
|
|
((GtkAdjustment *) brushsize_adjust)->value *
|
|
event->pressure, 0.75 + 0.25 * event->pressure,
|
|
strength_func(((GtkAdjustment *) strength_adjust)->
|
|
value, event->pressure));
|
|
gtk_widget_queue_draw(widget);
|
|
dist -= spacing;
|
|
}
|
|
|
|
lastx = event->x;
|
|
lasty = event->y;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void dry(GtkWidget * da)
|
|
{
|
|
g_print("drying...");
|
|
gtk_label_set_text(GTK_LABEL(paintname), "drying...");
|
|
gtk_widget_draw(paintname, NULL);
|
|
gdk_flush();
|
|
wet_flow(pack->layers[1]);
|
|
adsorb_cnt++;
|
|
if (adsorb_cnt == 2) {
|
|
wet_adsorb(pack->layers[1], pack->layers[0]);
|
|
wet_dry(pack->layers[1]);
|
|
adsorb_cnt = 0;
|
|
}
|
|
|
|
gtk_widget_draw(da, NULL);
|
|
#if 0
|
|
gtk_label_set_text(GTK_LABEL(paintname), paintstr);
|
|
#endif
|
|
g_print("done\n");
|
|
}
|
|
|
|
static gint wet_dry_button_press(GtkWidget * widget, GtkWidget * da)
|
|
{
|
|
dry(da);
|
|
|
|
timo = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gint clear_button_press(GtkWidget * widget, GtkWidget * da)
|
|
{
|
|
wet_layer_clear(pack->layers[0]);
|
|
wet_layer_clone_texture(pack->layers[0], pack->layers[1]);
|
|
wet_layer_clear(pack->layers[1]);
|
|
wet_layer_clone_texture(pack->layers[1], pack->layers[0]);
|
|
|
|
gtk_widget_draw(da, NULL);
|
|
|
|
stop_drying();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gint dry_timer(gpointer * dummy)
|
|
{
|
|
GtkWidget *da = (GtkWidget *) dummy;
|
|
|
|
timo++;
|
|
if (timo >= 10) {
|
|
if (gtk_toggle_button_get_active
|
|
(GTK_TOGGLE_BUTTON(autodryb))) {
|
|
dry(da);
|
|
}
|
|
|
|
timo -= 2;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gint
|
|
wet_expose(GtkWidget * widget, GdkEventExpose * event, WetPack * pack)
|
|
{
|
|
byte *rgb;
|
|
int rowstride;
|
|
|
|
#ifdef VERBOSE
|
|
g_print("expose: %d layers\n", pack->n_layers);
|
|
#endif
|
|
|
|
rowstride = event->area.width * 3;
|
|
rowstride = (rowstride + 3) & -4; /* align to 4-byte boundary */
|
|
rgb = g_new(byte, event->area.height * rowstride);
|
|
|
|
wet_pack_render(rgb, rowstride,
|
|
pack,
|
|
event->area.x, event->area.y,
|
|
event->area.width, event->area.height);
|
|
|
|
gdk_draw_rgb_image(widget->window,
|
|
widget->style->black_gc,
|
|
event->area.x, event->area.y,
|
|
event->area.width, event->area.height,
|
|
GDK_RGB_DITHER_MAX, rgb, rowstride);
|
|
|
|
g_free(rgb);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void init_input(void)
|
|
{
|
|
GList *tmp_list;
|
|
GdkDeviceInfo *info;
|
|
|
|
tmp_list = gdk_input_list_devices();
|
|
|
|
info = NULL;
|
|
while (tmp_list) {
|
|
info = (GdkDeviceInfo *) tmp_list->data;
|
|
#ifdef VERBOSE
|
|
g_print("device: %s\n", info->name);
|
|
#endif
|
|
if (!g_strcasecmp(info->name, "wacom") ||
|
|
!g_strcasecmp(info->name, "stylus") ||
|
|
!g_strcasecmp(info->name, "eraser")) {
|
|
gdk_input_set_mode(info->deviceid,
|
|
GDK_MODE_SCREEN);
|
|
}
|
|
tmp_list = tmp_list->next;
|
|
}
|
|
if (!info)
|
|
return;
|
|
}
|
|
|
|
static gint
|
|
pselect_expose(GtkWidget * widget, GdkEventExpose * event, WetPack * pack)
|
|
{
|
|
byte *rgb;
|
|
int x;
|
|
int paint_quad, paint_num;
|
|
int last_pn;
|
|
int bg;
|
|
|
|
#ifdef VERBOSE
|
|
g_print("expose: %d layers\n", pack->n_layers);
|
|
#endif
|
|
|
|
rgb = g_new(byte, pack->layers[0]->width * 3);
|
|
|
|
last_pn = 0;
|
|
for (x = 0; x < pack->layers[0]->width; x++) {
|
|
paint_quad =
|
|
floor(4 * x * n_paints / pack->layers[0]->width + 0.5);
|
|
paint_num = paint_quad >> 2;
|
|
if (last_pn != paint_num) {
|
|
rgb[x * 3] = 255;
|
|
rgb[x * 3 + 1] = 255;
|
|
rgb[x * 3 + 2] = 255;
|
|
last_pn = paint_num;
|
|
} else {
|
|
if ((paint_quad & 3) > 0 && (paint_quad & 3) < 3)
|
|
bg = 0;
|
|
else
|
|
bg = 255;
|
|
rgb[x * 3] = bg;
|
|
rgb[x * 3 + 1] = bg;
|
|
rgb[x * 3 + 2] = bg;
|
|
wet_composite(&rgb[x * 3], 0, &paintbox[paint_num],
|
|
0, 1, 1);
|
|
}
|
|
}
|
|
|
|
gdk_draw_rgb_image(widget->window,
|
|
widget->style->black_gc,
|
|
event->area.x, event->area.y,
|
|
event->area.width, event->area.height,
|
|
GDK_RGB_DITHER_MAX,
|
|
rgb + (event->area.x) * 3, 0);
|
|
|
|
g_free(rgb);
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
pselect_button_press(GtkWidget * widget, GdkEventButton * event)
|
|
{
|
|
int paint_num;
|
|
int wet;
|
|
|
|
#ifdef VERBOSE
|
|
g_print("pselect button press %f %f %f\n", event->x, event->y,
|
|
event->pressure);
|
|
|
|
#endif
|
|
paint_num = floor((event->x * n_paints) / pack->layers[0]->width);
|
|
|
|
/* preserve wetness */
|
|
wet = paint.w;
|
|
paint = paintbox[paint_num];
|
|
paint.w = wet;
|
|
paintstr = paintnames[paint_num];
|
|
/*
|
|
gtk_adjustment_set_value (GTK_ADJUSTMENT (wetness_adjust), paint.w);
|
|
*/
|
|
gtk_label_set_text(GTK_LABEL(paintname), paintstr);
|
|
|
|
stop_drying();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void wetness_update(GtkAdjustment * adj, gpointer data)
|
|
{
|
|
paint.w = floor(15 * adj->value + 0.5);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
GtkWidget *w;
|
|
GtkWidget *v;
|
|
GtkWidget *eb;
|
|
GtkWidget *da;
|
|
GtkWidget *peb;
|
|
GtkWidget *pda;
|
|
GtkWidget *h;
|
|
GtkWidget *b;
|
|
GtkWidget *db;
|
|
GtkWidget *h2;
|
|
GtkWidget *l;
|
|
GtkWidget *brushsize;
|
|
GtkWidget *wetness;
|
|
GtkWidget *strength;
|
|
int xs = 512;
|
|
int ys = 512;
|
|
|
|
gtk_init(&argc, &argv);
|
|
|
|
if (argc >= 3) {
|
|
xs = atoi(argv[1]);
|
|
ys = atoi(argv[2]);
|
|
if (xs == 0)
|
|
xs = 512;
|
|
if (ys == 0)
|
|
ys = 512;
|
|
}
|
|
|
|
|
|
init_input();
|
|
|
|
gdk_rgb_init();
|
|
|
|
gtk_widget_set_default_colormap(gdk_rgb_get_cmap());
|
|
gtk_widget_set_default_visual(gdk_rgb_get_visual());
|
|
|
|
pack = wet_pack_new(xs, ys);
|
|
|
|
wet_pack_maketexture(pack, 1, 0.7, 0.5);
|
|
|
|
w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_signal_connect(GTK_OBJECT(w), "destroy",
|
|
(GtkSignalFunc) gtk_main_quit, NULL);
|
|
|
|
v = gtk_vbox_new(FALSE, 2);
|
|
gtk_container_add(GTK_CONTAINER(w), v);
|
|
gtk_widget_show(v);
|
|
|
|
eb = gtk_event_box_new();
|
|
gtk_container_add(GTK_CONTAINER(v), eb);
|
|
gtk_widget_show(eb);
|
|
|
|
gtk_widget_set_extension_events(eb, GDK_EXTENSION_EVENTS_ALL);
|
|
|
|
gtk_widget_set_events(eb, GDK_EXPOSURE_MASK
|
|
| GDK_LEAVE_NOTIFY_MASK
|
|
| GDK_BUTTON_PRESS_MASK
|
|
| GDK_KEY_PRESS_MASK
|
|
| GDK_POINTER_MOTION_MASK
|
|
| GDK_PROXIMITY_OUT_MASK);
|
|
|
|
gtk_signal_connect(GTK_OBJECT(eb), "button_press_event",
|
|
(GtkSignalFunc) wet_button_press, NULL);
|
|
gtk_signal_connect(GTK_OBJECT(eb), "motion_notify_event",
|
|
(GtkSignalFunc) wet_motion, NULL);
|
|
|
|
da = gtk_drawing_area_new();
|
|
gtk_drawing_area_size(GTK_DRAWING_AREA(da), xs, ys);
|
|
gtk_container_add(GTK_CONTAINER(eb), da);
|
|
gtk_widget_show(da);
|
|
|
|
gtk_signal_connect(GTK_OBJECT(da), "expose_event",
|
|
(GtkSignalFunc) wet_expose, pack);
|
|
|
|
peb = gtk_event_box_new();
|
|
gtk_container_add(GTK_CONTAINER(v), peb);
|
|
gtk_widget_show(peb);
|
|
|
|
gtk_widget_set_extension_events(peb, GDK_EXTENSION_EVENTS_ALL);
|
|
|
|
gtk_widget_set_events(peb, GDK_EXPOSURE_MASK
|
|
| GDK_LEAVE_NOTIFY_MASK
|
|
| GDK_BUTTON_PRESS_MASK
|
|
| GDK_KEY_PRESS_MASK
|
|
| GDK_PROXIMITY_OUT_MASK);
|
|
|
|
gtk_signal_connect(GTK_OBJECT(peb), "button_press_event",
|
|
(GtkSignalFunc) pselect_button_press, NULL);
|
|
|
|
pda = gtk_drawing_area_new();
|
|
gtk_drawing_area_size(GTK_DRAWING_AREA(pda), xs, 16);
|
|
gtk_container_add(GTK_CONTAINER(peb), pda);
|
|
gtk_widget_show(pda);
|
|
|
|
gtk_signal_connect(GTK_OBJECT(pda), "expose_event",
|
|
(GtkSignalFunc) pselect_expose, pack);
|
|
|
|
paintname = gtk_label_new(paintstr);
|
|
gtk_container_add(GTK_CONTAINER(v), paintname);
|
|
gtk_widget_show(paintname);
|
|
|
|
h = gtk_hbox_new(TRUE, 5);
|
|
gtk_container_add(GTK_CONTAINER(v), h);
|
|
gtk_widget_show(h);
|
|
|
|
b = gtk_button_new_with_label("Dry");
|
|
gtk_container_add(GTK_CONTAINER(h), b);
|
|
gtk_widget_show(b);
|
|
|
|
gtk_signal_connect(GTK_OBJECT(b), "clicked",
|
|
(GtkSignalFunc) wet_dry_button_press, da);
|
|
|
|
autodryb = gtk_toggle_button_new_with_label("Auto Dry");
|
|
gtk_container_add(GTK_CONTAINER(h), autodryb);
|
|
gtk_widget_show(autodryb);
|
|
|
|
db = gtk_button_new_with_label("Clear");
|
|
gtk_container_add(GTK_CONTAINER(h), db);
|
|
gtk_widget_show(db);
|
|
|
|
gtk_signal_connect(GTK_OBJECT(db), "clicked",
|
|
(GtkSignalFunc) clear_button_press, da);
|
|
|
|
h2 = gtk_hbox_new(FALSE, 5);
|
|
gtk_container_add(GTK_CONTAINER(v), h2);
|
|
gtk_widget_show(h2);
|
|
|
|
l = gtk_label_new("Brush size: ");
|
|
gtk_container_add(GTK_CONTAINER(h2), l);
|
|
gtk_widget_show(l);
|
|
|
|
brushsize_adjust = gtk_adjustment_new(10, 0, 32, 0.1, 0.1, 0);
|
|
brushsize = gtk_hscale_new(GTK_ADJUSTMENT(brushsize_adjust));
|
|
gtk_container_add(GTK_CONTAINER(h2), brushsize);
|
|
gtk_widget_show(brushsize);
|
|
|
|
h2 = gtk_hbox_new(FALSE, 5);
|
|
gtk_container_add(GTK_CONTAINER(v), h2);
|
|
gtk_widget_show(h2);
|
|
|
|
l = gtk_label_new("Wetness: ");
|
|
gtk_container_add(GTK_CONTAINER(h2), l);
|
|
gtk_widget_show(l);
|
|
|
|
wetness_adjust = gtk_adjustment_new(16, 0, 16, 1.0, 1.0, 0);
|
|
wetness = gtk_hscale_new(GTK_ADJUSTMENT(wetness_adjust));
|
|
gtk_container_add(GTK_CONTAINER(h2), wetness);
|
|
gtk_widget_show(wetness);
|
|
gtk_signal_connect(GTK_OBJECT(wetness_adjust), "value_changed",
|
|
(GtkSignalFunc) wetness_update, NULL);
|
|
|
|
h2 = gtk_hbox_new(FALSE, 5);
|
|
gtk_container_add(GTK_CONTAINER(v), h2);
|
|
gtk_widget_show(h2);
|
|
|
|
l = gtk_label_new("Strength: ");
|
|
gtk_container_add(GTK_CONTAINER(h2), l);
|
|
gtk_widget_show(l);
|
|
|
|
strength_adjust = gtk_adjustment_new(1, 0, 2, 0.1, 0.1, 0);
|
|
strength = gtk_hscale_new(GTK_ADJUSTMENT(strength_adjust));
|
|
gtk_scale_set_digits(GTK_SCALE(strength), 2);
|
|
gtk_container_add(GTK_CONTAINER(h2), strength);
|
|
gtk_widget_show(strength);
|
|
|
|
gtk_widget_show(w);
|
|
|
|
gtk_timeout_add(50, (GtkFunction) dry_timer, da);
|
|
|
|
gtk_main();
|
|
|
|
return 0;
|
|
}
|