Gimp/app/core/gimphistogram.c
Michael Natterer fc9da4c9a3 app: allow to toggle the histogram dialog between gamma and linear
Change GimpHistogram to take a "gboolean linear" parameter and always
honor that parameter, so both kinds of histograms can now be created
for all drawables.

Add a horrible "Linear" toggle to the histogram dockable which always
defaults to the active layer's actual pixel format, but can be
switched at any time. This UI is ugly and needs to change.

On the PDB, default to gamma-corrected if the plug-in is unaware of
higher precision, and to the drawable's native pixel format otherwise.

Other places using histograms (e.g. levels, curves) are unchanged.
2016-12-26 17:32:17 +01:00

981 lines
28 KiB
C

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimphistogram module Copyright (C) 1999 Jay Cox <jaycox@gimp.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "libgimpmath/gimpmath.h"
#include "libgimpcolor/gimpcolor.h"
#include "core-types.h"
#include "gegl/gimp-babl.h"
#include "gimphistogram.h"
enum
{
PROP_0,
PROP_N_CHANNELS,
PROP_N_BINS,
PROP_VALUES
};
struct _GimpHistogramPrivate
{
gboolean linear;
gint n_channels;
gint n_bins;
gdouble *values;
};
/* local function prototypes */
static void gimp_histogram_finalize (GObject *object);
static void gimp_histogram_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_histogram_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static gint64 gimp_histogram_get_memsize (GimpObject *object,
gint64 *gui_size);
static void gimp_histogram_alloc_values (GimpHistogram *histogram,
gint n_components,
gint n_bins);
G_DEFINE_TYPE (GimpHistogram, gimp_histogram, GIMP_TYPE_OBJECT)
#define parent_class gimp_histogram_parent_class
static void
gimp_histogram_class_init (GimpHistogramClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
object_class->finalize = gimp_histogram_finalize;
object_class->set_property = gimp_histogram_set_property;
object_class->get_property = gimp_histogram_get_property;
gimp_object_class->get_memsize = gimp_histogram_get_memsize;
g_object_class_install_property (object_class, PROP_N_CHANNELS,
g_param_spec_int ("n-channels", NULL, NULL,
0, 6, 0,
GIMP_PARAM_READABLE));
g_object_class_install_property (object_class, PROP_N_BINS,
g_param_spec_int ("n-bins", NULL, NULL,
256, 1024, 1024,
GIMP_PARAM_READABLE));
/* this is just for notifications */
g_object_class_install_property (object_class, PROP_VALUES,
g_param_spec_boolean ("values", NULL, NULL,
FALSE,
G_PARAM_READABLE));
g_type_class_add_private (klass, sizeof (GimpHistogramPrivate));
}
static void
gimp_histogram_init (GimpHistogram *histogram)
{
histogram->priv = G_TYPE_INSTANCE_GET_PRIVATE (histogram,
GIMP_TYPE_HISTOGRAM,
GimpHistogramPrivate);
histogram->priv->n_bins = 256;
}
static void
gimp_histogram_finalize (GObject *object)
{
GimpHistogram *histogram = GIMP_HISTOGRAM (object);
gimp_histogram_clear_values (histogram);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_histogram_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_histogram_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpHistogram *histogram = GIMP_HISTOGRAM (object);
switch (property_id)
{
case PROP_N_CHANNELS:
g_value_set_int (value, histogram->priv->n_channels);
break;
case PROP_N_BINS:
g_value_set_int (value, histogram->priv->n_bins);
break;
case PROP_VALUES:
/* return a silly boolean */
g_value_set_boolean (value, histogram->priv->values != NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gint64
gimp_histogram_get_memsize (GimpObject *object,
gint64 *gui_size)
{
GimpHistogram *histogram = GIMP_HISTOGRAM (object);
gint64 memsize = 0;
if (histogram->priv->values)
memsize += (histogram->priv->n_channels *
histogram->priv->n_bins * sizeof (gdouble));
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
gui_size);
}
/* public functions */
GimpHistogram *
gimp_histogram_new (gboolean linear)
{
GimpHistogram *histogram = g_object_new (GIMP_TYPE_HISTOGRAM, NULL);
histogram->priv->linear = linear;
return histogram;
}
/**
* gimp_histogram_duplicate:
* @histogram: a %GimpHistogram
*
* Creates a duplicate of @histogram. The duplicate has a reference
* count of 1 and contains the values from @histogram.
*
* Return value: a newly allocated %GimpHistogram
**/
GimpHistogram *
gimp_histogram_duplicate (GimpHistogram *histogram)
{
GimpHistogram *dup;
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), NULL);
dup = gimp_histogram_new (histogram->priv->linear);
dup->priv->n_channels = histogram->priv->n_channels;
dup->priv->n_bins = histogram->priv->n_bins;
dup->priv->values = g_memdup (histogram->priv->values,
sizeof (gdouble) *
dup->priv->n_channels *
dup->priv->n_bins);
return dup;
}
void
gimp_histogram_calculate (GimpHistogram *histogram,
GeglBuffer *buffer,
const GeglRectangle *buffer_rect,
GeglBuffer *mask,
const GeglRectangle *mask_rect)
{
GimpHistogramPrivate *priv;
GeglBufferIterator *iter;
const Babl *format;
gint n_components;
gint n_bins;
g_return_if_fail (GIMP_IS_HISTOGRAM (histogram));
g_return_if_fail (GEGL_IS_BUFFER (buffer));
g_return_if_fail (buffer_rect != NULL);
priv = histogram->priv;
format = gegl_buffer_get_format (buffer);
if (babl_format_get_type (format, 0) == babl_type ("u8"))
n_bins = 256;
else
n_bins = 1024;
if (babl_format_is_palette (format))
{
if (babl_format_has_alpha (format))
{
if (priv->linear)
format = babl_format ("RGB float");
else
format = babl_format ("R'G'B' float");
}
else
{
if (priv->linear)
format = babl_format ("RGBA float");
else
format = babl_format ("R'G'B'A float");
}
}
else
{
const Babl *model = babl_format_get_model (format);
if (model == babl_model ("Y") ||
model == babl_model ("Y'"))
{
if (priv->linear)
format = babl_format ("Y float");
else
format = babl_format ("Y' float");
}
else if (model == babl_model ("YA") ||
model == babl_model ("Y'A"))
{
if (priv->linear)
format = babl_format ("YA float");
else
format = babl_format ("Y'A float");
}
else if (model == babl_model ("RGB") ||
model == babl_model ("R'G'B'"))
{
if (priv->linear)
format = babl_format ("RGB float");
else
format = babl_format ("R'G'B' float");
}
else if (model == babl_model ("RGBA") ||
model == babl_model ("R'G'B'A"))
{
if (priv->linear)
format = babl_format ("RGBA float");
else
format = babl_format ("R'G'B'A float");
}
else
{
g_return_if_reached ();
}
}
n_components = babl_format_get_n_components (format);
g_object_freeze_notify (G_OBJECT (histogram));
gimp_histogram_alloc_values (histogram, n_components, n_bins);
iter = gegl_buffer_iterator_new (buffer, buffer_rect, 0, format,
GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
if (mask)
gegl_buffer_iterator_add (iter, mask, mask_rect, 0,
babl_format ("Y float"),
GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
#define VALUE(c,i) (priv->values[(c) * priv->n_bins + \
(gint) (CLAMP ((i), 0.0, 1.0) * \
(priv->n_bins - 0.0001))])
while (gegl_buffer_iterator_next (iter))
{
const gfloat *data = iter->data[0];
gint length = iter->length;
gfloat max;
gfloat luminance;
if (mask)
{
const gfloat *mask_data = iter->data[1];
switch (n_components)
{
case 1:
while (length--)
{
const gdouble masked = *mask_data;
VALUE (0, data[0]) += masked;
data += n_components;
mask_data += 1;
}
break;
case 2:
while (length--)
{
const gdouble masked = *mask_data;
const gdouble weight = data[1];
VALUE (0, data[0]) += weight * masked;
VALUE (1, data[1]) += masked;
data += n_components;
mask_data += 1;
}
break;
case 3: /* calculate separate value values */
while (length--)
{
const gdouble masked = *mask_data;
VALUE (1, data[0]) += masked;
VALUE (2, data[1]) += masked;
VALUE (3, data[2]) += masked;
max = MAX (data[0], data[1]);
max = MAX (data[2], max);
VALUE (0, max) += masked;
luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
VALUE (4, luminance) += masked;
data += n_components;
mask_data += 1;
}
break;
case 4: /* calculate separate value values */
while (length--)
{
const gdouble masked = *mask_data;
const gdouble weight = data[3];
VALUE (1, data[0]) += weight * masked;
VALUE (2, data[1]) += weight * masked;
VALUE (3, data[2]) += weight * masked;
VALUE (4, data[3]) += masked;
max = MAX (data[0], data[1]);
max = MAX (data[2], max);
VALUE (0, max) += weight * masked;
luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
VALUE (5, luminance) += weight * masked;
data += n_components;
mask_data += 1;
}
break;
}
}
else /* no mask */
{
switch (n_components)
{
case 1:
while (length--)
{
VALUE (0, data[0]) += 1.0;
data += n_components;
}
break;
case 2:
while (length--)
{
const gdouble weight = data[1];
VALUE (0, data[0]) += weight;
VALUE (1, data[1]) += 1.0;
data += n_components;
}
break;
case 3: /* calculate separate value values */
while (length--)
{
VALUE (1, data[0]) += 1.0;
VALUE (2, data[1]) += 1.0;
VALUE (3, data[2]) += 1.0;
max = MAX (data[0], data[1]);
max = MAX (data[2], max);
VALUE (0, max) += 1.0;
luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
VALUE (4, luminance) += 1.0;
data += n_components;
}
break;
case 4: /* calculate separate value values */
while (length--)
{
const gdouble weight = data[3];
VALUE (1, data[0]) += weight;
VALUE (2, data[1]) += weight;
VALUE (3, data[2]) += weight;
VALUE (4, data[3]) += 1.0;
max = MAX (data[0], data[1]);
max = MAX (data[2], max);
VALUE (0, max) += weight;
luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
VALUE (5, luminance) += weight;
data += n_components;
}
break;
}
}
}
g_object_notify (G_OBJECT (histogram), "values");
g_object_thaw_notify (G_OBJECT (histogram));
#undef VALUE
}
void
gimp_histogram_clear_values (GimpHistogram *histogram)
{
g_return_if_fail (GIMP_IS_HISTOGRAM (histogram));
if (histogram->priv->values)
{
g_free (histogram->priv->values);
histogram->priv->values = NULL;
g_object_notify (G_OBJECT (histogram), "values");
}
if (histogram->priv->n_channels)
{
histogram->priv->n_channels = 0;
g_object_notify (G_OBJECT (histogram), "n-channels");
}
}
#define HISTOGRAM_VALUE(c,i) (priv->values[(c) * priv->n_bins + (i)])
gdouble
gimp_histogram_get_maximum (GimpHistogram *histogram,
GimpHistogramChannel channel)
{
GimpHistogramPrivate *priv;
gdouble max = 0.0;
gint x;
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
priv = histogram->priv;
/* the gray alpha channel is in slot 1 */
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
channel = 1;
/* the luminance channel is in slot 4 */
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
channel = 4;
if (! priv->values ||
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
return 0.0;
if (channel == GIMP_HISTOGRAM_RGB)
{
for (x = 0; x < priv->n_bins; x++)
{
max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, x));
max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, x));
max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, x));
}
}
else
{
for (x = 0; x < priv->n_bins; x++)
{
max = MAX (max, HISTOGRAM_VALUE (channel, x));
}
}
return max;
}
gdouble
gimp_histogram_get_value (GimpHistogram *histogram,
GimpHistogramChannel channel,
gint bin)
{
GimpHistogramPrivate *priv;
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
priv = histogram->priv;
/* the gray alpha channel is in slot 1 */
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
channel = 1;
/* the luminance channel is in slot 4 */
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
channel = 4;
if (! priv->values ||
bin < 0 || bin >= priv->n_bins ||
(channel == GIMP_HISTOGRAM_RGB && priv->n_channels < 4) ||
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
return 0.0;
if (channel == GIMP_HISTOGRAM_RGB)
{
gdouble min = HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, bin);
min = MIN (min, HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, bin));
return MIN (min, HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, bin));
}
else
{
return HISTOGRAM_VALUE (channel, bin);
}
}
gdouble
gimp_histogram_get_component (GimpHistogram *histogram,
gint component,
gint bin)
{
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
if (histogram->priv->n_channels > 4)
component++;
return gimp_histogram_get_value (histogram, component, bin);
}
gint
gimp_histogram_n_channels (GimpHistogram *histogram)
{
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0);
return histogram->priv->n_channels - 2;
}
gint
gimp_histogram_n_bins (GimpHistogram *histogram)
{
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0);
return histogram->priv->n_bins;
}
gdouble
gimp_histogram_get_count (GimpHistogram *histogram,
GimpHistogramChannel channel,
gint start,
gint end)
{
GimpHistogramPrivate *priv;
gint i;
gdouble count = 0.0;
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
priv = histogram->priv;
/* the gray alpha channel is in slot 1 */
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
channel = 1;
/* the luminance channel is in slot 4 */
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
channel = 4;
if (channel == GIMP_HISTOGRAM_RGB)
return (gimp_histogram_get_count (histogram,
GIMP_HISTOGRAM_RED, start, end) +
gimp_histogram_get_count (histogram,
GIMP_HISTOGRAM_GREEN, start, end) +
gimp_histogram_get_count (histogram,
GIMP_HISTOGRAM_BLUE, start, end));
if (! priv->values ||
start > end ||
channel >= priv->n_channels)
return 0.0;
start = CLAMP (start, 0, priv->n_bins - 1);
end = CLAMP (end, 0, priv->n_bins - 1);
for (i = start; i <= end; i++)
count += HISTOGRAM_VALUE (channel, i);
return count;
}
gdouble
gimp_histogram_get_mean (GimpHistogram *histogram,
GimpHistogramChannel channel,
gint start,
gint end)
{
GimpHistogramPrivate *priv;
gint i;
gdouble mean = 0.0;
gdouble count;
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
priv = histogram->priv;
/* the gray alpha channel is in slot 1 */
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
channel = 1;
/* the luminance channel is in slot 4 */
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
channel = 4;
if (! priv->values ||
start > end ||
(channel == GIMP_HISTOGRAM_RGB && priv->n_channels < 5) ||
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
return 0.0;
start = CLAMP (start, 0, priv->n_bins - 1);
end = CLAMP (end, 0, priv->n_bins - 1);
if (channel == GIMP_HISTOGRAM_RGB)
{
for (i = start; i <= end; i++)
{
gdouble factor = (gdouble) i / (gdouble) (priv->n_bins - 1);
mean += (factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
}
}
else
{
for (i = start; i <= end; i++)
{
gdouble factor = (gdouble) i / (gdouble) (priv->n_bins - 1);
mean += factor * HISTOGRAM_VALUE (channel, i);
}
}
count = gimp_histogram_get_count (histogram, channel, start, end);
if (count > 0.0)
return mean / count;
return mean;
}
gdouble
gimp_histogram_get_median (GimpHistogram *histogram,
GimpHistogramChannel channel,
gint start,
gint end)
{
GimpHistogramPrivate *priv;
gint i;
gdouble sum = 0.0;
gdouble count;
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), -1.0);
priv = histogram->priv;
/* the gray alpha channel is in slot 1 */
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
channel = 1;
/* the luminance channel is in slot 4 */
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
channel = 4;
if (! priv->values ||
start > end ||
(channel == GIMP_HISTOGRAM_RGB && priv->n_channels < 5) ||
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
return 0.0;
start = CLAMP (start, 0, priv->n_bins - 1);
end = CLAMP (end, 0, priv->n_bins - 1);
count = gimp_histogram_get_count (histogram, channel, start, end);
if (channel == GIMP_HISTOGRAM_RGB)
{
for (i = start; i <= end; i++)
{
sum += (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
if (sum * 2 > count)
return ((gdouble) i / (gdouble) (priv->n_bins - 1));
}
}
else
{
for (i = start; i <= end; i++)
{
sum += HISTOGRAM_VALUE (channel, i);
if (sum * 2 > count)
return ((gdouble) i / (gdouble) (priv->n_bins - 1));
}
}
return -1.0;
}
/*
* adapted from GNU ocrad 0.14 : page_image_io.cc : otsu_th
*
* N. Otsu, "A threshold selection method from gray-level histograms,"
* IEEE Trans. Systems, Man, and Cybernetics, vol. 9, no. 1, pp. 62-66, 1979.
*/
gdouble
gimp_histogram_get_threshold (GimpHistogram *histogram,
GimpHistogramChannel channel,
gint start,
gint end)
{
GimpHistogramPrivate *priv;
gint i;
gint maxval;
gdouble *hist = NULL;
gdouble *chist = NULL;
gdouble *cmom = NULL;
gdouble hist_max = 0.0;
gdouble chist_max = 0.0;
gdouble cmom_max = 0.0;
gdouble bvar_max = 0.0;
gint threshold = 127;
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), -1);
priv = histogram->priv;
/* the gray alpha channel is in slot 1 */
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
channel = 1;
/* the luminance channel is in slot 4 */
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
channel = 4;
if (! priv->values ||
start > end ||
(channel == GIMP_HISTOGRAM_RGB && priv->n_channels < 5) ||
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
return 0;
start = CLAMP (start, 0, priv->n_bins - 1);
end = CLAMP (end, 0, priv->n_bins - 1);
maxval = end - start;
hist = g_newa (gdouble, maxval + 1);
chist = g_newa (gdouble, maxval + 1);
cmom = g_newa (gdouble, maxval + 1);
if (channel == GIMP_HISTOGRAM_RGB)
{
for (i = start; i <= end; i++)
hist[i - start] = (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
}
else
{
for (i = start; i <= end; i++)
hist[i - start] = HISTOGRAM_VALUE (channel, i);
}
hist_max = hist[0];
chist[0] = hist[0];
cmom[0] = 0;
for (i = 1; i <= maxval; i++)
{
if (hist[i] > hist_max)
hist_max = hist[i];
chist[i] = chist[i-1] + hist[i];
cmom[i] = cmom[i-1] + i * hist[i];
}
chist_max = chist[maxval];
cmom_max = cmom[maxval];
bvar_max = 0;
for (i = 0; i < maxval; ++i)
{
if (chist[i] > 0 && chist[i] < chist_max)
{
gdouble bvar;
bvar = (gdouble) cmom[i] / chist[i];
bvar -= (cmom_max - cmom[i]) / (chist_max - chist[i]);
bvar *= bvar;
bvar *= chist[i];
bvar *= chist_max - chist[i];
if (bvar > bvar_max)
{
bvar_max = bvar;
threshold = start + i;
}
}
}
return threshold;
}
gdouble
gimp_histogram_get_std_dev (GimpHistogram *histogram,
GimpHistogramChannel channel,
gint start,
gint end)
{
GimpHistogramPrivate *priv;
gint i;
gdouble dev = 0.0;
gdouble count;
gdouble mean;
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
priv = histogram->priv;
/* the gray alpha channel is in slot 1 */
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
channel = 1;
/* the luminance channel is in slot 4 */
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
channel = 4;
if (! priv->values ||
start > end ||
(channel == GIMP_HISTOGRAM_RGB && priv->n_channels < 5) ||
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
return 0.0;
mean = gimp_histogram_get_mean (histogram, channel, start, end);
count = gimp_histogram_get_count (histogram, channel, start, end);
if (count == 0.0)
count = 1.0;
for (i = start; i <= end; i++)
{
gdouble value;
if (channel == GIMP_HISTOGRAM_RGB)
{
value = (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
}
else
{
value = gimp_histogram_get_value (histogram, channel, i);
}
dev += value * SQR (((gdouble) i / (gdouble) (priv->n_bins - 1)) - mean);
}
return sqrt (dev / count);
}
/* private functions */
static void
gimp_histogram_alloc_values (GimpHistogram *histogram,
gint n_components,
gint n_bins)
{
GimpHistogramPrivate *priv = histogram->priv;
if (n_components + 2 != priv->n_channels ||
n_bins != priv->n_bins)
{
gimp_histogram_clear_values (histogram);
if (n_components + 2 != priv->n_channels)
{
priv->n_channels = n_components + 2;
g_object_notify (G_OBJECT (histogram), "n-channels");
}
if (n_bins != priv->n_bins)
{
priv->n_bins = n_bins;
g_object_notify (G_OBJECT (histogram), "n-bins");
}
priv->values = g_new0 (gdouble, priv->n_channels * priv->n_bins);
}
else
{
memset (priv->values, 0,
priv->n_channels * priv->n_bins * sizeof (gdouble));
}
}