gimp/plug-ins/common/color-cube-analyze.c

498 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* This is a plug-in for GIMP.
*
* 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/>.
*
*/
/*
* Analyze colorcube.
*
* Author: robert@experimental.net
*/
#include "config.h"
#include <string.h>
#include <glib/gstdio.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#include "libgimp/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-ccanalyze"
#define PLUG_IN_BINARY "color-cube-analyze"
#define PLUG_IN_ROLE "gimp-color-cube-analyze"
/* size of histogram image */
#define PREWIDTH 256
#define PREHEIGHT 150
/* lets prototype */
static void query (void);
static void run (const gchar *name,
gint n_params,
const GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals);
static void doDialog (void);
static void analyze (GimpDrawable *drawable);
static void histogram (guchar r,
guchar g,
guchar b,
gdouble a);
static void fillPreview (GtkWidget *preview);
static void insertcolor (guchar r,
guchar g,
guchar b,
gdouble a);
static void doLabel (GtkWidget *table,
const char *format,
...) G_GNUC_PRINTF (2, 3);
/* some global variables */
static gint width, height, bpp;
static gdouble hist_red[256], hist_green[256], hist_blue[256];
static gdouble maxred = 0.0, maxgreen = 0.0, maxblue = 0.0;
static gint uniques = 0;
static gint32 imageID;
/* lets declare what we want to do */
const GimpPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
/* run program */
MAIN ()
/* tell GIMP who we are */
static void
query (void)
{
static const GimpParamDef args[] =
{
{ GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
};
static const GimpParamDef return_vals[] =
{
{ GIMP_PDB_INT32, "num-colors", "Number of colors in the image" }
};
gimp_install_procedure (PLUG_IN_PROC,
N_("Analyze the set of colors in the image"),
"Analyze colorcube and print some information about "
"the current image (also displays a color-histogram)",
"robert@experimental.net",
"robert@experimental.net",
"June 20th, 1997",
N_("Colorcube A_nalysis..."),
"RGB*, GRAY*, INDEXED*",
GIMP_PLUGIN,
G_N_ELEMENTS (args), G_N_ELEMENTS (return_vals),
args, return_vals);
}
/* main function */
static void
run (const gchar *name,
gint n_params,
const GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals)
{
static GimpParam values[2];
GimpRunMode run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
GimpDrawable *drawable;
run_mode = param[0].data.d_int32;
INIT_I18N ();
*nreturn_vals = 2;
*return_vals = values;
if (run_mode == GIMP_RUN_NONINTERACTIVE)
{
if (n_params != 3)
status = GIMP_PDB_CALLING_ERROR;
}
if (status == GIMP_PDB_SUCCESS)
{
drawable = gimp_drawable_get (param[2].data.d_drawable);
imageID = param[1].data.d_image;
if (gimp_drawable_is_rgb (drawable->drawable_id) ||
gimp_drawable_is_gray (drawable->drawable_id) ||
gimp_drawable_is_indexed (drawable->drawable_id))
{
memset (hist_red, 0, sizeof (hist_red));
memset (hist_green, 0, sizeof (hist_green));
memset (hist_blue, 0, sizeof (hist_blue));
gimp_tile_cache_ntiles (2 *
(drawable->width / gimp_tile_width () + 1));
analyze (drawable);
/* show dialog after we analyzed image */
if (run_mode != GIMP_RUN_NONINTERACTIVE)
doDialog ();
}
else
status = GIMP_PDB_EXECUTION_ERROR;
gimp_drawable_detach (drawable);
}
values[0].type = GIMP_PDB_STATUS;
values[0].data.d_status = status;
values[1].type = GIMP_PDB_INT32;
values[1].data.d_int32 = uniques;
}
/* do the analyzing */
static void
analyze (GimpDrawable *drawable)
{
GimpPixelRgn srcPR;
guchar *src_row, *cmap;
gint x, y, numcol;
gint x1, y1, x2, y2;
guchar r, g, b;
gint a;
guchar idx;
gboolean gray;
gboolean has_alpha;
gboolean has_sel;
guchar *sel;
GimpPixelRgn selPR;
gint ofsx, ofsy;
GimpDrawable *selDrawable;
gimp_progress_init (_("Colorcube Analysis"));
gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
/*
* Get the size of the input image (this will/must be the same
* as the size of the output image).
*/
width = drawable->width;
height = drawable->height;
bpp = drawable->bpp;
has_sel = !gimp_selection_is_empty (imageID);
gimp_drawable_offsets (drawable->drawable_id, &ofsx, &ofsy);
/* initialize the pixel region */
gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE);
cmap = gimp_image_get_colormap (imageID, &numcol);
gray = (gimp_drawable_is_gray (drawable->drawable_id) ||
gimp_item_is_channel (drawable->drawable_id));
has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
selDrawable = gimp_drawable_get (gimp_image_get_selection (imageID));
gimp_pixel_rgn_init (&selPR,
selDrawable,
0, 0, width, height, FALSE, FALSE);
/* allocate row buffer */
src_row = g_new (guchar, (x2 - x1) * bpp);
sel = g_new (guchar, x2 - x1);
for (y = y1; y < y2; y++)
{
gimp_pixel_rgn_get_row (&srcPR, src_row, x1, y, (x2 - x1));
if (has_sel)
gimp_pixel_rgn_get_row (&selPR, sel, x1 + ofsx, y + ofsy, (x2 - x1));
for (x = 0; x < x2 - x1; x++)
{
/* Start with full opacity. */
a = 255;
/*
* If the image is indexed, fetch RGB values
* from colormap.
*/
if (cmap)
{
idx = src_row[x * bpp];
r = cmap[idx * 3];
g = cmap[idx * 3 + 1];
b = cmap[idx * 3 + 2];
if (has_alpha)
a = src_row[x * bpp + 1];
}
else if (gray)
{
r = g = b = src_row[x * bpp];
if (has_alpha)
a = src_row[x * bpp + 1];
}
else
{
r = src_row[x * bpp];
g = src_row[x * bpp + 1];
b = src_row[x * bpp + 2];
if (has_alpha)
a = src_row[x * bpp + 3];
}
if (has_sel)
a *= sel[x];
else
a *= 255;
if (a != 0)
insertcolor (r, g, b, (gdouble) a * (1.0 / (255.0 * 255.0)));
}
/* tell the user what we're doing */
if ((y % 10) == 0)
gimp_progress_update ((gdouble) y / (gdouble) (y2 - y1));
}
gimp_progress_update (1.0);
/* clean up */
gimp_drawable_detach (selDrawable);
g_free (src_row);
g_free (sel);
}
static void
insertcolor (guchar r,
guchar g,
guchar b,
gdouble a)
{
static GHashTable *hash_table;
guint key;
if (!hash_table)
hash_table = g_hash_table_new (g_direct_hash, g_direct_equal);
histogram (r, g, b, a);
key = r + 256 * (g + 256 * b);
if (g_hash_table_lookup (hash_table, GINT_TO_POINTER (key)))
{
return;
}
g_hash_table_insert (hash_table, GINT_TO_POINTER (key),
GINT_TO_POINTER (1));
uniques++;
}
/*
* Update RGB count, and keep track of maximum values (which aren't used
* anywhere as of yet, but they might be useful sometime).
*/
static void
histogram (guchar r,
guchar g,
guchar b,
gdouble a)
{
hist_red[r] += a;
hist_green[g] += a;
hist_blue[b] += a;
if (hist_red[r] > maxred)
maxred = hist_red[r];
if (hist_green[g] > maxgreen)
maxgreen = hist_green[g];
if (hist_blue[b] > maxblue)
maxblue = hist_blue[b];
}
/* show our results */
static void
doDialog (void)
{
GtkWidget *dialog;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *frame;
GtkWidget *preview;
gimp_ui_init (PLUG_IN_BINARY, TRUE);
dialog = gimp_dialog_new (_("Colorcube Analysis"), PLUG_IN_ROLE,
NULL, 0,
gimp_standard_help_func, PLUG_IN_PROC,
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
NULL);
gimp_window_set_transient (GTK_WINDOW (dialog));
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
vbox, TRUE, TRUE, 0);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
/* use preview for histogram window */
preview = gimp_preview_area_new ();
gtk_widget_set_size_request (preview, PREWIDTH, PREHEIGHT);
gtk_container_add (GTK_CONTAINER (frame), preview);
/* output results */
doLabel (vbox, _("Image dimensions: %d × %d"), width, height);
if (uniques == 0)
doLabel (vbox, _("No colors"));
else if (uniques == 1)
doLabel (vbox, _("Only one unique color"));
else
doLabel (vbox, _("Number of unique colors: %d"), uniques);
/* show stuff */
gtk_widget_show_all (dialog);
fillPreview (preview);
gimp_dialog_run (GIMP_DIALOG (dialog));
gtk_widget_destroy (dialog);
}
/* shortcut */
static void
doLabel (GtkWidget *vbox,
const gchar *format,
...)
{
GtkWidget *label;
gchar *text;
va_list args;
va_start (args, format);
text = g_strdup_vprintf (format, args);
va_end (args);
label = gtk_label_new (text);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
g_free (text);
}
/* fill our preview image with the color-histogram */
static void
fillPreview (GtkWidget *preview)
{
guchar *image, *column, *pixel;
gint x, y, rowstride;
gdouble histcount, val;
rowstride = PREWIDTH * 3;
image = g_new0 (guchar, PREWIDTH * rowstride);
for (x = 0, column = image; x < PREWIDTH; x++, column += 3)
{
/*
* For every channel, calculate a logarithmic value, scale it,
* and build a one-pixel bar.
* ... in the respective channel, preserving the other ones. --hb
*/
histcount = hist_red[x] > 1.0 ? hist_red[x] : 1.0;
val = log (histcount) * (PREHEIGHT / 12);
if (val > PREHEIGHT)
val = PREHEIGHT;
y = PREHEIGHT - 1;
pixel = column + (y * rowstride);
for (; y > (PREHEIGHT - val); y--)
{
pixel[0] = 255;
pixel -= rowstride;
}
histcount = hist_green[x] > 1.0 ? hist_green[x] : 1.0;
val = log (histcount) * (PREHEIGHT / 12);
if (val > PREHEIGHT)
val = PREHEIGHT;
y = PREHEIGHT - 1;
pixel = column + (y * rowstride);
for (; y > (PREHEIGHT - val); y--)
{
pixel[1] = 255;
pixel -= rowstride;
}
histcount = hist_blue[x] > 1.0 ? hist_blue[x] : 1.0;
val = log (histcount) * (PREHEIGHT / 12);
if (val > PREHEIGHT)
val = PREHEIGHT;
y = PREHEIGHT - 1;
pixel = column + (y * rowstride);
for (; y > (PREHEIGHT - val); y--)
{
pixel[2] = 255;
pixel -= rowstride;
}
}
/* move our data into the preview image */
gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
0, 0, PREWIDTH, PREHEIGHT,
GIMP_RGB_IMAGE,
image,
3 * PREWIDTH);
g_free (image);
}