2006-12-09 21:33:38 +00:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
* Despeckle (adaptive median) filter
|
1997-11-24 22:05:25 +00:00
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
* Copyright 1997-1998 Michael Sweet (mike@easysw.com)
|
2010-09-05 15:38:44 +02:00
|
|
|
* optimized in 2010 by Przemyslaw Zych (kermidt.zed@gmail.com)
|
1997-11-24 22:05:25 +00:00
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
* 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.
|
1997-11-24 22:05:25 +00:00
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
* 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.
|
1997-11-24 22:05:25 +00:00
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
2018-07-11 23:27:07 +02:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
1997-11-24 22:05:25 +00:00
|
|
|
*/
|
2000-01-13 15:39:26 +00:00
|
|
|
|
1999-11-23 23:49:42 +00:00
|
|
|
#include "config.h"
|
1997-11-24 22:05:25 +00:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
2000-01-06 16:40:17 +00:00
|
|
|
|
2024-11-02 00:09:39 +01:00
|
|
|
#include "libgimpcolor/gimpcolor-private.h"
|
|
|
|
|
1997-11-24 22:05:25 +00:00
|
|
|
#include <libgimp/gimp.h>
|
|
|
|
#include <libgimp/gimpui.h>
|
2000-01-06 16:40:17 +00:00
|
|
|
|
1999-11-23 23:49:42 +00:00
|
|
|
#include "libgimp/stdplugins-intl.h"
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2000-04-30 21:03:44 +00:00
|
|
|
|
1997-11-24 22:05:25 +00:00
|
|
|
/*
|
|
|
|
* Constants...
|
|
|
|
*/
|
|
|
|
|
2005-08-13 21:39:56 +00:00
|
|
|
#define PLUG_IN_PROC "plug-in-despeckle"
|
|
|
|
#define PLUG_IN_BINARY "despeckle"
|
2010-09-05 15:38:44 +02:00
|
|
|
#define PLUG_IN_VERSION "May 2010"
|
2004-07-31 10:38:05 +00:00
|
|
|
#define SCALE_WIDTH 100
|
|
|
|
#define ENTRY_WIDTH 3
|
2010-09-05 15:38:44 +02:00
|
|
|
#define MAX_RADIUS 30
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2004-07-31 10:38:05 +00:00
|
|
|
#define FILTER_ADAPTIVE 0x01
|
2000-04-30 21:03:44 +00:00
|
|
|
#define FILTER_RECURSIVE 0x02
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2010-09-05 15:38:44 +02:00
|
|
|
/* List that stores pixels falling in to the same luma bucket */
|
2019-09-20 12:54:50 +02:00
|
|
|
#define MAX_LIST_ELEMS SQR(2 * MAX_RADIUS + 1)
|
|
|
|
|
2010-09-05 15:38:44 +02:00
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
typedef struct
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
2010-09-05 19:32:57 +02:00
|
|
|
const guchar *elems[MAX_LIST_ELEMS];
|
|
|
|
gint start;
|
|
|
|
gint count;
|
|
|
|
} PixelsList;
|
2010-09-05 15:38:44 +02:00
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
typedef struct
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
2010-09-05 19:32:57 +02:00
|
|
|
gint elems[256]; /* Number of pixels that fall into each luma bucket */
|
2010-09-05 15:38:44 +02:00
|
|
|
PixelsList origs[256]; /* Original pixels */
|
2010-09-05 19:32:57 +02:00
|
|
|
gint xmin;
|
|
|
|
gint ymin;
|
|
|
|
gint xmax;
|
|
|
|
gint ymax; /* Source rect */
|
2010-09-05 15:38:44 +02:00
|
|
|
} DespeckleHistogram;
|
|
|
|
|
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
typedef struct _Despeckle Despeckle;
|
|
|
|
typedef struct _DespeckleClass DespeckleClass;
|
2010-09-05 15:38:44 +02:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
struct _Despeckle
|
|
|
|
{
|
|
|
|
GimpPlugIn parent_instance;
|
|
|
|
};
|
2004-10-30 12:57:03 +00:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
struct _DespeckleClass
|
|
|
|
{
|
|
|
|
GimpPlugInClass parent_class;
|
|
|
|
};
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2000-01-13 15:39:26 +00:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
#define DESPECKLE_TYPE (despeckle_get_type ())
|
2023-10-18 18:29:37 +02:00
|
|
|
#define DESPECKLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DESPECKLE_TYPE, Despeckle))
|
2000-01-13 15:39:26 +00:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
GType despeckle_get_type (void) G_GNUC_CONST;
|
2000-01-14 12:41:00 +00:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
static GList * despeckle_query_procedures (GimpPlugIn *plug_in);
|
|
|
|
static GimpProcedure * despeckle_create_procedure (GimpPlugIn *plug_in,
|
|
|
|
const gchar *name);
|
2000-01-13 15:39:26 +00:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
static GimpValueArray * despeckle_run (GimpProcedure *procedure,
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
GimpImage *image,
|
2021-04-02 02:55:46 +02:00
|
|
|
GimpDrawable **drawables,
|
2023-06-21 01:09:41 +02:00
|
|
|
GimpProcedureConfig *config,
|
2019-09-02 13:06:57 +02:00
|
|
|
gpointer run_data);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
static gboolean despeckle (GimpDrawable *drawable,
|
|
|
|
GObject *config,
|
|
|
|
GError **error);
|
2019-09-20 12:54:50 +02:00
|
|
|
static void despeckle_median (GObject *config,
|
|
|
|
guchar *src,
|
2019-09-02 13:06:57 +02:00
|
|
|
guchar *dst,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
|
|
|
gint bpp,
|
|
|
|
gboolean preview);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-20 19:26:07 +02:00
|
|
|
static gboolean despeckle_dialog (GimpProcedure *procedure,
|
|
|
|
GObject *config,
|
|
|
|
GimpDrawable *drawable);
|
2019-09-02 13:06:57 +02:00
|
|
|
|
2019-09-20 12:54:50 +02:00
|
|
|
static void preview_update (GtkWidget *preview,
|
|
|
|
GObject *config);
|
2019-09-02 13:06:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (Despeckle, despeckle, GIMP_TYPE_PLUG_IN)
|
|
|
|
|
|
|
|
GIMP_MAIN (DESPECKLE_TYPE)
|
2022-05-26 00:59:36 +02:00
|
|
|
DEFINE_STD_SET_I18N
|
2019-09-02 13:06:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* Number of pixels in actual histogram falling into each category */
|
|
|
|
static gint hist0; /* Less than min threshold */
|
|
|
|
static gint hist255; /* More than max threshold */
|
|
|
|
static gint histrest; /* From min to max */
|
|
|
|
|
|
|
|
static DespeckleHistogram histogram;
|
2000-01-13 15:39:26 +00:00
|
|
|
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
static void
|
|
|
|
despeckle_class_init (DespeckleClass *klass)
|
|
|
|
{
|
|
|
|
GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
plug_in_class->query_procedures = despeckle_query_procedures;
|
|
|
|
plug_in_class->create_procedure = despeckle_create_procedure;
|
2022-05-26 00:59:36 +02:00
|
|
|
plug_in_class->set_i18n = STD_SET_I18N;
|
2019-09-02 13:06:57 +02:00
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
|
|
|
static void
|
2019-09-02 13:06:57 +02:00
|
|
|
despeckle_init (Despeckle *despeckle)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
static GList *
|
|
|
|
despeckle_query_procedures (GimpPlugIn *plug_in)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
2019-09-02 13:06:57 +02:00
|
|
|
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
static GimpProcedure *
|
|
|
|
despeckle_create_procedure (GimpPlugIn *plug_in,
|
|
|
|
const gchar *name)
|
|
|
|
{
|
|
|
|
GimpProcedure *procedure = NULL;
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
if (! strcmp (name, PLUG_IN_PROC))
|
|
|
|
{
|
2023-10-01 18:23:57 +02:00
|
|
|
procedure = gimp_image_procedure_new (plug_in, name,
|
|
|
|
GIMP_PDB_PROC_TYPE_PLUGIN,
|
|
|
|
despeckle_run, NULL, NULL);
|
2019-09-02 13:06:57 +02:00
|
|
|
|
|
|
|
gimp_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
2021-04-02 02:55:46 +02:00
|
|
|
gimp_procedure_set_sensitivity_mask (procedure,
|
|
|
|
GIMP_PROCEDURE_SENSITIVE_DRAWABLE);
|
2019-09-02 13:06:57 +02:00
|
|
|
|
2022-07-04 22:50:53 +02:00
|
|
|
gimp_procedure_set_menu_label (procedure, _("Des_peckle..."));
|
2019-09-02 13:06:57 +02:00
|
|
|
gimp_procedure_add_menu_path (procedure, "<Image>/Filters/Enhance");
|
|
|
|
|
|
|
|
gimp_procedure_set_documentation (procedure,
|
2022-07-04 22:50:53 +02:00
|
|
|
_("Remove speckle noise from the "
|
|
|
|
"image"),
|
2023-04-07 00:18:51 +00:00
|
|
|
_("This plug-in selectively performs "
|
|
|
|
"a median or adaptive box filter on "
|
|
|
|
"an image."),
|
2019-09-02 13:06:57 +02:00
|
|
|
name);
|
|
|
|
gimp_procedure_set_attribution (procedure,
|
|
|
|
"Michael Sweet <mike@easysw.com>",
|
|
|
|
"Copyright 1997-1998 by Michael Sweet",
|
|
|
|
PLUG_IN_VERSION);
|
|
|
|
|
2024-06-12 16:53:12 +00:00
|
|
|
gimp_procedure_add_int_argument (procedure, "radius",
|
|
|
|
_("R_adius"),
|
|
|
|
_("Filter box radius"),
|
|
|
|
1, MAX_RADIUS, 3,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
2024-07-26 18:56:53 +00:00
|
|
|
gimp_procedure_add_choice_argument (procedure, "type",
|
|
|
|
_("_Filter Type"),
|
|
|
|
_("Filter type"),
|
|
|
|
gimp_choice_new_with_values ("median", 0, _("Median"), NULL,
|
|
|
|
"adaptive", FILTER_ADAPTIVE, _("Adaptive"), NULL,
|
|
|
|
"recursive-median", FILTER_RECURSIVE, _("Recursive-Median"), NULL,
|
|
|
|
"recursive-adaptive", 3, _("Recursive-Adaptive"), NULL,
|
|
|
|
NULL),
|
|
|
|
"adaptive",
|
|
|
|
G_PARAM_READWRITE);
|
2024-06-12 16:53:12 +00:00
|
|
|
|
|
|
|
gimp_procedure_add_int_argument (procedure, "black",
|
|
|
|
_("_Black level"),
|
|
|
|
_("Black level"),
|
|
|
|
-1, 255, 7,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_int_argument (procedure, "white",
|
|
|
|
_("_White level"),
|
|
|
|
_("White level"),
|
|
|
|
0, 256, 248,
|
|
|
|
G_PARAM_READWRITE);
|
2019-09-02 13:06:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return procedure;
|
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
static GimpValueArray *
|
|
|
|
despeckle_run (GimpProcedure *procedure,
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
GimpImage *image,
|
2021-04-02 02:55:46 +02:00
|
|
|
GimpDrawable **drawables,
|
2023-06-21 01:09:41 +02:00
|
|
|
GimpProcedureConfig *config,
|
2019-09-02 13:06:57 +02:00
|
|
|
gpointer run_data)
|
|
|
|
{
|
2023-06-21 01:09:41 +02:00
|
|
|
GimpDrawable *drawable;
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
GError *error = NULL;
|
2019-09-20 12:54:50 +02:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
gegl_init (NULL, NULL);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2024-10-28 12:16:13 +01:00
|
|
|
if (gimp_core_object_array_get_length ((GObject **) drawables) != 1)
|
2021-04-02 02:55:46 +02:00
|
|
|
{
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2023-06-21 01:09:41 +02:00
|
|
|
if (! gimp_drawable_is_rgb (drawable) && ! gimp_drawable_is_gray (drawable))
|
|
|
|
return gimp_procedure_new_return_values (procedure, GIMP_PDB_EXECUTION_ERROR, NULL);
|
2019-09-20 12:54:50 +02:00
|
|
|
|
2023-06-21 01:09:41 +02:00
|
|
|
if (run_mode == GIMP_RUN_INTERACTIVE && ! despeckle_dialog (procedure, G_OBJECT (config), drawable))
|
|
|
|
return gimp_procedure_new_return_values (procedure, GIMP_PDB_CANCEL, NULL);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
if (! despeckle (drawable, G_OBJECT (config), &error))
|
|
|
|
return gimp_procedure_new_return_values (procedure,
|
|
|
|
GIMP_PDB_EXECUTION_ERROR,
|
|
|
|
error);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-02 13:06:57 +02:00
|
|
|
return gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL);
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
2008-03-29 02:30:58 +00:00
|
|
|
static inline guchar
|
|
|
|
pixel_luminance (const guchar *p,
|
|
|
|
gint bpp)
|
|
|
|
{
|
|
|
|
switch (bpp)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
return p[0];
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
return GIMP_RGB_LUMINANCE (p[0], p[1], p[2]);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0; /* should not be reached */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
pixel_copy (guchar *dest,
|
|
|
|
const guchar *src,
|
|
|
|
gint bpp)
|
|
|
|
{
|
|
|
|
switch (bpp)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
*dest++ = *src++;
|
|
|
|
case 3:
|
|
|
|
*dest++ = *src++;
|
|
|
|
case 2:
|
|
|
|
*dest++ = *src++;
|
|
|
|
case 1:
|
|
|
|
*dest++ = *src++;
|
|
|
|
}
|
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 'despeckle()' - Despeckle an image using a median filter.
|
|
|
|
*
|
|
|
|
* A median filter basically collects pixel values in a region around the
|
|
|
|
* target pixel, sorts them, and uses the median value. This code uses a
|
|
|
|
* circular row buffer to improve performance.
|
|
|
|
*
|
2018-04-18 20:57:03 +02:00
|
|
|
* The adaptive filter is based on the median filter but analyzes the histogram
|
2004-10-30 12:57:03 +00:00
|
|
|
* of the region around the target pixel and adjusts the despeckle diameter
|
1997-11-24 22:05:25 +00:00
|
|
|
* accordingly.
|
|
|
|
*/
|
|
|
|
|
2019-09-20 12:54:50 +02:00
|
|
|
static const Babl *
|
|
|
|
get_u8_format (GimpDrawable *drawable)
|
|
|
|
{
|
|
|
|
if (gimp_drawable_is_rgb (drawable))
|
|
|
|
{
|
|
|
|
if (gimp_drawable_has_alpha (drawable))
|
|
|
|
return babl_format ("R'G'B'A u8");
|
|
|
|
else
|
|
|
|
return babl_format ("R'G'B' u8");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (gimp_drawable_has_alpha (drawable))
|
|
|
|
return babl_format ("Y'A u8");
|
|
|
|
else
|
|
|
|
return babl_format ("Y' u8");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
static gboolean
|
2019-09-20 12:54:50 +02:00
|
|
|
despeckle (GimpDrawable *drawable,
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
GObject *config,
|
|
|
|
GError **error)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
2019-07-10 15:40:20 +02:00
|
|
|
GeglBuffer *src_buffer;
|
|
|
|
GeglBuffer *dest_buffer;
|
|
|
|
const Babl *format;
|
|
|
|
guchar *src;
|
|
|
|
guchar *dst;
|
|
|
|
gint img_bpp;
|
|
|
|
gint x, y;
|
|
|
|
gint width, height;
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
gsize bufsize = 0;
|
2019-07-10 15:40:20 +02:00
|
|
|
|
2019-08-13 17:31:13 +02:00
|
|
|
if (! gimp_drawable_mask_intersect (drawable,
|
2009-06-07 23:52:37 +02:00
|
|
|
&x, &y, &width, &height))
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
return TRUE;
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-20 12:54:50 +02:00
|
|
|
format = get_u8_format (drawable);
|
2019-07-10 15:40:20 +02:00
|
|
|
img_bpp = babl_format_get_bytes_per_pixel (format);
|
|
|
|
|
2019-08-13 17:31:13 +02:00
|
|
|
src_buffer = gimp_drawable_get_buffer (drawable);
|
|
|
|
dest_buffer = gimp_drawable_get_shadow_buffer (drawable);
|
2004-12-23 23:19:43 +00:00
|
|
|
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
if (! g_size_checked_mul (&bufsize, width, height) ||
|
|
|
|
! g_size_checked_mul (&bufsize, bufsize, img_bpp))
|
|
|
|
{
|
|
|
|
g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
|
|
|
|
_("Image dimensions too large: width %d x height %d"),
|
|
|
|
width, height);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
src = g_try_malloc (bufsize);
|
|
|
|
dst = g_try_malloc (bufsize);
|
|
|
|
|
|
|
|
if (src == NULL || dst == NULL)
|
|
|
|
{
|
|
|
|
g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
|
|
|
|
_("There was not enough memory to complete the operation."));
|
|
|
|
g_free (src);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
2004-12-23 23:19:43 +00:00
|
|
|
|
2019-07-10 15:40:20 +02:00
|
|
|
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x, y, width, height), 1.0,
|
|
|
|
format, src,
|
|
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-20 12:54:50 +02:00
|
|
|
despeckle_median (config,
|
|
|
|
src, dst, width, height, img_bpp, FALSE);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-07-10 15:40:20 +02:00
|
|
|
gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, y, width, height), 0,
|
|
|
|
format, dst,
|
|
|
|
GEGL_AUTO_ROWSTRIDE);
|
|
|
|
|
|
|
|
g_object_unref (src_buffer);
|
|
|
|
g_object_unref (dest_buffer);
|
2004-12-23 23:19:43 +00:00
|
|
|
|
2019-08-13 17:31:13 +02:00
|
|
|
gimp_drawable_merge_shadow (drawable, TRUE);
|
|
|
|
gimp_drawable_update (drawable, x, y, width, height);
|
2004-10-30 12:57:03 +00:00
|
|
|
|
2004-12-23 23:19:43 +00:00
|
|
|
g_free (dst);
|
|
|
|
g_free (src);
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
|
|
|
|
return TRUE;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
2019-07-10 15:40:20 +02:00
|
|
|
static gboolean
|
2019-09-20 19:26:07 +02:00
|
|
|
despeckle_dialog (GimpProcedure *procedure,
|
|
|
|
GObject *config,
|
|
|
|
GimpDrawable *drawable)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
2024-07-26 18:56:53 +00:00
|
|
|
GtkWidget *dialog;
|
|
|
|
GtkWidget *preview;
|
|
|
|
GtkWidget *vbox;
|
|
|
|
GtkWidget *scale;
|
|
|
|
GtkWidget *combo;
|
|
|
|
gboolean run;
|
2000-04-30 21:03:44 +00:00
|
|
|
|
2019-09-20 19:39:00 +02:00
|
|
|
gimp_ui_init (PLUG_IN_BINARY);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-20 19:26:07 +02:00
|
|
|
dialog = gimp_procedure_dialog_new (procedure,
|
|
|
|
GIMP_PROCEDURE_CONFIG (config),
|
|
|
|
_("Despeckle"));
|
2005-09-05 21:40:29 +00:00
|
|
|
|
2024-07-26 18:56:53 +00:00
|
|
|
combo = gimp_procedure_dialog_get_widget (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"type", G_TYPE_NONE);
|
2023-04-07 00:18:51 +00:00
|
|
|
gtk_widget_set_margin_bottom (combo, 12);
|
2000-01-14 12:41:00 +00:00
|
|
|
|
2000-01-13 15:39:26 +00:00
|
|
|
/*
|
2004-10-30 12:57:03 +00:00
|
|
|
* Box size (diameter) control...
|
2000-01-13 15:39:26 +00:00
|
|
|
*/
|
2023-04-07 00:18:51 +00:00
|
|
|
scale = gimp_procedure_dialog_get_scale_entry (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"radius", 1.0);
|
|
|
|
gtk_widget_set_margin_bottom (scale, 12);
|
2000-01-13 15:39:26 +00:00
|
|
|
/*
|
|
|
|
* Black level control...
|
|
|
|
*/
|
2023-04-07 00:18:51 +00:00
|
|
|
scale = gimp_procedure_dialog_get_scale_entry (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"black", 1.0);
|
|
|
|
gtk_widget_set_margin_bottom (scale, 12);
|
2000-01-13 15:39:26 +00:00
|
|
|
/*
|
|
|
|
* White level control...
|
|
|
|
*/
|
2023-04-07 00:18:51 +00:00
|
|
|
gimp_procedure_dialog_get_scale_entry (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"white", 1.0);
|
|
|
|
|
|
|
|
vbox = gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"despeckle-vbox",
|
|
|
|
"type", "radius", "black", "white",
|
|
|
|
NULL);
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
1998-01-25 09:29:29 +00:00
|
|
|
|
2024-02-03 20:09:46 +06:00
|
|
|
preview = gimp_procedure_dialog_get_drawable_preview (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"preview", drawable);
|
|
|
|
gtk_widget_set_margin_bottom (preview, 12);
|
|
|
|
|
2023-04-07 00:18:51 +00:00
|
|
|
g_object_set_data (config, "drawable", drawable);
|
|
|
|
|
|
|
|
g_signal_connect (preview, "invalidated",
|
|
|
|
G_CALLBACK (preview_update),
|
|
|
|
config);
|
2019-09-20 12:54:50 +02:00
|
|
|
|
|
|
|
g_signal_connect_swapped (config, "notify",
|
2004-10-30 12:57:03 +00:00
|
|
|
G_CALLBACK (gimp_preview_invalidate),
|
|
|
|
preview);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2023-04-07 00:18:51 +00:00
|
|
|
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog),
|
2024-02-03 20:09:46 +06:00
|
|
|
"preview", "despeckle-vbox",
|
2023-04-07 00:18:51 +00:00
|
|
|
NULL);
|
2000-01-13 15:39:26 +00:00
|
|
|
gtk_widget_show (dialog);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-20 19:26:07 +02:00
|
|
|
run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog));
|
2003-11-06 15:27:05 +00:00
|
|
|
|
|
|
|
gtk_widget_destroy (dialog);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2003-11-06 15:27:05 +00:00
|
|
|
return run;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-09-20 12:54:50 +02:00
|
|
|
preview_update (GtkWidget *widget,
|
|
|
|
GObject *config)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
GimpPreview *preview = GIMP_PREVIEW (widget);
|
2019-09-20 12:54:50 +02:00
|
|
|
GimpDrawable *drawable = g_object_get_data (config, "drawable");
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
gsize bufsize = 0;
|
2019-09-20 12:54:50 +02:00
|
|
|
GeglBuffer *src_buffer;
|
|
|
|
const Babl *format;
|
|
|
|
guchar *dst;
|
|
|
|
guchar *src;
|
|
|
|
gint img_bpp;
|
|
|
|
gint x1,y1;
|
|
|
|
gint width, height;
|
|
|
|
|
|
|
|
format = get_u8_format (drawable);
|
2019-07-10 15:40:20 +02:00
|
|
|
img_bpp = babl_format_get_bytes_per_pixel (format);
|
2006-02-14 11:27:34 +00:00
|
|
|
|
2018-05-04 09:46:37 +02:00
|
|
|
gimp_preview_get_size (preview, &width, &height);
|
2004-09-29 13:39:23 +00:00
|
|
|
gimp_preview_get_position (preview, &x1, &y1);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-13 17:31:13 +02:00
|
|
|
src_buffer = gimp_drawable_get_buffer (drawable);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for
integer overflow when allocating buffers, nor do we check for failed
allocations.
A potential integer overflow vulnerability exists in the GIMP
"Despeckle" plug-in. The issue occurs due to unchecked multiplication
of image dimensions (width, height) and bytes-per-pixel (img_bpp),
which can result in allocating insufficient memory and subsequently
performing out-of-bounds writes. This could lead to heap corruption and
potential denial-of-service (DoS) or arbitrary code execution in
certain scenarios.
Vulnerability Details
•width and height are of type guint (signed 32-bit int).
•Multiplying width * height * img_bpp can result in a value exceeding
the bounds of gsize.
•g_new() does not perform overflow protection; if the size wraps around,
less memory than needed will be allocated.
•Subsequent pixel processing loops write beyond the allocated memory
region (src, dst).
Proof of Concept (PoC)
Open a specially crafted image with very large dimensions (e.g.,
70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash
due to heap corruption, or undefined behavior may occur.
We applied the suggested changes and in addition adjusted the despeckle
function to be able to set error messages, and check for NULL
allocations.
2025-05-01 12:42:17 -04:00
|
|
|
if (! g_size_checked_mul (&bufsize, width, height) ||
|
|
|
|
! g_size_checked_mul (&bufsize, bufsize, img_bpp))
|
|
|
|
return;
|
|
|
|
|
|
|
|
src = g_try_malloc (bufsize);
|
|
|
|
dst = g_try_malloc (bufsize);
|
|
|
|
|
|
|
|
if (src == NULL || dst == NULL)
|
|
|
|
{
|
|
|
|
g_free (src);
|
|
|
|
return;
|
|
|
|
}
|
2006-02-14 11:27:34 +00:00
|
|
|
|
2019-07-10 15:40:20 +02:00
|
|
|
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y1, width, height), 1.0,
|
|
|
|
format, src,
|
|
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-20 12:54:50 +02:00
|
|
|
despeckle_median (config,
|
|
|
|
src, dst, width, height, img_bpp, TRUE);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2004-10-30 12:57:03 +00:00
|
|
|
gimp_preview_draw_buffer (preview, dst, width * img_bpp);
|
2006-02-14 11:27:34 +00:00
|
|
|
|
2019-07-10 15:40:20 +02:00
|
|
|
g_object_unref (src_buffer);
|
|
|
|
|
2004-10-30 12:57:03 +00:00
|
|
|
g_free (src);
|
2006-02-14 11:27:34 +00:00
|
|
|
g_free (dst);
|
2004-10-30 12:57:03 +00:00
|
|
|
}
|
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
static inline void
|
|
|
|
list_add_elem (PixelsList *list,
|
|
|
|
const guchar *elem)
|
|
|
|
{
|
|
|
|
const gint pos = list->start + list->count++;
|
|
|
|
|
|
|
|
list->elems[pos >= MAX_LIST_ELEMS ? pos - MAX_LIST_ELEMS : pos] = elem;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
list_del_elem (PixelsList* list)
|
|
|
|
{
|
|
|
|
list->count--;
|
|
|
|
list->start++;
|
|
|
|
|
|
|
|
if (list->start >= MAX_LIST_ELEMS)
|
|
|
|
list->start = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline const guchar *
|
|
|
|
list_get_random_elem (PixelsList *list)
|
|
|
|
{
|
|
|
|
const gint pos = list->start + rand () % list->count;
|
|
|
|
|
|
|
|
if (pos >= MAX_LIST_ELEMS)
|
|
|
|
return list->elems[pos - MAX_LIST_ELEMS];
|
|
|
|
|
|
|
|
return list->elems[pos];
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
histogram_add (DespeckleHistogram *hist,
|
|
|
|
guchar val,
|
|
|
|
const guchar *orig)
|
|
|
|
{
|
|
|
|
hist->elems[val]++;
|
|
|
|
list_add_elem (&hist->origs[val], orig);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
histogram_remove (DespeckleHistogram *hist,
|
|
|
|
guchar val)
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
2010-09-05 19:32:57 +02:00
|
|
|
hist->elems[val]--;
|
|
|
|
list_del_elem (&hist->origs[val]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
histogram_clean (DespeckleHistogram *hist)
|
|
|
|
{
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
{
|
|
|
|
hist->elems[i] = 0;
|
|
|
|
hist->origs[i].count = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline const guchar *
|
|
|
|
histogram_get_median (DespeckleHistogram *hist,
|
|
|
|
const guchar *_default)
|
|
|
|
{
|
|
|
|
gint count = histrest;
|
|
|
|
gint i;
|
|
|
|
gint sum = 0;
|
|
|
|
|
|
|
|
if (! count)
|
|
|
|
return _default;
|
|
|
|
|
|
|
|
count = (count + 1) / 2;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while ((sum += hist->elems[i]) < count)
|
|
|
|
i++;
|
|
|
|
|
|
|
|
return list_get_random_elem (&hist->origs[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
add_val (DespeckleHistogram *hist,
|
2019-09-20 12:54:50 +02:00
|
|
|
gint black_level,
|
|
|
|
gint white_level,
|
2010-09-05 19:32:57 +02:00
|
|
|
const guchar *src,
|
|
|
|
gint width,
|
|
|
|
gint bpp,
|
|
|
|
gint x,
|
|
|
|
gint y)
|
|
|
|
{
|
|
|
|
const gint pos = (x + (y * width)) * bpp;
|
|
|
|
const gint value = pixel_luminance (src + pos, bpp);
|
2010-09-05 15:38:44 +02:00
|
|
|
|
|
|
|
if (value > black_level && value < white_level)
|
2019-09-20 12:54:50 +02:00
|
|
|
{
|
|
|
|
histogram_add (hist, value, src + pos);
|
|
|
|
histrest++;
|
|
|
|
}
|
2010-09-05 15:38:44 +02:00
|
|
|
else
|
2019-09-20 12:54:50 +02:00
|
|
|
{
|
|
|
|
if (value <= black_level)
|
|
|
|
hist0++;
|
2010-09-05 15:38:44 +02:00
|
|
|
|
2019-09-20 12:54:50 +02:00
|
|
|
if (value >= white_level)
|
|
|
|
hist255++;
|
|
|
|
}
|
2010-09-05 15:38:44 +02:00
|
|
|
}
|
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
static inline void
|
|
|
|
del_val (DespeckleHistogram *hist,
|
2019-09-20 12:54:50 +02:00
|
|
|
gint black_level,
|
|
|
|
gint white_level,
|
2010-09-05 19:32:57 +02:00
|
|
|
const guchar *src,
|
|
|
|
gint width,
|
|
|
|
gint bpp,
|
|
|
|
gint x,
|
|
|
|
gint y)
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
2010-09-05 19:32:57 +02:00
|
|
|
const gint pos = (x + (y * width)) * bpp;
|
|
|
|
const gint value = pixel_luminance (src + pos, bpp);
|
2010-09-05 15:38:44 +02:00
|
|
|
|
|
|
|
if (value > black_level && value < white_level)
|
2019-09-20 12:54:50 +02:00
|
|
|
{
|
|
|
|
histogram_remove (hist, value);
|
|
|
|
histrest--;
|
|
|
|
}
|
2010-09-05 15:38:44 +02:00
|
|
|
else
|
2019-09-20 12:54:50 +02:00
|
|
|
{
|
|
|
|
if (value <= black_level)
|
|
|
|
hist0--;
|
2010-09-05 15:38:44 +02:00
|
|
|
|
2019-09-20 12:54:50 +02:00
|
|
|
if (value >= white_level)
|
|
|
|
hist255--;
|
|
|
|
}
|
2010-09-05 15:38:44 +02:00
|
|
|
}
|
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
static inline void
|
|
|
|
add_vals (DespeckleHistogram *hist,
|
2019-09-20 12:54:50 +02:00
|
|
|
gint black_level,
|
|
|
|
gint white_level,
|
2010-09-05 19:32:57 +02:00
|
|
|
const guchar *src,
|
|
|
|
gint width,
|
|
|
|
gint bpp,
|
|
|
|
gint xmin,
|
|
|
|
gint ymin,
|
|
|
|
gint xmax,
|
|
|
|
gint ymax)
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
|
|
|
gint x;
|
|
|
|
gint y;
|
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
if (xmin > xmax)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (y = ymin; y <= ymax; y++)
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
2010-09-05 19:32:57 +02:00
|
|
|
for (x = xmin; x <= xmax; x++)
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
2019-09-20 12:54:50 +02:00
|
|
|
add_val (hist,
|
|
|
|
black_level, white_level,
|
|
|
|
src, width, bpp, x, y);
|
2010-09-05 15:38:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
static inline void
|
|
|
|
del_vals (DespeckleHistogram *hist,
|
2019-09-20 12:54:50 +02:00
|
|
|
gint black_level,
|
|
|
|
gint white_level,
|
2010-09-05 19:32:57 +02:00
|
|
|
const guchar *src,
|
|
|
|
gint width,
|
|
|
|
gint bpp,
|
|
|
|
gint xmin,
|
|
|
|
gint ymin,
|
|
|
|
gint xmax,
|
|
|
|
gint ymax)
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
|
|
|
gint x;
|
|
|
|
gint y;
|
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
if (xmin > xmax)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (y = ymin; y <= ymax; y++)
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
2010-09-05 19:32:57 +02:00
|
|
|
for (x = xmin; x <= xmax; x++)
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
2019-09-20 12:54:50 +02:00
|
|
|
del_val (hist,
|
|
|
|
black_level, white_level,
|
|
|
|
src, width, bpp, x, y);
|
2010-09-05 15:38:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
static inline void
|
|
|
|
update_histogram (DespeckleHistogram *hist,
|
2019-09-20 12:54:50 +02:00
|
|
|
gint black_level,
|
|
|
|
gint white_level,
|
2010-09-05 19:32:57 +02:00
|
|
|
const guchar *src,
|
|
|
|
gint width,
|
|
|
|
gint bpp,
|
|
|
|
gint xmin,
|
|
|
|
gint ymin,
|
|
|
|
gint xmax,
|
|
|
|
gint ymax)
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
2010-09-05 19:32:57 +02:00
|
|
|
/* assuming that radious of the box can change no more than one
|
|
|
|
pixel in each call */
|
2010-09-05 15:38:44 +02:00
|
|
|
/* assuming that box is moving either right or down */
|
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
del_vals (hist,
|
2019-09-20 12:54:50 +02:00
|
|
|
black_level, white_level,
|
2010-09-05 19:32:57 +02:00
|
|
|
src, width, bpp, hist->xmin, hist->ymin, xmin - 1, hist->ymax);
|
2019-09-20 12:54:50 +02:00
|
|
|
del_vals (hist,
|
|
|
|
black_level, white_level,
|
|
|
|
src, width, bpp, xmin, hist->ymin, xmax, ymin - 1);
|
|
|
|
del_vals (hist,
|
|
|
|
black_level, white_level,
|
|
|
|
src, width, bpp, xmin, ymax + 1, xmax, hist->ymax);
|
2010-09-05 15:38:44 +02:00
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
add_vals (hist,
|
2019-09-20 12:54:50 +02:00
|
|
|
black_level, white_level,
|
|
|
|
src, width, bpp, hist->xmax + 1, ymin, xmax, ymax);
|
|
|
|
add_vals (hist,
|
|
|
|
black_level, white_level,
|
|
|
|
src, width, bpp, xmin, ymin, hist->xmax, hist->ymin - 1);
|
|
|
|
add_vals (hist,
|
|
|
|
black_level, white_level,
|
2010-09-05 19:32:57 +02:00
|
|
|
src, width, bpp, hist->xmin, hist->ymax + 1, hist->xmax, ymax);
|
2010-09-05 15:38:44 +02:00
|
|
|
|
|
|
|
hist->xmin = xmin;
|
|
|
|
hist->ymin = ymin;
|
|
|
|
hist->xmax = xmax;
|
|
|
|
hist->ymax = ymax;
|
|
|
|
}
|
|
|
|
|
2004-10-30 12:57:03 +00:00
|
|
|
static void
|
2019-09-20 12:54:50 +02:00
|
|
|
despeckle_median (GObject *config,
|
|
|
|
guchar *src,
|
2004-10-30 12:57:03 +00:00
|
|
|
guchar *dst,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
|
|
|
gint bpp,
|
|
|
|
gboolean preview)
|
|
|
|
{
|
2019-09-20 12:54:50 +02:00
|
|
|
gint radius;
|
|
|
|
gint filter_type;
|
|
|
|
gint black_level;
|
|
|
|
gint white_level;
|
2010-09-05 15:38:44 +02:00
|
|
|
guint progress;
|
|
|
|
guint max_progress;
|
|
|
|
gint x, y;
|
2011-10-08 08:19:22 +05:30
|
|
|
gint adapt_radius;
|
2010-09-05 15:38:44 +02:00
|
|
|
gint pos;
|
|
|
|
gint ymin;
|
|
|
|
gint ymax;
|
|
|
|
gint xmin;
|
|
|
|
gint xmax;
|
|
|
|
|
2019-09-20 12:54:50 +02:00
|
|
|
g_object_get (config,
|
|
|
|
"radius", &radius,
|
|
|
|
"black", &black_level,
|
|
|
|
"white", &white_level,
|
|
|
|
NULL);
|
2024-07-26 18:56:53 +00:00
|
|
|
filter_type = gimp_procedure_config_get_choice_id (GIMP_PROCEDURE_CONFIG (config),
|
|
|
|
"type");
|
2019-09-20 12:54:50 +02:00
|
|
|
|
2010-09-05 15:38:44 +02:00
|
|
|
memset (&histogram, 0, sizeof(histogram));
|
2005-03-11 14:12:38 +00:00
|
|
|
progress = 0;
|
|
|
|
max_progress = width * height;
|
1998-01-25 09:29:29 +00:00
|
|
|
|
2007-04-25 10:34:30 +00:00
|
|
|
if (! preview)
|
2019-07-10 15:40:20 +02:00
|
|
|
gimp_progress_init (_("Despeckle"));
|
2005-03-11 21:57:02 +00:00
|
|
|
|
2011-10-08 08:19:22 +05:30
|
|
|
adapt_radius = radius;
|
2005-03-11 21:57:02 +00:00
|
|
|
for (y = 0; y < height; y++)
|
2004-10-30 12:57:03 +00:00
|
|
|
{
|
2010-09-05 15:38:44 +02:00
|
|
|
x = 0;
|
2011-10-08 08:19:22 +05:30
|
|
|
ymin = MAX (0, y - adapt_radius);
|
|
|
|
ymax = MIN (height - 1, y + adapt_radius);
|
|
|
|
xmin = MAX (0, x - adapt_radius);
|
|
|
|
xmax = MIN (width - 1, x + adapt_radius);
|
2010-09-05 15:38:44 +02:00
|
|
|
hist0 = 0;
|
|
|
|
histrest = 0;
|
|
|
|
hist255 = 0;
|
|
|
|
histogram_clean (&histogram);
|
|
|
|
histogram.xmin = xmin;
|
|
|
|
histogram.ymin = ymin;
|
|
|
|
histogram.xmax = xmax;
|
|
|
|
histogram.ymax = ymax;
|
2010-09-05 19:32:57 +02:00
|
|
|
add_vals (&histogram,
|
2019-09-20 12:54:50 +02:00
|
|
|
black_level, white_level,
|
2010-09-05 19:32:57 +02:00
|
|
|
src, width, bpp,
|
2019-07-10 15:40:20 +02:00
|
|
|
histogram.xmin, histogram.ymin,
|
|
|
|
histogram.xmax, histogram.ymax);
|
2005-03-11 21:57:02 +00:00
|
|
|
|
2007-05-03 20:53:25 +00:00
|
|
|
for (x = 0; x < width; x++)
|
2004-07-31 10:38:05 +00:00
|
|
|
{
|
2010-09-05 15:38:44 +02:00
|
|
|
const guchar *pixel;
|
2010-09-05 19:32:57 +02:00
|
|
|
|
2011-10-08 08:19:22 +05:30
|
|
|
ymin = MAX (0, y - adapt_radius); /* update ymin, ymax when adapt_radius changed (FILTER_ADAPTIVE) */
|
|
|
|
ymax = MIN (height - 1, y + adapt_radius);
|
|
|
|
xmin = MAX (0, x - adapt_radius);
|
|
|
|
xmax = MIN (width - 1, x + adapt_radius);
|
2005-03-11 14:12:38 +00:00
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
update_histogram (&histogram,
|
2019-09-20 12:54:50 +02:00
|
|
|
black_level, white_level,
|
2010-09-05 19:32:57 +02:00
|
|
|
src, width, bpp, xmin, ymin, xmax, ymax);
|
2004-07-31 10:38:05 +00:00
|
|
|
|
2010-09-05 15:38:44 +02:00
|
|
|
pos = (x + (y * width)) * bpp;
|
|
|
|
pixel = histogram_get_median (&histogram, src + pos);
|
2004-07-31 10:38:05 +00:00
|
|
|
|
2010-09-05 19:32:57 +02:00
|
|
|
if (filter_type & FILTER_RECURSIVE)
|
2010-09-05 15:38:44 +02:00
|
|
|
{
|
2019-09-20 12:54:50 +02:00
|
|
|
del_val (&histogram,
|
|
|
|
black_level, white_level,
|
|
|
|
src, width, bpp, x, y);
|
|
|
|
|
2010-09-05 15:38:44 +02:00
|
|
|
pixel_copy (src + pos, pixel, bpp);
|
2019-09-20 12:54:50 +02:00
|
|
|
|
|
|
|
add_val (&histogram,
|
|
|
|
black_level, white_level,
|
|
|
|
src, width, bpp, x, y);
|
2010-09-05 15:38:44 +02:00
|
|
|
}
|
2007-05-03 20:53:25 +00:00
|
|
|
|
2010-09-05 15:38:44 +02:00
|
|
|
pixel_copy (dst + pos, pixel, bpp);
|
2004-07-31 10:38:05 +00:00
|
|
|
|
2004-10-30 12:57:03 +00:00
|
|
|
/*
|
|
|
|
* Check the histogram and adjust the diameter accordingly...
|
|
|
|
*/
|
|
|
|
if (filter_type & FILTER_ADAPTIVE)
|
|
|
|
{
|
2011-10-08 08:19:22 +05:30
|
|
|
if (hist0 >= adapt_radius || hist255 >= adapt_radius)
|
2004-07-31 10:38:05 +00:00
|
|
|
{
|
2011-10-08 08:19:22 +05:30
|
|
|
if (adapt_radius < radius)
|
|
|
|
adapt_radius++;
|
2004-10-30 12:57:03 +00:00
|
|
|
}
|
2011-10-08 08:19:22 +05:30
|
|
|
else if (adapt_radius > 1)
|
2004-10-30 12:57:03 +00:00
|
|
|
{
|
2011-10-08 08:19:22 +05:30
|
|
|
adapt_radius--;
|
2004-08-30 22:10:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-12-23 23:19:43 +00:00
|
|
|
|
2005-03-11 21:57:02 +00:00
|
|
|
progress += width;
|
2004-12-23 23:19:43 +00:00
|
|
|
|
2007-04-25 10:34:30 +00:00
|
|
|
if (! preview && y % 32 == 0)
|
2005-03-11 14:12:38 +00:00
|
|
|
gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
|
2004-10-30 12:57:03 +00:00
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2007-04-25 10:34:30 +00:00
|
|
|
if (! preview)
|
2004-12-23 23:19:43 +00:00
|
|
|
gimp_progress_update (1.0);
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|