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 / blinds.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  25.0 KB  |  1,006 lines

  1. /*
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This is a plug-in for the GIMP.
  5.  *
  6.  * Blinds plug-in. Distort an image as though it was stuck to 
  7.  * window blinds and the blinds where opened/closed.
  8.  *
  9.  * Copyright (C) 1997 Andy Thomas  alt@picnic.demon.co.uk
  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.  * A fair proprotion of this code was taken from the Whirl plug-in
  26.  * which was copyrighted by Federico Mena Quintero (as below).
  27.  * 
  28.  * Whirl plug-in --- distort an image into a whirlpool
  29.  * Copyright (C) 1997 Federico Mena Quintero           
  30.  *
  31.  */
  32.  
  33. /* Change log:-
  34.  * 
  35.  * Version 0.5 10 June 1997.
  36.  * Changes required to work with 0.99.10.
  37.  *
  38.  * Version 0.4 20 May 1997.
  39.  * Fixed problem with using this plugin in GIMP_RUN_NONINTERACTIVE mode
  40.  *
  41.  * Version 0.3 8 May 1997.
  42.  * Make preview work in Quartics words "The Right Way".
  43.  *
  44.  * Allow the background to be transparent.
  45.  *
  46.  * Version 0.2 1 May 1997 (not released).
  47.  * Added patches supplied by Tim Mooney mooney@dogbert.cc.ndsu.NoDak.edu
  48.  * to allow the plug-in to build with Digitals compiler.
  49.  */
  50.  
  51. #include "config.h"
  52.  
  53. #include <stdio.h>
  54. #include <stdlib.h>
  55. #include <string.h>
  56.  
  57. #include <gtk/gtk.h>
  58.  
  59. #include <libgimp/gimp.h>
  60. #include <libgimp/gimpui.h>
  61.  
  62. #include "libgimp/stdplugins-intl.h"
  63.  
  64.  
  65. /***** Magic numbers *****/
  66.  
  67. /* Don't make preview >255!!! It won't work for horizontal blinds */
  68. #define PREVIEW_SIZE 128 
  69. #define SCALE_WIDTH  150
  70.  
  71. #define MAX_FANS      10
  72.  
  73. #define HORIZONTAL     0
  74. #define VERTICAL       1
  75.  
  76. /* Variables set in dialog box */
  77. typedef struct data
  78. {
  79.   gint angledsp;
  80.   gint numsegs;
  81.   gint orientation;
  82.   gint bg_trans;
  83. } BlindVals;
  84.  
  85. typedef struct
  86. {
  87.   GtkWidget *preview;
  88.   guchar     preview_row[PREVIEW_SIZE * 4];
  89.   gint       run;
  90.   guchar    *pv_cache;
  91.   gint       img_bpp;
  92. } BlindsInterface;
  93.  
  94. static BlindsInterface bint =
  95. {
  96.   NULL,          /* Preview */
  97.   { '4', 'u' },  /* Preview_row */
  98.   FALSE,         /* run */
  99.   NULL,
  100.   4              /* bpp of drawable */
  101. };
  102.  
  103. /* Array to hold each size of fans. And no there are not each the
  104.  * same size (rounding errors...)
  105.  */
  106.  
  107. gint fanwidths[MAX_FANS];
  108.  
  109. GimpDrawable *blindsdrawable;
  110.  
  111. static void      query  (void);
  112. static void      run    (gchar    *name,
  113.              gint      nparams,
  114.              GimpParam   *param,
  115.              gint     *nreturn_vals,
  116.              GimpParam  **return_vals);
  117.  
  118. static gint      blinds_dialog       (void);
  119.  
  120. static void      blinds_ok_callback    (GtkWidget     *widget,
  121.                     gpointer       data);
  122. static void      blinds_scale_update   (GtkAdjustment *adjustment,
  123.                     gint          *size_val);
  124. static void      blinds_radio_update   (GtkWidget     *widget,
  125.                     gpointer       data);
  126. static void      blinds_button_update  (GtkWidget     *widget,
  127.                     gpointer       data);
  128. static void      dialog_update_preview (void);
  129. static void     cache_preview         (void);
  130. static void      apply_blinds          (void);
  131. static int       blinds_get_bg         (guchar        *bg);
  132.  
  133. GimpPlugInInfo PLUG_IN_INFO =
  134. {
  135.   NULL,    /* init_proc */
  136.   NULL,    /* quit_proc */
  137.   query,   /* query_proc */
  138.   run,     /* run_proc */
  139. };
  140.  
  141. /* Values when first invoked */
  142. static BlindVals bvals =
  143. {
  144.   30,
  145.   3,
  146.   HORIZONTAL,
  147.   FALSE
  148. };
  149.  
  150. /* Stuff for the preview bit */
  151. static gint  sel_x1, sel_y1, sel_x2, sel_y2;
  152. static gint  sel_width, sel_height;
  153. static gint  preview_width, preview_height;
  154. static gint  has_alpha;
  155.  
  156.  
  157. MAIN ()
  158.  
  159. static void
  160. query (void)
  161. {
  162.   static GimpParamDef args[] =
  163.   {
  164.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  165.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  166.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  167.     { GIMP_PDB_INT32, "angle_dsp", "Angle of Displacement " },
  168.     { GIMP_PDB_INT32, "number_of_segments", "Number of segments in blinds" },
  169.     { GIMP_PDB_INT32, "orientation", "orientation; 0 = Horizontal, 1 = Vertical" },
  170.     { GIMP_PDB_INT32, "backgndg_trans", "background transparent; FALSE,TRUE" }
  171.   };
  172.   static gint nargs = sizeof (args) / sizeof (args[0]);
  173.  
  174.   gimp_install_procedure ("plug_in_blinds",
  175.               "Adds a blinds effect to the image. Rather like "
  176.               "putting the image on a set of window blinds and "
  177.               "the closing or opening the blinds",
  178.               "More here later",
  179.               "Andy Thomas",
  180.               "Andy Thomas",
  181.               "1997",
  182.               N_("<Image>/Filters/Distorts/Blinds..."),
  183.               "RGB*, GRAY*",
  184.               GIMP_PLUGIN,
  185.               nargs, 0,
  186.               args, NULL);
  187. }
  188.  
  189. static void
  190. run (gchar    *name,
  191.      gint      nparams,
  192.      GimpParam   *param,
  193.      gint     *nreturn_vals,
  194.      GimpParam  **return_vals)
  195. {
  196.   static GimpParam values[1];
  197.   GimpDrawable *drawable;
  198.   GimpRunModeType run_mode;
  199.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  200.  
  201.   gint pwidth, pheight;
  202.  
  203.   run_mode = param[0].data.d_int32;
  204.  
  205.   *nreturn_vals = 1;
  206.   *return_vals = values;
  207.  
  208.   values[0].type = GIMP_PDB_STATUS;
  209.   values[0].data.d_status = status;
  210.  
  211.   if (run_mode == GIMP_RUN_INTERACTIVE)
  212.     {
  213.       INIT_I18N_UI();
  214.     }
  215.   else
  216.     {
  217.       INIT_I18N();
  218.     }
  219.  
  220.   blindsdrawable = drawable = 
  221.     gimp_drawable_get (param[2].data.d_drawable);
  222.  
  223.   gimp_drawable_mask_bounds (drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  224.  
  225.   sel_width  = sel_x2 - sel_x1;
  226.   sel_height = sel_y2 - sel_y1;
  227.   
  228.   /* Calculate preview size */
  229.   
  230.   if (sel_width > sel_height)
  231.     {
  232.       pwidth  = MIN (sel_width, PREVIEW_SIZE);
  233.       pheight = sel_height * pwidth / sel_width;
  234.     }
  235.   else
  236.     {
  237.       pheight = MIN (sel_height, PREVIEW_SIZE);
  238.       pwidth  = sel_width * pheight / sel_height;
  239.     }
  240.  
  241.   preview_width  = MAX (pwidth, 2);  /* Min size is 2 */
  242.   preview_height = MAX (pheight, 2); 
  243.  
  244.   switch (run_mode)
  245.     {
  246.     case GIMP_RUN_INTERACTIVE:
  247.       gimp_get_data ("plug_in_blinds", &bvals);
  248.       if (! blinds_dialog())
  249.     {
  250.       gimp_drawable_detach (drawable);
  251.       return;
  252.     }
  253.       break;
  254.  
  255.     case GIMP_RUN_NONINTERACTIVE:
  256.       if (nparams != 7)
  257.     status = GIMP_PDB_CALLING_ERROR;
  258.       if (status == GIMP_PDB_SUCCESS)
  259.     {
  260.       bvals.angledsp = param[3].data.d_int32;
  261.       bvals.numsegs = param[4].data.d_int32;
  262.       bvals.orientation = param[5].data.d_int32;
  263.       bvals.bg_trans = param[6].data.d_int32;
  264.     }
  265.       break;
  266.  
  267.     case GIMP_RUN_WITH_LAST_VALS:
  268.       gimp_get_data ("plug_in_blinds", &bvals);
  269.       break;
  270.  
  271.     default:
  272.       break;
  273.     }
  274.  
  275.   if (gimp_drawable_is_rgb (drawable->id) ||
  276.       gimp_drawable_is_gray (drawable->id))
  277.     {
  278.       gimp_progress_init ( _("Adding Blinds..."));
  279.  
  280.       apply_blinds ();
  281.    
  282.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  283.     gimp_displays_flush ();
  284.  
  285.       if (run_mode == GIMP_RUN_INTERACTIVE)
  286.     gimp_set_data ("plug_in_blinds", &bvals, sizeof (BlindVals));
  287.     }
  288.   else
  289.     {
  290.       status = GIMP_PDB_EXECUTION_ERROR;
  291.     }
  292.  
  293.   values[0].data.d_status = status;
  294.  
  295.   gimp_drawable_detach (drawable);
  296. }
  297.  
  298.  
  299. /* Build the dialog up. This was the hard part! */
  300. static gint
  301. blinds_dialog (void)
  302. {
  303.   GtkWidget *dlg;
  304.   GtkWidget *main_vbox;
  305.   GtkWidget *hbox;
  306.   GtkWidget *vbox;
  307.   GtkWidget *frame;
  308.   GtkWidget *toggle_vbox;
  309.   GtkWidget *xframe;
  310.   GtkWidget *table;
  311.   GtkObject *size_data;
  312.   GtkWidget *toggle;
  313.  
  314.   gimp_ui_init ("blinds", TRUE);
  315.  
  316.   cache_preview (); /* Get the preview image and store it also set has_alpha */
  317.  
  318.   dlg = gimp_dialog_new (_("Blinds"), "blinds",
  319.              gimp_standard_help_func, "filters/blinds.html",
  320.              GTK_WIN_POS_MOUSE,
  321.              FALSE, TRUE, FALSE,
  322.  
  323.              _("OK"), blinds_ok_callback,
  324.              NULL, NULL, NULL, TRUE, FALSE,
  325.              _("Cancel"), gtk_widget_destroy,
  326.              NULL, 1, NULL, FALSE, TRUE,
  327.  
  328.              NULL);
  329.  
  330.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  331.               GTK_SIGNAL_FUNC (gtk_main_quit),
  332.               NULL);
  333.  
  334.   main_vbox = gtk_vbox_new (FALSE, 4);
  335.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
  336.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), main_vbox);
  337.   gtk_widget_show (main_vbox);
  338.  
  339.   hbox = gtk_hbox_new (FALSE, 6);
  340.   gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
  341.   gtk_widget_show (hbox);
  342.  
  343.   frame = gtk_frame_new (_("Preview"));
  344.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  345.   gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
  346.   gtk_widget_show (frame);
  347.  
  348.   table = gtk_table_new (1, 1, FALSE); 
  349.   gtk_container_set_border_width (GTK_CONTAINER (table), 4); 
  350.   gtk_container_add (GTK_CONTAINER (frame), table); 
  351.   gtk_widget_show (table); 
  352.  
  353.   xframe = gtk_frame_new (NULL);
  354.   gtk_frame_set_shadow_type (GTK_FRAME (xframe), GTK_SHADOW_IN);
  355.   gtk_table_attach (GTK_TABLE (table), xframe, 0, 1, 0, 1,
  356.             GTK_EXPAND, GTK_EXPAND, 0, 0);
  357.   gtk_widget_show (xframe);
  358.  
  359.   bint.preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  360.   gtk_preview_size (GTK_PREVIEW (bint.preview), preview_width, preview_height);
  361.   gtk_container_add (GTK_CONTAINER (xframe), bint.preview);
  362.   gtk_widget_show(bint.preview);
  363.  
  364.   vbox = gtk_vbox_new (FALSE, 4);
  365.   gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
  366.   gtk_widget_show (vbox);
  367.  
  368.   frame =
  369.     gimp_radio_group_new2 (TRUE, _("Orientation"),
  370.                blinds_radio_update,
  371.                &bvals.orientation, (gpointer) bvals.orientation,
  372.  
  373.                _("Horizontal"), (gpointer) HORIZONTAL, NULL,
  374.                _("Vertical"),   (gpointer) VERTICAL, NULL,
  375.  
  376.                NULL);
  377.   gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
  378.   gtk_widget_show (frame);
  379.  
  380.   frame = gtk_frame_new (_("Background"));
  381.   gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
  382.  
  383.   toggle_vbox = gtk_vbox_new (FALSE, 2);
  384.   gtk_container_set_border_width (GTK_CONTAINER (toggle_vbox), 2);
  385.   gtk_container_add (GTK_CONTAINER (frame), toggle_vbox);
  386.  
  387.   toggle = gtk_check_button_new_with_label (_("Transparent"));
  388.   gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
  389.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  390.               GTK_SIGNAL_FUNC (blinds_button_update),
  391.               &bvals.bg_trans);
  392.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.bg_trans);
  393.  
  394.   if (!has_alpha)
  395.     {
  396.       gtk_widget_set_sensitive (toggle, FALSE);
  397.       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
  398.     }
  399.  
  400.   gtk_widget_show (toggle);
  401.  
  402.   gtk_widget_show (toggle_vbox);
  403.   gtk_widget_show (frame);
  404.  
  405.   frame = gtk_frame_new (_("Parameter Settings"));
  406.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  407.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  408.  
  409.   table = gtk_table_new (2, 3, FALSE);
  410.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  411.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  412.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  413.   gtk_container_add (GTK_CONTAINER (frame), table);
  414.  
  415.   size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  416.                     _("Displacement:"), SCALE_WIDTH, 0,
  417.                     bvals.angledsp, 1, 90, 1, 15, 0,
  418.                     TRUE, 0, 0,
  419.                     NULL, NULL);
  420.   gtk_signal_connect (GTK_OBJECT (size_data), "value_changed",
  421.               GTK_SIGNAL_FUNC (blinds_scale_update),
  422.               &bvals.angledsp);
  423.  
  424.   size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  425.                     _("Num Segments:"), SCALE_WIDTH, 0,
  426.                     bvals.numsegs, 1, MAX_FANS, 1, 2, 0,
  427.                     TRUE, 0, 0,
  428.                     NULL, NULL);
  429.   gtk_signal_connect (GTK_OBJECT (size_data), "value_changed",
  430.               GTK_SIGNAL_FUNC (blinds_scale_update),
  431.               &bvals.numsegs);
  432.  
  433.   gtk_widget_show (frame);
  434.   gtk_widget_show (table);
  435.  
  436.   gtk_widget_show (dlg);
  437.  
  438.   dialog_update_preview ();
  439.  
  440.   gtk_main ();
  441.   gdk_flush ();
  442.  
  443.   return bint.run;
  444. }
  445.  
  446. static void
  447. blinds_ok_callback (GtkWidget *widget,
  448.             gpointer   data)
  449. {
  450.   bint.run = TRUE;
  451.  
  452.   gtk_widget_destroy (GTK_WIDGET (data));
  453. }
  454.  
  455. static void
  456. blinds_radio_update (GtkWidget *widget,
  457.              gpointer   data)
  458. {
  459.   gimp_radio_button_update (widget, data);
  460.  
  461.   if (GTK_TOGGLE_BUTTON (widget)->active)
  462.     dialog_update_preview ();
  463. }                  
  464.  
  465. static void
  466. blinds_button_update (GtkWidget *widget,
  467.               gpointer   data)
  468. {
  469.   gimp_toggle_button_update (widget, data);
  470.  
  471.   dialog_update_preview ();
  472. }                  
  473.  
  474. static void
  475. blinds_scale_update (GtkAdjustment *adjustment,
  476.              gint          *value)
  477. {
  478.   gimp_int_adjustment_update (adjustment, value);
  479.  
  480.   dialog_update_preview ();
  481.  
  482. /* Cache the preview image - updates are a lot faster. */
  483. /* The preview_cache will contain the small image */
  484.  
  485. static void
  486. cache_preview (void)
  487. {
  488.   GimpPixelRgn src_rgn;
  489.   int y,x;
  490.   guchar *src_rows;
  491.   guchar *p;
  492.   int isgrey = 0;
  493.  
  494.   gimp_pixel_rgn_init (&src_rgn, blindsdrawable,
  495.                sel_x1, sel_y1, sel_width, sel_height, FALSE, FALSE);
  496.  
  497.   src_rows = g_new (guchar, sel_width * 4); 
  498.   p = bint.pv_cache = g_new (guchar, preview_width * preview_height * 4);
  499.  
  500.   bint.img_bpp = gimp_drawable_bpp (blindsdrawable->id);   
  501.  
  502.   has_alpha = gimp_drawable_has_alpha (blindsdrawable->id);
  503.  
  504.   if (bint.img_bpp < 3)
  505.     {
  506.       bint.img_bpp = 3 + has_alpha;
  507.     }
  508.  
  509.   switch (gimp_drawable_type (blindsdrawable->id))
  510.     {
  511.     case GIMP_GRAYA_IMAGE:
  512.     case GIMP_GRAY_IMAGE:
  513.       isgrey = 1;
  514.     default:
  515.       break;
  516.     }
  517.  
  518.   for (y = 0; y < preview_height; y++)
  519.     {
  520.       gimp_pixel_rgn_get_row (&src_rgn,
  521.                   src_rows,
  522.                   sel_x1,
  523.                   sel_y1 + (y * sel_height) / preview_height,
  524.                   sel_width);
  525.  
  526.       for (x = 0; x < (preview_width); x++)
  527.     {
  528.       /* Get the pixels of each col */
  529.       gint i;
  530.       for (i = 0 ; i < 3; i++)
  531.         p[x * bint.img_bpp + i] =
  532.           src_rows[((x * sel_width) / preview_width) * src_rgn.bpp +
  533.               ((isgrey) ? 0 : i)]; 
  534.       if (has_alpha)
  535.         p[x * bint.img_bpp + 3] =
  536.           src_rows[((x * sel_width) / preview_width) * src_rgn.bpp +
  537.               ((isgrey) ? 1 : 3)];
  538.     }
  539.       p += (preview_width * bint.img_bpp);
  540.     }
  541.   g_free (src_rows);
  542. }
  543.  
  544. static void 
  545. blindsapply (guchar *srow,
  546.          guchar *drow,
  547.          gint    width,
  548.          gint    bpp,
  549.          guchar *bg)
  550. {
  551.   guchar *src;
  552.   guchar *dst;
  553.   gint i,j,k;
  554.   gdouble ang;
  555.   gint available;
  556.  
  557.   /* Make the row 'shrink' around points along its length */
  558.   /* The bvals.numsegs determins how many segments to slip it in to */
  559.   /* The angle is the conceptual 'rotation' of each of these segments */
  560.  
  561.   /* Note the row is considered to be made up of a two dim array actual
  562.    * pixel locations and the RGB colour at these locations.
  563.    */
  564.  
  565.   /* In the process copy the src row to the destination row */
  566.  
  567.   /* Fill in with background color ? */
  568.   for (i = 0 ; i < width ; i++)
  569.     {
  570.       dst = &drow[i*bpp];
  571.  
  572.       for (j = 0 ; j < bpp; j++)
  573.     {
  574.       dst[j] = bg[j];
  575.     }
  576.     }
  577.  
  578.   /* Apply it */
  579.  
  580.   available = width;
  581.   for ( i = 0 ; i < bvals.numsegs; i++)
  582.     {
  583.       /* Width of segs are variable */
  584.       fanwidths[i] = available / (bvals.numsegs - i);
  585.       available -= fanwidths[i];
  586.     }
  587.  
  588.   /* do center points  first - just for fun...*/
  589.   available = 0;
  590.   for (k = 1; k <= bvals.numsegs; k++)
  591.     {
  592.       int point;
  593.  
  594.       point = available + fanwidths[k - 1] / 2;
  595.  
  596.       available += fanwidths[k - 1];
  597.  
  598.       src = &srow[point * bpp];
  599.       dst = &drow[point * bpp];
  600.   
  601.       /* Copy pixels across */
  602.       for (j = 0 ; j < bpp; j++)
  603.     {
  604.       dst[j] = src[j];
  605.     }
  606.     }
  607.  
  608.   /* Disp for each point */
  609.   ang = (bvals.angledsp * 2 * G_PI) / 360; /* Angle in rads */
  610.   ang = (1 - fabs (cos (ang)));
  611.  
  612.   available = 0;
  613.   for (k = 0 ; k < bvals.numsegs; k++)
  614.     {
  615.       int dx; /* Amount to move by */
  616.       int fw;
  617.  
  618.       for (i = 0 ; i < (fanwidths[k]/2) ; i++)
  619.     {
  620.       /* Copy pixels across of left half of fan */
  621.       fw = fanwidths[k] / 2;
  622.       dx = (int) (ang * ((double) (fw - (double)(i % fw))));
  623.  
  624.       src = &srow[(available + i) * bpp];      
  625.       dst = &drow[(available + i + dx) * bpp];
  626.  
  627.       for (j = 0; j < bpp; j++)
  628.         {
  629.           dst[j] = src[j];
  630.         }
  631.  
  632.       /* Right side */
  633.       j = i + 1;
  634.       src = &srow[(available + fanwidths[k] - j
  635.                - (fanwidths[k] % 2)) * bpp];      
  636.       dst = &drow[(available + fanwidths[k] - j
  637.                - (fanwidths[k] % 2) - dx) * bpp];
  638.  
  639.       for (j = 0; j < bpp; j++)
  640.         {
  641.           dst[j] = src[j];
  642.         }
  643.     }
  644.  
  645.       available += fanwidths[k];
  646.     }
  647. }
  648.  
  649. static int
  650. blinds_get_bg (guchar *bg)
  651. {
  652.   /* Get the background color */
  653.   int retval = FALSE; /*Return TRUE if of GREYA type */
  654.  
  655.   switch (gimp_drawable_type (blindsdrawable->id))
  656.     {
  657.     case GIMP_RGBA_IMAGE:
  658.       bg[3] = bvals.bg_trans ? 0 : 255;
  659.       break;
  660.  
  661.     case GIMP_RGB_IMAGE :
  662.       gimp_palette_get_background (&bg[0], &bg[1], &bg[2]);
  663.       break;
  664.  
  665.     case GIMP_GRAYA_IMAGE:
  666.       retval = TRUE;
  667.       bg[3] = bvals.bg_trans ? 0 : 255;
  668.       break;
  669.  
  670.     case GIMP_GRAY_IMAGE:
  671.       bg[2] = 0;
  672.       bg[1] = 0;
  673.       bg[0] = 0;
  674.       break;
  675.  
  676.     default:
  677.       break;
  678.     }
  679.  
  680.   return retval;
  681. }
  682.  
  683. static void
  684. dialog_update_preview (void)
  685. {
  686.   int     y;
  687.   guchar *p;
  688.   guchar  bg[4];
  689.   gint    check, check_0, check_1;
  690.   
  691.   p = bint.pv_cache;
  692.  
  693.   blinds_get_bg (bg);
  694.  
  695.   if (bvals.orientation)
  696.     {
  697.       for (y = 0; y < preview_height; y++)
  698.     {
  699.       /* bint.preview_row - this row contains the row to apply the action to */
  700.       blindsapply (p, bint.preview_row, preview_width, bint.img_bpp, bg);
  701.  
  702.       if ((y / GIMP_CHECK_SIZE) & 1)
  703.         {
  704.           check_0 = GIMP_CHECK_DARK * 255;
  705.           check_1 = GIMP_CHECK_LIGHT * 255;
  706.         }
  707.       else
  708.         {
  709.           check_0 = GIMP_CHECK_LIGHT * 255;
  710.           check_1 = GIMP_CHECK_DARK * 255;
  711.         }
  712.  
  713.       if (bint.img_bpp > 3)
  714.         {
  715.           int i,j;
  716.         for (i = 0, j = 0 ; i < sizeof(bint.preview_row); i += 4, j += 3 )
  717.           {
  718.         gint alphaval;
  719.         if (((i/4) / GIMP_CHECK_SIZE) & 1)
  720.           check = check_0;
  721.         else
  722.           check = check_1;
  723.  
  724.         alphaval = bint.preview_row[i + 3];
  725.  
  726.          bint.preview_row[j] = 
  727.           check + (((bint.preview_row[i] - check)*alphaval)/255);
  728.          bint.preview_row[j + 1] = 
  729.           check + (((bint.preview_row[i + 1] - check)*alphaval)/255);
  730.          bint.preview_row[j + 2] = 
  731.           check + (((bint.preview_row[i + 2] - check)*alphaval)/255);
  732.           }
  733.       }
  734.     
  735.       gtk_preview_draw_row(GTK_PREVIEW(bint.preview),
  736.                    bint.preview_row, 0, y, preview_width);
  737.     
  738.       p += preview_width * bint.img_bpp;
  739.     } 
  740.     }
  741.   else
  742.     {
  743.       /* Horizontal blinds */
  744.       /* Apply the blinds algo to a single column -
  745.        * this act as a transfomation matrix for the 
  746.        * rows. Make row 0 invalid so we can find it again!
  747.        */
  748.       int i;
  749.       int loop1,loop2;
  750.       guchar *sr = g_new(guchar,(preview_height)*4);
  751.       guchar *dr = g_new(guchar,(preview_height)*4);
  752.       guchar dummybg[4];
  753.       /* Copy into here after translation*/
  754.       guchar copy_row[PREVIEW_SIZE*4]; 
  755.  
  756.       memset (dummybg, 0, 4);
  757.       memset (dr, 0, (preview_height) * 4); /* all dr rows are background rows */
  758.  
  759.       /* Fill in with background color ? */
  760.       for (i = 0 ; i < preview_width ; i++)
  761.     {
  762.       int j;
  763.       int bd = bint.img_bpp;
  764.       guchar *dst;
  765.       dst = &bint.preview_row[i*bd];
  766.       
  767.       for (j = 0 ; j < bd; j++)
  768.         {
  769.           dst[j] = bg[j];
  770.         }
  771.     }
  772.  
  773.       for ( y = 0 ; y < preview_height; y++)
  774.     {
  775.       sr[y] = y+1;
  776.     }
  777.  
  778.       /* Bit of a fiddle since blindsapply really works on an image
  779.        * row not a set of bytes. - preview can't be > 255
  780.        * or must make dr sr int rows. 
  781.        */
  782.       blindsapply (sr, dr, preview_height, 1, dummybg);
  783.  
  784.       for (y = 0; y < preview_height; y++)
  785.     {
  786.       if ((y / GIMP_CHECK_SIZE) & 1)
  787.         {
  788.           check_0 = GIMP_CHECK_DARK * 255;
  789.           check_1 = GIMP_CHECK_LIGHT * 255;
  790.         }
  791.       else
  792.         {
  793.           check_0 = GIMP_CHECK_LIGHT * 255;
  794.           check_1 = GIMP_CHECK_DARK * 255;
  795.         }
  796.  
  797.       if (dr[y] == 0)
  798.         {
  799.           /* Draw background line */
  800.           p = bint.preview_row;
  801.         }
  802.       else
  803.         {
  804.           /* Draw line from src */
  805.           p = bint.pv_cache + (preview_width * bint.img_bpp * (dr[y] - 1));
  806.         }
  807.  
  808.       if (bint.img_bpp > 3)
  809.         {
  810.           /* Take account of alpha channel HERE*/
  811.           for (loop1 = 0, loop2 = 0 ; 
  812.            loop1 < preview_width*bint.img_bpp; 
  813.            loop1 += 4, loop2 += 3)
  814.         {
  815.           gint alphaval;
  816.           if (((loop1/4) / GIMP_CHECK_SIZE) & 1)
  817.             check = check_0;
  818.           else
  819.             check = check_1;
  820.  
  821.           alphaval = p[loop1 + 3];
  822.  
  823.           copy_row[loop2] = 
  824.             check + (((p[loop1] - check) * alphaval) / 255);
  825.           copy_row[loop2 + 1] = 
  826.             check + (((p[loop1 + 1] - check) * alphaval) / 255);
  827.           copy_row[loop2 + 2] = 
  828.             check + (((p[loop1 + 2] - check) * alphaval) / 255);
  829.         }
  830.           p = ©_row[0];
  831.         }
  832.       gtk_preview_draw_row (GTK_PREVIEW (bint.preview),
  833.                 p, 0, y, preview_width);
  834.     } 
  835.       g_free (sr);
  836.       g_free (dr);
  837.     }
  838.  
  839.   gtk_widget_draw (bint.preview, NULL);
  840.   gdk_flush ();
  841. }
  842.  
  843. /* STEP tells us how many rows/columns to gulp down in one go... */
  844. /* Note all the "4" literals around here are to do with the depths
  845.  * of the images. Makes it easier to deal with for my small brain.
  846.  */
  847.  
  848. #define STEP 40
  849.  
  850. static void
  851. apply_blinds (void)
  852. {
  853.   GimpPixelRgn des_rgn;
  854.   GimpPixelRgn src_rgn;
  855.   guchar *src_rows,*des_rows;
  856.   int x,y;
  857.   guchar bg[4];
  858.  
  859.   /* Adjust aplha channel if GREYA */
  860.   if (blinds_get_bg (bg) == TRUE)
  861.     bg[1] = bvals.bg_trans  ? 0 : 255;
  862.  
  863.   gimp_pixel_rgn_init (&src_rgn, blindsdrawable,
  864.                sel_x1, sel_y1, sel_width, sel_height, FALSE, FALSE);
  865.   gimp_pixel_rgn_init (&des_rgn, blindsdrawable,
  866.                sel_x1, sel_y1, sel_width, sel_height, TRUE, TRUE);
  867.  
  868.   src_rows = g_new (guchar, MAX (sel_width, sel_height) * 4 * STEP); 
  869.   des_rows = g_new (guchar, MAX (sel_width, sel_height) * 4 * STEP); 
  870.  
  871.   if (bvals.orientation)
  872.     {
  873.       for (y = 0; y < sel_height; y += STEP) 
  874.     {
  875.       int rr;
  876.       int step;
  877.  
  878.       if((y + STEP) > sel_height)
  879.         step = sel_height - y;
  880.       else
  881.         step = STEP;
  882.  
  883.       gimp_pixel_rgn_get_rect (&src_rgn,
  884.                    src_rows,
  885.                    sel_x1,
  886.                    sel_y1 + y,
  887.                    sel_width,
  888.                    step);
  889.  
  890.       /* OK I could make this better */
  891.       for (rr = 0; rr < STEP; rr++)
  892.         blindsapply (src_rows + (sel_width * rr * src_rgn.bpp),
  893.              des_rows + (sel_width * rr * src_rgn.bpp),
  894.              sel_width, src_rgn.bpp, bg);
  895.  
  896.       gimp_pixel_rgn_set_rect (&des_rgn,
  897.                    des_rows,
  898.                    sel_x1,
  899.                    sel_y1 + y,
  900.                    sel_width,
  901.                    step);
  902.  
  903.       gimp_progress_update ((double) y / (double) sel_height);
  904.     }
  905.     }
  906.   else
  907.     {
  908.       /* Horizontal blinds */
  909.       /* Apply the blinds algo to a single column -
  910.        * this act as a transfomation matrix for the 
  911.        * rows. Make row 0 invalid so we can find it again!
  912.        */
  913.       int i;
  914.       gint *sr = g_new (gint, sel_height * 4);
  915.       gint *dr = g_new (gint, sel_height * 4);
  916.       guchar *dst = g_new (guchar, STEP * 4);
  917.       guchar dummybg[4];
  918.  
  919.       memset (dummybg, 0, 4);
  920.       memset (dr, 0, sel_height * 4); /* all dr rows are background rows */
  921.       for (y = 0; y < sel_height; y++)
  922.     {
  923.       sr[y] = y+1;
  924.     }
  925.  
  926.       /* Hmmm. does this work portably? */
  927.       /* This "swaps the intergers around that are held in in the
  928.        * sr & dr arrays. 
  929.        */
  930.       blindsapply ((guchar *) sr, (guchar *) dr,
  931.            sel_height, sizeof (gint), dummybg);
  932.  
  933.       /* Fill in with background color ? */
  934.       for (i = 0 ; i < STEP ; i++)
  935.     {
  936.       int j;
  937.       guchar *bgdst;
  938.       bgdst = &dst[i * src_rgn.bpp];
  939.  
  940.       for (j = 0 ; j < src_rgn.bpp; j++)
  941.         {
  942.           bgdst[j] = bg[j];
  943.         }
  944.     }
  945.  
  946.       for (x = 0; x < sel_width; x += STEP) 
  947.     {
  948.       int rr;
  949.       int step;
  950.       guchar *p;
  951.  
  952.       if((x + STEP) > sel_width)
  953.         step = sel_width - x;
  954.       else
  955.         step = STEP;
  956.  
  957.       gimp_pixel_rgn_get_rect (&src_rgn,
  958.                    src_rows,
  959.                    sel_x1 + x,
  960.                    sel_y1,
  961.                    step,
  962.                    sel_height);
  963.  
  964.       /* OK I could make this better */
  965.       for (rr = 0; rr < sel_height; rr++)
  966.         {
  967.           if(dr[rr] == 0)
  968.           {
  969.           /* Draw background line */
  970.           p = dst;
  971.           }
  972.           else
  973.           {
  974.           /* Draw line from src */
  975.           p = src_rows + (step * src_rgn.bpp * (dr[rr] - 1));
  976.           }
  977.           memcpy (des_rows + (rr * step * src_rgn.bpp), p,
  978.               step * src_rgn.bpp);
  979.         }
  980.  
  981.       gimp_pixel_rgn_set_rect (&des_rgn,
  982.                    des_rows,
  983.                    sel_x1 + x,
  984.                    sel_y1,
  985.                    step,
  986.                    sel_height);
  987.  
  988.       gimp_progress_update ((double) x / (double) sel_width);
  989.     }
  990.  
  991.       g_free (dst);
  992.       g_free (sr);
  993.       g_free (dr);
  994.     }
  995.  
  996.   g_free (src_rows);
  997.   g_free (des_rows);
  998.  
  999.   gimp_drawable_flush (blindsdrawable);
  1000.   gimp_drawable_merge_shadow (blindsdrawable->id, TRUE);
  1001.   gimp_drawable_update (blindsdrawable->id,
  1002.             sel_x1, sel_y1, sel_width, sel_height);  
  1003.   
  1004. }
  1005.