~ubuntu-branches/ubuntu/hoary/gimp/hoary

« back to all changes in this revision

Viewing changes to plug-ins/Lighting/lighting_preview.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2005-04-04 14:51:23 UTC
  • Revision ID: james.westby@ubuntu.com-20050404145123-9py049eeelfymur8
Tags: upstream-2.2.2
ImportĀ upstreamĀ versionĀ 2.2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*************************************************/
 
2
/* Compute a preview image and preview wireframe */
 
3
/*************************************************/
 
4
 
 
5
#include "config.h"
 
6
 
 
7
#include <gtk/gtk.h>
 
8
 
 
9
#include <libgimp/gimp.h>
 
10
#include <libgimpmath/gimpmath.h>
 
11
 
 
12
#include "lighting_main.h"
 
13
#include "lighting_ui.h"
 
14
#include "lighting_image.h"
 
15
#include "lighting_apply.h"
 
16
#include "lighting_shade.h"
 
17
 
 
18
#include "lighting_preview.h"
 
19
 
 
20
#define LIGHT_SYMBOL_SIZE 8
 
21
 
 
22
gint handle_xpos = 0, handle_ypos = 0;
 
23
 
 
24
BackBuffer backbuf = { 0, 0, 0, 0, NULL };
 
25
 
 
26
/* g_free()'ed on exit */
 
27
gdouble *xpostab = NULL;
 
28
gdouble *ypostab = NULL;
 
29
 
 
30
static gint xpostab_size = -1;  /* if preview size change, do realloc */
 
31
static gint ypostab_size = -1;
 
32
 
 
33
gboolean    light_hit           = FALSE;
 
34
gboolean    left_button_pressed = FALSE;
 
35
static guint preview_update_timer = 0;
 
36
 
 
37
 
 
38
/* Protos */
 
39
/* ====== */
 
40
static gboolean
 
41
interactive_preview_timer_callback ( gpointer data );
 
42
 
 
43
static void
 
44
compute_preview (gint startx, gint starty, gint w, gint h)
 
