home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / app / curves.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-20  |  44.7 KB  |  1,791 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <stdio.h>
  22.  
  23. #include <gtk/gtk.h>
  24.  
  25. #include "apptypes.h"
  26.  
  27. #include "appenv.h"
  28. #include "cursorutil.h"
  29. #include "drawable.h"
  30. #include "gdisplay.h"
  31. #include "gimphistogram.h"
  32. #include "gimpui.h"
  33. #include "curves.h"
  34. #include "gimplut.h"
  35.  
  36. #include "libgimp/gimpenv.h"
  37. #include "libgimp/gimpmath.h"
  38.  
  39. #include "libgimp/gimpintl.h"
  40.  
  41. #define GRAPH          0x1
  42. #define XRANGE_TOP     0x2
  43. #define XRANGE_BOTTOM  0x4
  44. #define YRANGE         0x8
  45. #define DRAW          0x10
  46. #define ALL           0xFF
  47.  
  48. /*  NB: take care when changing these values: make sure the curve[] array in
  49.  *  curves.h is large enough.
  50.  */
  51. #define GRAPH_WIDTH    256
  52. #define GRAPH_HEIGHT   256
  53. #define XRANGE_WIDTH   256
  54. #define XRANGE_HEIGHT   16
  55. #define YRANGE_WIDTH    16
  56. #define YRANGE_HEIGHT  256
  57. #define RADIUS           3
  58. #define MIN_DISTANCE     8
  59.  
  60. #define RANGE_MASK  GDK_EXPOSURE_MASK | \
  61.                     GDK_ENTER_NOTIFY_MASK
  62.  
  63. #define GRAPH_MASK  GDK_EXPOSURE_MASK | \
  64.             GDK_POINTER_MOTION_MASK | \
  65.             GDK_POINTER_MOTION_HINT_MASK | \
  66.                     GDK_ENTER_NOTIFY_MASK | \
  67.             GDK_BUTTON_PRESS_MASK | \
  68.             GDK_BUTTON_RELEASE_MASK | \
  69.             GDK_BUTTON1_MOTION_MASK
  70.  
  71. /*  the curves structures  */
  72.  
  73. typedef struct _Curves Curves;
  74.  
  75. struct _Curves
  76. {
  77.   gint x, y;    /*  coords for last mouse click  */
  78. };
  79.  
  80. typedef gdouble CRMatrix[4][4];
  81.  
  82. /*  the curves tool options  */
  83. static ToolOptions  * curves_options = NULL;
  84.  
  85. /*  the curves dialog  */
  86. static CurvesDialog * curves_dialog = NULL;
  87.  
  88. /*  the curves file dialog  */
  89. static GtkWidget *file_dlg = NULL;
  90. static gboolean   load_save;
  91.  
  92. static GtkWidget *channel_items[5];
  93.  
  94. static CRMatrix CR_basis =
  95. {
  96.   { -0.5,  1.5, -1.5,  0.5 },
  97.   {  1.0, -2.5,  2.0, -0.5 },
  98.   { -0.5,  0.0,  0.5,  0.0 },
  99.   {  0.0,  1.0,  0.0,  0.0 },
  100. };
  101.  
  102.  
  103. /*  curves action functions  */
  104.  
  105. static void   curves_button_press   (Tool *, GdkEventButton *, gpointer);
  106. static void   curves_button_release (Tool *, GdkEventButton *, gpointer);
  107. static void   curves_motion         (Tool *, GdkEventMotion *, gpointer);
  108. static void   curves_control        (Tool *, ToolAction,       gpointer);
  109.  
  110. static CurvesDialog * curves_dialog_new (void);
  111.  
  112. static void   curves_update           (CurvesDialog *, int);
  113. static void   curves_plot_curve       (CurvesDialog *, int, int, int, int);
  114. static void   curves_preview          (CurvesDialog *);
  115.  
  116. static void   curves_channel_callback (GtkWidget *, gpointer);
  117.  
  118. static void   curves_smooth_callback      (GtkWidget *, gpointer);
  119. static void   curves_free_callback        (GtkWidget *, gpointer);
  120.  
  121. static void   curves_channel_reset        (int);
  122. static void   curves_reset_callback       (GtkWidget *, gpointer);
  123. static void   curves_ok_callback          (GtkWidget *, gpointer);
  124. static void   curves_cancel_callback      (GtkWidget *, gpointer);
  125. static void   curves_load_callback        (GtkWidget *, gpointer);
  126. static void   curves_save_callback        (GtkWidget *, gpointer);
  127. static void   curves_preview_update       (GtkWidget *, gpointer);
  128. static gint   curves_xrange_events        (GtkWidget *, GdkEvent *, CurvesDialog *);
  129. static gint   curves_yrange_events        (GtkWidget *, GdkEvent *, CurvesDialog *);
  130. static gint   curves_graph_events         (GtkWidget *, GdkEvent *, CurvesDialog *);
  131. static void   curves_CR_compose           (CRMatrix, CRMatrix, CRMatrix);
  132.  
  133. static void   file_dialog_create          (GtkWidget *);
  134. static void   file_dialog_ok_callback     (GtkWidget *, gpointer);
  135. static void   file_dialog_cancel_callback (GtkWidget *, gpointer);
  136.  
  137. static gboolean  curves_read_from_file    (FILE *f);
  138. static void      curves_write_to_file     (FILE *f);
  139.  
  140.  
  141. /*  curves machinery  */
  142.  
  143. gfloat
  144. curves_lut_func (CurvesDialog *cd,
  145.          gint          nchannels,
  146.          gint          channel,
  147.          gfloat        value)
  148. {
  149.   gfloat f;
  150.   gint index;
  151.   gdouble inten;
  152.   gint j;
  153.  
  154.   if (nchannels == 1)
  155.     j = 0;
  156.   else
  157.     j = channel + 1;
  158.  
  159.   inten = value;
  160.  
  161.   /* For color images this runs through the loop with j = channel +1
  162.      the first time and j = 0 the second time */
  163.   /* For bw images this runs through the loop with j = 0 the first and
  164.      only time  */
  165.   for (; j >= 0; j -= (channel + 1))
  166.   {
  167.     /* don't apply the overall curve to the alpha channel */
  168.     if (j == 0 && (nchannels == 2 || nchannels == 4)
  169.     && channel == nchannels -1)
  170.       return inten;
  171.  
  172.     if (inten < 0.0)
  173.       inten = cd->curve[j][0]/255.0;
  174.     else if (inten >= 1.0)
  175.       inten = cd->curve[j][255]/255.0;
  176.     else /* interpolate the curve */
  177.     {
  178.       index = floor(inten * 255.0);
  179.       f = inten*255.0 - index;
  180.       inten = ((1.0 - f) * cd->curve[j][index    ] + 
  181.            (      f) * cd->curve[j][index + 1] ) / 255.0;
  182.     }
  183.   }
  184.   return inten;
  185. }
  186.  
  187. static void
  188. curves_colour_update (Tool           *tool,
  189.               GDisplay       *gdisp,
  190.               GimpDrawable   *drawable,
  191.               gint            x,
  192.               gint            y)
  193. {
  194.   guchar *color;
  195.   gint    offx;
  196.   gint    offy;
  197.   gint    maxval;
  198.   gboolean       has_alpha;
  199.   gboolean       is_indexed;
  200.   GimpImageType  sample_type;
  201.  
  202.   if(!tool || tool->state != ACTIVE)
  203.     return;
  204.  
  205.   drawable_offsets (drawable, &offx, &offy);
  206.  
  207.   x -= offx;
  208.   y -= offy;
  209.  
  210.   if (!(color = image_map_get_color_at(curves_dialog->image_map, x, y)))
  211.     return;
  212.  
  213.   sample_type = gimp_drawable_type (drawable);
  214.   is_indexed  = gimp_drawable_is_indexed (drawable);
  215.   has_alpha   = GIMP_IMAGE_TYPE_HAS_ALPHA (sample_type);
  216.  
  217.   curves_dialog->col_value[GIMP_HISTOGRAM_RED] = color[RED_PIX];
  218.   curves_dialog->col_value[GIMP_HISTOGRAM_GREEN] = color[GREEN_PIX];
  219.   curves_dialog->col_value[GIMP_HISTOGRAM_BLUE] = color[BLUE_PIX];
  220.  
  221.   if (has_alpha)
  222.     {
  223.       curves_dialog->col_value [GIMP_HISTOGRAM_ALPHA] = color[3];
  224.     }
  225.  
  226.   if (is_indexed)
  227.     curves_dialog->col_value [GIMP_HISTOGRAM_ALPHA] = color[4];
  228.  
  229.   maxval = MAX (color[RED_PIX], color[GREEN_PIX]);
  230.   curves_dialog->col_value[GIMP_HISTOGRAM_VALUE] = MAX (maxval, color[BLUE_PIX]);
  231.  
  232.   g_free (color);
  233. }
  234.  
  235. static void
  236. curves_add_point (GimpDrawable *drawable,
  237.           gint          x,
  238.           gint          y,
  239.           gint          cchan)
  240. {
  241.   /* Add point onto the curve */
  242.   gint closest_point = 0;
  243.   gint distance;
  244.   gint curvex;
  245.   gint i;
  246.  
  247.   switch (curves_dialog->curve_type[cchan])
  248.     {
  249.     case SMOOTH:
  250.       curvex = curves_dialog->col_value[cchan];
  251.       distance = G_MAXINT;
  252.       for (i = 0; i < 17; i++)
  253.     {
  254.       if (curves_dialog->points[cchan][i][0] != -1)
  255.         if (abs (curvex - curves_dialog->points[cchan][i][0]) < distance)
  256.           {
  257.         distance = abs (curvex - curves_dialog->points[cchan][i][0]);
  258.         closest_point = i;
  259.           }
  260.     }
  261.       
  262.       if (distance > MIN_DISTANCE)
  263.     closest_point = (curvex + 8) / 16;
  264.       
  265.       curves_dialog->points[cchan][closest_point][0] = curvex;
  266.       curves_dialog->points[cchan][closest_point][1] = curves_dialog->curve[cchan][curvex];
  267.       break;
  268.       
  269.     case GFREE:
  270.       curves_dialog->curve[cchan][x] = 255 - y;
  271.       break;
  272.     }
  273. }
  274.  
  275. /*  curves action functions  */
  276.  
  277. static void
  278. curves_button_press (Tool           *tool,
  279.              GdkEventButton *bevent,
  280.              gpointer        gdisp_ptr)
  281. {
  282.   gint x, y;
  283.   GDisplay     *gdisp;
  284.   GimpDrawable *drawable;
  285.  
  286.   gdisp = gdisp_ptr;
  287.   drawable = gimage_active_drawable (gdisp->gimage);
  288.  
  289.   tool->gdisp_ptr = gdisp;
  290.  
  291.   if (drawable != tool->drawable)
  292.     {
  293.       active_tool->preserve = TRUE;
  294.       image_map_abort (curves_dialog->image_map);
  295.       active_tool->preserve = FALSE;
  296.  
  297.       tool->drawable = drawable;
  298.  
  299.       curves_dialog->drawable = drawable;
  300.       curves_dialog->color = drawable_color (drawable);
  301.       curves_dialog->image_map = image_map_create (gdisp, drawable);
  302.     }
  303.  
  304.   tool->state = ACTIVE;
  305.  
  306.   gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y,
  307.                    FALSE, FALSE);
  308.   curves_colour_update (tool, gdisp, drawable, x, y);
  309.   curves_update (curves_dialog, GRAPH | DRAW);
  310. }
  311.  
  312. static void
  313. curves_button_release (Tool           *tool,
  314.                GdkEventButton *bevent,
  315.                gpointer        gdisp_ptr)
  316. {
  317.   gint x, y;
  318.   GimpDrawable *drawable;
  319.   GDisplay     *gdisp;
  320.  
  321.   gdisp = (GDisplay *) gdisp_ptr;
  322.  
  323.   if(!curves_dialog || 
  324.      !gdisp || 
  325.      !(drawable = gimage_active_drawable (gdisp->gimage)))
  326.      return;
  327.  
  328.   gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y, FALSE, FALSE);
  329.   curves_colour_update (tool, gdisp, drawable, x, y);
  330.  
  331.   if (bevent->state & GDK_SHIFT_MASK)
  332.     {
  333.       curves_add_point (drawable, x, y, curves_dialog->channel);
  334.       curves_calculate_curve (curves_dialog);
  335.     }
  336.   else if (bevent->state & GDK_CONTROL_MASK)
  337.     {
  338.       curves_add_point (drawable, x, y, GIMP_HISTOGRAM_VALUE);
  339.       curves_add_point (drawable, x, y, GIMP_HISTOGRAM_RED);
  340.       curves_add_point (drawable, x, y, GIMP_HISTOGRAM_GREEN);
  341.       curves_add_point (drawable, x, y, GIMP_HISTOGRAM_BLUE);
  342.       curves_add_point (drawable, x, y, GIMP_HISTOGRAM_ALPHA);
  343.       curves_calculate_curve (curves_dialog);
  344.     }
  345.  
  346.   curves_update (curves_dialog, GRAPH | DRAW);
  347. }
  348.  
  349. static void
  350. curves_motion (Tool           *tool,
  351.            GdkEventMotion *mevent,
  352.            gpointer        gdisp_ptr)
  353. {
  354.   gint x, y;
  355.   GDisplay     *gdisp;
  356.   GimpDrawable *drawable;
  357.  
  358.   gdisp = (GDisplay *) gdisp_ptr;
  359.  
  360.   if(!curves_dialog || 
  361.      !gdisp || 
  362.      !(drawable = gimage_active_drawable (gdisp->gimage)))
  363.      return;
  364.  
  365.   gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, FALSE, FALSE);
  366.   curves_colour_update (tool, gdisp, drawable, x, y);
  367.   curves_update (curves_dialog, GRAPH | DRAW);
  368. }
  369.  
  370. static void
  371. curves_control (Tool       *tool,
  372.         ToolAction  action,
  373.         gpointer    gdisp_ptr)
  374. {
  375.   switch (action)
  376.     {
  377.     case PAUSE:
  378.       break;
  379.  
  380.     case RESUME:
  381.       break;
  382.  
  383.     case HALT:
  384.       curves_dialog_hide ();
  385.       break;
  386.  
  387.     default:
  388.       break;
  389.     }
  390. }
  391.  
  392. Tool *
  393. tools_new_curves (void)
  394. {
  395.   Tool   *tool;
  396.   Curves *private;
  397.  
  398.   /*  The tool options  */
  399.   if (!curves_options)
  400.     {
  401.       curves_options = tool_options_new (_("Curves"));
  402.       tools_register (CURVES, curves_options);
  403.     }
  404.  
  405.   tool = tools_new_tool (CURVES);
  406.   private = g_new0 (Curves, 1);
  407.  
  408.   tool->scroll_lock = TRUE;   /*  Disallow scrolling  */
  409.   tool->preserve    = FALSE;  /*  Don't preserve on drawable change  */
  410.  
  411.   tool->private = (void *) private;
  412.  
  413.   tool->button_press_func   = curves_button_press;
  414.   tool->button_release_func = curves_button_release;
  415.   tool->motion_func         = curves_motion;
  416.   tool->control_func        = curves_control;
  417.  
  418.   return tool;
  419. }
  420.  
  421. void
  422. curves_dialog_hide (void)
  423. {
  424.   if (curves_dialog)
  425.     curves_cancel_callback (NULL, (gpointer) curves_dialog);
  426. }
  427.  
  428. void
  429. tools_free_curves (Tool *tool)
  430. {
  431.   Curves *private;
  432.  
  433.   private = (Curves *) tool->private;
  434.  
  435.   /*  Close the color select dialog  */
  436.   curves_dialog_hide ();
  437.  
  438.   g_free (private);
  439. }
  440.  
  441. void
  442. curves_initialize (GDisplay *gdisp)
  443. {
  444.   gint i, j;
  445.  
  446.   if (drawable_indexed (gimage_active_drawable (gdisp->gimage)))
  447.     {
  448.       g_message (_("Curves for indexed drawables cannot be adjusted."));
  449.       return;
  450.     }
  451.  
  452.   /*  The curves dialog  */
  453.   if (!curves_dialog)
  454.     {
  455.       curves_dialog = curves_dialog_new ();
  456.     }
  457.      
  458.   /*  Initialize the values  */
  459.   curves_dialog->channel = GIMP_HISTOGRAM_VALUE;
  460.   for (i = 0; i < 5; i++)
  461.     for (j = 0; j < 256; j++)
  462.       curves_dialog->curve[i][j] = j;
  463.   
  464.   for (i = 0; i < 5; i++)
  465.     {
  466.       curves_channel_reset (i);
  467.     }
  468.  
  469.   curves_dialog->drawable  = gimage_active_drawable (gdisp->gimage);
  470.   curves_dialog->color     = drawable_color (curves_dialog->drawable);
  471.   curves_dialog->image_map = image_map_create (gdisp, curves_dialog->drawable);
  472.  
  473.   /* check for alpha channel */
  474.   if (drawable_has_alpha (curves_dialog->drawable))
  475.     gtk_widget_set_sensitive (channel_items[4], TRUE);
  476.   else 
  477.     gtk_widget_set_sensitive (channel_items[4], FALSE);
  478.   
  479.   /*  hide or show the channel menu based on image type  */
  480.   if (curves_dialog->color)
  481.     for (i = 0; i < 4; i++) 
  482.        gtk_widget_set_sensitive (channel_items[i], TRUE);
  483.   else 
  484.     for (i = 1; i < 4; i++) 
  485.       gtk_widget_set_sensitive (channel_items[i], FALSE);
  486.  
  487.   /* set the current selection */
  488.   gtk_option_menu_set_history (GTK_OPTION_MENU (curves_dialog->channel_menu),
  489.                    curves_dialog->channel);
  490.  
  491.   gimp_lut_setup (curves_dialog->lut, 
  492.           (GimpLutFunc) curves_lut_func,
  493.                   (void *) curves_dialog, 
  494.           gimp_drawable_bytes (curves_dialog->drawable));
  495.  
  496.   if (!GTK_WIDGET_VISIBLE (curves_dialog->shell))
  497.     gtk_widget_show (curves_dialog->shell);
  498.  
  499.   curves_update (curves_dialog, GRAPH | DRAW);
  500. }
  501.  
  502. void
  503. curves_free (void)
  504. {
  505.   if (curves_dialog)
  506.     {
  507.       if (curves_dialog->image_map)
  508.     {
  509.       active_tool->preserve = TRUE;
  510.       image_map_abort (curves_dialog->image_map);
  511.       active_tool->preserve = FALSE;
  512.  
  513.       curves_dialog->image_map = NULL;
  514.     }
  515.  
  516.       if (curves_dialog->pixmap)
  517.     gdk_pixmap_unref (curves_dialog->pixmap);
  518.  
  519.       gtk_widget_destroy (curves_dialog->shell);
  520.     }
  521. }
  522.  
  523. /*******************/
  524. /*  Curves dialog  */
  525. /*******************/
  526.  
  527. static CurvesDialog *
  528. curves_dialog_new (void)
  529. {
  530.   CurvesDialog *cd;
  531.   GtkWidget *vbox;
  532.   GtkWidget *hbox;
  533.   GtkWidget *hbbox;
  534.   GtkWidget *label;
  535.   GtkWidget *frame;
  536.   GtkWidget *toggle;
  537.   GtkWidget *channel_hbox;
  538.   GtkWidget *table;
  539.   GtkWidget *button;
  540.   gint i, j;
  541.  
  542.   cd = g_new (CurvesDialog, 1);
  543.   cd->cursor_ind_height = -1;
  544.   cd->cursor_ind_width  = -1;
  545.   cd->preview           = TRUE;
  546.   cd->pixmap            = NULL;
  547.   cd->channel           = GIMP_HISTOGRAM_VALUE;
  548.  
  549.   for (i = 0; i < 5; i++)
  550.     cd->curve_type[i] = SMOOTH;
  551.  
  552.   for (i = 0; i < 5; i++)
  553.     for (j = 0; j < 256; j++)
  554.       cd->curve[i][j] = j;
  555.  
  556.   for (i = 0; i < (sizeof (cd->col_value) / sizeof (cd->col_value[0])); i++)
  557.     cd->col_value[i] = 0;
  558.  
  559.   cd->lut = gimp_lut_new ();
  560.  
  561.   /*  The shell and main vbox  */
  562.   cd->shell = gimp_dialog_new (_("Curves"), "curves",
  563.                    tools_help_func, tool_info[CURVES].private_tip,
  564.                    GTK_WIN_POS_NONE,
  565.                    FALSE, TRUE, FALSE,
  566.  
  567.                    _("OK"), curves_ok_callback,
  568.                    cd, NULL, NULL, TRUE, FALSE,
  569.                    _("Reset"), curves_reset_callback,
  570.                    cd, NULL, NULL, FALSE, FALSE,
  571.                    _("Cancel"), curves_cancel_callback,
  572.                    cd, NULL, NULL, FALSE, TRUE,
  573.  
  574.                    NULL);
  575.  
  576.   vbox = gtk_vbox_new (FALSE, 4);
  577.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  578.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (cd->shell)->vbox), vbox);
  579.  
  580.   /*  The option menu for selecting channels  */
  581.   channel_hbox = gtk_hbox_new (FALSE, 4);
  582.   gtk_box_pack_start (GTK_BOX (vbox), channel_hbox, FALSE, FALSE, 0);
  583.  
  584.   label = gtk_label_new (_("Modify Curves for Channel:"));
  585.   gtk_box_pack_start (GTK_BOX (channel_hbox), label, FALSE, FALSE, 0);
  586.  
  587.   cd->channel_menu = gimp_option_menu_new2
  588.     (FALSE, curves_channel_callback,
  589.      cd, (gpointer) cd->channel,
  590.  
  591.      _("Value"), (gpointer) GIMP_HISTOGRAM_VALUE, &channel_items[0],
  592.      _("Red"),   (gpointer) GIMP_HISTOGRAM_RED,   &channel_items[1],
  593.      _("Green"), (gpointer) GIMP_HISTOGRAM_GREEN, &channel_items[2],
  594.      _("Blue"),  (gpointer) GIMP_HISTOGRAM_BLUE,  &channel_items[3],
  595.      _("Alpha"), (gpointer) GIMP_HISTOGRAM_ALPHA, &channel_items[4],
  596.  
  597.      NULL);
  598.  
  599.   gtk_box_pack_start (GTK_BOX (channel_hbox), cd->channel_menu, FALSE, FALSE, 2);
  600.  
  601.   gtk_widget_show (label);
  602.   gtk_widget_show (cd->channel_menu);
  603.   gtk_widget_show (channel_hbox);
  604.  
  605.   /*  The table for the yrange and the graph  */
  606.   table = gtk_table_new (2, 2, FALSE);
  607.   gtk_table_set_col_spacings (GTK_TABLE (table), 2);
  608.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  609.   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  610.  
  611.   /*  The range drawing area  */
  612.   frame = gtk_frame_new (NULL);
  613.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  614.   gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 0, 1,
  615.             GTK_EXPAND, GTK_EXPAND, 0, 0);
  616.  
  617.   cd->yrange = gtk_preview_new (GTK_PREVIEW_COLOR);
  618.   gtk_preview_size (GTK_PREVIEW (cd->yrange), YRANGE_WIDTH, YRANGE_HEIGHT);
  619.   gtk_widget_set_events (cd->yrange, RANGE_MASK);
  620.   gtk_container_add (GTK_CONTAINER (frame), cd->yrange);
  621.  
  622.   gtk_signal_connect (GTK_OBJECT (cd->yrange), "event",
  623.               GTK_SIGNAL_FUNC (curves_yrange_events),
  624.               cd);
  625.  
  626.   gtk_widget_show (cd->yrange);
  627.   gtk_widget_show (frame);
  628.  
  629.   /*  The curves graph  */
  630.   frame = gtk_frame_new (NULL);
  631.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  632.   gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 0, 1,
  633.             GTK_SHRINK | GTK_FILL,
  634.             GTK_SHRINK | GTK_FILL, 0, 0);
  635.  
  636.   cd->graph = gtk_drawing_area_new ();
  637.   gtk_drawing_area_size (GTK_DRAWING_AREA (cd->graph),
  638.              GRAPH_WIDTH + RADIUS * 2,
  639.              GRAPH_HEIGHT + RADIUS * 2);
  640.   gtk_widget_set_events (cd->graph, GRAPH_MASK);
  641.   gtk_container_add (GTK_CONTAINER (frame), cd->graph);
  642.  
  643.   gtk_signal_connect (GTK_OBJECT (cd->graph), "event",
  644.               GTK_SIGNAL_FUNC (curves_graph_events),
  645.               cd);
  646.  
  647.   gtk_widget_show (cd->graph);
  648.   gtk_widget_show (frame);
  649.  
  650.   /*  The range drawing area  */
  651.   frame = gtk_frame_new (NULL);
  652.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  653.   gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 1, 2,
  654.             GTK_EXPAND, GTK_EXPAND, 0, 0);
  655.  
  656.   cd->xrange = gtk_preview_new (GTK_PREVIEW_COLOR);
  657.   gtk_preview_size (GTK_PREVIEW (cd->xrange), XRANGE_WIDTH, XRANGE_HEIGHT);
  658.   gtk_widget_set_events (cd->xrange, RANGE_MASK);
  659.   gtk_container_add (GTK_CONTAINER (frame), cd->xrange);
  660.  
  661.   gtk_signal_connect (GTK_OBJECT (cd->xrange), "event",
  662.               GTK_SIGNAL_FUNC (curves_xrange_events),
  663.               cd);
  664.  
  665.   gtk_widget_show (cd->xrange);
  666.   gtk_widget_show (frame);
  667.   gtk_widget_show (table);
  668.  
  669.   /*  Horizontal box for preview  */
  670.   hbox = gtk_hbox_new (FALSE, 4);
  671.   gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  672.  
  673.   /*  The option menu for selecting the drawing method  */
  674.   label = gtk_label_new (_("Curve Type:"));
  675.   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  676.   gtk_widget_show (label);
  677.  
  678.   cd->curve_type_menu = gimp_option_menu_new
  679.     (FALSE,
  680.  
  681.      _("Smooth"), curves_smooth_callback, cd, NULL, NULL, TRUE,
  682.      _("Free"),   curves_free_callback,   cd, NULL, NULL, FALSE,
  683.  
  684.      NULL);
  685.   gtk_box_pack_start (GTK_BOX (hbox), cd->curve_type_menu, FALSE, FALSE, 2);
  686.   gtk_widget_show (cd->curve_type_menu);
  687.  
  688.   gtk_widget_show (hbox);
  689.  
  690.   /*  The preview toggle  */
  691.   toggle = gtk_check_button_new_with_label (_("Preview"));
  692.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->preview);
  693.   gtk_box_pack_end (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
  694.  
  695.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  696.               GTK_SIGNAL_FUNC (curves_preview_update),
  697.               cd);
  698.  
  699.   gtk_widget_show (toggle);
  700.   gtk_widget_show (hbox);
  701.  
  702.   /*  Horizontal button box for load / save  */
  703.   hbbox = gtk_hbutton_box_new ();
  704.   gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbbox), 4);
  705.   gtk_button_box_set_layout (GTK_BUTTON_BOX (hbbox), GTK_BUTTONBOX_SPREAD);
  706.   gtk_box_pack_end (GTK_BOX (vbox), hbbox, FALSE, FALSE, 0);
  707.  
  708.   button = gtk_button_new_with_label (_("Load"));
  709.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  710.   gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
  711.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  712.               GTK_SIGNAL_FUNC (curves_load_callback),
  713.               cd->shell);
  714.   gtk_widget_show (button);
  715.  
  716.   button = gtk_button_new_with_label (_("Save"));
  717.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  718.   gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
  719.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  720.               GTK_SIGNAL_FUNC (curves_save_callback),
  721.               cd->shell);
  722.   gtk_widget_show (button);
  723.  
  724.   gtk_widget_show (hbbox);
  725.   gtk_widget_show (vbox);
  726.  
  727.   return cd;
  728. }
  729.  
  730. static void
  731. curve_print_loc (CurvesDialog *cd,
  732.          gint          xpos,
  733.          gint          ypos)
  734. {
  735.   gchar buf[32];
  736.   gint  width;
  737.   gint  ascent;
  738.   gint  descent;
  739.  
  740.   if (cd->cursor_ind_width < 0)
  741.     {
  742.       /* Calc max extents */
  743.       gdk_string_extents (cd->graph->style->font,
  744.               "x:888 y:888",
  745.               NULL,
  746.               NULL,
  747.               &width,
  748.               &ascent,
  749.               &descent);
  750.     
  751.       cd->cursor_ind_width = width;
  752.       cd->cursor_ind_height = ascent + descent;
  753.       cd->cursor_ind_ascent = ascent;
  754.     }
  755.   
  756.   if (xpos >= 0 && xpos <= 255 && ypos >=0 && ypos <= 255)
  757.     {
  758.       g_snprintf (buf, sizeof (buf), "x:%d y:%d",xpos,ypos);
  759.  
  760.       gdk_draw_rectangle (cd->graph->window, 
  761.               cd->graph->style->bg_gc[GTK_STATE_ACTIVE],
  762.               TRUE, RADIUS*2 + 2 , RADIUS*2 + 2, 
  763.               cd->cursor_ind_width+4, 
  764.               cd->cursor_ind_height+5);
  765.  
  766.       gdk_draw_rectangle (cd->graph->window, 
  767.               cd->graph->style->black_gc,
  768.               FALSE, RADIUS*2 + 2 , RADIUS*2 + 2, 
  769.               cd->cursor_ind_width+3, 
  770.               cd->cursor_ind_height+4);
  771.  
  772.       gdk_draw_string (cd->graph->window,
  773.                cd->graph->style->font,
  774.                cd->graph->style->black_gc,
  775.                RADIUS*2 + 4,
  776.                RADIUS*2 + 5 + cd->cursor_ind_ascent,
  777.                buf);
  778.     }
  779. }
  780.  
  781. /* TODO: preview alpha channel stuff correctly.  -- austin, 20/May/99 */
  782. static void
  783. curves_update (CurvesDialog *cd,
  784.            gint          update)
  785. {
  786.   GdkRectangle area;
  787.   gint   i, j;
  788.   gchar  buf[32];
  789.   gint   offset;
  790.   gint   sel_channel;
  791.   
  792.   if(cd->color) {
  793.     sel_channel = cd->channel;
  794.   } else {
  795.     if(cd->channel == 2)
  796.       sel_channel = GIMP_HISTOGRAM_ALPHA;
  797.     else
  798.       sel_channel = GIMP_HISTOGRAM_VALUE;
  799.   }
  800.  
  801.  
  802.   if (update & XRANGE_TOP)
  803.     {
  804.       guchar buf[XRANGE_WIDTH * 3];
  805.  
  806.       switch (sel_channel)
  807.     {
  808.     case GIMP_HISTOGRAM_VALUE:
  809.     case GIMP_HISTOGRAM_ALPHA:
  810.       for (i = 0; i < XRANGE_HEIGHT / 2; i++)
  811.         {
  812.           for (j = 0; j < XRANGE_WIDTH ; j++)
  813.         {
  814.           buf[j*3+0] = cd->curve[sel_channel][j];
  815.           buf[j*3+1] = cd->curve[sel_channel][j];
  816.           buf[j*3+2] = cd->curve[sel_channel][j];
  817.         }
  818.           gtk_preview_draw_row (GTK_PREVIEW (cd->xrange),
  819.                     buf, 0, i, XRANGE_WIDTH);
  820.         }
  821.       break;
  822.  
  823.     case GIMP_HISTOGRAM_RED:
  824.     case GIMP_HISTOGRAM_GREEN:
  825.     case GIMP_HISTOGRAM_BLUE:
  826.       {
  827.         for (i = 0; i < XRANGE_HEIGHT / 2; i++)
  828.           {
  829.         for (j = 0; j < XRANGE_WIDTH; j++)
  830.           {
  831.             buf[j*3+0] = cd->curve[GIMP_HISTOGRAM_RED][j];
  832.             buf[j*3+1] = cd->curve[GIMP_HISTOGRAM_GREEN][j];
  833.             buf[j*3+2] = cd->curve[GIMP_HISTOGRAM_BLUE][j];
  834.           }
  835.         gtk_preview_draw_row (GTK_PREVIEW (cd->xrange),
  836.                       buf, 0, i, XRANGE_WIDTH);
  837.           }
  838.         break;
  839.       }
  840.  
  841.     default:
  842.       g_warning ("unknown channel type %d, can't happen!?!?",
  843.              cd->channel);
  844.       break;
  845.     }
  846.  
  847.       if (update & DRAW)
  848.     {
  849.       area.x = 0;
  850.       area.y = 0;
  851.       area.width = XRANGE_WIDTH;
  852.       area.height = XRANGE_HEIGHT / 2;
  853.       gtk_widget_draw (cd->xrange, &area);
  854.     }
  855.     }
  856.   if (update & XRANGE_BOTTOM)
  857.     {
  858.       guchar buf[XRANGE_WIDTH * 3];
  859.  
  860.       for (i = 0; i < XRANGE_WIDTH; i++)
  861.       {
  862.     buf[i*3+0] = i;
  863.     buf[i*3+1] = i;
  864.     buf[i*3+2] = i;
  865.       }
  866.  
  867.       for (i = XRANGE_HEIGHT / 2; i < XRANGE_HEIGHT; i++)
  868.     gtk_preview_draw_row (GTK_PREVIEW (cd->xrange), buf, 0, i, XRANGE_WIDTH);
  869.  
  870.       if (update & DRAW)
  871.     {
  872.       area.x = 0;
  873.       area.y = XRANGE_HEIGHT / 2;
  874.       area.width = XRANGE_WIDTH;
  875.       area.height = XRANGE_HEIGHT / 2;
  876.       gtk_widget_draw (cd->xrange, &area);
  877.     }
  878.     }
  879.   if (update & YRANGE)
  880.     {
  881.       guchar buf[YRANGE_WIDTH * 3];
  882.       guchar pix[3];
  883.  
  884.       for (i = 0; i < YRANGE_HEIGHT; i++)
  885.     {
  886.       switch (sel_channel)
  887.         {
  888.         case GIMP_HISTOGRAM_VALUE:
  889.         case GIMP_HISTOGRAM_ALPHA:
  890.           pix[0] = pix[1] = pix[2] = (255 - i);
  891.           break;
  892.  
  893.         case GIMP_HISTOGRAM_RED:
  894.         case GIMP_HISTOGRAM_GREEN:
  895.         case GIMP_HISTOGRAM_BLUE:
  896.           pix[0] = pix[1] = pix[2] = 0;
  897.           pix[sel_channel - 1] = (255 - i);
  898.           break;
  899.  
  900.         default:
  901.           g_warning ("unknown channel type %d, can't happen!?!?",
  902.              cd->channel);
  903.           break;
  904.         }
  905.  
  906.       for (j = 0; j < YRANGE_WIDTH * 3; j++)
  907.         buf[j] = pix[j%3];
  908.  
  909.       gtk_preview_draw_row (GTK_PREVIEW (cd->yrange),
  910.                 buf, 0, i, YRANGE_WIDTH);
  911.     }
  912.  
  913.       if (update & DRAW)
  914.     gtk_widget_draw (cd->yrange, NULL);
  915.     }
  916.   if ((update & GRAPH) && (update & DRAW) && cd->pixmap != NULL)
  917.     {
  918.       GdkPoint points[256];
  919.  
  920.       /*  Clear the pixmap  */
  921.       gdk_draw_rectangle (cd->pixmap, cd->graph->style->bg_gc[GTK_STATE_NORMAL],
  922.               TRUE, 0, 0,
  923.               GRAPH_WIDTH + RADIUS * 2, GRAPH_HEIGHT + RADIUS * 2);
  924.  
  925.       /*  Draw the grid lines  */
  926.       for (i = 0; i < 5; i++)
  927.     {
  928.       gdk_draw_line (cd->pixmap, cd->graph->style->dark_gc[GTK_STATE_NORMAL],
  929.              RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS,
  930.              GRAPH_WIDTH + RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS);
  931.       gdk_draw_line (cd->pixmap, cd->graph->style->dark_gc[GTK_STATE_NORMAL],
  932.              i * (GRAPH_WIDTH / 4) + RADIUS, RADIUS,
  933.              i * (GRAPH_WIDTH / 4) + RADIUS, GRAPH_HEIGHT + RADIUS);
  934.     }
  935.  
  936.       /*  Draw the curve  */
  937.       for (i = 0; i < 256; i++)
  938.     {
  939.       points[i].x = i + RADIUS;
  940.       points[i].y = 255 - cd->curve[cd->channel][i] + RADIUS;
  941.     }
  942.       gdk_draw_points (cd->pixmap, cd->graph->style->black_gc, points, 256);
  943.  
  944.       /*  Draw the points  */
  945.       if (cd->curve_type[cd->channel] == SMOOTH)
  946.     for (i = 0; i < 17; i++)
  947.       {
  948.         if (cd->points[cd->channel][i][0] != -1)
  949.           gdk_draw_arc (cd->pixmap, cd->graph->style->black_gc, TRUE,
  950.                 cd->points[cd->channel][i][0],
  951.                 255 - cd->points[cd->channel][i][1],
  952.                 RADIUS * 2, RADIUS * 2, 0, 23040);
  953.       }
  954.  
  955.       /* draw the colour line */
  956.       gdk_draw_line (cd->pixmap, cd->graph->style->black_gc,
  957.              cd->col_value[sel_channel]+RADIUS,RADIUS,
  958.              cd->col_value[sel_channel]+RADIUS,GRAPH_HEIGHT + RADIUS);
  959.  
  960.       /* and xpos indicator */
  961.       g_snprintf (buf, sizeof (buf), "x:%d",cd->col_value[sel_channel]);
  962.       
  963.       if ((cd->col_value[sel_channel]+RADIUS) < 127)
  964.     {
  965.       offset = RADIUS + 4;
  966.     }
  967.       else
  968.     {
  969.       offset = - gdk_string_width (cd->graph->style->font,buf) - 2;
  970.     }
  971.  
  972.       gdk_draw_string (cd->pixmap,
  973.                cd->graph->style->font,
  974.                cd->graph->style->black_gc,
  975.                cd->col_value[sel_channel]+offset,
  976.                GRAPH_HEIGHT,
  977.                buf);
  978.  
  979.       gdk_draw_pixmap (cd->graph->window, cd->graph->style->black_gc, cd->pixmap,
  980.                0, 0, 0, 0, GRAPH_WIDTH + RADIUS * 2, GRAPH_HEIGHT + RADIUS * 2);
  981.       
  982.     }
  983. }
  984.  
  985. static void
  986. curves_plot_curve (CurvesDialog *cd,
  987.            gint          p1,
  988.            gint          p2,
  989.            gint          p3,
  990.            gint          p4)
  991. {
  992.   CRMatrix geometry;
  993.   CRMatrix tmp1, tmp2;
  994.   CRMatrix deltas;
  995.   gdouble  x, dx, dx2, dx3;
  996.   gdouble  y, dy, dy2, dy3;
  997.   gdouble  d, d2, d3;
  998.   gint     lastx, lasty;
  999.   gint32   newx, newy;
  1000.   gint     i;
  1001.  
  1002.   /* construct the geometry matrix from the segment */
  1003.   for (i = 0; i < 4; i++)
  1004.     {
  1005.       geometry[i][2] = 0;
  1006.       geometry[i][3] = 0;
  1007.     }
  1008.  
  1009.   for (i = 0; i < 2; i++)
  1010.     {
  1011.       geometry[0][i] = cd->points[cd->channel][p1][i];
  1012.       geometry[1][i] = cd->points[cd->channel][p2][i];
  1013.       geometry[2][i] = cd->points[cd->channel][p3][i];
  1014.       geometry[3][i] = cd->points[cd->channel][p4][i];
  1015.     }
  1016.  
  1017.   /* subdivide the curve 1000 times */
  1018.   /* n can be adjusted to give a finer or coarser curve */
  1019.   d = 1.0 / 1000;
  1020.   d2 = d * d;
  1021.   d3 = d * d * d;
  1022.  
  1023.   /* construct a temporary matrix for determining the forward differencing deltas */
  1024.   tmp2[0][0] = 0;     tmp2[0][1] = 0;     tmp2[0][2] = 0;    tmp2[0][3] = 1;
  1025.   tmp2[1][0] = d3;    tmp2[1][1] = d2;    tmp2[1][2] = d;    tmp2[1][3] = 0;
  1026.   tmp2[2][0] = 6*d3;  tmp2[2][1] = 2*d2;  tmp2[2][2] = 0;    tmp2[2][3] = 0;
  1027.   tmp2[3][0] = 6*d3;  tmp2[3][1] = 0;     tmp2[3][2] = 0;    tmp2[3][3] = 0;
  1028.  
  1029.   /* compose the basis and geometry matrices */
  1030.   curves_CR_compose (CR_basis, geometry, tmp1);
  1031.  
  1032.   /* compose the above results to get the deltas matrix */
  1033.   curves_CR_compose (tmp2, tmp1, deltas);
  1034.  
  1035.   /* extract the x deltas */
  1036.   x = deltas[0][0];
  1037.   dx = deltas[1][0];
  1038.   dx2 = deltas[2][0];
  1039.   dx3 = deltas[3][0];
  1040.  
  1041.   /* extract the y deltas */
  1042.   y = deltas[0][1];
  1043.   dy = deltas[1][1];
  1044.   dy2 = deltas[2][1];
  1045.   dy3 = deltas[3][1];
  1046.  
  1047.   lastx = CLAMP (x, 0, 255);
  1048.   lasty = CLAMP (y, 0, 255);
  1049.  
  1050.   cd->curve[cd->channel][lastx] = lasty;
  1051.  
  1052.   /* loop over the curve */
  1053.   for (i = 0; i < 1000; i++)
  1054.     {
  1055.       /* increment the x values */
  1056.       x += dx;
  1057.       dx += dx2;
  1058.       dx2 += dx3;
  1059.  
  1060.       /* increment the y values */
  1061.       y += dy;
  1062.       dy += dy2;
  1063.       dy2 += dy3;
  1064.  
  1065.       newx = CLAMP0255 (ROUND (x));
  1066.       newy = CLAMP0255 (ROUND (y));
  1067.  
  1068.       /* if this point is different than the last one...then draw it */
  1069.       if ((lastx != newx) || (lasty != newy))
  1070.     cd->curve[cd->channel][newx] = newy;
  1071.  
  1072.       lastx = newx;
  1073.       lasty = newy;
  1074.     }
  1075. }
  1076.  
  1077. void
  1078. curves_calculate_curve (CurvesDialog *cd)
  1079. {
  1080.   gint i;
  1081.   gint points[17];
  1082.   gint num_pts;
  1083.   gint p1, p2, p3, p4;
  1084.  
  1085.   switch (cd->curve_type[cd->channel])
  1086.     {
  1087.     case GFREE:
  1088.       break;
  1089.     case SMOOTH:
  1090.       /*  cycle through the curves  */
  1091.       num_pts = 0;
  1092.       for (i = 0; i < 17; i++)
  1093.     if (cd->points[cd->channel][i][0] != -1)
  1094.       points[num_pts++] = i;
  1095.  
  1096.       /*  Initialize boundary curve points */
  1097.       if (num_pts != 0)
  1098.     {
  1099.       for (i = 0; i < cd->points[cd->channel][points[0]][0]; i++)
  1100.         cd->curve[cd->channel][i] = cd->points[cd->channel][points[0]][1];
  1101.       for (i = cd->points[cd->channel][points[num_pts - 1]][0]; i < 256; i++)
  1102.         cd->curve[cd->channel][i] = cd->points[cd->channel][points[num_pts - 1]][1];
  1103.     }
  1104.  
  1105.       for (i = 0; i < num_pts - 1; i++)
  1106.     {
  1107.       p1 = (i == 0) ? points[i] : points[(i - 1)];
  1108.       p2 = points[i];
  1109.       p3 = points[(i + 1)];
  1110.       p4 = (i == (num_pts - 2)) ? points[(num_pts - 1)] : points[(i + 2)];
  1111.  
  1112.       curves_plot_curve (cd, p1, p2, p3, p4);
  1113.     }
  1114.       break;
  1115.     }
  1116.   gimp_lut_setup (cd->lut, (GimpLutFunc) curves_lut_func,
  1117.           (void *) cd, gimp_drawable_bytes (cd->drawable));
  1118.  
  1119. }
  1120.  
  1121. static void
  1122. curves_preview (CurvesDialog *cd)
  1123. {
  1124.   if (!cd->image_map)
  1125.     {
  1126.       g_message ("curves_preview(): No image map");
  1127.       return;
  1128.     }
  1129.  
  1130.   active_tool->preserve = TRUE;
  1131.   image_map_apply (cd->image_map,  (ImageMapApplyFunc)gimp_lut_process_2,
  1132.            (void *) cd->lut);
  1133.   active_tool->preserve = FALSE;
  1134. }
  1135.  
  1136. static void
  1137. curves_channel_callback (GtkWidget *widget,
  1138.              gpointer   data)
  1139. {
  1140.   CurvesDialog *cd;
  1141.  
  1142.   cd = (CurvesDialog *) data;
  1143.  
  1144.   gimp_menu_item_update (widget, &cd->channel);
  1145.  
  1146.   if(!cd->color) {
  1147.     if(cd->channel > 1)
  1148.       {
  1149.     cd->channel = 2;
  1150.       }
  1151.     else 
  1152.       {
  1153.     cd->channel = 1;
  1154.       }
  1155.   } 
  1156.  
  1157.   gtk_option_menu_set_history (GTK_OPTION_MENU (cd->curve_type_menu), 
  1158.                    cd->curve_type[cd->channel]);
  1159.  
  1160.   curves_update (cd, XRANGE_TOP | YRANGE | GRAPH | DRAW);
  1161. }
  1162.  
  1163. static void
  1164. curves_smooth_callback (GtkWidget *widget,
  1165.             gpointer   data)
  1166. {
  1167.   CurvesDialog *cd;
  1168.   gint   i;
  1169.   gint32 index;
  1170.  
  1171.   cd = (CurvesDialog *) data;
  1172.  
  1173.   if (cd->curve_type[cd->channel] != SMOOTH)
  1174.     {
  1175.       cd->curve_type[cd->channel] = SMOOTH;
  1176.  
  1177.       /*  pick representative points from the curve and make them control points  */
  1178.       for (i = 0; i <= 8; i++)
  1179.     {
  1180.       index = CLAMP0255 (i * 32);
  1181.       cd->points[cd->channel][i * 2][0] = index;
  1182.       cd->points[cd->channel][i * 2][1] = cd->curve[cd->channel][index];
  1183.     }
  1184.  
  1185.       curves_calculate_curve (cd);
  1186.       curves_update (cd, GRAPH | DRAW);
  1187.  
  1188.       if (cd->preview)
  1189.     curves_preview (cd);
  1190.     }
  1191. }
  1192.  
  1193. static void
  1194. curves_free_callback (GtkWidget *widget,
  1195.               gpointer   data)
  1196. {
  1197.   CurvesDialog *cd;
  1198.  
  1199.   cd = (CurvesDialog *) data;
  1200.  
  1201.   if (cd->curve_type[cd->channel] != GFREE)
  1202.     {
  1203.       cd->curve_type[cd->channel] = GFREE;
  1204.       curves_calculate_curve (cd);
  1205.       curves_update (cd, GRAPH | DRAW);
  1206.  
  1207.       if (cd->preview)
  1208.     curves_preview (cd);
  1209.     }
  1210. }
  1211.  
  1212. static void
  1213. curves_channel_reset (int i)
  1214. {
  1215.   gint j;
  1216.  
  1217.   curves_dialog->grab_point = -1;
  1218.  
  1219.   for (j = 0; j < 17; j++)
  1220.   {
  1221.     curves_dialog->points[i][j][0] = -1;
  1222.     curves_dialog->points[i][j][1] = -1;
  1223.   }
  1224.   curves_dialog->points[i][0][0] = 0;
  1225.   curves_dialog->points[i][0][1] = 0;
  1226.   curves_dialog->points[i][16][0] = 255;
  1227.   curves_dialog->points[i][16][1] = 255;
  1228. }
  1229.  
  1230.  
  1231. static void
  1232. curves_reset_callback (GtkWidget *widget,
  1233.                gpointer   data)
  1234. {
  1235.   CurvesDialog *cd;
  1236.   gint i;
  1237.  
  1238.   cd = (CurvesDialog *) data;
  1239.  
  1240.   /*  Initialize the values  */
  1241.   for (i = 0; i < 256; i++)
  1242.     cd->curve[cd->channel][i] = i;
  1243.  
  1244.   curves_channel_reset (cd->channel);
  1245.  
  1246.   curves_calculate_curve (cd);
  1247.   curves_update (cd, GRAPH | XRANGE_TOP | DRAW);
  1248.  
  1249.   if (cd->preview)
  1250.     curves_preview (cd);
  1251. }
  1252.  
  1253. static void
  1254. curves_ok_callback (GtkWidget *widget,
  1255.             gpointer   data)
  1256. {
  1257.   CurvesDialog *cd;
  1258.  
  1259.   cd = (CurvesDialog *) data;
  1260.  
  1261.   gimp_dialog_hide (cd->shell);
  1262.  
  1263.   active_tool->preserve = TRUE;  /* We're about to dirty... */
  1264.  
  1265.   if (!cd->preview)
  1266.     image_map_apply (cd->image_map, (ImageMapApplyFunc)gimp_lut_process_2,
  1267.              (void *) cd->lut);
  1268.  
  1269.   if (cd->image_map)
  1270.     image_map_commit (cd->image_map);
  1271.  
  1272.   active_tool->preserve = FALSE;
  1273.  
  1274.   cd->image_map = NULL;
  1275.  
  1276.   active_tool->gdisp_ptr = NULL;
  1277.   active_tool->drawable = NULL;
  1278. }
  1279.  
  1280. static void
  1281. curves_cancel_callback (GtkWidget *widget,
  1282.             gpointer   data)
  1283. {
  1284.   CurvesDialog *cd;
  1285.  
  1286.   cd = (CurvesDialog *) data;
  1287.  
  1288.   gimp_dialog_hide (cd->shell);
  1289.  
  1290.   if (cd->image_map)
  1291.     {
  1292.       active_tool->preserve = TRUE;
  1293.       image_map_abort (cd->image_map);
  1294.       active_tool->preserve = FALSE;
  1295.  
  1296.       gdisplays_flush ();
  1297.       cd->image_map = NULL;
  1298.     }
  1299.  
  1300.   active_tool->gdisp_ptr = NULL;
  1301.   active_tool->drawable = NULL;
  1302. }
  1303.  
  1304. static void
  1305. curves_load_callback (GtkWidget *widget,
  1306.               gpointer   data)
  1307. {
  1308.   if (!file_dlg)
  1309.     file_dialog_create (GTK_WIDGET (data));
  1310.   else if (GTK_WIDGET_VISIBLE (file_dlg)) 
  1311.     return;
  1312.  
  1313.   load_save = TRUE;
  1314.  
  1315.   gtk_window_set_title (GTK_WINDOW (file_dlg), _("Load Curves"));
  1316.   gtk_widget_show (file_dlg);
  1317. }
  1318.  
  1319. static void
  1320. curves_save_callback (GtkWidget *widget,
  1321.               gpointer   data)
  1322. {
  1323.   if (!file_dlg)
  1324.     file_dialog_create (GTK_WIDGET (data));
  1325.   else if (GTK_WIDGET_VISIBLE (file_dlg)) 
  1326.     return;
  1327.  
  1328.   load_save = FALSE;
  1329.  
  1330.   gtk_window_set_title (GTK_WINDOW (file_dlg), _("Save Curves"));
  1331.   gtk_widget_show (file_dlg);
  1332. }
  1333.  
  1334. static void
  1335. curves_preview_update (GtkWidget *widget,
  1336.                gpointer   data)
  1337. {
  1338.   CurvesDialog *cd;
  1339.  
  1340.   cd = (CurvesDialog *) data;
  1341.   
  1342.   if (GTK_TOGGLE_BUTTON (widget)->active)
  1343.     {
  1344.       cd->preview = TRUE;
  1345.       curves_preview (cd);
  1346.     }
  1347.   else
  1348.     {
  1349.       cd->preview = FALSE;
  1350.       if (cd->image_map)
  1351.     {
  1352.       active_tool->preserve = TRUE;
  1353.       image_map_clear (cd->image_map);
  1354.       active_tool->preserve = FALSE;
  1355.       gdisplays_flush ();
  1356.     }
  1357.     }
  1358. }
  1359.  
  1360. static gint
  1361. curves_graph_events (GtkWidget    *widget,
  1362.              GdkEvent     *event,
  1363.              CurvesDialog *cd)
  1364. {
  1365.   static GdkCursorType cursor_type = GDK_TOP_LEFT_ARROW;
  1366.   GdkCursorType new_type;
  1367.   GdkEventButton *bevent;
  1368.   GdkEventMotion *mevent;
  1369.   gint i;
  1370.   gint tx, ty;
  1371.   gint x, y;
  1372.   gint closest_point;
  1373.   gint distance;
  1374.   gint x1, x2, y1, y2;
  1375.  
  1376.   new_type      = GDK_X_CURSOR;
  1377.   closest_point = 0;
  1378.  
  1379.   /*  get the pointer position  */
  1380.   gdk_window_get_pointer (cd->graph->window, &tx, &ty, NULL);
  1381.   x = CLAMP ((tx - RADIUS), 0, 255);
  1382.   y = CLAMP ((ty - RADIUS), 0, 255);
  1383.  
  1384.   distance = G_MAXINT;
  1385.   for (i = 0; i < 17; i++)
  1386.     {
  1387.       if (cd->points[cd->channel][i][0] != -1)
  1388.     if (abs (x - cd->points[cd->channel][i][0]) < distance)
  1389.       {
  1390.         distance = abs (x - cd->points[cd->channel][i][0]);
  1391.         closest_point = i;
  1392.       }
  1393.     }
  1394.   if (distance > MIN_DISTANCE)
  1395.     closest_point = (x + 8) / 16;
  1396.  
  1397.   switch (event->type)
  1398.     {
  1399.     case GDK_EXPOSE:
  1400.       if (cd->pixmap == NULL)
  1401.     cd->pixmap = gdk_pixmap_new (cd->graph->window,
  1402.                      GRAPH_WIDTH + RADIUS * 2,
  1403.                      GRAPH_HEIGHT + RADIUS * 2, -1);
  1404.  
  1405.       curves_update (cd, GRAPH | DRAW);
  1406.       break;
  1407.  
  1408.     case GDK_BUTTON_PRESS:
  1409.       bevent = (GdkEventButton *) event;
  1410.       new_type = GDK_TCROSS;
  1411.  
  1412.       switch (cd->curve_type[cd->channel])
  1413.     {
  1414.     case SMOOTH:
  1415.       /*  determine the leftmost and rightmost points  */
  1416.       cd->leftmost = -1;
  1417.       for (i = closest_point - 1; i >= 0; i--)
  1418.         if (cd->points[cd->channel][i][0] != -1)
  1419.           {
  1420.         cd->leftmost = cd->points[cd->channel][i][0];
  1421.         break;
  1422.           }
  1423.       cd->rightmost = 256;
  1424.       for (i = closest_point + 1; i < 17; i++)
  1425.         if (cd->points[cd->channel][i][0] != -1)
  1426.           {
  1427.         cd->rightmost = cd->points[cd->channel][i][0];
  1428.         break;
  1429.           }
  1430.  
  1431.       cd->grab_point = closest_point;
  1432.       cd->points[cd->channel][cd->grab_point][0] = x;
  1433.       cd->points[cd->channel][cd->grab_point][1] = 255 - y;
  1434.  
  1435.       break;
  1436.  
  1437.     case GFREE:
  1438.       cd->curve[cd->channel][x] = 255 - y;
  1439.       cd->grab_point = x;
  1440.       cd->last = y;
  1441.       break;
  1442.     }
  1443.       
  1444.       curves_calculate_curve (cd);
  1445.       curves_update (cd, GRAPH | XRANGE_TOP | DRAW);
  1446.       gtk_grab_add (widget);
  1447.  
  1448.       break;
  1449.  
  1450.     case GDK_BUTTON_RELEASE:
  1451.       new_type = GDK_FLEUR;
  1452.       cd->grab_point = -1;
  1453.  
  1454.       if (cd->preview)
  1455.     curves_preview (cd);
  1456.  
  1457.       gtk_grab_remove (widget);
  1458.  
  1459.       break;
  1460.  
  1461.     case GDK_MOTION_NOTIFY:
  1462.       mevent = (GdkEventMotion *) event;
  1463.  
  1464.       if (mevent->is_hint)
  1465.     {
  1466.       mevent->x = tx;
  1467.       mevent->y = ty;
  1468.     }
  1469.  
  1470.       switch (cd->curve_type[cd->channel])
  1471.     {
  1472.     case SMOOTH:
  1473.       /*  If no point is grabbed...  */
  1474.       if (cd->grab_point == -1)
  1475.         {
  1476.           if (cd->points[cd->channel][closest_point][0] != -1)
  1477.         new_type = GDK_FLEUR;
  1478.           else
  1479.         new_type = GDK_TCROSS;
  1480.         }
  1481.       /*  Else, drag the grabbed point  */
  1482.       else
  1483.         {
  1484.           new_type = GDK_TCROSS;
  1485.  
  1486.           cd->points[cd->channel][cd->grab_point][0] = -1;
  1487.  
  1488.           if (x > cd->leftmost && x < cd->rightmost)
  1489.         {
  1490.           closest_point = (x + 8) / 16;
  1491.           if (cd->points[cd->channel][closest_point][0] == -1)
  1492.             cd->grab_point = closest_point;
  1493.           cd->points[cd->channel][cd->grab_point][0] = x;
  1494.           cd->points[cd->channel][cd->grab_point][1] = 255 - y;
  1495.         }
  1496.  
  1497.           curves_calculate_curve (cd);
  1498.           curves_update (cd, GRAPH | XRANGE_TOP | DRAW);
  1499.         }
  1500.       break;
  1501.  
  1502.     case GFREE:
  1503.       if (cd->grab_point != -1)
  1504.         {
  1505.           if (cd->grab_point > x)
  1506.         {
  1507.           x1 = x;
  1508.           x2 = cd->grab_point;
  1509.           y1 = y;
  1510.           y2 = cd->last;
  1511.         }
  1512.           else
  1513.         {
  1514.           x1 = cd->grab_point;
  1515.           x2 = x;
  1516.           y1 = cd->last;
  1517.           y2 = y;
  1518.         }
  1519.  
  1520.           if (x2 != x1)
  1521.         for (i = x1; i <= x2; i++)
  1522.           cd->curve[cd->channel][i] = 255 - (y1 + ((y2 - y1) * (i - x1)) / (x2 - x1));
  1523.           else
  1524.         cd->curve[cd->channel][x] = 255 - y;
  1525.  
  1526.           cd->grab_point = x;
  1527.           cd->last = y;
  1528.  
  1529.           curves_update (cd, GRAPH | XRANGE_TOP | DRAW);
  1530.         }
  1531.  
  1532.       if (mevent->state & GDK_BUTTON1_MASK)
  1533.         new_type = GDK_TCROSS;
  1534.       else
  1535.         new_type = GDK_PENCIL;
  1536.       break;
  1537.     }
  1538.  
  1539.       if (new_type != cursor_type)
  1540.     {
  1541.       cursor_type = new_type;
  1542.       change_win_cursor (cd->graph->window, cursor_type,
  1543.                  TOOL_TYPE_NONE, CURSOR_MODIFIER_NONE, FALSE);
  1544.     }
  1545.  
  1546.       curve_print_loc (cd, x, 255 - y);
  1547.  
  1548.       break;
  1549.  
  1550.     default:
  1551.       break;
  1552.     }
  1553.  
  1554.   return FALSE;
  1555. }
  1556.  
  1557. static gint
  1558. curves_xrange_events (GtkWidget    *widget,
  1559.               GdkEvent     *event,
  1560.               CurvesDialog *cd)
  1561. {
  1562.   switch (event->type)
  1563.     {
  1564.     case GDK_EXPOSE:
  1565.       curves_update (cd, XRANGE_TOP | XRANGE_BOTTOM);
  1566.       break;
  1567.  
  1568.     case GDK_DELETE:
  1569.       break;
  1570.  
  1571.     default:
  1572.       break;
  1573.     }
  1574.  
  1575.   return FALSE;
  1576. }
  1577.  
  1578. static gint
  1579. curves_yrange_events (GtkWidget    *widget,
  1580.               GdkEvent     *event,
  1581.               CurvesDialog *cd)
  1582. {
  1583.   switch (event->type)
  1584.     {
  1585.     case GDK_EXPOSE:
  1586.       curves_update (cd, YRANGE);
  1587.       break;
  1588.  
  1589.     default:
  1590.       break;
  1591.     }
  1592.  
  1593.   return FALSE;
  1594. }
  1595.  
  1596. static void
  1597. curves_CR_compose (CRMatrix a,
  1598.            CRMatrix b,
  1599.            CRMatrix ab)
  1600. {
  1601.   gint i, j;
  1602.  
  1603.   for (i = 0; i < 4; i++)
  1604.     {
  1605.       for (j = 0; j < 4; j++)
  1606.         {
  1607.           ab[i][j] = (a[i][0] * b[0][j] +
  1608.                       a[i][1] * b[1][j] +
  1609.                       a[i][2] * b[2][j] +
  1610.                       a[i][3] * b[3][j]);
  1611.         }
  1612.     }
  1613. }
  1614.  
  1615.  
  1616. static void
  1617. file_dialog_create (GtkWidget *parent)
  1618. {
  1619.   gchar *temp;
  1620.  
  1621.   file_dlg = gtk_file_selection_new (_("Load/Save Curves"));
  1622.   gtk_window_set_wmclass (GTK_WINDOW (file_dlg), "load_save_curves", "Gimp");
  1623.   gtk_window_set_position (GTK_WINDOW (file_dlg), GTK_WIN_POS_MOUSE);
  1624.  
  1625.   gtk_container_set_border_width (GTK_CONTAINER (file_dlg), 2);
  1626.   gtk_container_set_border_width (GTK_CONTAINER (GTK_FILE_SELECTION (file_dlg)->button_area), 2);
  1627.  
  1628.   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_dlg)->cancel_button),
  1629.               "clicked", GTK_SIGNAL_FUNC (file_dialog_cancel_callback),
  1630.               NULL);
  1631.   gtk_signal_connect (GTK_OBJECT (file_dlg), "delete_event",
  1632.               GTK_SIGNAL_FUNC (file_dialog_cancel_callback),
  1633.               NULL);
  1634.   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_dlg)->ok_button),
  1635.               "clicked", GTK_SIGNAL_FUNC (file_dialog_ok_callback),
  1636.               NULL);
  1637.  
  1638.   gtk_signal_connect (GTK_OBJECT (parent), "unmap",
  1639.               GTK_SIGNAL_FUNC (file_dialog_cancel_callback),
  1640.               NULL);
  1641.  
  1642.   temp = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "curves" G_DIR_SEPARATOR_S,
  1643.                     gimp_directory ());
  1644.   gtk_file_selection_set_filename (GTK_FILE_SELECTION (file_dlg), temp);
  1645.   g_free (temp);
  1646.  
  1647.   gimp_help_connect_help_accel (file_dlg, tools_help_func,
  1648.                 tool_info[CURVES].private_tip);
  1649. }
  1650.  
  1651. static void
  1652. file_dialog_ok_callback (GtkWidget *widget,
  1653.              gpointer   data)
  1654. {
  1655.   FILE  *f;
  1656.   gchar *filename;
  1657.  
  1658.   filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (file_dlg));
  1659.  
  1660.   if (load_save)
  1661.     {
  1662.       f = fopen (filename, "rt");
  1663.  
  1664.       if (!f)
  1665.     {
  1666.       g_message (_("Unable to open file %s"), filename);
  1667.       return;
  1668.     }
  1669.  
  1670.       if (!curves_read_from_file (f))
  1671.     {
  1672.       g_message (("Error in reading file %s"), filename);
  1673.       return;
  1674.     }
  1675.  
  1676.       fclose (f);
  1677.     }
  1678.   else
  1679.     {
  1680.       f = fopen (filename, "wt");
  1681.  
  1682.       if (!f)
  1683.     {
  1684.       g_message (_("Unable to open file %s"), filename);
  1685.       return;
  1686.     }
  1687.  
  1688.       curves_write_to_file (f);
  1689.  
  1690.       fclose (f);
  1691.     }
  1692.  
  1693.   file_dialog_cancel_callback (file_dlg, NULL);
  1694. }
  1695.  
  1696. static void
  1697. file_dialog_cancel_callback (GtkWidget *widget,
  1698.                  gpointer   data)
  1699. {
  1700.   gimp_dialog_hide (file_dlg);
  1701. }
  1702.  
  1703. static gboolean
  1704. curves_read_from_file (FILE *f)
  1705. {
  1706.   gint  i, j;
  1707.   gint  fields;
  1708.   gchar buf[50];
  1709.   gint  index[5][17];
  1710.   gint  value[5][17];
  1711.   gint  current_channel;
  1712.   
  1713.   if (!fgets (buf, 50, f))
  1714.     return FALSE;
  1715.  
  1716.   if (strcmp (buf, "# GIMP Curves File\n") != 0)
  1717.     return FALSE;
  1718.  
  1719.   for (i = 0; i < 5; i++)
  1720.     {
  1721.       for (j = 0; j < 17; j++)
  1722.     {
  1723.       fields = fscanf (f, "%d %d ", &index[i][j], &value[i][j]);
  1724.       if (fields != 2)
  1725.         {
  1726.           g_print ("fields != 2");
  1727.           return FALSE;
  1728.         }
  1729.     }
  1730.     }
  1731.  
  1732.   for (i = 0; i < 5; i++)
  1733.     {
  1734.       curves_dialog->curve_type[i] = SMOOTH;
  1735.       for (j = 0; j < 17; j++)
  1736.     {
  1737.       curves_dialog->points[i][j][0] = index[i][j];
  1738.       curves_dialog->points[i][j][1] = value[i][j];
  1739.     }
  1740.     }
  1741.  
  1742.   /* this is ugly, but works ... */
  1743.   current_channel = curves_dialog->channel;
  1744.   for (i = 0; i < 5; i++)
  1745.     {
  1746.       curves_dialog->channel = i;
  1747.       curves_calculate_curve (curves_dialog);
  1748.     }
  1749.   curves_dialog->channel = current_channel;
  1750.  
  1751.   curves_update (curves_dialog, ALL);
  1752.   gtk_option_menu_set_history (GTK_OPTION_MENU (curves_dialog->curve_type_menu),
  1753.                    SMOOTH);
  1754.  
  1755.   if (curves_dialog->preview)
  1756.     curves_preview (curves_dialog);
  1757.  
  1758.   return TRUE;
  1759. }
  1760.  
  1761. static void
  1762. curves_write_to_file (FILE *f)
  1763. {
  1764.   gint   i, j;
  1765.   gint32 index;
  1766.  
  1767.   for (i = 0; i < 5; i++)
  1768.     if (curves_dialog->curve_type[i] == GFREE)
  1769.       {  
  1770.     /*  pick representative points from the curve and make them control points  */
  1771.     for (j = 0; j <= 8; j++)
  1772.       {
  1773.         index = CLAMP0255 (j * 32);
  1774.         curves_dialog->points[i][j * 2][0] = index;
  1775.         curves_dialog->points[i][j * 2][1] = curves_dialog->curve[i][index];
  1776.       }      
  1777.       }
  1778.   
  1779.   fprintf (f, "# GIMP Curves File\n");
  1780.  
  1781.   for (i = 0; i < 5; i++)
  1782.     {
  1783.       for (j = 0; j < 17; j++)
  1784.     fprintf (f, "%d %d ", curves_dialog->points[i][j][0], 
  1785.                       curves_dialog->points[i][j][1]);
  1786.       
  1787.       fprintf (f, "\n");
  1788.     }
  1789. }
  1790.  
  1791.