colorify.c 11.4 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2 3 4 5 6
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * Colorify. Changes the pixel's luminosity to a specified color
 * Copyright (C) 1997 Francisco Bustamante
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10 11 12 13 14 15 16 17
 * (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
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19 20
 */

21 22
#include "config.h"

23 24
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
25

26 27
#include "libgimp/stdplugins-intl.h"

28

29 30
#define PLUG_IN_PROC    "plug-in-colorify"
#define PLUG_IN_BINARY  "colorify"
31
#define PLUG_IN_ROLE    "gimp-colorify"
32 33
#define PLUG_IN_VERSION "1.1"

Sven Neumann's avatar
Sven Neumann committed
34 35
#define COLOR_SIZE 30

36
static void      query (void);
37
static void      run   (const gchar      *name,
David Odin's avatar
David Odin committed
38 39 40 41 42 43 44 45
                        gint              nparams,
                        const GimpParam  *param,
                        gint             *nreturn_vals,
                        GimpParam       **return_vals);

static void      colorify                  (GimpDrawable *drawable,
                                            GimpPreview  *preview);
static gboolean  colorify_dialog           (GimpDrawable *drawable);
46
static void      predefined_color_callback (GtkWidget    *widget,
David Odin's avatar
David Odin committed
47
                                            gpointer      data);
48 49 50

typedef struct
{
Sven Neumann's avatar
Sven Neumann committed
51
  GimpRGB  color;
52 53 54 55
} ColorifyVals;

static ColorifyVals cvals =
{
56
  { 1.0, 1.0, 1.0, 1.0 }
57 58
};

59
static GimpRGB button_color[] =
60
{
61 62 63 64 65 66 67
  { 1.0, 0.0, 0.0, 1.0 },
  { 1.0, 1.0, 0.0, 1.0 },
  { 0.0, 1.0, 0.0, 1.0 },
  { 0.0, 1.0, 1.0, 1.0 },
  { 0.0, 0.0, 1.0, 1.0 },
  { 1.0, 0.0, 1.0, 1.0 },
  { 1.0, 1.0, 1.0, 1.0 },
68 69
};

70
const GimpPlugInInfo PLUG_IN_INFO =
71
{
72 73 74 75
  NULL,
  NULL,
  query,
  run,
76 77
};

78
static GtkWidget *custom_color_button = NULL;
79 80 81 82 83 84 85 86

static gint lum_red_lookup[256];
static gint lum_green_lookup[256];
static gint lum_blue_lookup[256];
static gint final_red_lookup[256];
static gint final_green_lookup[256];
static gint final_blue_lookup[256];

87 88 89 90

MAIN ()

static void
91
query (void)
92
{
93
  static const GimpParamDef args[] =
94
  {
95
    { GIMP_PDB_INT32,    "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
96 97 98
    { GIMP_PDB_IMAGE,    "image",    "Input image"    },
    { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
    { GIMP_PDB_COLOR,    "color",    "Color to apply" }
99 100
  };

101
  gimp_install_procedure (PLUG_IN_PROC,
102
                          N_("Replace all colors with shades of a specified color"),
David Odin's avatar
David Odin committed
103 104 105 106
                          "Makes an average of the RGB channels and uses it "
                          "to set the color",
                          "Francisco Bustamante",
                          "Francisco Bustamante",
107
                          PLUG_IN_VERSION,
108
                          N_("Colorif_y..."),
David Odin's avatar
David Odin committed
109 110 111 112
                          "RGB*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args), 0,
                          args, NULL);
113 114 115
}

static void
116 117 118 119 120
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
121
{
Sven Neumann's avatar
Sven Neumann committed
122 123 124
  GimpPDBStatusType  status;
  static GimpParam   values[1];
  GimpDrawable      *drawable;
125
  GimpRunMode        run_mode;
126

127
  INIT_I18N ();
128

Sven Neumann's avatar
Sven Neumann committed
129
  status = GIMP_PDB_SUCCESS;
130
  run_mode = param[0].data.d_int32;
131

Sven Neumann's avatar
Sven Neumann committed
132
  values[0].type = GIMP_PDB_STATUS;
133
  values[0].data.d_status = status;
134

135 136
  *nreturn_vals = 1;
  *return_vals = values;
137

138
  drawable = gimp_drawable_get (param[2].data.d_drawable);
139

140 141
  switch (run_mode)
    {
Sven Neumann's avatar
Sven Neumann committed
142
    case GIMP_RUN_INTERACTIVE:
143
      gimp_get_data (PLUG_IN_PROC, &cvals);
David Odin's avatar
David Odin committed
144 145
      if (!colorify_dialog (drawable))
        return;
146
      break;
147

Sven Neumann's avatar
Sven Neumann committed
148
    case GIMP_RUN_NONINTERACTIVE:
149
      if (nparams != 4)
David Odin's avatar
David Odin committed
150
        status = GIMP_PDB_CALLING_ERROR;
151

Sven Neumann's avatar
Sven Neumann committed
152
      if (status == GIMP_PDB_SUCCESS)
David Odin's avatar
David Odin committed
153
        cvals.color = param[3].data.d_color;
154 155
      break;

Sven Neumann's avatar
Sven Neumann committed
156
    case GIMP_RUN_WITH_LAST_VALS:
157
      /*  Possibly retrieve data  */
158
      gimp_get_data (PLUG_IN_PROC, &cvals);
159 160 161 162 163
      break;

    default:
      break;
    }
164

Sven Neumann's avatar
Sven Neumann committed
165
  if (status == GIMP_PDB_SUCCESS)
166
    {
167
      gimp_progress_init (_("Colorifying"));
168

David Odin's avatar
David Odin committed
169
      colorify (drawable, NULL);
170

Sven Neumann's avatar
Sven Neumann committed
171
      if (run_mode == GIMP_RUN_INTERACTIVE)
172
        gimp_set_data (PLUG_IN_PROC, &cvals, sizeof (ColorifyVals));
173

Sven Neumann's avatar
Sven Neumann committed
174
      if (run_mode != GIMP_RUN_NONINTERACTIVE)
175
        gimp_displays_flush ();
176
    }
177

178 179
  gimp_drawable_detach (drawable);

180
  values[0].data.d_status = status;
181 182
}

183
static void
184 185 186 187
colorify_func (const guchar *src,
               guchar       *dest,
               gint          bpp,
               gpointer      data)
188
{
189 190
  gint lum;

David Odin's avatar
David Odin committed
191 192 193
  lum = lum_red_lookup[src[0]]   +
        lum_green_lookup[src[1]] +
        lum_blue_lookup[src[2]];
194

195 196 197
  dest[0] = final_red_lookup[lum];
  dest[1] = final_green_lookup[lum];
  dest[2] = final_blue_lookup[lum];
198

199 200 201 202
  if (bpp == 4)
    dest[3] = src[3];
}

203
static void
David Odin's avatar
David Odin committed
204 205
colorify (GimpDrawable *drawable,
          GimpPreview  *preview)
206
{
207
  gint  i;
208 209 210

  for (i = 0; i < 256; i ++)
    {
211 212 213
      lum_red_lookup[i]     = i * GIMP_RGB_LUMINANCE_RED;
      lum_green_lookup[i]   = i * GIMP_RGB_LUMINANCE_GREEN;
      lum_blue_lookup[i]    = i * GIMP_RGB_LUMINANCE_BLUE;
Sven Neumann's avatar
Sven Neumann committed
214 215 216
      final_red_lookup[i]   = i * cvals.color.r;
      final_green_lookup[i] = i * cvals.color.g;
      final_blue_lookup[i]  = i * cvals.color.b;
217 218
    }

David Odin's avatar
David Odin committed
219 220 221 222 223
  if (preview)
    {
      gint    width, height, bytes;
      guchar *src;

224
      src = gimp_zoom_preview_get_source (GIMP_ZOOM_PREVIEW (preview),
225
                                          &width, &height, &bytes);
David Odin's avatar
David Odin committed
226 227 228 229 230 231 232 233
      for (i = 0; i < width * height; i++)
        colorify_func (src + i * bytes, src + i * bytes, bytes, NULL);

      gimp_preview_draw_buffer (preview, src, width * bytes);
      g_free (src);
    }
  else
    {
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
      GimpPixelRgn  srcPR, destPR;
      gint          x1, y1, x2, y2;
      gpointer      pr;
      gint          total_area;
      gint          area_so_far;
      gint          count;

      gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);

      total_area  = (x2 - x1) * (y2 - y1);
      area_so_far = 0;

      if (total_area <= 0)
        return;

      /* Initialize the pixel regions. */
      gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
                           FALSE, FALSE);
      gimp_pixel_rgn_init (&destPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
                           TRUE, TRUE);

      for (pr = gimp_pixel_rgns_register (2, &srcPR, &destPR), count = 0;
           pr != NULL;
           pr = gimp_pixel_rgns_process (pr), count++)
        {
          const guchar *src  = srcPR.data;
          guchar       *dest = destPR.data;
          gint          row;

          for (row = 0; row < srcPR.h; row++)
            {
              const guchar *s      = src;
              guchar       *d      = dest;
              gint          pixels = srcPR.w;

              while (pixels--)
                {
                  colorify_func (s, d, srcPR.bpp, NULL);

                  s += srcPR.bpp;
                  d += destPR.bpp;
                }

              src  += srcPR.rowstride;
              dest += destPR.rowstride;
            }

          area_so_far += srcPR.w * srcPR.h;

          if ((count % 16) == 0)
            gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area);
        }

      /*  update the processed region  */
      gimp_drawable_flush (drawable);
      gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
      gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
David Odin's avatar
David Odin committed
291
    }
292 293
}