45
{
 
46
  gint xcnt, ycnt, f1, f2;
 
47
  gdouble imagex, imagey;
 
48
  gint32 index = 0;
 
49
  GimpRGB color;
 
50
  GimpRGB lightcheck, darkcheck;
 
51
  GimpVector3 pos;
 
52
  get_ray_func ray_func;
 
53
 
 
54
  if (xpostab_size != w)
 
55
    {
 
56
      if (xpostab)
 
57
        {
 
58
          g_free (xpostab);
 
59
          xpostab = NULL;
 
60
        }
 
61
    }
 
62
 
 
63
  if (!xpostab)
 
64
    {
 
65
      xpostab = g_new (gdouble, w);
 
66
      xpostab_size = w;
 
67
    }
 
68
 
 
69
  if (ypostab_size != h)
 
70
    {
 
71
      if (ypostab)
 
72
        {
 
73
          g_free (ypostab);
 
74
          ypostab = NULL;
 
75
        }
 
76
    }
 
77
 
 
78
  if (!ypostab)
 
79
    {
 
80
      ypostab = g_new (gdouble, h);
 
81
      ypostab_size = h;
 
82
    }
 
83
 
 
84
  for (xcnt = 0; xcnt < w; xcnt++)
 
85
    xpostab[xcnt] = (gdouble) width *((gdouble) xcnt / (gdouble) w);
 
86
  for (ycnt = 0; ycnt < h; ycnt++)
 
87
    ypostab[ycnt] = (gdouble) height *((gdouble) ycnt / (gdouble) h);
 
88
 
 
89
  precompute_init (width, height);
 
90
 
 
91
  gimp_rgba_set (&lightcheck,
 
92
                 GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT,
 
93
                 1.0);
 
94
  gimp_rgba_set (&darkcheck, GIMP_CHECK_DARK, GIMP_CHECK_DARK,
 
95
                 GIMP_CHECK_DARK, 1.0);
 
96
 
 
97
  if (mapvals.bump_mapped == TRUE && mapvals.bumpmap_id != -1)
 
98
    {
 
99
      gimp_pixel_rgn_init (&bump_region,
 
100
                           gimp_drawable_get (mapvals.bumpmap_id),
 
101
                           0, 0, width, height, FALSE, FALSE);
 
102
    }
 
103
 
 
104
  imagey = 0;
 
105
 
 
106
  if (mapvals.previewquality)
 
107
    ray_func = get_ray_color;
 
108
  else
 
109
    ray_func = get_ray_color_no_bilinear;
 
110
 
 
111
  if (mapvals.env_mapped == TRUE && mapvals.envmap_id != -1)
 
112
    {
 
113
      env_width = gimp_drawable_width (mapvals.envmap_id);
 
114
      env_height = gimp_drawable_height (mapvals.envmap_id);
 
115
 
 
116
      gimp_pixel_rgn_init (&env_region,
 
117
                           gimp_drawable_get (mapvals.envmap_id), 0,
 
118
                           0, env_width, env_height, FALSE, FALSE);
 
119
 
 
120
      if (mapvals.previewquality)
 
121
        ray_func = get_ray_color_ref;
 
122
      else
 
123
        ray_func = get_ray_color_no_bilinear_ref;
 
124
    }
 
125
 
 
126
  for (ycnt = 0; ycnt < PREVIEW_HEIGHT; ycnt++)
 
127
    {
 
128
      for (xcnt = 0; xcnt < PREVIEW_WIDTH; xcnt++)
 
129
        {
 
130
          if ((ycnt >= starty && ycnt < (starty + h)) &&
 
131
              (xcnt >= startx && xcnt < (startx + w)))
 
132
            {
 
133
              imagex = xpostab[xcnt - startx];
 
134
              imagey = ypostab[ycnt - starty];
 
135
              pos = int_to_posf (imagex, imagey);
 
136
 
 
137
              if (mapvals.bump_mapped == TRUE &&
 
138
                  mapvals.bumpmap_id != -1 &&
 
139
                  xcnt == startx)
 
140
                {
 
141
                  pos_to_float (pos.x, pos.y, &imagex, &imagey);
 
142
                  precompute_normals (0, width, RINT (imagey));
 
143
                }
 
144
 
 
145
              color = (*ray_func) (&pos);
 
146
 
 
147
              if (color.a < 1.0)
 
148
                {
 
149
                  f1 = ((xcnt % 32) < 16);
 
150
                  f2 = ((ycnt % 32) < 16);
 
151
                  f1 = f1 ^ f2;
 
152
 
 
153
                  if (f1)
 
154
                    {
 
155
                      if (color.a == 0.0)
 
156
                        color = lightcheck;
 
157
                      else
 
158
                        gimp_rgb_composite (&color,
 
159
                                            &lightcheck,
 
160
                                            GIMP_RGB_COMPOSITE_BEHIND);
 
161
                    }
 
162
                  else
 
163
                    {
 
164
                      if (color.a == 0.0)
 
165
                        color = darkcheck;
 
166
                      else
 
167
                        gimp_rgb_composite (&color,
 
168
                                            &darkcheck,
 
169
                                            GIMP_RGB_COMPOSITE_BEHIND);
 
170
                    }
 
171
                }
 
172
 
 
173
              gimp_rgb_get_uchar (&color,
 
174
                                  preview_rgb_data + index,
 
175
                                  preview_rgb_data + index +
 
176
                                  1,
 
177
                                  preview_rgb_data + index +
 
178
                                  2);
 
179
              index += 3;
 
180
              imagex++;
 
181
            }
 
182
          else
 
183
            {
 
184
              preview_rgb_data[index++] = 200;
 
185
              preview_rgb_data[index++] = 200;
 
186
              preview_rgb_data[index++] = 200;
 
187
            }
 
188
        }
 
189
    }
 
190
}
 
191
 
 
192
static void
 
193
compute_preview_rectangle (gint * xp, gint * yp, gint * wid, gint * heig)
 
