gimp/plug-ins/lighting/lighting-preview.c

526 lines
14 KiB
C
Raw Permalink Normal View History

1998-06-11 14:04:59 +08:00
/*************************************************/
/* Compute a preview image and preview wireframe */
/*************************************************/
#include "config.h"
#include <gtk/gtk.h>
#include <libgimp/gimp.h>
#include <libgimpmath/gimpmath.h>
#include "lighting-main.h"
#include "lighting-ui.h"
#include "lighting-image.h"
#include "lighting-apply.h"
#include "lighting-shade.h"
#include "lighting-preview.h"
1998-06-11 14:04:59 +08:00
#define LIGHT_SYMBOL_SIZE 8
static gint handle_xpos = 0, handle_ypos = 0;
/* g_free()'ed on exit */
gdouble *xpostab = NULL;
gdouble *ypostab = NULL;
1998-06-11 14:04:59 +08:00
static gint xpostab_size = -1; /* if preview size change, do realloc */
static gint ypostab_size = -1;
applied gimp-lecorfec-99041[02]-0, changes follow * applied gimp-lecorfec-99041[02]-0, changes follow * plug-ins/FractalExplorer/Dialogs.h (make_color_map): replaced free with g_free to fix segfault. * plug-ins/Lighting/lighting_preview.c (compute_preview): allocate xpostab and ypostab only when needed (it could also be allocated on stack with a compilation-fixed size like MapObject). It avoids to lose some Kb on each preview :) Also reindented (unfortunate C-c C-q) some other lines. * plug-ins/Lighting/lighting_main.c (run): release allocated postabs. * plug-ins/Lighting/lighting_ui.c: callbacks now have only one argument because gck widget use gtk_signal_connect_object. Caused segfault for scale widget. * plug-ins/autocrop/autocrop.c (doit): return if image has only background (thus fixing a segfault). * plug-ins/emboss/emboss.c (pluginCore, emboss_do_preview): replaced malloc/free with g_malloc/g_free (unneeded, but shouldn't everyone use glib calls ? :) * plug-ins/flame/flame.c : replaced a segfaulting free, and several harmless malloc/free pairs. * plug-ins/flame/megawidget.c (mw_preview_build): replaced harmless malloc/free pair. Note : mwp->bits is malloc'ed but seems to be never freed. * plug-ins/fractaltrace/fractaltrace.c (pixels_free): replaced a bunch of segfaulting free. (pixels_get, dialog_show): replaced gtk_signal_connect_object with gtk_signal_connect to accomodate callbacks (caused STRANGE dialog behaviour, coz you destroyed buttons one by one). * plug-ins/illusion/illusion.c (dialog): same gtk_signal_connect_object replacement for same reasons. * plug-ins/libgck/gck/gckcolor.c : changed all gck_rgb_to_color* functions to use a static GdkColor instead of a malloc'ed area. Provided reentrant functions with the old behaviour (gck_rgb_to_color*_r). Made some private functions static, too. gck_rgb_to_gdkcolor now use the new functions while gck_rgb_to_gdkcolor_r is the reentrant version. Also affected by this change: gck_gc_set_foreground and gck_gc_set_background (no more free(color)). * plug-ins/libgck/gck/gckcolor.h : added the gck_rgb_to_gdkcolor_r proto. * plug-ins/lic/lic.c (ok_button_clicked, cancel_button_clicked) : segfault on gtk_widget_destroy, now calls gtk_main_quit. (dialog_destroy) : segfault on window closure when called by "destroy" event. Now called by "delete_event". * plug-ins/megawidget/megawidget.c (mw_preview_build): replaced harmless malloc/free pair. Note : mwp->bits is malloc'ed but seems to be never freed. * plug-ins/png/png.c (load_image): replaced 2 segfaulting free. * plug-ins/print/print-ps.c (ps_print): replaced a segfaulting free (called many times :). * plug-ins/sgi/sgi.c (load_image, save_image): replaced a bunch of segfaulting free, and did some harmless inits to avoid a few gcc warnings. * plug-ins/wind/wind.c (render_wind): replaced a segfaulting free. (render_blast): replaced harmless malloc/free pair. * plug-ins/bmp/bmpread.c (ReadImage): yet another free()/g_free() problem fixed. * plug-ins/exchange/exchange.c (real_exchange): ditto. * plug-ins/fp/fp.h: added Frames_Check_Button_In_A_Box proto. * plug-ins/fp/fp_gtk.c: closing subdialogs via window manager wasn't handled, thus leading to errors and crashes. Now delete_event signals the dialog control button to close a dialog with the good way. * plug-ins/ifscompose/ifscompose.c (value_pair_create): tried to set events mask on scale widget (a NO_WINDOW widget). * plug-ins/png/png.c (save_image): Replaced 2 free() with g_free() for g_malloc'ed memory. Mysteriously I corrected the loading bug but not the saving one :) -Yosh
1999-04-16 05:49:09 +08:00
static gboolean light_hit = FALSE;
static gboolean left_button_pressed = FALSE;
static guint preview_update_timer = 0;
1998-06-11 14:04:59 +08:00
/* Protos */
/* ====== */
static gboolean interactive_preview_timer_callback (gpointer data);
void composite_behind (gdouble *color1,
gdouble *color2);
1998-06-11 14:04:59 +08:00
static void
compute_preview (gint startx,
gint starty,
gint w,
gint h)
1998-06-11 14:04:59 +08:00
{
gint xcnt, ycnt, f1, f2;
guchar r, g, b;
gdouble imagex, imagey;
gint32 index = 0;
gdouble color[4];
gdouble lightcheck[4] = { GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT, 1.0 };
gdouble darkcheck[4] = { GIMP_CHECK_DARK, GIMP_CHECK_DARK, GIMP_CHECK_DARK, 1.0 };
GimpVector3 pos;
get_ray_func ray_func;
if (xpostab_size != w)
{
if (xpostab)
{
g_free (xpostab);
xpostab = NULL;
}
}
if (!xpostab)
{
xpostab = g_new (gdouble, w);
xpostab_size = w;
}
if (ypostab_size != h)
{
if (ypostab)
{
g_free (ypostab);
ypostab = NULL;
}
}
if (!ypostab)
{
ypostab = g_new (gdouble, h);
ypostab_size = h;
}
for (xcnt = 0; xcnt < w; xcnt++)
xpostab[xcnt] = (gdouble) width *((gdouble) xcnt / (gdouble) w);
for (ycnt = 0; ycnt < h; ycnt++)
ypostab[ycnt] = (gdouble) height *((gdouble) ycnt / (gdouble) h);
precompute_init (width, height);
if (mapvals.bump_mapped == TRUE && mapvals.bumpmap_id != -1)
{
bumpmap_setup (gimp_drawable_get_by_id (mapvals.bumpmap_id));
}
imagey = 0;
if (mapvals.previewquality)
ray_func = get_ray_color;
else
ray_func = get_ray_color_no_bilinear;
if (mapvals.env_mapped == TRUE && mapvals.envmap_id != -1)
{
envmap_setup (gimp_drawable_get_by_id (mapvals.envmap_id));
if (mapvals.previewquality)
ray_func = get_ray_color_ref;
else
ray_func = get_ray_color_no_bilinear_ref;
}
cairo_surface_flush (preview_surface);
for (ycnt = 0; ycnt < PREVIEW_HEIGHT; ycnt++)
{
index = ycnt * preview_rgb_stride;
for (xcnt = 0; xcnt < PREVIEW_WIDTH; xcnt++)
{
if ((ycnt >= starty && ycnt < (starty + h)) &&
(xcnt >= startx && xcnt < (startx + w)))
{
imagex = xpostab[xcnt - startx];
imagey = ypostab[ycnt - starty];
pos = int_to_posf (imagex, imagey);
if (mapvals.bump_mapped == TRUE &&
mapvals.bumpmap_id != -1 &&
xcnt == startx)
{
pos_to_float (pos.x, pos.y, &imagex, &imagey);
precompute_normals (0, width, RINT (imagey));
}
(*ray_func) (&pos, color);
if (color[3] < 1.0)
{
f1 = ((xcnt % 32) < 16);
f2 = ((ycnt % 32) < 16);
f1 = f1 ^ f2;
if (f1)
{
if (color[3] == 0.0)
{
for (gint i = 0; i < 4; i++)
color[i] = lightcheck[i];
}
else
{
composite_behind (color, lightcheck);
}
}
else
{
if (color[3] == 0.0)
{
for (gint i = 0; i < 4; i++)
color[i] = darkcheck[i];
}
else
{
composite_behind (color, darkcheck);
}
}
}
r = (guchar) (color[0] * 255);
g = (guchar) (color[1] * 255);
b = (guchar) (color[2] * 255);
GIMP_CAIRO_RGB24_SET_PIXEL((preview_rgb_data + index), r, g, b);
index += 4;
imagex++;
}
else
{
preview_rgb_data[index++] = 200;
preview_rgb_data[index++] = 200;
preview_rgb_data[index++] = 200;
index++;
}
}
}
cairo_surface_mark_dirty (preview_surface);
1998-06-11 14:04:59 +08:00
}
static void
compute_preview_rectangle (gint * xp, gint * yp, gint * wid, gint * heig)
1998-06-11 14:04:59 +08:00
{
gdouble x, y, w, h;
1998-06-11 14:04:59 +08:00
if (width >= height)
{
w = (PREVIEW_WIDTH - 50.0);
h = (gdouble) height *(w / (gdouble) width);
x = (PREVIEW_WIDTH - w) / 2.0;
y = (PREVIEW_HEIGHT - h) / 2.0;
}
else
{
h = (PREVIEW_HEIGHT - 50.0);
w = (gdouble) width *(h / (gdouble) height);
x = (PREVIEW_WIDTH - w) / 2.0;
y = (PREVIEW_HEIGHT - h) / 2.0;
}
*xp = RINT (x);
*yp = RINT (y);
*wid = RINT (w);
*heig = RINT (h);
1998-06-11 14:04:59 +08:00
}
/*************************************************/
/* Check if the given position is within the */
/* light marker. Return TRUE if so, FALSE if not */
/*************************************************/
static gboolean
check_handle_hit (gint xpos, gint ypos)
1998-06-11 14:04:59 +08:00
{
gint dx,dy,r;
gint k = mapvals.light_selected;
dx = handle_xpos - xpos;
dy = handle_ypos - ypos;
if (mapvals.lightsource[k].type == POINT_LIGHT ||
mapvals.lightsource[k].type == DIRECTIONAL_LIGHT)
{
r = sqrt (dx * dx + dy * dy) + 0.5;
if ((gint) r > 7)
{
return (FALSE);
}
else
{
return (TRUE);
}
}
return FALSE;
1998-06-11 14:04:59 +08:00
}
/****************************************/
/* Draw a light symbol */
1998-06-11 14:04:59 +08:00
/****************************************/
static void
draw_handles (cairo_t *cr)
1998-06-11 14:04:59 +08:00
{
gdouble dxpos, dypos;
gint startx, starty, pw, ph;
GimpVector3 viewpoint;
GimpVector3 light_position;
gint k = mapvals.light_selected;
gfloat length;
gfloat delta_x = 0.0;
gfloat delta_y = 0.0;
/* calculate handle position */
compute_preview_rectangle (&startx, &starty, &pw, &ph);
switch (mapvals.lightsource[k].type)
{
case NO_LIGHT:
return;
case POINT_LIGHT:
case SPOT_LIGHT:
/* swap z to reverse light position */
viewpoint = mapvals.viewpoint;
viewpoint.z = -viewpoint.z;
light_position = mapvals.lightsource[k].position;
gimp_vector_3d_to_2d (startx, starty, pw, ph, &dxpos, &dypos,
&viewpoint, &light_position);
handle_xpos = (gint) (dxpos + 0.5);
handle_ypos = (gint) (dypos + 0.5);
break;
case DIRECTIONAL_LIGHT:
light_position.x = light_position.y = 0.5;
light_position.z = 0;
viewpoint.z = -viewpoint.z;
gimp_vector_3d_to_2d (startx, starty, pw, ph, &dxpos, &dypos,
&viewpoint, &light_position);
length = PREVIEW_HEIGHT / 4;
delta_x = mapvals.lightsource[k].direction.x * length;
delta_y = mapvals.lightsource[k].direction.y * length;
handle_xpos = dxpos + delta_x;
handle_ypos = dypos + delta_y;
break;
}
if (mapvals.lightsource[k].type != NO_LIGHT)
{
GdkRGBA color;
cairo_set_line_width (cr, 1.0);
color.red = 0.0;
color.green = 0.2;
color.blue = 1.0;
color.alpha = 1.0;
gdk_cairo_set_source_rgba (cr, &color);
1998-06-11 14:04:59 +08:00
/* draw circle at light position */
switch (mapvals.lightsource[k].type)
{
case POINT_LIGHT:
case SPOT_LIGHT:
cairo_arc (cr, handle_xpos, handle_ypos,
2011-12-23 17:17:34 +08:00
LIGHT_SYMBOL_SIZE/2, 0, 2 * G_PI);
cairo_fill (cr);
break;
case DIRECTIONAL_LIGHT:
cairo_arc (cr, handle_xpos, handle_ypos,
2011-12-23 17:17:34 +08:00
LIGHT_SYMBOL_SIZE/2, 0, 2 * G_PI);
cairo_fill (cr);
cairo_move_to (cr, handle_xpos, handle_ypos);
cairo_line_to (cr, startx + pw/2, starty + ph/2);
cairo_stroke (cr);
break;
case NO_LIGHT:
break;
}
}
1998-06-11 14:04:59 +08:00
}
/*************************************************/
/* Update light position given new screen coords */
/*************************************************/
void
update_light (gint xpos, gint ypos)
{
gint startx, starty, pw, ph;
GimpVector3 vp;
gint k = mapvals.light_selected;
compute_preview_rectangle (&startx, &starty, &pw, &ph);
vp = mapvals.viewpoint;
vp.z = -vp.z;
1998-06-11 14:04:59 +08:00
switch (mapvals.lightsource[k].type)
{
case NO_LIGHT:
break;
case POINT_LIGHT:
case SPOT_LIGHT:
gimp_vector_2d_to_3d (startx,
starty,
pw,
ph,
xpos, ypos, &vp, &mapvals.lightsource[k].position);
break;
case DIRECTIONAL_LIGHT:
gimp_vector_2d_to_3d (startx,
starty,
pw,
ph,
xpos, ypos, &vp, &mapvals.lightsource[k].direction);
break;
}
1998-06-11 14:04:59 +08:00
}
1998-06-11 14:04:59 +08:00
/******************************************************************/
/* Draw preview image. if DoCompute is TRUE then recompute image. */
/******************************************************************/
void
preview_compute (void)
1998-06-11 14:04:59 +08:00
{
GdkDisplay *display = gtk_widget_get_display (previewarea);
GdkCursor *cursor;
gint startx, starty, pw, ph;
1998-06-11 14:04:59 +08:00
compute_preview_rectangle (&startx, &starty, &pw, &ph);
if (GDK_IS_WINDOW (gtk_widget_get_window (previewarea)))
{
cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
gdk_window_set_cursor (gtk_widget_get_window (previewarea), cursor);
g_object_unref (cursor);
compute_preview (startx, starty, pw, ph);
cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
gdk_window_set_cursor (gtk_widget_get_window (previewarea), cursor);
g_object_unref (cursor);
gdk_display_flush (display);
}
}
/******************************/
/* Preview area event handler */
/******************************/
gboolean
preview_events (GtkWidget *area,
GdkEvent *event)
{
switch (event->type)
{
case GDK_ENTER_NOTIFY:
break;
case GDK_LEAVE_NOTIFY:
break;
case GDK_BUTTON_PRESS:
light_hit = check_handle_hit (event->button.x, event->button.y);
left_button_pressed = TRUE;
break;
case GDK_BUTTON_RELEASE:
left_button_pressed = FALSE;
break;
case GDK_MOTION_NOTIFY:
if (left_button_pressed == TRUE &&
light_hit == TRUE &&
mapvals.interactive_preview == TRUE )
{
gtk_widget_queue_draw (previewarea);
interactive_preview_callback(NULL);
update_light (event->motion.x, event->motion.y);
}
break;
default:
break;
}
1998-06-11 14:04:59 +08:00
return FALSE;
}
gboolean
2010-11-03 04:49:54 +08:00
preview_draw (GtkWidget *area,
cairo_t *cr)
{
cairo_set_source_surface (cr, preview_surface, 0.0, 0.0);
cairo_paint (cr);
/* draw symbols if enabled in UI */
if (mapvals.interactive_preview)
{
draw_handles (cr);
}
return FALSE;
}
void
interactive_preview_callback (GtkWidget *widget)
{
2019-07-10 18:19:13 +08:00
if (preview_update_timer != 0)
g_source_remove (preview_update_timer);
preview_update_timer = g_timeout_add (100,
2019-07-10 18:19:13 +08:00
interactive_preview_timer_callback,
NULL);
}
static gboolean
interactive_preview_timer_callback (gpointer data)
{
gint k = mapvals.light_selected;
gchar *pos_x = g_strdup_printf (("light-position-x-%d"), k + 1);
gchar *pos_y = g_strdup_printf (("light-position-y-%d"), k + 1);
gchar *pos_z = g_strdup_printf (("light-position-z-%d"), k + 1);
gchar *dir_x = g_strdup_printf (("light-direction-x-%d"), k + 1);
gchar *dir_y = g_strdup_printf (("light-direction-y-%d"), k + 1);
gchar *dir_z = g_strdup_printf (("light-direction-z-%d"), k + 1);
mapvals.update_enabled = FALSE; /* disable apply_settings() */
g_object_set (mapvals.config,
pos_x, mapvals.lightsource[k].position.x,
pos_y, mapvals.lightsource[k].position.y,
pos_z, mapvals.lightsource[k].position.z,
dir_x, mapvals.lightsource[k].direction.x,
dir_y, mapvals.lightsource[k].direction.y,
dir_z, mapvals.lightsource[k].direction.z,
NULL);
g_free (pos_x);
g_free (pos_y);
g_free (pos_z);
g_free (dir_x);
g_free (dir_y);
g_free (dir_z);
mapvals.update_enabled = TRUE;
preview_compute ();
gtk_widget_queue_draw (previewarea);
preview_update_timer = 0;
return FALSE;
1998-06-11 14:04:59 +08:00
}
void
composite_behind (gdouble *color1,
gdouble *color2)
{
g_return_if_fail (color1 != NULL);
g_return_if_fail (color2 != NULL);
if (color1[3] < 1.0)
{
gdouble factor = color2[3] * (1.0 - color1[3]);
color1[0] = color2[0] * factor + color1[0] * color1[3];
color1[1] = color2[1] * factor + color1[1] * color1[3];
color1[2] = color2[2] * factor + color1[2] * color1[3];
color1[3] = factor + color1[3];
}
}