Sven Neumann's avatar
Sven Neumann committed
294
static gboolean
David Odin's avatar
David Odin committed
295
colorify_dialog (GimpDrawable *drawable)
296
{
297
  GtkWidget *dialog;
David Odin's avatar
David Odin committed
298 299
  GtkWidget *main_vbox;
  GtkWidget *preview;
300 301
  GtkWidget *label;
  GtkWidget *button;
302
  GtkWidget *grid;
303
  GtkWidget *color_area;
Sven Neumann's avatar
Sven Neumann committed
304
  gint       i;
305
  gboolean   run;
306

307
  gimp_ui_init (PLUG_IN_BINARY, TRUE);
308

309
  dialog = gimp_dialog_new (_("Colorify"), PLUG_IN_ROLE,
310
                            NULL, 0,
311
                            gimp_standard_help_func, PLUG_IN_PROC,
David Odin's avatar
David Odin committed
312

313 314
                            _("_Cancel"), GTK_RESPONSE_CANCEL,
                            _("_OK"),     GTK_RESPONSE_OK,
David Odin's avatar
David Odin committed
315 316

                            NULL);
317

318
  gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
319 320 321
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);
322

323
  gimp_window_set_transient (GTK_WINDOW (dialog));
324

Michael Natterer's avatar
Michael Natterer committed
325
  main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