194
{
 
195
  gdouble x, y, w, h;
 
196
 
 
197
  if (width >= height)
 
198
    {
 
199
      w = (PREVIEW_WIDTH - 50.0);
 
200
      h = (gdouble) height *(w / (gdouble) width);
 
201
 
 
202
      x = (PREVIEW_WIDTH - w) / 2.0;
 
203
      y = (PREVIEW_HEIGHT - h) / 2.0;
 
204
    }
 
205
  else
 
206
    {
 
207
      h = (PREVIEW_HEIGHT - 50.0);
 
208
      w = (gdouble) width *(h / (gdouble) height);
 
209
      x = (PREVIEW_WIDTH - w) / 2.0;
 
210
      y = (PREVIEW_HEIGHT - h) / 2.0;
 
211
    }
 
212
  *xp = RINT (x);
 
213
  *yp = RINT (y);
 
214
  *wid = RINT (w);
 
215
  *heig = RINT (h);
 
216
}
 
217
 
 
218
/*************************************************/
 
219
/* Check if the given position is within the     */
 
220
/* light marker. Return TRUE if so, FALSE if not */
 
221
/*************************************************/
 
222
 
 
223
gboolean
 
224
check_handle_hit (gint xpos, gint ypos)
 
225
{
 
226
  gint dx,dy,r;
 
227
  gint k = mapvals.light_selected;
 
228
 
 
229
  dx = handle_xpos - xpos;
 
230
  dy = handle_ypos - ypos;
 
231
 
 
232
 
 
233
  if (mapvals.lightsource[k].type == POINT_LIGHT ||
 
234
      mapvals.lightsource[k].type == DIRECTIONAL_LIGHT)
 
235
    {
 
236
      r = sqrt (dx * dx + dy * dy) + 0.5;
 
237
      if ((gint) r > 7)
 
238
        {
 
239
          return (FALSE);
 
240
        }
 
241
      else
 
242
        {
 
243
          return (TRUE);
 
244
        }
 
245
    }
 
246
  return FALSE;
 
247
}
 
248
 
 
249
/****************************************/
 
250
/* Draw a light symbol                  */
 
251
/****************************************/
 
252
 
 
253
 
 
254
static void
 
255
draw_handles (void)
 
