GLib has a specific type for byte arrays: `GBytes` (and it's underlying GType `G_TYPE_BYTES`). By using this type, we can avoid having a `GimpUint8Array` which is a bit cumbersome to use for both the C API, as well as bindings. By using `GBytes`, we allow other languages to pass on byte arrays as they are used to, while the bindings will make sure to do the right thing. In the end, it makes the API a little bit simpler for everyone, and reduces confusion for people who are used to working with byte arrays in other C/GLib based code (and not having 2 different types to denote the same thing). Related: https://gitlab.gnome.org/GNOME/gimp/-/issues/5919
778 lines
23 KiB
C
778 lines
23 KiB
C
/*
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* This is a plug-in for GIMP.
|
|
*
|
|
* Blinds plug-in. Distort an image as though it was stuck to
|
|
* window blinds and the blinds where opened/closed.
|
|
*
|
|
* Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* A fair proportion of this code was taken from the Whirl plug-in
|
|
* which was copyrighted by Federico Mena Quintero (as below).
|
|
*
|
|
* Whirl plug-in --- distort an image into a whirlpool
|
|
* Copyright (C) 1997 Federico Mena Quintero
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <libgimp/gimp.h>
|
|
#include <libgimp/gimpui.h>
|
|
|
|
#include "libgimp/stdplugins-intl.h"
|
|
|
|
/***** Magic numbers *****/
|
|
|
|
#define PLUG_IN_PROC "plug-in-blinds"
|
|
#define PLUG_IN_BINARY "blinds"
|
|
#define PLUG_IN_ROLE "gimp-blinds"
|
|
|
|
#define MAX_FANS 100
|
|
|
|
|
|
typedef struct _Blinds Blinds;
|
|
typedef struct _BlindsClass BlindsClass;
|
|
|
|
struct _Blinds
|
|
{
|
|
GimpPlugIn parent_instance;
|
|
};
|
|
|
|
struct _BlindsClass
|
|
{
|
|
GimpPlugInClass parent_class;
|
|
};
|
|
|
|
|
|
#define BLINDS_TYPE (blinds_get_type ())
|
|
#define BLINDS (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BLINDS_TYPE, Blinds))
|
|
|
|
GType blinds_get_type (void) G_GNUC_CONST;
|
|
|
|
static GList * blinds_query_procedures (GimpPlugIn *plug_in);
|
|
static GimpProcedure * blinds_create_procedure (GimpPlugIn *plug_in,
|
|
const gchar *name);
|
|
|
|
static GimpValueArray * blinds_run (GimpProcedure *procedure,
|
|
GimpRunMode run_mode,
|
|
GimpImage *image,
|
|
gint n_drawables,
|
|
GimpDrawable **drawables,
|
|
const GimpValueArray *args,
|
|
gpointer run_data);
|
|
|
|
static gboolean blinds_dialog (GimpProcedure *procedure,
|
|
GObject *config,
|
|
GimpDrawable *drawable);
|
|
|
|
static void dialog_update_preview (GtkWidget *widget,
|
|
GObject *config);
|
|
static void apply_blinds (GObject *config,
|
|
GimpDrawable *drawable);
|
|
|
|
|
|
G_DEFINE_TYPE (Blinds, blinds, GIMP_TYPE_PLUG_IN)
|
|
|
|
GIMP_MAIN (BLINDS_TYPE)
|
|
DEFINE_STD_SET_I18N
|
|
|
|
|
|
/* Array to hold each size of fans. And no there are not each the
|
|
* same size (rounding errors...)
|
|
*/
|
|
|
|
static gint fanwidths[MAX_FANS];
|
|
|
|
static void
|
|
blinds_class_init (BlindsClass *klass)
|
|
{
|
|
GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);
|
|
|
|
plug_in_class->query_procedures = blinds_query_procedures;
|
|
plug_in_class->create_procedure = blinds_create_procedure;
|
|
plug_in_class->set_i18n = STD_SET_I18N;
|
|
}
|
|
|
|
static void
|
|
blinds_init (Blinds *blinds)
|
|
{
|
|
}
|
|
|
|
static GList *
|
|
blinds_query_procedures (GimpPlugIn *plug_in)
|
|
{
|
|
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
|
}
|
|
|
|
static GimpProcedure *
|
|
blinds_create_procedure (GimpPlugIn *plug_in,
|
|
const gchar *name)
|
|
{
|
|
GimpProcedure *procedure = NULL;
|
|
|
|
if (! strcmp (name, PLUG_IN_PROC))
|
|
{
|
|
procedure = gimp_image_procedure_new (plug_in, name,
|
|
GIMP_PDB_PROC_TYPE_PLUGIN,
|
|
blinds_run, NULL, NULL);
|
|
|
|
gimp_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
|
gimp_procedure_set_sensitivity_mask (procedure,
|
|
GIMP_PROCEDURE_SENSITIVE_DRAWABLE);
|
|
|
|
gimp_procedure_set_menu_label (procedure, _("_Blinds..."));
|
|
gimp_procedure_add_menu_path (procedure, "<Image>/Filters/Distorts");
|
|
|
|
gimp_procedure_set_documentation (procedure,
|
|
_("Simulate an image painted on "
|
|
"window blinds"),
|
|
"More here later",
|
|
name);
|
|
gimp_procedure_set_attribution (procedure,
|
|
"Andy Thomas",
|
|
"Andy Thomas",
|
|
"1997");
|
|
|
|
GIMP_PROC_ARG_INT (procedure, "angle-displacement",
|
|
_("_Displacement"),
|
|
_("Angle of Displacement"),
|
|
0, 360, 30,
|
|
G_PARAM_READWRITE);
|
|
|
|
GIMP_PROC_ARG_INT (procedure, "num-segments",
|
|
_("_Number of segments"),
|
|
_("Number of segments in blinds"),
|
|
1, 1024, 3,
|
|
G_PARAM_READWRITE);
|
|
|
|
GIMP_PROC_ARG_INT (procedure, "orientation",
|
|
_("Orientation"),
|
|
_("The orientation"),
|
|
GIMP_ORIENTATION_HORIZONTAL,
|
|
GIMP_ORIENTATION_VERTICAL,
|
|
GIMP_ORIENTATION_HORIZONTAL,
|
|
G_PARAM_READWRITE);
|
|
|
|
GIMP_PROC_ARG_BOOLEAN (procedure, "bg-transparent",
|
|
_("_Transparent"),
|
|
_("Background transparent"),
|
|
FALSE,
|
|
G_PARAM_READWRITE);
|
|
}
|
|
|
|
return procedure;
|
|
}
|
|
|
|
static GimpValueArray *
|
|
blinds_run (GimpProcedure *procedure,
|
|
GimpRunMode run_mode,
|
|
GimpImage *image,
|
|
gint n_drawables,
|
|
GimpDrawable **drawables,
|
|
const GimpValueArray *args,
|
|
gpointer run_data)
|
|
{
|
|
GimpProcedureConfig *config;
|
|
GimpDrawable *drawable;
|
|
|
|
gegl_init (NULL, NULL);
|
|
|
|
if (n_drawables != 1)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
g_set_error (&error, GIMP_PLUG_IN_ERROR, 0,
|
|
_("Procedure '%s' only works with one drawable."),
|
|
PLUG_IN_PROC);
|
|
|
|
return gimp_procedure_new_return_values (procedure,
|
|
GIMP_PDB_CALLING_ERROR,
|
|
error);
|
|
}
|
|
else
|
|
{
|
|
drawable = drawables[0];
|
|
}
|
|
|
|
config = gimp_procedure_create_config (procedure);
|
|
gimp_procedure_config_begin_run (config, NULL, run_mode, args);
|
|
|
|
switch (run_mode)
|
|
{
|
|
case GIMP_RUN_INTERACTIVE:
|
|
if (! blinds_dialog (procedure, G_OBJECT (config), drawable))
|
|
{
|
|
gimp_procedure_config_end_run (config, GIMP_PDB_CANCEL);
|
|
g_object_unref (config);
|
|
|
|
return gimp_procedure_new_return_values (procedure,
|
|
GIMP_PDB_CANCEL,
|
|
NULL);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (gimp_drawable_is_rgb (drawable) ||
|
|
gimp_drawable_is_gray (drawable))
|
|
{
|
|
gimp_progress_init (_("Adding blinds"));
|
|
|
|
apply_blinds (G_OBJECT (config), drawable);
|
|
|
|
if (run_mode != GIMP_RUN_NONINTERACTIVE)
|
|
gimp_displays_flush ();
|
|
}
|
|
else
|
|
{
|
|
gimp_procedure_config_end_run (config, GIMP_PDB_EXECUTION_ERROR);
|
|
g_object_unref (config);
|
|
|
|
return gimp_procedure_new_return_values (procedure,
|
|
GIMP_PDB_EXECUTION_ERROR,
|
|
NULL);
|
|
}
|
|
|
|
gimp_procedure_config_end_run (config, GIMP_PDB_SUCCESS);
|
|
g_object_unref (config);
|
|
|
|
return gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
blinds_dialog (GimpProcedure *procedure,
|
|
GObject *config,
|
|
GimpDrawable *drawable)
|
|
{
|
|
GtkWidget *dialog;
|
|
GtkWidget *preview;
|
|
GtkWidget *vbox;
|
|
GtkWidget *hbox;
|
|
GtkWidget *scale;
|
|
GtkListStore *store;
|
|
gboolean run;
|
|
|
|
gimp_ui_init (PLUG_IN_BINARY);
|
|
|
|
dialog = gimp_procedure_dialog_new (procedure,
|
|
GIMP_PROCEDURE_CONFIG (config),
|
|
_("Blinds"));
|
|
|
|
gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
|
GTK_RESPONSE_OK,
|
|
GTK_RESPONSE_CANCEL,
|
|
-1);
|
|
|
|
gimp_window_set_transient (GTK_WINDOW (dialog));
|
|
|
|
preview = gimp_drawable_preview_new_from_drawable (drawable);
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
|
preview, TRUE, TRUE, 0);
|
|
gtk_widget_set_margin_bottom (preview, 12);
|
|
gtk_widget_show (preview);
|
|
|
|
store = gimp_int_store_new (_("Horizontal"), GIMP_ORIENTATION_HORIZONTAL,
|
|
_("Vertical"), GIMP_ORIENTATION_VERTICAL,
|
|
NULL);
|
|
gimp_procedure_dialog_get_int_radio (GIMP_PROCEDURE_DIALOG (dialog),
|
|
"orientation", GIMP_INT_STORE (store));
|
|
|
|
gimp_procedure_dialog_get_label (GIMP_PROCEDURE_DIALOG (dialog),
|
|
"bg_label", _("Background"));
|
|
|
|
gimp_procedure_dialog_fill_frame (GIMP_PROCEDURE_DIALOG (dialog),
|
|
"bg-frame", "bg_label", FALSE,
|
|
"bg-transparent");
|
|
gimp_procedure_dialog_set_sensitive (GIMP_PROCEDURE_DIALOG (dialog), "bg-frame",
|
|
gimp_drawable_has_alpha (drawable),
|
|
NULL, NULL, FALSE);
|
|
|
|
hbox = gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog),
|
|
"blinds-hbox", "orientation",
|
|
"bg-frame", NULL);
|
|
gtk_orientable_set_orientation (GTK_ORIENTABLE (hbox),
|
|
GTK_ORIENTATION_HORIZONTAL);
|
|
gtk_box_set_spacing (GTK_BOX (hbox), 12);
|
|
gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
|
|
gtk_widget_set_margin_bottom (hbox, 12);
|
|
|
|
scale = gimp_procedure_dialog_get_scale_entry (GIMP_PROCEDURE_DIALOG (dialog),
|
|
"angle-displacement", 1.0);
|
|
gtk_widget_set_margin_bottom (scale, 12);
|
|
|
|
gimp_procedure_dialog_get_scale_entry (GIMP_PROCEDURE_DIALOG (dialog),
|
|
"num-segments", 1.0);
|
|
|
|
vbox = gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog),
|
|
"blinds-vbox", "blinds-hbox",
|
|
"angle-displacement", "num-segments",
|
|
NULL);
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
|
gtk_widget_set_size_request (vbox, 320, -1);
|
|
|
|
g_object_set_data (config, "drawable", drawable);
|
|
|
|
g_signal_connect (preview, "invalidated",
|
|
G_CALLBACK (dialog_update_preview),
|
|
config);
|
|
|
|
g_signal_connect_swapped (config, "notify",
|
|
G_CALLBACK (gimp_preview_invalidate),
|
|
preview);
|
|
|
|
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog),
|
|
"blinds-vbox",
|
|
NULL);
|
|
|
|
gtk_widget_show (dialog);
|
|
|
|
run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog));
|
|
|
|
gtk_widget_destroy (dialog);
|
|
|
|
return run;
|
|
}
|
|
|
|
static void
|
|
blindsapply (GObject *config,
|
|
const guchar *srow,
|
|
guchar *drow,
|
|
gint width,
|
|
gint bpp,
|
|
guchar *bg)
|
|
{
|
|
const guchar *src;
|
|
guchar *dst;
|
|
gint i,j,k;
|
|
gdouble ang;
|
|
gint available;
|
|
gint angledsp;
|
|
gint numsegs;
|
|
|
|
g_object_get (config,
|
|
"angle-displacement", &angledsp,
|
|
"num-segments", &numsegs,
|
|
NULL);
|
|
|
|
/* Make the row 'shrink' around points along its length */
|
|
/* The numsegs determines how many segments to slip it in to */
|
|
/* The angle is the conceptual 'rotation' of each of these segments */
|
|
|
|
/* Note the row is considered to be made up of a two dim array actual
|
|
* pixel locations and the RGB color at these locations.
|
|
*/
|
|
|
|
/* In the process copy the src row to the destination row */
|
|
|
|
/* Fill in with background color ? */
|
|
for (i = 0 ; i < width ; i++)
|
|
{
|
|
dst = &drow[i*bpp];
|
|
|
|
for (j = 0 ; j < bpp; j++)
|
|
{
|
|
dst[j] = bg[j];
|
|
}
|
|
}
|
|
|
|
/* Apply it */
|
|
|
|
available = width;
|
|
for (i = 0; i < numsegs; i++)
|
|
{
|
|
/* Width of segs are variable */
|
|
fanwidths[i] = available / (numsegs - i);
|
|
available -= fanwidths[i];
|
|
}
|
|
|
|
/* do center points first - just for fun...*/
|
|
available = 0;
|
|
for (k = 1; k <= numsegs; k++)
|
|
{
|
|
int point;
|
|
|
|
point = available + fanwidths[k - 1] / 2;
|
|
|
|
available += fanwidths[k - 1];
|
|
|
|
src = &srow[point * bpp];
|
|
dst = &drow[point * bpp];
|
|
|
|
/* Copy pixels across */
|
|
for (j = 0 ; j < bpp; j++)
|
|
{
|
|
dst[j] = src[j];
|
|
}
|
|
}
|
|
|
|
/* Disp for each point */
|
|
ang = (angledsp * 2 * G_PI) / 360; /* Angle in rads */
|
|
ang = (1 - fabs (cos (ang)));
|
|
|
|
available = 0;
|
|
for (k = 0 ; k < numsegs; k++)
|
|
{
|
|
int dx; /* Amount to move by */
|
|
int fw;
|
|
|
|
for (i = 0 ; i < (fanwidths[k]/2) ; i++)
|
|
{
|
|
/* Copy pixels across of left half of fan */
|
|
fw = fanwidths[k] / 2;
|
|
dx = (int) (ang * ((double) (fw - (double)(i % fw))));
|
|
|
|
src = &srow[(available + i) * bpp];
|
|
dst = &drow[(available + i + dx) * bpp];
|
|
|
|
for (j = 0; j < bpp; j++)
|
|
{
|
|
dst[j] = src[j];
|
|
}
|
|
|
|
/* Right side */
|
|
j = i + 1;
|
|
src = &srow[(available + fanwidths[k] - j
|
|
- (fanwidths[k] % 2)) * bpp];
|
|
dst = &drow[(available + fanwidths[k] - j
|
|
- (fanwidths[k] % 2) - dx) * bpp];
|
|
|
|
for (j = 0; j < bpp; j++)
|
|
{
|
|
dst[j] = src[j];
|
|
}
|
|
}
|
|
|
|
available += fanwidths[k];
|
|
}
|
|
}
|
|
|
|
static void
|
|
dialog_update_preview (GtkWidget *widget,
|
|
GObject *config)
|
|
{
|
|
GimpPreview *preview = GIMP_PREVIEW (widget);
|
|
GimpDrawable *drawable = g_object_get_data (config, "drawable");
|
|
gint y;
|
|
const guchar *p;
|
|
guchar *buffer;
|
|
GBytes *cache;
|
|
const guchar *cache_start;
|
|
GimpRGB background;
|
|
guchar bg[4];
|
|
gint width;
|
|
gint height;
|
|
gint bpp;
|
|
GimpOrientationType orientation;
|
|
gint bg_trans;
|
|
|
|
g_object_get (config,
|
|
"bg-transparent", &bg_trans,
|
|
"orientation", &orientation,
|
|
NULL);
|
|
|
|
gimp_preview_get_size (preview, &width, &height);
|
|
cache = gimp_drawable_get_thumbnail_data (drawable,
|
|
width, height,
|
|
&width, &height, &bpp);
|
|
p = cache_start = g_bytes_get_data (cache, NULL);
|
|
|
|
gimp_context_get_background (&background);
|
|
|
|
if (bg_trans)
|
|
gimp_rgb_set_alpha (&background, 0.0);
|
|
|
|
if (gimp_drawable_is_gray (drawable))
|
|
{
|
|
bg[0] = gimp_rgb_luminance_uchar (&background);
|
|
gimp_rgba_get_uchar (&background, NULL, NULL, NULL, bg + 3);
|
|
}
|
|
else
|
|
{
|
|
gimp_rgba_get_uchar (&background, bg, bg + 1, bg + 2, bg + 3);
|
|
}
|
|
|
|
buffer = g_new (guchar, width * height * bpp);
|
|
|
|
if (orientation == GIMP_ORIENTATION_VERTICAL)
|
|
{
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
blindsapply (config, p,
|
|
buffer + y * width * bpp,
|
|
width,
|
|
bpp, bg);
|
|
p += width * bpp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Horizontal blinds */
|
|
/* Apply the blinds algo to a single column -
|
|
* this act as a transformation matrix for the
|
|
* rows. Make row 0 invalid so we can find it again!
|
|
*/
|
|
gint i;
|
|
guchar *sr = g_new (guchar, height * 4);
|
|
guchar *dr = g_new0 (guchar, height * 4);
|
|
guchar dummybg[4] = {0, 0, 0, 0};
|
|
|
|
/* Fill in with background color ? */
|
|
for (i = 0 ; i < width ; i++)
|
|
{
|
|
gint j;
|
|
gint bd = bpp;
|
|
guchar *dst;
|
|
dst = &buffer[i * bd];
|
|
|
|
for (j = 0 ; j < bd; j++)
|
|
{
|
|
dst[j] = bg[j];
|
|
}
|
|
}
|
|
|
|
for ( y = 0 ; y < height; y++)
|
|
{
|
|
sr[y] = y+1;
|
|
}
|
|
|
|
/* Bit of a fiddle since blindsapply really works on an image
|
|
* row not a set of bytes. - preview can't be > 255
|
|
* or must make dr sr int rows.
|
|
*/
|
|
blindsapply (config, sr, dr, height, 1, dummybg);
|
|
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
if (dr[y] == 0)
|
|
{
|
|
/* Draw background line */
|
|
p = buffer;
|
|
}
|
|
else
|
|
{
|
|
/* Draw line from src */
|
|
p = cache_start +
|
|
(width * bpp * (dr[y] - 1));
|
|
}
|
|
memcpy (buffer + y * width * bpp,
|
|
p,
|
|
width * bpp);
|
|
}
|
|
g_free (sr);
|
|
g_free (dr);
|
|
}
|
|
|
|
gimp_preview_draw_buffer (preview, buffer, width * bpp);
|
|
|
|
g_free (buffer);
|
|
g_bytes_unref (cache);
|
|
}
|
|
|
|
/* STEP tells us how many rows/columns to gulp down in one go... */
|
|
/* Note all the "4" literals around here are to do with the depths
|
|
* of the images. Makes it easier to deal with for my small brain.
|
|
*/
|
|
|
|
#define STEP 40
|
|
|
|
static void
|
|
apply_blinds (GObject *config,
|
|
GimpDrawable *drawable)
|
|
{
|
|
GeglBuffer *src_buffer;
|
|
GeglBuffer *dest_buffer;
|
|
const Babl *format;
|
|
guchar *src_rows, *des_rows;
|
|
gint bytes;
|
|
gint x, y;
|
|
GimpRGB background;
|
|
guchar bg[4];
|
|
gint sel_x1, sel_y1;
|
|
gint sel_width, sel_height;
|
|
GimpOrientationType orientation;
|
|
gint bg_trans;
|
|
|
|
g_object_get (config,
|
|
"bg-transparent", &bg_trans,
|
|
"orientation", &orientation,
|
|
NULL);
|
|
|
|
gimp_context_get_background (&background);
|
|
|
|
if (bg_trans)
|
|
gimp_rgb_set_alpha (&background, 0.0);
|
|
|
|
gimp_rgba_get_uchar (&background, bg, bg + 1, bg + 2, bg + 3);
|
|
|
|
if (! gimp_drawable_mask_intersect (drawable,
|
|
&sel_x1, &sel_y1,
|
|
&sel_width, &sel_height))
|
|
return;
|
|
|
|
if (gimp_drawable_has_alpha (drawable))
|
|
format = babl_format ("R'G'B'A u8");
|
|
else
|
|
format = babl_format ("R'G'B' u8");
|
|
|
|
bytes = babl_format_get_bytes_per_pixel (format);
|
|
|
|
src_buffer = gimp_drawable_get_buffer (drawable);
|
|
dest_buffer = gimp_drawable_get_shadow_buffer (drawable);
|
|
|
|
src_rows = g_new (guchar, MAX (sel_width, sel_height) * bytes * STEP);
|
|
des_rows = g_new (guchar, MAX (sel_width, sel_height) * bytes * STEP);
|
|
|
|
if (orientation == GIMP_ORIENTATION_VERTICAL)
|
|
{
|
|
for (y = 0; y < sel_height; y += STEP)
|
|
{
|
|
gint rr;
|
|
gint step;
|
|
|
|
if ((y + STEP) > sel_height)
|
|
step = sel_height - y;
|
|
else
|
|
step = STEP;
|
|
|
|
gegl_buffer_get (src_buffer,
|
|
GEGL_RECTANGLE (sel_x1, sel_y1 + y,
|
|
sel_width, step), 1.0,
|
|
format, src_rows,
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
|
|
|
/* OK I could make this better */
|
|
for (rr = 0; rr < STEP; rr++)
|
|
blindsapply (config,
|
|
src_rows + (sel_width * rr * bytes),
|
|
des_rows + (sel_width * rr * bytes),
|
|
sel_width, bytes, bg);
|
|
|
|
gegl_buffer_set (dest_buffer,
|
|
GEGL_RECTANGLE (sel_x1, sel_y1 + y,
|
|
sel_width, step), 0,
|
|
format, des_rows,
|
|
GEGL_AUTO_ROWSTRIDE);
|
|
|
|
gimp_progress_update ((double) y / (double) sel_height);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Horizontal blinds */
|
|
/* Apply the blinds algo to a single column -
|
|
* this act as a transformation matrix for the
|
|
* rows. Make row 0 invalid so we can find it again!
|
|
*/
|
|
gint i;
|
|
gint *sr = g_new (gint, sel_height * bytes);
|
|
gint *dr = g_new (gint, sel_height * bytes);
|
|
guchar *dst = g_new (guchar, STEP * bytes);
|
|
guchar dummybg[4];
|
|
|
|
memset (dummybg, 0, 4);
|
|
memset (dr, 0, sel_height * bytes); /* all dr rows are background rows */
|
|
for (y = 0; y < sel_height; y++)
|
|
{
|
|
sr[y] = y+1;
|
|
}
|
|
|
|
/* Hmmm. does this work portably? */
|
|
/* This "swaps" the integers around that are held in in the
|
|
* sr & dr arrays.
|
|
*/
|
|
blindsapply (config, (guchar *) sr, (guchar *) dr,
|
|
sel_height, sizeof (gint), dummybg);
|
|
|
|
/* Fill in with background color ? */
|
|
for (i = 0 ; i < STEP ; i++)
|
|
{
|
|
int j;
|
|
guchar *bgdst;
|
|
bgdst = &dst[i * bytes];
|
|
|
|
for (j = 0 ; j < bytes; j++)
|
|
{
|
|
bgdst[j] = bg[j];
|
|
}
|
|
}
|
|
|
|
for (x = 0; x < sel_width; x += STEP)
|
|
{
|
|
int rr;
|
|
int step;
|
|
guchar *p;
|
|
|
|
if((x + STEP) > sel_width)
|
|
step = sel_width - x;
|
|
else
|
|
step = STEP;
|
|
|
|
gegl_buffer_get (src_buffer,
|
|
GEGL_RECTANGLE (sel_x1 + x, sel_y1,
|
|
step, sel_height), 1.0,
|
|
format, src_rows,
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
|
|
|
/* OK I could make this better */
|
|
for (rr = 0; rr < sel_height; rr++)
|
|
{
|
|
if(dr[rr] == 0)
|
|
{
|
|
/* Draw background line */
|
|
p = dst;
|
|
}
|
|
else
|
|
{
|
|
/* Draw line from src */
|
|
p = src_rows + (step * bytes * (dr[rr] - 1));
|
|
}
|
|
memcpy (des_rows + (rr * step * bytes), p,
|
|
step * bytes);
|
|
}
|
|
|
|
gegl_buffer_set (dest_buffer,
|
|
GEGL_RECTANGLE (sel_x1 + x, sel_y1,
|
|
step, sel_height), 0,
|
|
format, des_rows,
|
|
GEGL_AUTO_ROWSTRIDE);
|
|
|
|
gimp_progress_update ((double) x / (double) sel_width);
|
|
}
|
|
|
|
g_free (dst);
|
|
g_free (sr);
|
|
g_free (dr);
|
|
}
|
|
|
|
g_free (src_rows);
|
|
g_free (des_rows);
|
|
|
|
g_object_unref (src_buffer);
|
|
g_object_unref (dest_buffer);
|
|
|
|
gimp_progress_update (1.0);
|
|
|
|
gimp_drawable_merge_shadow (drawable, TRUE);
|
|
gimp_drawable_update (drawable,
|
|
sel_x1, sel_y1, sel_width, sel_height);
|
|
}
|