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 / shift.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  12.1 KB  |  479 lines

  1. /* Shift --- image filter plug-in for The Gimp image manipulation program
  2.  * Copyright (C) 1997 Brian Degenhardt and Federico Mena Quintero
  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.  * Please direct all comments, questions, bug reports  etc to Brian Degenhardt
  19.  * bdegenha@ucsd.edu
  20.  *
  21.  * You can contact Federico Mena Quintero at quartic@polloux.fciencias.unam.mx
  22.  * You can contact the original The Gimp authors at gimp@xcf.berkeley.edu
  23.  */
  24.  
  25. #include "config.h"
  26.  
  27. #include <time.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30.  
  31. #include <gtk/gtk.h>
  32.  
  33. #include <libgimp/gimp.h>
  34. #include <libgimp/gimpui.h>
  35.  
  36. #include "libgimp/stdplugins-intl.h"
  37.  
  38.  
  39. /* Some useful macros */
  40.  
  41. #define SCALE_WIDTH     200
  42. #define TILE_CACHE_SIZE  16
  43. #define HORIZONTAL        0
  44. #define VERTICAL          1
  45. #define ENTRY_WIDTH      35
  46.  
  47. typedef struct
  48. {
  49.   gint shift_amount;
  50.   gint orientation;
  51. } ShiftValues;
  52.  
  53. typedef struct
  54. {
  55.   gint run;
  56. } ShiftInterface;
  57.  
  58.  
  59. /* Declare local functions.
  60.  */
  61. static void    query  (void);
  62. static void    run    (gchar    *name,
  63.                gint      nparams,
  64.                GimpParam   *param,
  65.                gint     *nreturn_vals,
  66.                GimpParam  **return_vals);
  67.  
  68. static void    shift  (GimpDrawable *drawable);
  69.  
  70. static gint    shift_dialog      (void);
  71. static void    shift_ok_callback (GtkWidget *widget,
  72.                   gpointer   data);
  73.  
  74. static GimpTile * shift_pixel (GimpDrawable *drawable,
  75.                 GimpTile     *tile,
  76.                 gint       x1,
  77.                 gint       y1,
  78.                 gint       x2,
  79.                 gint       y2,
  80.                 gint       x,
  81.                 gint       y,
  82.                 gint      *row,
  83.                 gint      *col,
  84.                 guchar    *pixel);
  85.  
  86.  
  87. /***** Local vars *****/
  88.  
  89. GimpPlugInInfo PLUG_IN_INFO =
  90. {
  91.   NULL,  /* init_proc  */
  92.   NULL,  /* quit_proc  */
  93.   query, /* query_proc */
  94.   run,   /* run_proc   */
  95. };
  96.  
  97. static ShiftValues shvals =
  98. {
  99.   5,          /* shift amount */
  100.   HORIZONTAL  /* orientation  */
  101. };
  102.  
  103. static ShiftInterface shint =
  104. {
  105.   FALSE   /*  run  */
  106. };
  107.  
  108. /***** Functions *****/
  109.  
  110. MAIN ()
  111.  
  112. static void
  113. query (void)
  114. {
  115.   static GimpParamDef args[] =
  116.   {
  117.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  118.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  119.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  120.     { GIMP_PDB_INT32, "shift_amount", "shift amount (0 <= shift_amount_x <= 200)" },
  121.     { GIMP_PDB_INT32, "orientation", "vertical, horizontal orientation" }
  122.   };
  123.   static gint nargs = sizeof (args) / sizeof (args[0]);
  124.  
  125.   gimp_install_procedure ("plug_in_shift",
  126.               "Shift the contents of the specified drawable",
  127.               "Shifts the pixels of the specified drawable. Each row will be displaced a random value of pixels.",
  128.               "Spencer Kimball and Peter Mattis, ported by Brian Degenhardt and Federico Mena Quintero",
  129.               "Brian Degenhardt",
  130.               "1997",
  131.               N_("<Image>/Filters/Distorts/Shift..."),
  132.               "RGB*, GRAY*",
  133.               GIMP_PLUGIN,
  134.               nargs, 0,
  135.               args, NULL);
  136. }
  137.  
  138. static void
  139. run (gchar  *name,
  140.      gint    nparams,
  141.      GimpParam  *param,
  142.      gint   *nreturn_vals,
  143.      GimpParam **return_vals)
  144. {
  145.   static GimpParam values[1];
  146.   GimpDrawable *drawable;
  147.   GimpRunModeType run_mode;
  148.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  149.  
  150.   run_mode = param[0].data.d_int32;
  151.  
  152.   /*  Get the specified drawable  */
  153.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  154.  
  155.   *nreturn_vals = 1;
  156.   *return_vals = values;
  157.  
  158.   values[0].type = GIMP_PDB_STATUS;
  159.   values[0].data.d_status = status;
  160.  
  161.   switch (run_mode)
  162.     {
  163.     case GIMP_RUN_INTERACTIVE:
  164.       INIT_I18N_UI();
  165.       /*  Possibly retrieve data  */
  166.       gimp_get_data ("plug_in_shift", &shvals);
  167.  
  168.       /*  First acquire information with a dialog  */
  169.       if (! shift_dialog ())
  170.     return;
  171.       break;
  172.  
  173.     case GIMP_RUN_NONINTERACTIVE:
  174.       INIT_I18N();
  175.       /*  Make sure all the arguments are there!  */
  176.       if (nparams != 5)
  177.     {
  178.       status = GIMP_PDB_CALLING_ERROR;
  179.     }
  180.       else
  181.     {
  182.       shvals.shift_amount = param[3].data.d_int32;
  183.           shvals.orientation = (param[4].data.d_int32) ? HORIZONTAL : VERTICAL;
  184.  
  185.       if (shvals.shift_amount < 0 || shvals.shift_amount > 200)
  186.         status = GIMP_PDB_CALLING_ERROR;
  187.     }
  188.       break;
  189.  
  190.     case GIMP_RUN_WITH_LAST_VALS:
  191.       INIT_I18N();
  192.       /*  Possibly retrieve data  */
  193.       gimp_get_data ("plug_in_shift", &shvals);
  194.       break;
  195.  
  196.     default:
  197.       break;
  198.     }
  199.  
  200.   if (status == GIMP_PDB_SUCCESS)
  201.     {
  202.       /*  Make sure that the drawable is gray or RGB color  */
  203.       if (gimp_drawable_is_rgb (drawable->id) ||
  204.       gimp_drawable_is_gray (drawable->id))
  205.     {
  206.       gimp_progress_init ( _("Shifting..."));
  207.  
  208.       /*  set the tile cache size  */
  209.       gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
  210.  
  211.       /*  run the shift effect  */
  212.       shift (drawable);
  213.  
  214.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  215.         gimp_displays_flush ();
  216.  
  217.       /*  Store data  */
  218.       if (run_mode == GIMP_RUN_INTERACTIVE)
  219.         gimp_set_data ("plug_in_shift", &shvals, sizeof (ShiftValues));
  220.     }
  221.       else
  222.     {
  223.       /* gimp_message ("shift: cannot operate on indexed color images"); */
  224.       status = GIMP_PDB_EXECUTION_ERROR;
  225.     }
  226.     }
  227.  
  228.   values[0].data.d_status = status;
  229.  
  230.   gimp_drawable_detach (drawable);
  231. }
  232.  
  233. static void
  234. shift (GimpDrawable *drawable)
  235. {
  236.   GimpPixelRgn dest_rgn;
  237.   GimpTile   * tile = NULL;
  238.   gint      row = -1;
  239.   gint      col = -1;
  240.   gpointer  pr;
  241.  
  242.   gint    width, height;
  243.   gint    bytes;
  244.   guchar *destline;
  245.   guchar *dest;
  246.   guchar *otherdest;
  247.   guchar  pixel[4][4];
  248.   gint    x1, y1, x2, y2;
  249.   gint    x, y;
  250.   gint    progress, max_progress;
  251.   gint    seed;
  252.  
  253.   gint amount;
  254.  
  255.   gint xdist, ydist;
  256.   gint xi, yi;
  257.  
  258.   gint k;
  259.   gint mod_value, sub_value;
  260.  
  261.   /* Get selection area */
  262.  
  263.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  264.  
  265.   width  = drawable->width;
  266.   height = drawable->height;
  267.   bytes  = drawable->bpp;
  268.  
  269.   progress     = 0;
  270.   max_progress = (x2 - x1) * (y2 - y1);
  271.  
  272.   amount = shvals.shift_amount;
  273.  
  274.   /* Initialize random stuff */
  275.   mod_value = amount + 1;
  276.   sub_value = mod_value / 2;
  277.   seed = time(NULL);
  278.  
  279. /* Shift the image.  It's a pretty simple algorithm.  If horizontal
  280.      is selected, then every row is shifted a random number of pixels
  281.      in the range of -shift_amount/2 to shift_amount/2.  The effect is
  282.      just reproduced with columns if vertical is selected.  Vertical
  283.      has been added since 0.54 so that the user doesn't have to rotate
  284.      the image to do a vertical shift.
  285.   */
  286.  
  287.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  288.                x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE);
  289.   for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
  290.        pr != NULL;
  291.        pr = gimp_pixel_rgns_process (pr))
  292.     {
  293.       if (shvals.orientation == VERTICAL)
  294.         {
  295.       destline = dest_rgn.data;
  296.       srand(seed+dest_rgn.x);
  297.  
  298.       for (x = dest_rgn.x; x < (dest_rgn.x + dest_rgn.w); x++)
  299.             {
  300.           dest = destline;
  301.           ydist = (rand() % mod_value) - sub_value;
  302.           for (y = dest_rgn.y; y < (dest_rgn.y + dest_rgn.h); y++)
  303.                 {
  304.           otherdest = dest;
  305.  
  306.           yi = (y + ydist + height)%height; /*  add width before % because % isn't a true modulo */
  307.  
  308.           tile = shift_pixel (drawable, tile, x1, y1, x2, y2, x, yi, &row, &col, pixel[0]);
  309.  
  310.           for (k = 0; k < bytes; k++)
  311.             *otherdest++ = pixel[0][k];
  312.           dest += dest_rgn.rowstride;
  313.                 }
  314.  
  315.           for (k = 0; k < bytes; k++)
  316.         destline++;
  317.             }
  318.  
  319.       progress += dest_rgn.w * dest_rgn.h;
  320.       gimp_progress_update ((double) progress / (double) max_progress);
  321.         }
  322.       else
  323.         {
  324.       destline = dest_rgn.data;
  325.       srand (seed+dest_rgn.y);
  326.  
  327.       for (y = dest_rgn.y; y < (dest_rgn.y + dest_rgn.h); y++)
  328.             {
  329.           dest = destline;
  330.           xdist = (rand() % mod_value) - sub_value;
  331.           for (x = dest_rgn.x; x < (dest_rgn.x + dest_rgn.w); x++)
  332.                 {
  333.           xi = (x + xdist + width)%width; /*  add width before % because % isn't a true modulo */
  334.  
  335.           tile = shift_pixel (drawable, tile, x1, y1, x2, y2, xi, y, &row, &col, pixel[0]);
  336.  
  337.           for (k = 0; k < bytes; k++)
  338.             *dest++ = pixel[0][k];
  339.                 }
  340.  
  341.           destline += dest_rgn.rowstride;
  342.             }
  343.  
  344.       progress += dest_rgn.w * dest_rgn.h;
  345.       gimp_progress_update ((double) progress / (double) max_progress);
  346.         }
  347.     }
  348.  
  349.   if (tile)
  350.     gimp_tile_unref (tile, FALSE);
  351.  
  352.   /*  update the region  */
  353.   gimp_drawable_flush (drawable);
  354.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  355.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  356. }
  357.  
  358.  
  359. static gint
  360. shift_dialog (void)
  361. {
  362.   GtkWidget *dlg;
  363.   GtkWidget *frame;
  364.   GtkWidget *radio_vbox;
  365.   GtkWidget *sep;
  366.   GtkWidget *table;
  367.   GtkObject *amount_data;
  368.  
  369.   gimp_ui_init ("shift", FALSE);
  370.  
  371.   dlg = gimp_dialog_new (_("Shift"), "shift",
  372.              gimp_standard_help_func, "filters/shift.html",
  373.              GTK_WIN_POS_MOUSE,
  374.              FALSE, TRUE, FALSE,
  375.  
  376.              _("OK"), shift_ok_callback,
  377.              NULL, NULL, NULL, TRUE, FALSE,
  378.              _("Cancel"), gtk_widget_destroy,
  379.              NULL, 1, NULL, FALSE, TRUE,
  380.  
  381.              NULL);
  382.  
  383.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  384.               GTK_SIGNAL_FUNC (gtk_main_quit),
  385.               NULL);
  386.  
  387.   /*  parameter settings  */
  388.   frame = 
  389.     gimp_radio_group_new2 (TRUE, _("Parameter Settings"),
  390.                gimp_radio_button_update,
  391.                &shvals.orientation, (gpointer) shvals.orientation,
  392.  
  393.                _("Shift Horizontally"), (gpointer) HORIZONTAL, NULL,
  394.                _("Shift Vertically"),   (gpointer) VERTICAL, NULL,
  395.  
  396.                NULL);
  397.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  398.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  399.  
  400.   radio_vbox = GTK_BIN (frame)->child;
  401.   gtk_container_set_border_width (GTK_CONTAINER (radio_vbox), 4);
  402.  
  403.   sep = gtk_hseparator_new ();
  404.   gtk_box_pack_start (GTK_BOX (radio_vbox), sep, FALSE, FALSE, 3);
  405.   gtk_widget_show (sep);
  406.  
  407.   table = gtk_table_new (1, 3, FALSE);
  408.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  409.   gtk_box_pack_start (GTK_BOX (radio_vbox), table, FALSE, FALSE, 0);
  410.   gtk_widget_show (table);
  411.  
  412.   amount_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  413.                       _("Shift Amount:"), SCALE_WIDTH, 0,
  414.                       shvals.shift_amount, 0, 200, 1, 10, 0,
  415.                       TRUE, 0, 0,
  416.                       NULL, NULL);
  417.   gtk_signal_connect (GTK_OBJECT (amount_data), "value_changed",
  418.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  419.               &shvals.shift_amount);
  420.  
  421.   gtk_widget_show (frame);
  422.  
  423.   gtk_widget_show (dlg);
  424.  
  425.   gtk_main ();
  426.   gdk_flush ();
  427.  
  428.   return shint.run;
  429. }
  430.  
  431. static void
  432. shift_ok_callback (GtkWidget *widget,
  433.            gpointer   data)
  434. {
  435.   shint.run = TRUE;
  436.  
  437.   gtk_widget_destroy (GTK_WIDGET (data));
  438. }
  439.  
  440. static GimpTile *
  441. shift_pixel (GimpDrawable *drawable,
  442.          GimpTile     *tile,
  443.          gint       x1,
  444.          gint       y1,
  445.          gint       x2,
  446.          gint       y2,
  447.          gint       x,
  448.          gint       y,
  449.          gint      *row,
  450.          gint      *col,
  451.          guchar    *pixel)
  452. {
  453.   static guchar empty_pixel[4] = {0, 0, 0, 0};
  454.   guchar *data;
  455.   gint    b;
  456.  
  457.   if (x >= x1 && y >= y1 && x < x2 && y < y2)
  458.     {
  459.       if ((x >> 6 != *col) || (y >> 6 != *row))
  460.     {
  461.       *col = x / 64;
  462.       *row = y / 64;
  463.       if (tile)
  464.         gimp_tile_unref (tile, FALSE);
  465.       tile = gimp_drawable_get_tile (drawable, FALSE, *row, *col);
  466.       gimp_tile_ref (tile);
  467.     }
  468.  
  469.       data = tile->data + tile->bpp * (tile->ewidth * (y % 64) + (x % 64));
  470.     }
  471.   else
  472.     data = empty_pixel;
  473.  
  474.   for (b = 0; b < drawable->bpp; b++)
  475.     pixel[b] = data[b];
  476.  
  477.   return tile;
  478. }
  479.