/* This file is an image processing operation for GEGL * * GEGL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * GEGL 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with GEGL; if not, see . * * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This is a plug-in for GIMP. * * Colormap-Rotation plug-in. Exchanges two color ranges. * * Copyright (C) 1999 Sven Anders (anderss@fmi.uni-passau.de) * Based on code from Pavel Grinfeld (pavel@ml.com) * Copyright (C) 2011 Robert Sasu * Copyright (C) 2011 Mukund Sivaraman */ #include "config.h" #include #ifdef GEGL_CHANT_PROPERTIES gegl_chant_boolean (s_cl, _("Clockwise"), FALSE, _("Switch to clockwise")) gegl_chant_int (s_fr, _("From:"), 0, 360, 0, _("Starting angle for the color rotation")) gegl_chant_int (s_to, _("To:"), 0, 360, 0, _("End angle for the color rotation")) gegl_chant_boolean (d_cl, _("Clockwise"), FALSE, _("Switch to clockwise")) gegl_chant_int (d_fr, _("From:"), 0, 360, 0, _("Starting angle for the color rotation")) gegl_chant_int (d_to, _("To:"), 0, 360, 0, _("End angle for the color rotation")) gegl_chant_boolean (gray, _("Grayscale"), FALSE, _("Choose in case of grayscale images")) gegl_chant_double (hue, _("Hue"), 0.0, 2.0, 0.0, _("The value of hue")) gegl_chant_double (saturation, _("Saturation"), 0.0, 1.0, 0.0, _("The value of saturation")) gegl_chant_boolean (change, _("Change/Treat to this"), FALSE, _("Change/Treat to this")) gegl_chant_double (threshold, _("Threshold"), 0.0, 1.0, 0.0, _("The value of gray threshold")) #else #define GEGL_CHANT_TYPE_FILTER #define GEGL_CHANT_C_FILE "color-rotate.c" #include "gegl-chant.h" #include #include #define TWO_PI (2 * G_PI) #define DEG_TO_RAD(d) (((d) * G_PI) / 180.0) static void prepare (GeglOperation *operation) { gegl_operation_set_format (operation, "input", babl_format ("RGBA float")); gegl_operation_set_format (operation, "output", babl_format ("RGBA float")); } static void rgb_to_hsv (gfloat r, gfloat g, gfloat b, gfloat *h, gfloat *s, gfloat *v) { float min; float delta; *v = MAX (MAX (r, g), b); min = MIN (MIN (r, g), b); delta = *v - min; if (delta == 0.0f) { *h = 0.0f; *s = 0.0f; } else { *s = delta / *v; if (r == *v) { *h = (g - b) / delta; if (*h < 0.0f) *h += 6.0f; } else if (g == *v) { *h = 2.0f + (b - r) / delta; } else { *h = 4.0f + (r - g) / delta; } *h /= 6.0f; } } static void hsv_to_rgb (gfloat h, gfloat s, gfloat v, gfloat *r, gfloat *g, gfloat *b) { if (s == 0.0) { *r = v; *g = v; *b = v; } else { int hi; float frac; float w, q, t; h *= 6.0; if (h >= 6.0) h -= 6.0; hi = (int) h; frac = h - hi; w = v * (1.0 - s); q = v * (1.0 - (s * frac)); t = v * (1.0 - (s * (1.0 - frac))); switch (hi) { case 0: *r = v; *g = t; *b = w; break; case 1: *r = q; *g = v; *b = w; break; case 2: *r = w; *g = v; *b = t; break; case 3: *r = w; *g = q; *b = v; break; case 4: *r = t; *g = w; *b = v; break; case 5: *r = v; *g = w; *b = q; break; } } } static gfloat angle_mod_2PI (gfloat angle) { if (angle < 0) return angle + TWO_PI; else if (angle > TWO_PI) return angle - TWO_PI; else return angle; } static gfloat angle_inside_slice (gfloat hue, gint from, gint to, gboolean cl) { gint cw_ccw = 1; if (!cl) cw_ccw = -1; return angle_mod_2PI (cw_ccw * DEG_TO_RAD(to - hue)) / angle_mod_2PI (cw_ccw * DEG_TO_RAD(from - to)); } static gboolean is_gray (gfloat s, gdouble threshold) { return (s <= threshold); } static gfloat linear (gfloat A, gfloat B, gfloat C, gfloat D, gfloat x) { if (B > A) { if (A<=x && x<=B) return C+(D-C)/(B-A)*(x-A); else if (A<=x+TWO_PI && x+TWO_PI<=B) return C+(D-C)/(B-A)*(x+TWO_PI-A); else return x; } else { if (B<=x && x<=A) return C+(D-C)/(B-A)*(x-A); else if (B<=x+TWO_PI && x+TWO_PI<=A) return C+(D-C)/(B-A)*(x+TWO_PI-A); else return x; } } static gfloat left_end (gint from, gint to, gboolean cl) { gfloat alpha = DEG_TO_RAD (from); gfloat beta = DEG_TO_RAD (to); gint cw_ccw = cl ? 1 : -1; switch (cw_ccw) { case (-1): if (alpha < beta) return alpha + TWO_PI; default: return alpha; /* 1 */ } } static gfloat right_end (gint from, gint to, gboolean cl) { gfloat alpha = DEG_TO_RAD (from); gfloat beta = DEG_TO_RAD (to); gint cw_ccw = cl ? 1 : -1; switch (cw_ccw) { case 1: if (beta < alpha) return beta + TWO_PI; default: return beta; /* -1 */ } } static void color_rotate (gfloat *src, gint offset, GeglChantO *o) { gfloat h, s, v; gboolean skip = FALSE; gfloat color[4]; gint i; rgb_to_hsv (src[offset], src[offset + 1], src[offset + 2], &h, &s, &v); if (is_gray (s, o->threshold)) { if (o->change == FALSE) { if (angle_inside_slice (o->hue, o->s_fr, o->s_to, o->s_cl) <= 1) { h = o->hue / TWO_PI; s = o->saturation; } else { skip = TRUE; } } else { skip = TRUE; hsv_to_rgb (o->hue / TWO_PI, o->saturation, v, color, color + 1, color + 2); color[3] = src[offset + 3]; } } if (! skip) { h = linear (left_end (o->s_fr, o->s_to, o->s_cl), right_end (o->s_fr, o->s_to, o->s_cl), left_end (o->d_fr, o->d_to, o->d_cl), right_end (o->d_fr, o->d_to, o->d_cl), h * TWO_PI); h = angle_mod_2PI (h) / TWO_PI; hsv_to_rgb (h, s, v, color, color + 1, color + 2); color[3] = src[offset + 3]; } for (i = 0; i < 4; i++) src[offset + i] = color[i]; } static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *result, gint level) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); const Babl *format = babl_format ("RGBA float"); gfloat *src_buf; gint x; src_buf = g_new0 (gfloat, result->width * result->height * 4); format = babl_format ("RGBA float"); gegl_buffer_get (input, result, 1.0, format, src_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); for (x = 0; x < result->width * result->height; x++) color_rotate (src_buf, 4 * x, o); gegl_buffer_set (output, result, 0, format, src_buf, GEGL_AUTO_ROWSTRIDE); g_free (src_buf); return TRUE; } static void gegl_chant_class_init (GeglChantClass *klass) { GeglOperationClass *operation_class; GeglOperationFilterClass *filter_class; operation_class = GEGL_OPERATION_CLASS (klass); filter_class = GEGL_OPERATION_FILTER_CLASS (klass); filter_class->process = process; operation_class->prepare = prepare; gegl_operation_class_set_keys (operation_class, "categories" , "color", "name" , "gegl:color-rotate", "description" , _("Rotate colors on the image"), NULL); } #endif