home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / pagecurl / pagecurl.c < prev   
Encoding:
C/C++ Source or Header  |  2000-08-28  |  32.7 KB  |  1,120 lines

  1. /* Page Curl 0.9 --- image filter plug-in for The Gimp
  2.  * Copyright (C) 1996 Federico Mena Quintero
  3.  * Ported to Gimp 1.0 1998 by Simon Budig <Simon.Budig@unix-ag.org>
  4.  *
  5.  * You can contact me at quartic@polloux.fciencias.unam.mx
  6.  * You can contact the original The Gimp authors at gimp@xcf.berkeley.edu
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 2 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program; if not, write to the Free Software
  20.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21.  *
  22.  */
  23.  
  24. /* TODO for v0.5 - in 0.9 still to do...
  25.  * As of version 0.5 alpha, the only thing that is not yet implemented
  26.  * is the "Warp curl" option.  Everything else seems to be working
  27.  * just fine.  Please email me if you find any bugs.  I know that the
  28.  * calculation code is horrible, but you don't want to tweak it anyway ;)
  29.  */
  30.  
  31. /*
  32.  * Ported to the 0.99.x architecture by Simon Budig, Simon.Budig@unix-ag.org
  33.  *  *** Why does gimp_drawable_add_alpha cause the plugin to produce an
  34.  *      ** WARNING **: received tile info did not match computed tile info
  35.  *      ** WARNING **: expected tile ack and received: 0
  36.  */
  37.  
  38. /*
  39.  * Version History
  40.  * 0.5: (1996) Version for Gimp 0.54 by Federico Mena Quintero
  41.  * 0.6: (Feb '98) First Version for Gimp 0.99.x, very buggy.
  42.  * 0.8: (Mar '98) First "stable" version
  43.  * 0.9: (May '98)
  44.  *      - Added support for Gradients. It is now possible to map
  45.  *        a gradient to the back of the curl.
  46.  *      - This implies a changed PDB-Interface: New "mode" parameter.
  47.  *      - Pagecurl now returns the ID of the new layer.
  48.  *      - Exchanged the meaning of FG/BG Color, because mostly the FG
  49.  *        color is darker.
  50.  */
  51. #include "config.h"
  52.  
  53. #include <stdio.h>
  54. #include <stdlib.h>
  55.  
  56. #include <gtk/gtk.h>
  57.  
  58. #include <libgimp/gimp.h>
  59. #include <libgimp/gimpui.h>
  60.  
  61. #include "libgimp/stdplugins-intl.h"
  62.  
  63. #include "curl0.xpm"
  64. #include "curl1.xpm"
  65. #include "curl2.xpm"
  66. #include "curl3.xpm"
  67. #include "curl4.xpm"
  68. #include "curl5.xpm"
  69. #include "curl6.xpm"
  70. #include "curl7.xpm"
  71.  
  72. #define PLUG_IN_NAME    "plug_in_pagecurl"
  73. #define PLUG_IN_VERSION "May 1998, 0.9"
  74. #define NGRADSAMPLES    256
  75.  
  76.  
  77. /***** Types *****/
  78.  
  79. typedef struct
  80. {
  81.   gint    do_curl_shade;
  82.   gint    do_curl_gradient;
  83.   gint    do_curl_warp;  /* Not yet supported... */
  84.  
  85.   gdouble do_curl_opacity;
  86.   gint    do_shade_under;
  87.  
  88.   gint    do_upper_left;
  89.   gint    do_upper_right;
  90.   gint    do_lower_left;
  91.   gint    do_lower_right;
  92.  
  93.   gint    do_vertical;
  94.   gint    do_horizontal;
  95. } CurlParams;
  96.  
  97. /***** Prototypes *****/
  98.  
  99. static void query (void);
  100. static void run   (gchar      *name,
  101.            gint        nparams,
  102.            GimpParam  *param,
  103.            gint       *nreturn_vals,
  104.            GimpParam **return_vals);
  105.  
  106. static void set_default_params (void);
  107.  
  108. static void dialog_ok_callback   (GtkWidget     *widget,
  109.                   gpointer       data);
  110. static void dialog_toggle_update (GtkWidget     *widget,
  111.                   gint32         value);
  112. static void dialog_scale_update  (GtkAdjustment *adjustment,
  113.                   gdouble       *value);
  114.  
  115. static gint do_dialog (void);
  116.  
  117. static void init_calculation (void);
  118.  
  119. static gint left_of_diagl  (gdouble x, gdouble y);
  120. static gint right_of_diagr (gdouble x, gdouble y);
  121. static gint below_diagb    (gdouble x, gdouble y);
  122. static gint right_of_diagm (gdouble x, gdouble y);
  123. static gint inside_circle  (gdouble x, gdouble y);
  124.  
  125. static void    do_curl_effect      (void);
  126. static void    clear_curled_region (void);
  127. static void    page_curl           (void);
  128. static guchar *get_samples         (GimpDrawable *drawable);
  129.  
  130. /***** Variables *****/
  131.  
  132. GimpPlugInInfo PLUG_IN_INFO =
  133. {
  134.   NULL,  /* init_proc  */
  135.   NULL,  /* quit_proc  */
  136.   query, /* query_proc */
  137.   run    /* run_proc   */
  138. };
  139.  
  140. static CurlParams curl;
  141.  
  142. /* Image parameters */
  143.  
  144. static gint32     image_id;
  145. static GimpDrawable *curl_layer;
  146. static GimpDrawable *drawable;
  147.  
  148. static GdkPixmap *gdk_curl_pixmaps[8];
  149. static GdkBitmap *gdk_curl_masks[8];
  150.  
  151. static GtkWidget *curl_pixmap_widget;
  152.  
  153. static gint   sel_x1, sel_y1, sel_x2, sel_y2;
  154. static gint   true_sel_width, true_sel_height;
  155. static gint   sel_width, sel_height;
  156. static gint   drawable_position;
  157. static gint   curl_run = FALSE;
  158. static gint32 curl_layer_ID;
  159.  
  160. /* Center and radius of circle */
  161.  
  162. static GimpVector2 center;
  163. static double      radius;
  164.  
  165. /* Useful points to keep around */
  166.  
  167. static GimpVector2 left_tangent;
  168. static GimpVector2 right_tangent;
  169.  
  170. /* Slopes --- these are *not* in the usual geometric sense! */
  171.  
  172. static gdouble diagl_slope;
  173. static gdouble diagr_slope;
  174. static gdouble diagb_slope;
  175. static gdouble diagm_slope;
  176.  
  177. /* User-configured parameters */
  178.  
  179. static guchar fore_color[3];
  180. static guchar back_color[3];
  181.  
  182.  
  183. /***** Functions *****/
  184.  
  185. MAIN ()
  186.  
  187. static void
  188. query (void)
  189. {
  190.   static GimpParamDef args[] =
  191.   {
  192.     { GIMP_PDB_INT32, "run_mode", "Interactive (0), non-interactive (1)" },
  193.     { GIMP_PDB_IMAGE, "image", "Input image" },
  194.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  195.     { GIMP_PDB_INT32, "mode", "Pagecurl-mode: Use FG- and BG-Color (0), Use current gradient (1)" },
  196.     { GIMP_PDB_INT32, "edge", "Edge to curl (1-4, clockwise, starting in the lower right edge)" },
  197.     { GIMP_PDB_INT32, "type", "vertical (0), horizontal (1)" },
  198.     { GIMP_PDB_INT32, "shade", "Shade the region under the curl (1) or not (0)" },
  199.   };
  200.   static gint nargs = sizeof (args) / sizeof (args[0]);
  201.  
  202.   static GimpParamDef return_vals[] =
  203.   {
  204.     { GIMP_PDB_LAYER, "Curl Layer", "The new layer with the curl." }
  205.   }; 
  206.   static gint nreturn_vals = sizeof (return_vals) / sizeof (return_vals[0]);
  207.  
  208.   INIT_I18N();
  209.  
  210.   gimp_install_procedure (PLUG_IN_NAME,
  211.               "Pagecurl effect",
  212.               "This plug-in creates a pagecurl-effect.",
  213.               "Federico Mena Quintero and Simon Budig",
  214.               "Federico Mena Quintero and Simon Budig",
  215.               PLUG_IN_VERSION,
  216.               N_("<Image>/Filters/Distorts/Pagecurl..."),
  217.               "RGBA, GRAYA",
  218.               GIMP_PLUGIN,
  219.               nargs,
  220.               nreturn_vals,
  221.               args,
  222.               return_vals);
  223. }
  224.  
  225. static void
  226. run (gchar      *name,
  227.      gint        nparams,
  228.      GimpParam  *param,
  229.      gint       *nreturn_vals,
  230.      GimpParam **return_vals)
  231. {
  232.   static GimpParam  values[2];
  233.   GimpRunModeType   run_mode;
  234.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  235.  
  236.   run_mode = param[0].data.d_int32;
  237.  
  238.   set_default_params ();
  239.  
  240.   /*  Possibly retrieve data  */
  241.   gimp_get_data (PLUG_IN_NAME, &curl);
  242.  
  243.   *nreturn_vals = 2;
  244.   *return_vals = values;
  245.  
  246.   values[0].type = GIMP_PDB_STATUS;
  247.   values[0].data.d_status = status;
  248.   values[1].type = GIMP_PDB_LAYER;
  249.   values[1].data.d_layer = -1;
  250.  
  251.   /*  Get the specified drawable  */
  252.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  253.   image_id = param[1].data.d_image;
  254.  
  255.   if ((gimp_drawable_is_rgb (drawable->id)
  256.        || gimp_drawable_is_gray (drawable->id))
  257.       && gimp_drawable_has_alpha (drawable->id))
  258.     {
  259.       switch (run_mode)
  260.     {
  261.     case GIMP_RUN_INTERACTIVE:
  262.       INIT_I18N_UI();
  263.       /*  First acquire information with a dialog  */
  264.       if (!do_dialog ())
  265.         return;
  266.       break;
  267.  
  268.     case GIMP_RUN_NONINTERACTIVE:
  269.       INIT_I18N();
  270.       /*  Make sure all the arguments are there!  */
  271.       if (nparams != 7)
  272.         status = GIMP_PDB_CALLING_ERROR;
  273.       if (status == GIMP_PDB_SUCCESS)
  274.         {
  275.           curl.do_curl_shade = (param[3].data.d_int32 == 0) ? 1 : 0;
  276.           curl.do_curl_gradient = 1 - curl.do_curl_shade;
  277.           switch (param[4].data.d_int32)
  278.         {
  279.         case 1:
  280.           curl.do_upper_left = 0;
  281.           curl.do_upper_right = 0;
  282.           curl.do_lower_left = 0;
  283.           curl.do_lower_right = 1;
  284.           break;
  285.         case 2:
  286.           curl.do_upper_left = 0;
  287.           curl.do_upper_right = 0;
  288.           curl.do_lower_left = 1;
  289.           curl.do_lower_right = 0;
  290.           break;
  291.         case 3:
  292.           curl.do_upper_left = 1;
  293.           curl.do_upper_right = 0;
  294.           curl.do_lower_left = 0;
  295.           curl.do_lower_right = 0;
  296.           break;
  297.         case 4:
  298.           curl.do_upper_left = 0;
  299.           curl.do_upper_right = 1;
  300.           curl.do_lower_left = 0;
  301.           curl.do_lower_right = 0;
  302.           break;
  303.         default:
  304.           break;
  305.         }
  306.           curl.do_vertical    = (param[5].data.d_int32) ? 0 : 1;
  307.           curl.do_horizontal  = 1 - curl.do_vertical;
  308.           curl.do_shade_under = (param[6].data.d_int32) ? 1 : 0;
  309.         }
  310.       break;
  311.  
  312.     case GIMP_RUN_WITH_LAST_VALS:
  313.       INIT_I18N();
  314.       break;
  315.  
  316.     default:
  317.       break;
  318.     }
  319.  
  320.       if (status == GIMP_PDB_SUCCESS)
  321.     {
  322.       page_curl ();
  323.       values[1].data.d_layer = curl_layer_ID;
  324.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  325.             gimp_displays_flush ();
  326.       if (run_mode == GIMP_RUN_INTERACTIVE)
  327.             gimp_set_data (PLUG_IN_NAME, &curl, sizeof (CurlParams));
  328.     }
  329.     }
  330.   else
  331.     /* Sorry - no indexed/noalpha images */
  332.     status = GIMP_PDB_EXECUTION_ERROR;
  333.  
  334.   values[0].data.d_status = status;
  335. }
  336.  
  337. /*****************************************************
  338.  * Functions to locate the current point in the curl.
  339.  *  The following functions assume an curl in the
  340.  *  lower right corner.
  341.  *  diagb crosses the two tangential points from the
  342.  *  circle with diagl and diagr.
  343.  *
  344.  *   +------------------------------------------------+
  345.  *   |                                          __-- /|
  346.  *   |                                      __--   _/ |
  347.  *   |                                  __--      /   |
  348.  *   |                              __--        _/    |
  349.  *   |                          __--           /      |
  350.  *   |                      __--____         _/       |
  351.  *   |                  __----~~    \      _/         |
  352.  *   |              __--             |   _/           |
  353.  *   |    diagl __--               _/| _/diagr        |
  354.  *   |      __--            diagm_/  |/               |
  355.  *   |  __--                    /___/                 |
  356.  *   +-------------------------+----------------------+
  357.  */
  358.  
  359. static gint
  360. left_of_diagl (gdouble x,
  361.            gdouble y)
  362. {
  363.   return (x < (sel_width + (y - sel_height) * diagl_slope));
  364. }
  365.  
  366. static gint
  367. right_of_diagr (gdouble x,
  368.         gdouble y)
  369. {
  370.   return (x > (sel_width + (y - sel_height) * diagr_slope));
  371. }
  372.  
  373. static gint
  374. below_diagb (gdouble x,
  375.          gdouble y)
  376. {
  377.   return (y < (right_tangent.y + (x - right_tangent.x) * diagb_slope));
  378. }
  379.  
  380. static gint
  381. right_of_diagm (gdouble x,
  382.         gdouble y)
  383. {
  384.   return (x > (sel_width + (y - sel_height) * diagm_slope));
  385. }
  386.  
  387. static gint
  388. inside_circle (gdouble x,
  389.            gdouble y)
  390. {
  391.   gdouble dx, dy;
  392.  
  393.   dx = x - center.x;
  394.   dy = y - center.y;
  395.  
  396.   return (sqrt (dx * dx + dy * dy) <= radius);
  397. }
  398.  
  399. static void
  400. set_default_params (void)
  401. {
  402.   curl.do_curl_shade = 1;
  403.   curl.do_curl_gradient = 0;
  404.   curl.do_curl_warp = 0;  /* Not yet supported... */
  405.  
  406.   curl.do_curl_opacity = 1.0;
  407.   curl.do_shade_under = 1;
  408.  
  409.   curl.do_upper_left = 0;
  410.   curl.do_upper_right = 0;
  411.   curl.do_lower_left = 0;
  412.   curl.do_lower_right = 1;
  413.  
  414.   curl.do_vertical = 1;
  415.   curl.do_horizontal = 0;
  416. }
  417.  
  418. /********************************************************************/
  419. /* dialog callbacks                                                 */
  420. /********************************************************************/
  421.  
  422. static void
  423. dialog_ok_callback (GtkWidget *widget,
  424.             gpointer   data)
  425. {
  426.   curl_run = TRUE;
  427.  
  428.   gtk_widget_destroy (GTK_WIDGET (data));
  429. }
  430.  
  431. static void
  432. dialog_scale_update (GtkAdjustment *adjustment,
  433.              gdouble       *value)
  434. {
  435.   *value = ((double) adjustment->value) / 100.0;
  436. }
  437.  
  438. static void
  439. dialog_toggle_update (GtkWidget *widget,
  440.               gint32     value)
  441. {
  442.   gint pixmapindex = 0;
  443.  
  444.   switch (value)
  445.     {
  446.     case 0:
  447.       curl.do_upper_left = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  448.       break;
  449.     case 1:
  450.       curl.do_upper_right = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  451.       break;
  452.     case 2:
  453.       curl.do_lower_left = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  454.       break;
  455.     case 3:
  456.       curl.do_lower_right = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  457.       break;
  458.     case 5:
  459.       curl.do_horizontal = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  460.       break;
  461.     case 6:
  462.       curl.do_vertical = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  463.       break;
  464.     case 8:
  465.       curl.do_shade_under = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  466.       return;
  467.       break;
  468.     case 9:
  469.       curl.do_curl_gradient = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  470.       curl.do_curl_shade = (GTK_TOGGLE_BUTTON (widget)->active) ? 0 : 1;
  471.       return;
  472.       break;
  473.     default:
  474.       break;
  475.     }
  476.  
  477.   if (curl.do_upper_left + curl.do_upper_right +
  478.       curl.do_lower_left + curl.do_lower_right == 1 &&
  479.       curl.do_horizontal + curl.do_vertical == 1)
  480.     {
  481.       pixmapindex = curl.do_lower_left + curl.do_upper_right * 2 +
  482.     curl.do_upper_left * 3 + curl.do_horizontal * 4;
  483.       if (pixmapindex < 0 || pixmapindex > 7)
  484.     pixmapindex = 0;
  485.       gtk_pixmap_set (GTK_PIXMAP (curl_pixmap_widget),
  486.               gdk_curl_pixmaps[pixmapindex],
  487.               gdk_curl_masks[pixmapindex]);
  488.     }
  489. }
  490.  
  491. static gint
  492. do_dialog (void) 
  493. {
  494.   /* Missing options: Color-dialogs? / own curl layer ? / transparency
  495.      to original drawable / Warp-curl (unsupported yet) */
  496.  
  497.   GtkWidget *dialog;
  498.   GtkWidget *hbox;
  499.   GtkWidget *vbox;
  500.   GtkWidget *vbox2;
  501.   GtkWidget *table;
  502.   GtkWidget *frame;
  503.   GtkWidget *shade_button;
  504.   GtkWidget *gradient_button;
  505.   GtkWidget *button;
  506.   GtkWidget *scale;
  507.   GtkStyle  *style;
  508.   GtkObject *adjustment;
  509.   gint pixmapindex;
  510.  
  511.   gimp_ui_init ("pagecurl", FALSE);
  512.  
  513.   dialog = gimp_dialog_new ( _("Pagecurl Effect"), "pagecurl",
  514.                 gimp_standard_help_func, "filters/pagecurl.html",
  515.                 GTK_WIN_POS_MOUSE,
  516.                 FALSE, TRUE, FALSE,
  517.  
  518.                 _("OK"), dialog_ok_callback,
  519.                 NULL, NULL, NULL, TRUE, FALSE,
  520.                 _("Cancel"), gtk_widget_destroy,
  521.                 NULL, 1, NULL, FALSE, TRUE,
  522.  
  523.                 NULL);
  524.  
  525.    gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  526.                GTK_SIGNAL_FUNC (gtk_main_quit),
  527.                NULL);
  528.  
  529.    vbox = gtk_vbox_new (FALSE, 4);
  530.    gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  531.    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0);
  532.    gtk_widget_show (vbox);
  533.  
  534.    frame = gtk_frame_new (_("Curl Location"));
  535.    gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
  536.  
  537.    table = gtk_table_new (3, 3, FALSE);
  538.    gtk_table_set_col_spacings (GTK_TABLE (table), 2);
  539.    gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  540.    gtk_container_set_border_width (GTK_CONTAINER (table), 2);
  541.    gtk_container_add (GTK_CONTAINER (frame), table);
  542.  
  543.    gtk_widget_realize (dialog);
  544.    style = gtk_widget_get_style (dialog);
  545.  
  546.    gdk_curl_pixmaps[0] =
  547.      gdk_pixmap_create_from_xpm_d (dialog->window,
  548.                    &(gdk_curl_masks[0]),
  549.                    &style->bg[GTK_STATE_NORMAL],
  550.                    curl0_xpm);
  551.    gdk_curl_pixmaps[1] =
  552.      gdk_pixmap_create_from_xpm_d (dialog->window,
  553.                    &(gdk_curl_masks[1]),
  554.                    &style->bg[GTK_STATE_NORMAL],
  555.                    curl1_xpm);
  556.    gdk_curl_pixmaps[2] =
  557.      gdk_pixmap_create_from_xpm_d (dialog->window,
  558.                    &(gdk_curl_masks[2]),
  559.                    &style->bg[GTK_STATE_NORMAL],
  560.                    curl2_xpm);
  561.    gdk_curl_pixmaps[3] =
  562.      gdk_pixmap_create_from_xpm_d (dialog->window,
  563.                    &(gdk_curl_masks[3]),
  564.                    &style->bg[GTK_STATE_NORMAL],
  565.                    curl3_xpm);
  566.    gdk_curl_pixmaps[4] =
  567.      gdk_pixmap_create_from_xpm_d (dialog->window,
  568.                    &(gdk_curl_masks[4]),
  569.                    &style->bg[GTK_STATE_NORMAL],
  570.                    curl4_xpm);
  571.    gdk_curl_pixmaps[5] =
  572.      gdk_pixmap_create_from_xpm_d (dialog->window,
  573.                    &(gdk_curl_masks[5]),
  574.                    &style->bg[GTK_STATE_NORMAL],
  575.                    curl5_xpm);
  576.    gdk_curl_pixmaps[6] =
  577.      gdk_pixmap_create_from_xpm_d (dialog->window,
  578.                    &(gdk_curl_masks[6]),
  579.                    &style->bg[GTK_STATE_NORMAL],
  580.                    curl6_xpm);
  581.    gdk_curl_pixmaps[7] =
  582.      gdk_pixmap_create_from_xpm_d (dialog->window,
  583.                    &(gdk_curl_masks[7]),
  584.                    &style->bg[GTK_STATE_NORMAL],
  585.                    curl7_xpm);
  586.  
  587.    pixmapindex = curl.do_lower_left + curl.do_upper_right * 2 +
  588.       curl.do_upper_left * 3 + curl.do_horizontal * 4;
  589.    if (pixmapindex < 0 || pixmapindex > 7)
  590.       pixmapindex = 0;
  591.    curl_pixmap_widget = gtk_pixmap_new (gdk_curl_pixmaps[pixmapindex],
  592.                     gdk_curl_masks[pixmapindex]);
  593.    gtk_table_attach (GTK_TABLE (table), curl_pixmap_widget, 1, 2, 1, 2,
  594.              GTK_SHRINK, GTK_SHRINK, 0, 0);
  595.    gtk_widget_show (curl_pixmap_widget);
  596.  
  597.    {
  598.      gint i;
  599.      gchar *name[] =
  600.      {
  601.        N_("Upper Left"),
  602.        N_("Upper Right"),
  603.        N_("Lower Left"),
  604.        N_("Lower Right")
  605.      };
  606.  
  607.      button = NULL;
  608.      for (i = 0; i < 4; i++)
  609.        {
  610.      button = gtk_radio_button_new_with_label
  611.        ((button == NULL) ?
  612.         NULL : gtk_radio_button_group (GTK_RADIO_BUTTON (button)),
  613.         gettext(name[i]));
  614.      gtk_toggle_button_set_active
  615.        (GTK_TOGGLE_BUTTON (button),
  616.         (i == 0 ? curl.do_upper_left : i == 1 ? curl.do_upper_right :
  617.          i == 2 ? curl.do_lower_left : curl.do_lower_right));
  618.  
  619.      gtk_signal_connect (GTK_OBJECT (button), "toggled",
  620.                  GTK_SIGNAL_FUNC (dialog_toggle_update),
  621.                  (gpointer) i);
  622.  
  623.      gtk_table_attach (GTK_TABLE (table), button,
  624.                (i % 2) ? 2 : 0, (i % 2) ? 3 : 1,
  625.                (i < 2) ? 0 : 2, (i < 2) ? 1 : 3,
  626.                GTK_SHRINK, GTK_SHRINK, 0, 0);
  627.  
  628.      gtk_widget_show (button);
  629.        }
  630.    }
  631.  
  632.    gtk_widget_show (table);
  633.    gtk_widget_show (frame);
  634.  
  635.    frame = gtk_frame_new (_("Curl Orientation"));
  636.    gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  637.    gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  638.  
  639.    hbox = gtk_hbox_new (FALSE, 4);
  640.    gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
  641.    gtk_container_add (GTK_CONTAINER (frame), hbox);
  642.  
  643.    {
  644.      gint i;
  645.      gchar *name[] =
  646.      {
  647.        N_("Horizontal"),
  648.        N_("Vertical")
  649.      };
  650.  
  651.      button = NULL;
  652.      for (i = 0; i < 2; i++)
  653.        {
  654.      button = gtk_radio_button_new_with_label
  655.        ((button == NULL) ?
  656.         NULL : gtk_radio_button_group (GTK_RADIO_BUTTON (button)),
  657.         gettext(name[i]));
  658.      gtk_toggle_button_set_active
  659.        (GTK_TOGGLE_BUTTON (button),
  660.         (i == 0 ? curl.do_horizontal : curl.do_vertical));
  661.  
  662.      gtk_signal_connect (GTK_OBJECT (button), "toggled",
  663.                  GTK_SIGNAL_FUNC (dialog_toggle_update),
  664.                  (gpointer) (i + 5));
  665.  
  666.      gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
  667.      gtk_widget_show (button);
  668.        }
  669.    }
  670.  
  671.    gtk_widget_show (hbox);
  672.    gtk_widget_show (frame);
  673.  
  674.    shade_button = gtk_check_button_new_with_label (_("Shade under Curl"));
  675.    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (shade_button),
  676.                  curl.do_shade_under ? TRUE : FALSE);
  677.    gtk_signal_connect (GTK_OBJECT (shade_button), "toggled",
  678.                GTK_SIGNAL_FUNC (dialog_toggle_update),
  679.                (gpointer) 8);
  680.    gtk_box_pack_start (GTK_BOX (vbox), shade_button, FALSE, FALSE, 0);
  681.    gtk_widget_show (shade_button);
  682.  
  683.    gradient_button =
  684.      gtk_check_button_new_with_label (_("Use Current Gradient\n"
  685.                     "instead of FG/BG-Color"));
  686.    gtk_label_set_justify (GTK_LABEL (GTK_BIN (gradient_button)->child),
  687.               GTK_JUSTIFY_LEFT);
  688.    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gradient_button),
  689.                  curl.do_curl_gradient ? TRUE : FALSE);
  690.    gtk_signal_connect (GTK_OBJECT (gradient_button), "toggled",
  691.                GTK_SIGNAL_FUNC (dialog_toggle_update),
  692.                (gpointer) 9);
  693.    gtk_box_pack_start (GTK_BOX (vbox), gradient_button, FALSE, FALSE, 0);
  694.    gtk_widget_show (gradient_button);
  695.  
  696.    frame = gtk_frame_new (_("Curl Opacity"));
  697.    gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  698.    gtk_widget_show (frame);
  699.  
  700.    vbox2 = gtk_vbox_new (FALSE, 0);
  701.    gtk_container_set_border_width (GTK_CONTAINER (vbox2), 2);
  702.    gtk_container_add (GTK_CONTAINER (frame), vbox2);
  703.    gtk_widget_show (vbox2);
  704.  
  705.    adjustment = gtk_adjustment_new (curl.do_curl_opacity * 100, 0.0, 100.0,
  706.                     1.0, 1.0, 0.0);
  707.    gtk_signal_connect (adjustment, "value_changed",
  708.                GTK_SIGNAL_FUNC (dialog_scale_update),
  709.                &(curl.do_curl_opacity));
  710.  
  711.    scale = gtk_hscale_new (GTK_ADJUSTMENT (adjustment));
  712.    gtk_widget_set_usize (GTK_WIDGET (scale), 150, 30);
  713.    gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
  714.    gtk_scale_set_digits (GTK_SCALE (scale), 0);
  715.    gtk_scale_set_draw_value (GTK_SCALE (scale), TRUE);
  716.    gtk_box_pack_start (GTK_BOX (vbox2), scale, TRUE, FALSE, 0);
  717.    gtk_widget_show (scale);
  718.  
  719.    gtk_widget_show (dialog);
  720.  
  721.    gtk_main ();
  722.    gdk_flush ();
  723.  
  724.    return curl_run;
  725. }
  726.  
  727. static void
  728. init_calculation (void)
  729. {
  730.   gdouble      k;
  731.   gdouble      alpha, beta;
  732.   gdouble      angle;
  733.   GimpVector2  v1, v2;
  734.   gint32      *image_layers, nlayers;
  735.  
  736.   gimp_layer_add_alpha (drawable->id);
  737.  
  738.   /* Image parameters */
  739.  
  740.   /* Determine Position of original Layer in the Layer stack. */
  741.  
  742.   image_layers = gimp_image_get_layers (image_id, &nlayers);
  743.   drawable_position = 0;
  744.   while (drawable_position < nlayers &&
  745.      image_layers[drawable_position] != drawable->id)
  746.     drawable_position++;
  747.   if (drawable_position >= nlayers)
  748.     {
  749.       fprintf (stderr, "Fatal error: drawable not found in layer stack.\n");
  750.       drawable_position = 0;
  751.    }
  752.   /* Get the bounds of the active selection */
  753.   gimp_drawable_mask_bounds (drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  754.  
  755.   true_sel_width = sel_x2 - sel_x1;
  756.   true_sel_height = sel_y2 - sel_y1;
  757.   if (curl.do_vertical)
  758.     {
  759.       sel_width = true_sel_width;
  760.       sel_height = true_sel_height;
  761.     }
  762.   else
  763.     {
  764.       sel_width = true_sel_height;
  765.       sel_height = true_sel_width;
  766.     }
  767.  
  768.   /* Circle parameters */
  769.  
  770.   alpha = atan ((double) sel_height / sel_width);
  771.   beta = alpha / 2.0;
  772.   k = sel_width / ((G_PI + alpha) * sin (beta) + cos (beta));
  773.   gimp_vector2_set (¢er, k * cos (beta), k * sin (beta));
  774.   radius = center.y;
  775.  
  776.   /* left_tangent  */
  777.  
  778.   gimp_vector2_set (&left_tangent, radius * -sin (alpha), radius * cos (alpha));
  779.   gimp_vector2_add (&left_tangent, &left_tangent, ¢er);
  780.  
  781.   /* right_tangent */
  782.  
  783.   gimp_vector2_sub (&v1, &left_tangent, ¢er);
  784.   gimp_vector2_set (&v2, sel_width - center.x, sel_height - center.y);
  785.   angle = -2.0 * acos (gimp_vector2_inner_product (&v1, &v2) /
  786.                (gimp_vector2_length (&v1) * gimp_vector2_length (&v2)));
  787.   gimp_vector2_set (&right_tangent,
  788.             v1.x * cos (angle) + v1.y * -sin (angle),
  789.             v1.x * sin (angle) + v1.y * cos (angle));
  790.   gimp_vector2_add (&right_tangent, &right_tangent, ¢er);
  791.  
  792.   /* Slopes */
  793.  
  794.   diagl_slope = (double) sel_width / sel_height;
  795.   diagr_slope = (sel_width - right_tangent.x) / (sel_height - right_tangent.y);
  796.   diagb_slope = (right_tangent.y - left_tangent.y) / (right_tangent.x - left_tangent.x);
  797.   diagm_slope = (sel_width - center.x) / sel_height;
  798.  
  799.   /* Colors */
  800.  
  801.   gimp_palette_get_foreground (&fore_color[0], &fore_color[1], &fore_color[2]);
  802.   gimp_palette_get_background (&back_color[0], &back_color[1], &back_color[2]);
  803. }
  804.  
  805. static void
  806. do_curl_effect (void)
  807. {
  808.   gint         x, y, color_image;
  809.   gint         x1, y1, k;
  810.   guint        alpha_pos, progress, max_progress;
  811.   gdouble      intensity, alpha, beta;
  812.   GimpVector2  v, dl, dr;
  813.   gdouble      dl_mag, dr_mag, angle, factor;
  814.   guchar      *pp, *dest, fore_grayval, back_grayval;
  815.   guchar      *gradsamp;
  816.   GimpPixelRgn    dest_rgn;
  817.   gpointer     pr;
  818.   guchar      *grad_samples = NULL;
  819.  
  820.   color_image = gimp_drawable_is_rgb (drawable->id);
  821.   curl_layer =
  822.     gimp_drawable_get (gimp_layer_new (image_id,
  823.                        _("Curl Layer"),
  824.                        true_sel_width,
  825.                        true_sel_height,
  826.                        color_image ? GIMP_RGBA_IMAGE : GIMP_GRAYA_IMAGE,
  827.                        100, GIMP_NORMAL_MODE));
  828.   gimp_image_add_layer (image_id, curl_layer->id, drawable_position);
  829.   curl_layer_ID = curl_layer->id;
  830.  
  831.   gimp_drawable_offsets (drawable->id, &x1, &y1);
  832.   gimp_layer_set_offsets (curl_layer->id, sel_x1 + x1, sel_y1 + y1);
  833.   gimp_tile_cache_ntiles (2 * (curl_layer->width / gimp_tile_width () + 1));
  834.  
  835.   /* Clear the newly created layer */
  836.   gimp_pixel_rgn_init (&dest_rgn, curl_layer,
  837.                0, 0, true_sel_width, true_sel_height, TRUE, FALSE);
  838.   for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
  839.        pr != NULL;
  840.        pr = gimp_pixel_rgns_process (pr))
  841.     {
  842.       dest = dest_rgn.data;
  843.       for (y1 = dest_rgn.y; y1 < dest_rgn.y + dest_rgn.h; y1++)
  844.     {
  845.       pp = dest;
  846.       for (x1 = dest_rgn.x; x1 < dest_rgn.x + dest_rgn.w; x1++)
  847.         {
  848.           for (k = 0; k < dest_rgn.bpp; k++)
  849.         pp[k] = 0;
  850.           pp += dest_rgn.bpp;
  851.         }
  852.       dest += dest_rgn.rowstride;
  853.     }
  854.     }
  855.  
  856.   gimp_drawable_flush (curl_layer);
  857.   gimp_drawable_update (curl_layer->id,
  858.             0, 0, curl_layer->width, curl_layer->height);
  859.  
  860.   gimp_pixel_rgn_init (&dest_rgn, curl_layer,
  861.                0, 0, true_sel_width, true_sel_height, TRUE, TRUE);
  862.  
  863.   /* Init shade_under */
  864.   gimp_vector2_set (&dl, -sel_width, -sel_height);
  865.   dl_mag = gimp_vector2_length (&dl);
  866.   gimp_vector2_set (&dr,
  867.             -(sel_width - right_tangent.x),
  868.             -(sel_height - right_tangent.y));
  869.   dr_mag = gimp_vector2_length (&dr);
  870.   alpha = acos (gimp_vector2_inner_product (&dl, &dr) / (dl_mag * dr_mag));
  871.   beta=alpha / 2;
  872.  
  873.   /* Init shade_curl */
  874.  
  875.   fore_grayval = INTENSITY (fore_color[0], fore_color[1], fore_color[2]);
  876.   back_grayval = INTENSITY (back_color[0], back_color[1], back_color[2]);
  877.  
  878.   /* Gradient Samples */
  879.   if (curl.do_curl_gradient)
  880.     grad_samples = get_samples (curl_layer);
  881.  
  882.   max_progress = 2 * sel_width * sel_height;
  883.   progress = 0;
  884.  
  885.   alpha_pos = dest_rgn.bpp - 1;
  886.  
  887.   /* Main loop */
  888.   for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
  889.        pr != NULL;
  890.        pr = gimp_pixel_rgns_process (pr))
  891.     {
  892.       dest = dest_rgn.data;
  893.  
  894.       for (y1 = dest_rgn.y; y1 < dest_rgn.y + dest_rgn.h; y1++)
  895.     {
  896.       pp = dest;
  897.       for (x1 = dest_rgn.x; x1 < dest_rgn.x + dest_rgn.w; x1++)
  898.         {
  899.           /* Map coordinates to get the curl correct... */
  900.           if (curl.do_horizontal)
  901.         {
  902.           x = (curl.do_lower_right || curl.do_lower_left) ? y1 : sel_width - 1 - y1;
  903.           y = (curl.do_upper_left || curl.do_lower_left) ? x1 : sel_height - 1 - x1;
  904.         }
  905.           else
  906.         {
  907.           x = (curl.do_upper_right || curl.do_lower_right) ? x1 : sel_width - 1 - x1;
  908.           y = (curl.do_upper_left || curl.do_upper_right) ? y1 : sel_height - 1 - y1;
  909.         }
  910.           if (left_of_diagl (x, y))
  911.         { /* uncurled region */
  912.           for (k = 0; k <= alpha_pos; k++)
  913.             pp[k] = 0;
  914.         }
  915.           else if (right_of_diagr (x, y) ||
  916.                (right_of_diagm (x, y) &&
  917.             below_diagb (x, y) &&
  918.             !inside_circle (x, y)))
  919.         {
  920.           /* curled region */
  921.           for (k = 0; k <= alpha_pos; k++)
  922.             pp[k] = 0;
  923.         }
  924.           else
  925.         {
  926.           v.x = -(sel_width - x);
  927.           v.y = -(sel_height - y);
  928.           angle = acos (gimp_vector2_inner_product (&v, &dl) /
  929.                 (gimp_vector2_length (&v) * dl_mag));
  930.  
  931.           if (inside_circle (x, y) || below_diagb (x, y))
  932.             {
  933.               /* Below the curl. */
  934.               factor = angle / alpha;
  935.               for (k = 0; k < alpha_pos; k++)
  936.             pp[k] = 0;
  937.               pp[alpha_pos] = curl.do_shade_under ? (guchar) ((float) 255 * (float) factor) : 0;
  938.             }
  939.           else
  940.             {
  941.               /* On the curl */
  942.               if (curl.do_curl_gradient)
  943.             {
  944.               /* Calculate position in Gradient (0 <= intensity <= 1) */
  945.               intensity = (angle/alpha) + sin(G_PI*2 * angle/alpha)*0.075;
  946.               /* Check boundaries */
  947.               intensity = (intensity < 0 ? 0 : (intensity > 1 ? 1 : intensity ));
  948.               gradsamp = &grad_samples[((guint) (intensity * NGRADSAMPLES)) * dest_rgn.bpp];
  949.               if (color_image)
  950.                 {
  951.                   pp[0] = gradsamp[0];
  952.                   pp[1] = gradsamp[1];
  953.                   pp[2] = gradsamp[2];
  954.                 }
  955.               else 
  956.                 pp[0] = gradsamp[0];
  957.               pp[alpha_pos] = (guchar) ((double) gradsamp[alpha_pos] * (1 - intensity*(1-curl.do_curl_opacity)));
  958.             }
  959.               else
  960.             {
  961.               intensity = pow (sin (G_PI * angle / alpha), 1.5);
  962.               if (color_image)
  963.                 {
  964.                   pp[0] = (intensity * back_color[0] + (1 - intensity) * fore_color[0]);
  965.                   pp[1] = (intensity * back_color[1] + (1 - intensity) * fore_color[1]);
  966.                   pp[2] = (intensity * back_color[2] + (1 - intensity) * fore_color[2]);
  967.                 }
  968.               else
  969.                 pp[0] = (intensity * back_grayval + (1 - intensity) * fore_grayval);
  970.               pp[alpha_pos] = (guchar) ((double) 255 * (1 - intensity*(1-curl.do_curl_opacity)));
  971.             }
  972.             }
  973.         }
  974.           pp += dest_rgn.bpp;
  975.         }
  976.       dest += dest_rgn.rowstride;
  977.     }
  978.       progress += dest_rgn.w * dest_rgn.h;
  979.       gimp_progress_update ((double) progress / (double) max_progress);
  980.     }
  981.  
  982.   gimp_drawable_flush (curl_layer);
  983.   gimp_drawable_merge_shadow (curl_layer->id, FALSE);
  984.   gimp_drawable_update (curl_layer->id,
  985.             0, 0, curl_layer->width, curl_layer->height);
  986.   gimp_drawable_detach (curl_layer);
  987.  
  988.   if (grad_samples != NULL)
  989.     g_free (grad_samples);
  990. }
  991.  
  992. /************************************************/
  993.  
  994. static void
  995. clear_curled_region (void)
  996. {
  997.   GimpPixelRgn src_rgn, dest_rgn;
  998.   gpointer pr;
  999.   gint x, y;
  1000.   guint x1, y1, i;
  1001.   guchar *dest, *src, *pp, *sp;
  1002.   guint alpha_pos, progress, max_progress;
  1003.  
  1004.   max_progress = 2 * sel_width * sel_height;
  1005.   progress = max_progress / 2;
  1006.  
  1007.   gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
  1008.   gimp_pixel_rgn_init (&src_rgn, drawable,
  1009.                sel_x1, sel_y1, true_sel_width, true_sel_height,
  1010.                FALSE, FALSE);
  1011.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  1012.                sel_x1, sel_y1, true_sel_width, true_sel_height,
  1013.                TRUE, TRUE);
  1014.   alpha_pos = dest_rgn.bpp - 1;
  1015.   if (dest_rgn.bpp != src_rgn.bpp)
  1016.     fprintf (stderr, "WARNING: src_rgn.bpp != dest_rgn.bpp!\n");
  1017.   for (pr = gimp_pixel_rgns_register (2, &dest_rgn, &src_rgn);
  1018.        pr != NULL;
  1019.        pr = gimp_pixel_rgns_process (pr))
  1020.     {
  1021.       dest = dest_rgn.data;
  1022.       src = src_rgn.data;
  1023.       for (y1 = dest_rgn.y; y1 < dest_rgn.y + dest_rgn.h; y1++)
  1024.     {
  1025.       sp = src;
  1026.       pp = dest;
  1027.       for (x1 = dest_rgn.x; x1 < dest_rgn.x + dest_rgn.w; x1++)
  1028.         {
  1029.           /* Map coordinates to get the curl correct... */
  1030.           if (curl.do_horizontal)
  1031.         {
  1032.           x = (curl.do_lower_right || curl.do_lower_left) ? y1 - sel_y1 : sel_width - 1 - (y1 - sel_y1);
  1033.           y = (curl.do_upper_left || curl.do_lower_left) ? x1 - sel_x1 : sel_height - 1 - (x1 - sel_x1);
  1034.         }
  1035.           else
  1036.         {
  1037.           x = (curl.do_upper_right || curl.do_lower_right) ? x1 - sel_x1 : sel_width - 1 - (x1 - sel_x1);
  1038.           y = (curl.do_upper_left || curl.do_upper_right) ? y1 - sel_y1 : sel_height - 1 - (y1 - sel_y1);
  1039.         }
  1040.           for (i = 0; i < alpha_pos; i++)
  1041.         pp[i] = sp[i];
  1042.           if (right_of_diagr (x, y) ||
  1043.           (right_of_diagm (x, y) &&
  1044.            below_diagb (x, y) &&
  1045.            !inside_circle (x, y)))
  1046.         {
  1047.           /* Right of the curl */
  1048.           pp[alpha_pos] = 0;
  1049.         }
  1050.           else
  1051.         {
  1052.           pp[alpha_pos] = sp[alpha_pos];
  1053.         }
  1054.           pp += dest_rgn.bpp;
  1055.           sp += src_rgn.bpp;
  1056.         }
  1057.       src += src_rgn.rowstride;
  1058.       dest += dest_rgn.rowstride;
  1059.     }
  1060.       progress += dest_rgn.w * dest_rgn.h;
  1061.       gimp_progress_update ((double) progress / (double) max_progress);
  1062.     }
  1063.   gimp_drawable_flush (drawable);
  1064.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  1065.   gimp_drawable_update (drawable->id, sel_x1, sel_y1, true_sel_width, true_sel_height);
  1066.   gimp_drawable_detach (drawable);
  1067. }
  1068.  
  1069. static void
  1070. page_curl (void)
  1071. {
  1072.   gimp_undo_push_group_start (image_id);
  1073.   gimp_progress_init ( _("Page Curl..."));
  1074.   init_calculation ();
  1075.   do_curl_effect ();
  1076.   clear_curled_region ();
  1077.   gimp_undo_push_group_end (image_id);
  1078. }
  1079.  
  1080. /*
  1081.   Returns NGRADSAMPLES samples of active gradient.
  1082.   Each sample has (gimp_drawable_bpp (drawable->id)) bytes.
  1083.   "ripped" from gradmap.c.
  1084.  */
  1085. static guchar *
  1086. get_samples (GimpDrawable *drawable)
  1087.  {
  1088.   gdouble       *f_samples, *f_samp;    /* float samples */
  1089.   guchar        *b_samples, *b_samp;    /* byte samples */
  1090.   gint          bpp, color, has_alpha, alpha;
  1091.   gint          i, j;
  1092.  
  1093.   f_samples = gimp_gradients_sample_uniform (NGRADSAMPLES);
  1094.  
  1095.   bpp       = gimp_drawable_bpp (drawable->id);
  1096.   color     = gimp_drawable_is_rgb (drawable->id);
  1097.   has_alpha = gimp_drawable_has_alpha (drawable->id);
  1098.   alpha     = (has_alpha ? bpp - 1 : bpp);
  1099.  
  1100.   b_samples = g_new (guchar, NGRADSAMPLES * bpp);
  1101.  
  1102.   for (i = 0; i < NGRADSAMPLES; i++)
  1103.     {
  1104.       b_samp = &b_samples[i * bpp];
  1105.       f_samp = &f_samples[i * 4];
  1106.       if (color)
  1107.         for (j = 0; j < 3; j++)
  1108.           b_samp[j] = f_samp[j] * 255;
  1109.       else
  1110.         b_samp[0] = INTENSITY (f_samp[0], f_samp[1], f_samp[2]) * 255;
  1111.  
  1112.       if (has_alpha)
  1113.         b_samp[alpha] = f_samp[3] * 255;
  1114.     }
  1115.  
  1116.   g_free (f_samples);
  1117.  
  1118.   return b_samples;
  1119. }
  1120.