/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * gimphistogram module Copyright (C) 1999 Jay Cox * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #ifdef ENABLE_MP #include #endif /* ENABLE_MP */ #include #include "apptypes.h" #include "drawable.h" #include "gimphistogram.h" #include "pixel_region.h" #include "gimpdrawable.h" #include "channel.h" #include "gimpimage.h" #include "gimprc.h" #include "pixel_processor.h" #include #include struct _GimpHistogram { gint bins; gdouble **values; gint nchannels; #ifdef ENABLE_MP pthread_mutex_t mutex; gint nthreads; gdouble ***tmp_values; gchar *tmp_slots; #endif /* ENABLE_MP */ }; GimpHistogram * gimp_histogram_new (void) { GimpHistogram *histogram; histogram = g_new0 (GimpHistogram, 1); histogram->bins = 0; histogram->values = NULL; histogram->nchannels = 0; #ifdef ENABLE_MP histogram->nthreads = 0; histogram->tmp_values = NULL; histogram->tmp_slots = NULL; #endif /* ENABLE_MP */ return histogram; } void gimp_histogram_free (GimpHistogram *histogram) { gint i; if (histogram->values) { for (i = 0; i < histogram->nchannels; i++) g_free (histogram->values[i]); g_free (histogram->values); } g_free (histogram); } static void gimp_histogram_calculate_sub_region (GimpHistogram *histogram, PixelRegion *region, PixelRegion *mask) { const guchar *src, *msrc; const guchar *m, *s; gdouble **values; gint h, w, max; #ifdef ENABLE_MP gint slot = 0; /* find an unused temporary slot to put our results in and lock it */ pthread_mutex_lock (&histogram->mutex); { while (histogram->tmp_slots[slot]) slot++; values = histogram->tmp_values[slot]; histogram->tmp_slots[slot] = 1; } pthread_mutex_unlock (&histogram->mutex); #else /* !ENABLE_MP */ values = histogram->values; #endif h = region->h; w = region->w; if (mask) { gdouble masked; src = region->data; msrc = region->data; while (h--) { s = src; m = msrc; w = region->w; switch(region->bytes) { case 1: while (w--) { masked = m[0] / 255.0; values[0][s[0]] += masked; s += 1; m += 1; } break; case 2: while (w--) { masked = m[0] / 255.0; values[0][s[0]] += masked; values[1][s[1]] += masked; s += 2; m += 1; } break; case 3: /* calculate seperate value values */ while (w--) { masked = m[0] / 255.0; values[1][s[0]] += masked; values[2][s[1]] += masked; values[3][s[2]] += masked; max = (s[0] > s[1]) ? s[0] : s[1]; if (s[2] > max) values[0][s[2]] += masked; else values[0][max] += masked; s += 3; m += 1; } break; case 4: /* calculate seperate value values */ while (w--) { masked = m[0] / 255.0; values[1][s[0]] += masked; values[2][s[1]] += masked; values[3][s[2]] += masked; values[4][s[3]] += masked; max = (s[0] > s[1]) ? s[0] : s[1]; if (s[2] > max) values[0][s[2]] += masked; else values[0][max] += masked; s += 3; m += 1; } break; } src += region->rowstride; msrc += mask->rowstride; } } else /* no mask */ { src = region->data; while (h--) { s = src; w = region->w; switch(region->bytes) { case 1: while (w--) { values[0][s[0]] += 1.0; s += 1; } break; case 2: while (w--) { values[0][s[0]] += 1.0; values[1][s[1]] += 1.0; s += 2; } break; case 3: /* calculate seperate value values */ while (w--) { values[1][s[0]] += 1.0; values[2][s[1]] += 1.0; values[3][s[2]] += 1.0; max = (s[0] > s[1]) ? s[0] : s[1]; if (s[2] > max) values[0][s[2]] += 1.0; else values[0][max] += 1.0; s += 3; } break; case 4: /* calculate seperate value values */ while (w--) { values[1][s[0]] += 1.0; values[2][s[1]] += 1.0; values[3][s[2]] += 1.0; values[4][s[3]] += 1.0; max = (s[0] > s[1]) ? s[0] : s[1]; if (s[2] > max) values[0][s[2]] += 1.0; else values[0][max] += 1.0; s += 4; } break; } src += region->rowstride; } } #ifdef ENABLE_MP /* unlock this slot */ /* we shouldn't have to use mutex locks here */ pthread_mutex_lock (&histogram->mutex); histogram->tmp_slots[slot] = 0; pthread_mutex_unlock (&histogram->mutex); #endif } static void gimp_histogram_alloc (GimpHistogram *histogram, gint bytes) { gint i; if (bytes + 1 != histogram->nchannels) { if (histogram->values) { for (i = 0; i < histogram->nchannels; i++) g_free (histogram->values[i]); g_free (histogram->values); } histogram->nchannels = bytes + 1; histogram->values = g_new0 (gdouble *, histogram->nchannels); for (i = 0; i < histogram->nchannels; i++) histogram->values[i] = g_new (double, 256); } } void gimp_histogram_calculate (GimpHistogram *histogram, PixelRegion *region, PixelRegion *mask) { gint i, j; #ifdef ENABLE_MP gint k; #endif gimp_histogram_alloc (histogram, region->bytes); #ifdef ENABLE_MP pthread_mutex_init (&histogram->mutex, NULL); histogram->tmp_slots = g_new0 (gchar, num_processors); histogram->tmp_values = g_new0 (gdouble **, num_processors); for (i = 0; i < num_processors; i++) { histogram->tmp_values[i] = g_new0 (double *, histogram->nchannels); histogram->tmp_slots[i] = 0; for (j = 0; j < histogram->nchannels; j++) { histogram->tmp_values[i][j] = g_new0 (gdouble, 256); for (k = 0; k < 256; k++) histogram->tmp_values[i][j][k] = 0.0; } } #endif for (i = 0; i < histogram->nchannels; i++) for (j = 0; j < 256; j++) histogram->values[i][j] = 0.0; pixel_regions_process_parallel ((p_func)gimp_histogram_calculate_sub_region, histogram, 2, region, mask); #ifdef ENABLE_MP /* add up all the tmp buffers and free their memmory */ for (i = 0; i < num_processors; i++) { for (j = 0; j < histogram->nchannels; j++) { for (k = 0; k < 256; k++) histogram->values[j][k] += histogram->tmp_values[i][j][k]; g_free (histogram->tmp_values[i][j]); } g_free (histogram->tmp_values[i]); } g_free (histogram->tmp_values); g_free (histogram->tmp_slots); #endif } void gimp_histogram_calculate_drawable (GimpHistogram *histogram, GimpDrawable *drawable) { PixelRegion region; PixelRegion mask; gint x1, y1, x2, y2; gint off_x, off_y; gboolean no_mask; no_mask = (gimp_drawable_mask_bounds (drawable, &x1, &y1, &x2, &y2) == FALSE); pixel_region_init (®ion, gimp_drawable_data (drawable), x1, y1, (x2 - x1), (y2 - y1), FALSE); if (!no_mask) { Channel *sel_mask; GimpImage *gimage; gimage = gimp_drawable_gimage (drawable); sel_mask = gimp_image_get_mask (gimage); gimp_drawable_offsets (drawable, &off_x, &off_y); pixel_region_init (&mask, gimp_drawable_data (GIMP_DRAWABLE (sel_mask)), x1 + off_x, y1 + off_y, (x2 - x1), (y2 - y1), FALSE); gimp_histogram_calculate (histogram, ®ion, &mask); } else { gimp_histogram_calculate (histogram, ®ion, NULL); } } gdouble gimp_histogram_get_maximum (GimpHistogram *histogram, GimpHistogramChannel channel) { gdouble max = 0.0; gint x; for (x = 0; x < 256; x++) if (histogram->values[channel][x] > max) max = histogram->values[channel][x]; return max; } gdouble gimp_histogram_get_value (GimpHistogram *histogram, GimpHistogramChannel channel, gint bin) { if (channel < histogram->nchannels && bin >= 0 && bin < 256) return histogram->values[channel][bin]; return 0.0; } gdouble gimp_histogram_get_channel (GimpHistogram *histogram, GimpHistogramChannel channel, gint bin) { if (histogram->nchannels > 3) return gimp_histogram_get_value (histogram, channel + 1, bin); else return gimp_histogram_get_value (histogram, channel , bin); } gint gimp_histogram_nchannels (GimpHistogram *histogram) { return histogram->nchannels - 1; } gdouble gimp_histogram_get_count (GimpHistogram *histogram, gint start, gint end) { gint i; gdouble count = 0.0; for (i = start; i <= end; i++) count += histogram->values[0][i]; return count; } gdouble gimp_histogram_get_mean (GimpHistogram *histogram, GimpHistogramChannel channel, gint start, gint end) { gint i; gdouble mean = 0.0; gdouble count; for (i = start; i <= end; i++) mean += i * histogram->values[channel][i]; count = gimp_histogram_get_count (histogram, start, end); if (count > 0.0) return mean / count; return mean; } gint gimp_histogram_get_median (GimpHistogram *histogram, GimpHistogramChannel channel, gint start, gint end) { gint i; gdouble sum = 0.0; gdouble count; count = gimp_histogram_get_count (histogram, start, end); for (i = start; i <= end; i++) { sum += i * histogram->values[channel][i]; if (sum * 2 > count) return i; } return -1; } gdouble gimp_histogram_get_std_dev (GimpHistogram *histogram, GimpHistogramChannel channel, gint start, gint end) { gint i; gdouble dev = 0.0; gdouble count; gdouble mean; mean = gimp_histogram_get_mean (histogram, channel, start, end); count = gimp_histogram_get_count (histogram, start, end); if (count == 0.0) count = 1.0; for (i = start; i <= end; i++) dev += gimp_histogram_get_value (histogram, channel, i) * (i - mean) * (i - mean); return sqrt (dev / count); }