home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / common / apply_lens.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  13.1 KB  |  482 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * Apply lens plug-in --- makes your selected part of the image look like it
  5.  *                        is viewed under a solid lens.
  6.  * Copyright (C) 1997 Morten Eriksen
  7.  * mortene@pvv.ntnu.no
  8.  * (If you do anything cool with this plug-in, or have ideas for
  9.  * improvements (which aren't on my ToDo-list) - send me an email).
  10.  *
  11.  * This program is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2 of the License, or
  14.  * (at your option) any later version.
  15.  *
  16.  * This program is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; if not, write to the Free Software
  23.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  24.  */
  25.  
  26. /* Version 0.1:
  27.  * 
  28.  * First release. No known serious bugs, and basically does what you want.
  29.  * All fancy features postponed until the next release, though. :)
  30.  *
  31.  */
  32.  
  33. /*
  34.   TO DO:
  35.   - antialiasing
  36.   - preview image
  37.   - adjustable (R, G, B and A) filter
  38.   - optimize for speed!
  39.   - refraction index warning dialog box when value < 1.0
  40.   - use "true" lens with specified thickness
  41.   - option to apply inverted lens
  42.   - adjustable "c" value in the ellipsoid formula
  43.   - radiobuttons for "ellipsoid" or "only horiz" and "only vert" (like in the
  44.     Ad*b* Ph*t*sh*p Spherify plug-in..)
  45.   - clean up source code
  46.  */
  47.  
  48. #include "config.h"
  49.  
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52.  
  53. #include <gtk/gtk.h>
  54.  
  55. #include <libgimp/gimp.h>
  56. #include <libgimp/gimpui.h>
  57.  
  58. #include "libgimp/stdplugins-intl.h"
  59.  
  60. #define ENTRY_WIDTH 100
  61.  
  62. /* Declare local functions.
  63.  */
  64. static void query (void);
  65. static void run   (gchar   *name,
  66.            gint     nparams,
  67.            GimpParam  *param,
  68.            gint    *nreturn_vals,
  69.            GimpParam **return_vals);
  70.  
  71. static void drawlens    (GimpDrawable *drawable);
  72.  
  73. static gint lens_dialog (GimpDrawable *drawable);
  74.  
  75. GimpPlugInInfo PLUG_IN_INFO =
  76. {
  77.   NULL,  /* init_proc  */
  78.   NULL,  /* quit_proc  */
  79.   query, /* query_proc */
  80.   run,   /* run_proc   */
  81. };
  82.  
  83. typedef struct
  84. {
  85.   gdouble refraction;
  86.   gint    keep_surr;
  87.   gint    use_bkgr;
  88.   gint    set_transparent;
  89. } LensValues;
  90.  
  91. static LensValues lvals =
  92. {
  93.   /* Lens refraction value */
  94.   1.7,
  95.   /* Surroundings options */
  96.   TRUE, FALSE, FALSE
  97. };
  98.  
  99. typedef struct
  100. {
  101.   gint run;
  102. } LensInterface;
  103.  
  104. static LensInterface bint =
  105. {
  106.   FALSE  /*  run  */
  107. };
  108.  
  109. MAIN ()
  110.  
  111. static void
  112. query (void)
  113. {
  114.   static GimpParamDef args[] =
  115.   {
  116.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  117.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  118.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  119.     { GIMP_PDB_FLOAT, "refraction", "Lens refraction index" },
  120.     { GIMP_PDB_INT32, "keep_surroundings", "Keep lens surroundings" },
  121.     { GIMP_PDB_INT32, "set_background", "Set lens surroundings to bkgr value" },
  122.     { GIMP_PDB_INT32, "set_transparent", "Set lens surroundings transparent" }
  123.   };
  124.   static gint nargs = sizeof (args)/ sizeof (args[0]);
  125.  
  126.   gimp_install_procedure ("plug_in_applylens",
  127.               "Apply a lens effect",
  128.               "This plug-in uses Snell's law to draw an ellipsoid lens over the image",
  129.               "Morten Eriksen",
  130.               "Morten Eriksen",
  131.               "1997",
  132.               N_("<Image>/Filters/Glass Effects/Apply Lens..."),
  133.               "RGB*, GRAY*, INDEXED*",
  134.               GIMP_PLUGIN,
  135.               nargs, 0,
  136.               args, NULL);
  137. }
  138.  
  139. static void
  140. run (gchar   *name,
  141.      gint     nparams,
  142.      GimpParam  *param,
  143.      gint    *nreturn_vals,
  144.      GimpParam **return_vals)
  145. {
  146.   static GimpParam values[1];
  147.   GimpDrawable *drawable;
  148.   GimpRunModeType run_mode;
  149.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  150.  
  151.   INIT_I18N_UI();
  152.  
  153.   run_mode = param[0].data.d_int32;
  154.  
  155.   values[0].type = GIMP_PDB_STATUS;
  156.   values[0].data.d_status = status;
  157.   
  158.   *nreturn_vals = 1;
  159.   *return_vals = values;
  160.   
  161.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  162.  
  163.   switch(run_mode)
  164.     {
  165.     case GIMP_RUN_INTERACTIVE:
  166.       gimp_get_data ("plug_in_applylens", &lvals);
  167.       if(!lens_dialog (drawable))
  168.     return;
  169.       break;
  170.  
  171.     case GIMP_RUN_NONINTERACTIVE:
  172.       if (nparams != 7)
  173.     status = GIMP_PDB_CALLING_ERROR;
  174.  
  175.       if (status == GIMP_PDB_SUCCESS)
  176.     {
  177.       lvals.refraction = param[3].data.d_float;
  178.       lvals.keep_surr = param[4].data.d_int32;
  179.       lvals.use_bkgr = param[5].data.d_int32;
  180.       lvals.set_transparent = param[6].data.d_int32;
  181.     }
  182.  
  183.       if (status == GIMP_PDB_SUCCESS && (lvals.refraction < 1.0))
  184.     status = GIMP_PDB_CALLING_ERROR;
  185.       break;
  186.  
  187.     case GIMP_RUN_WITH_LAST_VALS:
  188.       gimp_get_data ("plug_in_applylens", &lvals);
  189.       break;
  190.     
  191.     default:
  192.       break;
  193.     }
  194.  
  195.   gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
  196.   gimp_progress_init (_("Applying lens..."));
  197.   drawlens (drawable);
  198.  
  199.   if (run_mode != GIMP_RUN_NONINTERACTIVE)
  200.     gimp_displays_flush ();
  201.   if (run_mode == GIMP_RUN_INTERACTIVE)
  202.     gimp_set_data ("plug_in_applylens", &lvals, sizeof (LensValues));
  203.  
  204.   values[0].data.d_status = status;
  205.   
  206.   gimp_drawable_detach (drawable);
  207. }
  208.  
  209. /*
  210.   Ellipsoid formula: x^2/a^2 + y^2/b^2 + z^2/c^2 = 1
  211.  */
  212. static void
  213. find_projected_pos (gfloat  a,
  214.             gfloat  b,
  215.             gfloat  x,
  216.             gfloat  y,
  217.             gfloat *projx,
  218.             gfloat *projy)
  219. {
  220.   gfloat c;
  221.   gfloat n[3];
  222.   gfloat nxangle, nyangle, theta1, theta2;
  223.   gfloat ri1 = 1.0;
  224.   gfloat ri2 = lvals.refraction;
  225.  
  226.   /* PARAM */
  227.   c = MIN (a, b);
  228.  
  229.   n[0] = x;
  230.   n[1] = y;
  231.   n[2] = sqrt ((1 - x * x / (a * a) - y * y / (b * b)) * (c * c));
  232.  
  233.   nxangle = acos (n[0] / sqrt(n[0] * n[0] + n[2] * n[2]));
  234.   theta1 = G_PI / 2 - nxangle;
  235.   theta2 = asin (sin (theta1) * ri1 / ri2);
  236.   theta2 = G_PI / 2 - nxangle - theta2;
  237.   *projx = x - tan (theta2) * n[2];
  238.  
  239.   nyangle = acos (n[1]/sqrt (n[1] * n[1] + n[2] * n[2]));
  240.   theta1 = G_PI / 2 - nyangle;
  241.   theta2 = asin (sin (theta1) * ri1 / ri2);
  242.   theta2 = G_PI / 2 - nyangle - theta2;
  243.   *projy = y - tan (theta2) * n[2];
  244. }
  245.  
  246. static void
  247. drawlens (GimpDrawable *drawable)
  248. {
  249.   GimpPixelRgn srcPR, destPR;
  250.   gint width, height;
  251.   gint bytes;
  252.   gint row;
  253.   gint x1, y1, x2, y2;
  254.   guchar *src, *dest;
  255.   gint i, col;
  256.   gfloat regionwidth, regionheight, dx, dy, xsqr, ysqr;
  257.   gfloat a, b, asqr, bsqr, x, y;
  258.   glong pixelpos, pos;
  259.   guchar bgr_red, bgr_blue, bgr_green, alphaval;
  260.   GimpImageType drawtype = gimp_drawable_type (drawable->id);
  261.  
  262.   gimp_palette_get_background (&bgr_red, &bgr_green, &bgr_blue);
  263.  
  264.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  265.   regionwidth = x2 - x1;
  266.   a = regionwidth / 2;
  267.   regionheight = y2 - y1;
  268.   b = regionheight / 2;
  269.  
  270.   asqr = a * a;
  271.   bsqr = b * b;
  272.  
  273.   width = drawable->width;
  274.   height = drawable->height;
  275.   bytes = drawable->bpp;
  276.  
  277.   gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE);
  278.   gimp_pixel_rgn_init (&destPR, drawable, 0, 0, width, height, TRUE, TRUE);
  279.  
  280.   src  = g_malloc ((x2 - x1) * (y2 - y1) * bytes);
  281.   dest = g_malloc ((x2 - x1) * (y2 - y1) * bytes);
  282.   gimp_pixel_rgn_get_rect (&srcPR, src, x1, y1, regionwidth, regionheight);
  283.  
  284.   for (col = 0; col < regionwidth; col++)
  285.     {
  286.       dx = (gfloat) col - a + 0.5;
  287.       xsqr = dx * dx;
  288.       for (row = 0; row < regionheight; row++)
  289.     {
  290.       pixelpos = (col + row * regionwidth) * bytes;
  291.       dy = -((gfloat) row - b) - 0.5;
  292.       ysqr = dy * dy;
  293.       if (ysqr < (bsqr - (bsqr * xsqr) / asqr))
  294.         {
  295.           find_projected_pos (a, b, dx, dy, &x, &y);
  296.           y = -y;
  297.           pos = ((gint) (y + b) * regionwidth + (gint) (x + a)) * bytes;
  298.  
  299.           for (i = 0; i < bytes; i++)
  300.         {
  301.           dest[pixelpos + i] = src[pos + i];
  302.         }
  303.         }
  304.       else
  305.         {
  306.           if (lvals.keep_surr)
  307.         {
  308.           for (i = 0; i < bytes; i++)
  309.             {
  310.               dest[pixelpos + i] = src[pixelpos + i];
  311.             }
  312.         }
  313.           else
  314.         {
  315.           if (lvals.set_transparent)
  316.             alphaval = 0;
  317.           else
  318.             alphaval = 255;
  319.  
  320.           switch (drawtype)
  321.             {
  322.             case GIMP_INDEXEDA_IMAGE:
  323.               dest[pixelpos + 1] = alphaval;
  324.             case GIMP_INDEXED_IMAGE:
  325.               dest[pixelpos + 0] = 0;
  326.               break;
  327.  
  328.             case GIMP_RGBA_IMAGE:
  329.               dest[pixelpos + 3] = alphaval;
  330.             case GIMP_RGB_IMAGE:
  331.               dest[pixelpos + 0] = bgr_red;
  332.               dest[pixelpos + 1] = bgr_green;
  333.               dest[pixelpos + 2] = bgr_blue;
  334.               break;
  335.  
  336.             case GIMP_GRAYA_IMAGE:
  337.               dest[pixelpos + 1] = alphaval;
  338.             case GIMP_GRAY_IMAGE:
  339.               dest[pixelpos+0] = bgr_red;
  340.               break;
  341.             }
  342.         }
  343.         }
  344.     }
  345.       
  346.       if (((gint) (regionwidth-col) % 5) == 0)
  347.     gimp_progress_update ((gdouble) col / (gdouble) regionwidth);
  348.     }
  349.  
  350.   gimp_pixel_rgn_set_rect (&destPR, dest, x1, y1, regionwidth, regionheight);
  351.   g_free (src);
  352.   g_free (dest);
  353.  
  354.   gimp_drawable_flush (drawable);
  355.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  356.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  357. }
  358.  
  359. static void
  360. lens_ok_callback (GtkWidget *widget,
  361.           gpointer   data)
  362. {
  363.   bint.run = TRUE;
  364.  
  365.   gtk_widget_destroy (GTK_WIDGET (data));
  366. }
  367.  
  368. static gint
  369. lens_dialog (GimpDrawable *drawable)
  370. {
  371.   GtkWidget *dlg;
  372.   GtkWidget *label;
  373.   GtkWidget *toggle;
  374.   GtkWidget *frame;
  375.   GtkWidget *vbox;
  376.   GtkWidget *sep;
  377.   GtkWidget *hbox;
  378.   GtkWidget *spinbutton;
  379.   GtkObject *adj;
  380.   GSList *group = NULL;
  381.   GimpImageType drawtype;
  382.  
  383.   drawtype = gimp_drawable_type (drawable->id);
  384.  
  385.   gimp_ui_init ("apply_lens", FALSE);
  386.  
  387.   dlg = gimp_dialog_new (_("Lens Effect"), "apply_lens",
  388.              gimp_standard_help_func, "filters/apply_lens.html",
  389.              GTK_WIN_POS_MOUSE,
  390.              FALSE, TRUE, FALSE,
  391.  
  392.              _("OK"), lens_ok_callback,
  393.              NULL, NULL, NULL, TRUE, FALSE,
  394.              _("Cancel"), gtk_widget_destroy,
  395.              NULL, 1, NULL, FALSE, TRUE,
  396.  
  397.              NULL);
  398.  
  399.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  400.               GTK_SIGNAL_FUNC (gtk_main_quit),
  401.               NULL);
  402.  
  403.   frame = gtk_frame_new (_("Parameter Settings"));
  404.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  405.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  406.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  407.  
  408.   vbox = gtk_vbox_new (FALSE, 2);
  409.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  410.   gtk_container_add (GTK_CONTAINER (frame), vbox);
  411.  
  412.   toggle = gtk_radio_button_new_with_label (group,
  413.                         _("Keep Original Surroundings"));
  414.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  415.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  416.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  417.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  418.               &lvals.keep_surr);
  419.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), lvals.keep_surr);
  420.   gtk_widget_show (toggle);
  421.  
  422.   toggle =
  423.     gtk_radio_button_new_with_label (group,
  424.                      drawtype == GIMP_INDEXEDA_IMAGE ||
  425.                      drawtype == GIMP_INDEXED_IMAGE ?
  426.                      _("Set Surroundings to Index 0") :
  427.                      _("Set Surroundings to Background Color"));
  428.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  429.   gtk_box_pack_start(GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  430.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  431.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  432.               &lvals.use_bkgr);
  433.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), lvals.use_bkgr);
  434.   gtk_widget_show (toggle);
  435.  
  436.   if ((drawtype == GIMP_INDEXEDA_IMAGE) ||
  437.       (drawtype == GIMP_GRAYA_IMAGE) ||
  438.       (drawtype == GIMP_RGBA_IMAGE))
  439.     {
  440.       toggle =
  441.     gtk_radio_button_new_with_label (group,
  442.                      _("Make Surroundings Transparent"));
  443.       group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  444.       gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  445.       gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  446.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  447.               &lvals.set_transparent);
  448.       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
  449.                     lvals.set_transparent);
  450.       gtk_widget_show (toggle);
  451.   }
  452.  
  453.   sep = gtk_hseparator_new ();
  454.   gtk_box_pack_start (GTK_BOX (vbox), sep, FALSE, FALSE, 2);
  455.   gtk_widget_show (sep);
  456.  
  457.   hbox = gtk_hbox_new (FALSE, 4);
  458.   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  459.  
  460.   label = gtk_label_new (_("Lens Refraction Index:"));
  461.   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  462.   gtk_widget_show (label);
  463.  
  464.   spinbutton = gimp_spin_button_new (&adj, lvals.refraction,
  465.                      1.0, 100.0, 0.1, 1.0, 0, 1, 2);
  466.   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
  467.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  468.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  469.               &lvals.refraction);
  470.   gtk_widget_show (spinbutton);
  471.  
  472.   gtk_widget_show (hbox);
  473.   gtk_widget_show (vbox);
  474.   gtk_widget_show (frame);
  475.   gtk_widget_show (dlg);
  476.  
  477.   gtk_main ();
  478.   gdk_flush ();
  479.  
  480.   return bint.run;
  481. }
  482.