256
{
 
257
  gdouble     dxpos, dypos;
 
258
  gint        startx, starty, pw, ph;
 
259
  GimpVector3 viewpoint;
 
260
  GimpVector3 light_position;
 
261
  gint        k      = mapvals.light_selected;
 
262
 
 
263
  gfloat length;
 
264
  gfloat delta_x = 0.0;
 
265
  gfloat delta_y = 0.0;
 
266
 
 
267
  /* calculate handle position */
 
268
  compute_preview_rectangle (&startx, &starty, &pw, &ph);
 
269
  switch (mapvals.lightsource[k].type)
 
270
    {
 
271
    case NO_LIGHT:
 
272
      return;
 
273
    case POINT_LIGHT:
 
274
    case SPOT_LIGHT:
 
275
      /* swap z to reverse light position */
 
276
      viewpoint = mapvals.viewpoint;
 
277
      viewpoint.z = -viewpoint.z;
 
278
      light_position = mapvals.lightsource[k].position;
 
279
      gimp_vector_3d_to_2d (startx, starty, pw, ph, &dxpos, &dypos,
 
280
                            &viewpoint, &light_position);
 
281
      handle_xpos = (gint) (dxpos + 0.5);
 
282
      handle_ypos = (gint) (dypos + 0.5);
 
283
      break;
 
284
    case DIRECTIONAL_LIGHT:
 
285
      light_position.x = light_position.y = 0.5;
 
286
      light_position.z = 0;
 
287
      viewpoint.z = -viewpoint.z;
 
288
      gimp_vector_3d_to_2d (startx, starty, pw, ph, &dxpos, &dypos,
 
289
                            &viewpoint, &light_position);
 
290
      length = PREVIEW_HEIGHT / 4;
 
291
      delta_x = mapvals.lightsource[k].direction.x * length;
 
292
      delta_y = mapvals.lightsource[k].direction.y * length;
 
293
      handle_xpos = dxpos + delta_x;
 
294
      handle_ypos = dypos + delta_y;
 
295
      break;
 
296
    }
 
297
 
 
298
  gdk_gc_set_function (gc, GDK_COPY);
 
299
 
 
300
  if (mapvals.lightsource[k].type != NO_LIGHT)
 
301
    {
 
302
      GdkColor  color;
 
303
 
 
304
      /* Restore background if it has been saved */
 
305
      /* ======================================= */
 
306
 
 
307
      if (backbuf.image != NULL)
 
308
        {
 
309
          gdk_gc_set_function (gc, GDK_COPY);
 
310
          gdk_draw_image (previewarea->window, gc,
 
311
                          backbuf.image, 0, 0, backbuf.x,
 
312
                          backbuf.y, backbuf.w, backbuf.h);
 
313
          g_object_unref (backbuf.image);
 
314
          backbuf.image = NULL;
 
315
        }
 
316
 
 
317
      /* calculate backbuffer */
 
318
      switch (mapvals.lightsource[k].type)
 
319
        {
 
320
        case POINT_LIGHT:
 
321
          backbuf.x = handle_xpos - LIGHT_SYMBOL_SIZE / 2;
 
322
          backbuf.y = handle_ypos - LIGHT_SYMBOL_SIZE / 2;
 
323
          backbuf.w = LIGHT_SYMBOL_SIZE;
 
324
          backbuf.h = LIGHT_SYMBOL_SIZE;
 
325
          break;
 
326
        case DIRECTIONAL_LIGHT:
 
327
          if (delta_x <= 0)
 
328
            backbuf.x = handle_xpos;
 
329
          else
 
330
            backbuf.x = startx + pw/2;
 
331
          if (delta_y <= 0)
 
332
            backbuf.y = handle_ypos;
 
333
          else
 
334
            backbuf.y = starty + ph/2;
 
335
          backbuf.x -= LIGHT_SYMBOL_SIZE/2;
 
336
          backbuf.y -= LIGHT_SYMBOL_SIZE/2;
 
337
          backbuf.w = fabs(delta_x) + LIGHT_SYMBOL_SIZE;
 
338
          backbuf.h = fabs(delta_y) + LIGHT_SYMBOL_SIZE;
 
339
          break;
 
340
        case SPOT_LIGHT:
 
341
          backbuf.x = handle_xpos - LIGHT_SYMBOL_SIZE / 2;
 
342
          backbuf.y = handle_ypos - LIGHT_SYMBOL_SIZE / 2;
 
343
          backbuf.w = LIGHT_SYMBOL_SIZE;
 
344
          backbuf.h = LIGHT_SYMBOL_SIZE;
 
345
          break;
 
346
        case NO_LIGHT:
 
347
          break;
 
348
        }
 
349
 
 
350
      /* Save background */
 
351
      /* =============== */
 
352
      if ((backbuf.x >= 0) &&
 
353
          (backbuf.x <= PREVIEW_WIDTH) &&
 
354
          (backbuf.y >= 0) && (backbuf.y <= PREVIEW_HEIGHT))
 
355
        {
 
356
          /* clip coordinates to preview widget sizes */
 
357
          if ((backbuf.x + backbuf.w) > PREVIEW_WIDTH)
 
358
            backbuf.w = (PREVIEW_WIDTH - backbuf.x);
 
359
 
 
360
          if ((backbuf.y + backbuf.h) > PREVIEW_HEIGHT)
 
361
            backbuf.h = (PREVIEW_HEIGHT - backbuf.y);
 
362
 
 
363
          backbuf.image = gdk_drawable_get_image (previewarea->window,
 
364
                                                  backbuf.x, backbuf.y,
 
365
                                                  backbuf.w, backbuf.h);
 
366
        }
 
367
 
 
368
      color.red   = 0x0;
 
369
      color.green = 0x0;
 
370
      color.blue  = 0x0;
 
371
      gdk_gc_set_rgb_bg_color (gc, &color);
 
372
 
 
373
      color.red   = 0x0;
 
374
      color.green = 0x4000;
 
375
      color.blue  = 0xFFFF;
 
376
      gdk_gc_set_rgb_fg_color (gc, &color);
 
377
 
 
378
      /* draw circle at light position */
 
379
      switch (mapvals.lightsource[k].type)
 
380
        {
 
381
        case POINT_LIGHT:
 
382
        case SPOT_LIGHT:
 
383
          gdk_draw_arc (previewarea->window, gc, TRUE,
 
384
                        handle_xpos - LIGHT_SYMBOL_SIZE / 2,
 
385
                        handle_ypos - LIGHT_SYMBOL_SIZE / 2,
 
386
                        LIGHT_SYMBOL_SIZE,
 
387
                        LIGHT_SYMBOL_SIZE, 0, 360 * 64);
 
388
          break;
 
389
        case DIRECTIONAL_LIGHT:
 
390
          gdk_draw_arc (previewarea->window, gc, TRUE,
 
391
                        handle_xpos - LIGHT_SYMBOL_SIZE / 2,
 
392
                        handle_ypos - LIGHT_SYMBOL_SIZE / 2,
 
393
                        LIGHT_SYMBOL_SIZE,
 
394
                        LIGHT_SYMBOL_SIZE, 0, 360 * 64);
 
395
          gdk_draw_line (previewarea->window, gc,
 
396
                         handle_xpos, handle_ypos, startx+pw/2 , starty + ph/2);
 
397
          break;
 
398
        case NO_LIGHT:
 
399
          break;
 
400
        }
 
401
    }
 
402
}
 