David Odin's avatar
David Odin committed
326
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
327 328
  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
                      main_vbox, TRUE, TRUE, 0);
David Odin's avatar
David Odin committed
329
  gtk_widget_show (main_vbox);
Sven Neumann's avatar
Sven Neumann committed
330

331
  preview = gimp_zoom_preview_new_from_drawable_id (drawable->drawable_id);
David Odin's avatar
David Odin committed
332 333 334 335 336
  gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
  gtk_widget_show (preview);
  g_signal_connect_swapped (preview, "invalidated",
                            G_CALLBACK (colorify),
                            drawable);
337

338 339 340 341 342
  grid = gtk_grid_new ();
  gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
  gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
  gtk_box_pack_start (GTK_BOX (main_vbox), grid, FALSE, FALSE, 0);
  gtk_widget_show (grid);
343

344
  label = gtk_label_new (_("Custom color:"));
345
  gtk_grid_attach (GTK_GRID (grid), label, 4, 0, 2, 1);
346 347
  gtk_widget_show (label);

348
  custom_color_button = gimp_color_button_new (_("Colorify Custom Color"),
David Odin's avatar
David Odin committed
349 350 351
                                               COLOR_SIZE, COLOR_SIZE,
                                               &cvals.color,
                                               GIMP_COLOR_AREA_FLAT);
352
  g_signal_connect (custom_color_button, "color-changed",
353
                    G_CALLBACK (gimp_color_button_get_color),
David Odin's avatar
David Odin committed
354
                    &cvals.color);
355
  g_signal_connect_swapped (custom_color_button, "color-changed",
David Odin's avatar
David Odin committed
356 357
                            G_CALLBACK (gimp_preview_invalidate),
                            preview);
358

359
  gtk_grid_attach (GTK_GRID (grid), custom_color_button, 6, 0, 1, 1);
360
  gtk_widget_show (custom_color_button);
Sven Neumann's avatar
Sven Neumann committed
361

362 363
  for (i = 0; i < 7; i++)
    {
Sven Neumann's avatar
Sven Neumann committed
364
      button = gtk_button_new ();
365
      color_area = gimp_color_area_new (&button_color[i],
David Odin's avatar
David Odin committed
366 367
                                        GIMP_COLOR_AREA_FLAT,
                                        GDK_BUTTON2_MASK);
368
      gtk_widget_set_size_request (GTK_WIDGET (color_area),
David Odin's avatar
David Odin committed
369
                                   COLOR_SIZE, COLOR_SIZE);
370
      gtk_container_add (GTK_CONTAINER (button), color_area);
371
      g_signal_connect (button, "clicked",
David Odin's avatar
David Odin committed
372
                        G_CALLBACK (predefined_color_callback),
373
                        &button_color[i]);
374
      gtk_widget_show (color_area);
375

376
      gtk_grid_attach (GTK_GRID (grid), button, i, 1, 1, 1);
377 378 379 380 381
      gtk_widget_show (button);
    }

  gtk_widget_show (dialog);

382
  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
383

384
  gtk_widget_destroy (dialog);
385

386
  return run;
387 388 389 390
}

static void
predefined_color_callback (GtkWidget *widget,
David Odin's avatar
David Odin committed
391
                           gpointer   data)
392
{
393
  gimp_color_button_set_color (GIMP_COLOR_BUTTON (custom_color_button),
David Odin's avatar
David Odin committed
394
                               (GimpRGB *) data);
395
}