403
 
 
404
 
 
405
/*************************************************/
 
406
/* Update light position given new screen coords */
 
407
/*************************************************/
 
408
 
 
409
void
 
410
update_light (gint xpos, gint ypos)
 
411
{
 
412
  gint startx, starty, pw, ph;
 
413
  GimpVector3  vp;
 
414
  gint         k = mapvals.light_selected;
 
415
 
 
416
  compute_preview_rectangle (&startx, &starty, &pw, &ph);
 
417
 
 
418
  vp = mapvals.viewpoint;
 
419
  vp.z = -vp.z;
 
420
 
 
421
  switch (mapvals.lightsource[k].type)
 
422
    {
 
423
    case        NO_LIGHT:
 
424
      break;
 
425
    case        POINT_LIGHT:
 
426
    case        SPOT_LIGHT:
 
427
      gimp_vector_2d_to_3d (startx,
 
428
                            starty,
 
429
                            pw,
 
430
                            ph,
 
431
                            xpos, ypos, &vp, &mapvals.lightsource[k].position);
 
432
      break;
 
433
    case DIRECTIONAL_LIGHT:
 
434
      gimp_vector_2d_to_3d (startx,
 
435
                            starty,
 
436
                            pw,
 
437
                            ph,
 
438
                            xpos, ypos, &vp, &mapvals.lightsource[k].direction);
 
439
      break;
 
440
    }
 
441
}
 
442
 
 
443
 
 
444
/******************************************************************/
 
445
/* Draw preview image. if DoCompute is TRUE then recompute image. */
 
446
/******************************************************************/
 
447
 
 
448
void
 
449
draw_preview_image (gboolean recompute)
 
450
{
 
451
  gint      startx, starty, pw, ph;
 
452
  GdkColor  color;
 
453
 
 
454
  color.red   = 0x0;
 
455
  color.green = 0x0;
 
456
  color.blue  = 0x0;
 
457
  gdk_gc_set_rgb_bg_color (gc, &color);
 
458
 
 
459
  color.red   = 0xFFFF;
 
460
  color.green = 0xFFFF;
 
461
  color.blue  = 0xFFFF;
 
462
  gdk_gc_set_rgb_fg_color (gc, &color);
 
463
 
 
464
  gdk_gc_set_function (gc, GDK_COPY);
 
465
 
 
466
  compute_preview_rectangle (&startx, &starty, &pw, &ph);
 
467
 
 
468
  if (recompute)
 
469
    {
 
470
      GdkDisplay *display = gtk_widget_get_display (previewarea);
 
471
      GdkCursor  *cursor;
 
472
 
 
473
      cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
 
474
 
 
475
      gdk_window_set_cursor (previewarea->window, cursor);
 
476
      gdk_cursor_unref (cursor);
 
477
 
 
478
      compute_preview (startx, starty, pw, ph);
 
479
      cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
 
480
      gdk_window_set_cursor (previewarea->window, cursor);
 
481
      gdk_cursor_unref (cursor);
 
482
      gdk_flush ();
 
483
 
 
484
      /* if we recompute, clear backbuf, so we don't
 
485
       * restore the wrong bitmap */
 
486
      if (backbuf.image != NULL)
 
487
        {
 
488
          g_object_unref (backbuf.image);
 
489
          backbuf.image = NULL;
 
490
        }
 
491
    }
 
492
 
 
493
  gdk_draw_rgb_image (previewarea->window, gc,
 
494
                      0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT,
 
495
                      GDK_RGB_DITHER_MAX, preview_rgb_data,
 
496
                      3 * PREVIEW_WIDTH);
 
497
 
 
498
  /* draw symbols if enabled in UI */
 
499
  if (mapvals.interactive_preview)
 
500
    {
 
501
      draw_handles ();
 
502
    }
 
503
}
 
504
 
 
505
/******************************/
 
506
/* Preview area event handler */
 
507
/******************************/
 
508
 
 
509
gboolean
 
510
preview_events (GtkWidget *area,
 
511
                GdkEvent  *event)
 
512
{
 
513
  switch (event->type)
 
514
    {
 
515
    case GDK_EXPOSE:
 
516
 
 
517
      /* Is this the first exposure? */
 
518
      /* =========================== */
 
519
      if (!gc)
 
520
        {
 
521
          gc = gdk_gc_new (area->window);
 
522
          draw_preview_image (TRUE);
 
523
        }
 
524
      else
 
525
        draw_preview_image (FALSE);
 
526
      break;
 
527
    case GDK_ENTER_NOTIFY:
 
528
      break;
 
529
    case GDK_LEAVE_NOTIFY:
 
530
      break;
 
531
    case GDK_BUTTON_PRESS:
 
532
      light_hit = check_handle_hit (event->button.x, event->button.y);
 
533
      left_button_pressed = TRUE;
 
534
      break;
 
535
    case GDK_BUTTON_RELEASE:
 
536
      left_button_pressed = FALSE;
 
537
      break;
 
538
    case GDK_MOTION_NOTIFY:
 
539
      if (left_button_pressed == TRUE &&
 
540
          light_hit == TRUE &&
 
541
          mapvals.interactive_preview == TRUE )
 
542
        {
 
543
          draw_handles();
 
544
          interactive_preview_callback(NULL);
 
545
          update_light (event->motion.x, event->motion.y);
 
546
        }
 
547
      break;
 
548
    default:
 
549
      break;
 
550
    }
 
551
 
 
552
  return FALSE;
 
553
}
 
554
 
 
555
void
 
556
interactive_preview_callback (GtkWidget *widget)
 
557
{
 
558
  if ( preview_update_timer != 0)
 
559
    {
 
560
      g_source_remove ( preview_update_timer );
 
561
    }
 
562
  /* start new timer */
 
563
  preview_update_timer = g_timeout_add (100,
 
564
                                        interactive_preview_timer_callback, NULL);
 
565
}
 
566
 
 
567
static gboolean
 
568
interactive_preview_timer_callback ( gpointer data )
 
569
{
 
570
  gint k = mapvals.light_selected;
 
571
 
 
572
  mapvals.update_enabled = FALSE;  /* disable apply_settings() */
 
573
 
 
574
  gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_x),
 
575
                             mapvals.lightsource[k].position.x);
 
576
  gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_y),
 
577
                             mapvals.lightsource[k].position.y);
 
578
  gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_z),
 
579
                             mapvals.lightsource[k].position.z);
 
580
  gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_x),
 
581
                             mapvals.lightsource[k].direction.x);
 
582
  gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_y),
 
583
                             mapvals.lightsource[k].direction.y);
 
584
  gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_z),
 
585
                             mapvals.lightsource[k].direction.z);
 
586
 
 
587
  mapvals.update_enabled = TRUE;
 
588
 
 
589
  draw_preview_image (TRUE);
 
590
 
 
591
  preview_update_timer = 0;
 
592
 
 
593
  return FALSE;
 
594
}