~ubuntu-branches/ubuntu/maverick/gimp/maverick-updates

« back to all changes in this revision

Viewing changes to modules/cdisplay_colorblind.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Holbach
  • Date: 2005-12-09 19:44:52 UTC
  • Revision ID: james.westby@ubuntu.com-20051209194452-yggpemjlofpjqyf4
Tags: upstream-2.2.9
ImportĀ upstreamĀ versionĀ 2.2.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* The GIMP -- an image manipulation program
 
2
 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
 
3
 *
 
4
 * cdisplay_colorblind.c
 
5
 * Copyright (C) 2002-2003 Michael Natterer <mitch@gimp.org>,
 
6
 *                         Sven Neumann <sven@gimp.org>,
 
7
 *                         Robert Dougherty <bob@vischeck.com> and
 
8
 *                         Alex Wade <alex@vischeck.com>
 
9
 *
 
10
 * This code is an implementation of an algorithm described by Hans Brettel,
 
11
 * Francoise Vienot and John Mollon in the Journal of the Optical Society of
 
12
 * America V14(10), pg 2647. (See http://vischeck.com/ for more info.)
 
13
 *
 
14
 * This program is free software; you can redistribute it and/or modify
 
15
 * it under the terms of the GNU General Public License as published by
 
16
 * the Free Software Foundation; either version 2 of the License, or
 
17
 * (at your option) any later version.
 
18
 *
 
19
 * This program is distributed in the hope that it will be useful,
 
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
22
 * GNU General Public License for more details.
 
23
 *
 
24
 * You should have received a copy of the GNU General Public License
 
25
 * along with this program; if not, write to the Free Software
 
26
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
27
 */
 
28
 
 
29
#include "config.h"
 
30
 
 
31
#include <stdio.h>
 
32
#include <string.h>
 
33
 
 
34
#include <gtk/gtk.h>
 
35
 
 
36
#include "libgimpbase/gimpbase.h"
 
37
#include "libgimpmodule/gimpmodule.h"
 
38
#include "libgimpwidgets/gimpwidgets.h"
 
39
#include "libgimpmath/gimpmath.h"
 
40
 
 
41
#include "libgimp/libgimp-intl.h"
 
42
 
 
43
 
 
44
typedef enum
 
45
{
 
46
  COLORBLIND_DEFICIENCY_PROTANOPIA,
 
47
  COLORBLIND_DEFICIENCY_DEUTERANOPIA,
 
48
  COLORBLIND_DEFICIENCY_TRITANOPIA
 
49
} ColorblindDeficiency;
 
50
 
 
51
#define CDISPLAY_TYPE_COLORBLIND_DEFICIENCY (cdisplay_colorblind_deficiency_type)
 
52
static GType  cdisplay_colorblind_deficiency_get_type (GTypeModule *module);
 
53
 
 
54
static const GEnumValue cdisplay_colorblind_deficiency_enum_values[] =
 
55
{
 
56
  { COLORBLIND_DEFICIENCY_PROTANOPIA,
 
57
    N_("Protanopia (insensitivity to red)"),     "protanopia"   },
 
58
  { COLORBLIND_DEFICIENCY_DEUTERANOPIA,
 
59
    N_("Deuteranopia (insensitivity to green)"), "deuteranopia" },
 
60
  { COLORBLIND_DEFICIENCY_TRITANOPIA,
 
61
    N_("Tritanopia (insensitivity to blue)"),    "tritanopia"   },
 
62
  { 0, NULL, NULL }
 
63
};
 
64
 
 
65
 
 
66
#define DEFAULT_DEFICIENCY  COLORBLIND_DEFICIENCY_DEUTERANOPIA
 
67
#define COLOR_CACHE_SIZE    1021
 
68
 
 
69
 
 
70
#define CDISPLAY_TYPE_COLORBLIND            (cdisplay_colorblind_type)
 
71
#define CDISPLAY_COLORBLIND(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CDISPLAY_TYPE_COLORBLIND, CdisplayColorblind))
 
72
#define CDISPLAY_COLORBLIND_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), CDISPLAY_TYPE_COLORBLIND, CdisplayColorblindClass))
 
73
#define CDISPLAY_IS_COLORBLIND(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CDISPLAY_TYPE_COLORBLIND))
 
74
#define CDISPLAY_IS_COLORBLIND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CDISPLAY_TYPE_COLORBLIND))
 
75
 
 
76
 
 
77
typedef struct _CdisplayColorblind      CdisplayColorblind;
 
78
typedef struct _CdisplayColorblindClass CdisplayColorblindClass;
 
79
 
 
80
struct _CdisplayColorblind
 
81
{
 
82
  GimpColorDisplay      parent_instance;
 
83
 
 
84
  ColorblindDeficiency  deficiency;
 
85
 
 
86
  gfloat                rgb2lms[9];
 
87
  gfloat                lms2rgb[9];
 
88
  gfloat                gammaRGB[3];
 
89
 
 
90
  gfloat                a1, b1, c1;
 
91
  gfloat                a2, b2, c2;
 
92
  gfloat                inflection;
 
93
 
 
94
  guint32               cache[2 * COLOR_CACHE_SIZE];
 
95
 
 
96
  GtkWidget            *hbox;
 
97
  GtkWidget            *combo;
 
98
};
 
99
 
 
100
struct _CdisplayColorblindClass
 
101
{
 
102
  GimpColorDisplayClass  parent_instance;
 
103
};
 
104
 
 
105
 
 
106
enum
 
107
{
 
108
  PROP_0,
 
109
  PROP_DEFICIENCY
 
110
};
 
111
 
 
112
 
 
113
static GType   cdisplay_colorblind_get_type   (GTypeModule             *module);
 
114
static void    cdisplay_colorblind_class_init (CdisplayColorblindClass *klass);
 
115
static void    cdisplay_colorblind_init       (CdisplayColorblind      *colorblind);
 
116
 
 
117
static void    cdisplay_colorblind_dispose      (GObject               *object);
 
118
static void    cdisplay_colorblind_set_property (GObject               *object,
 
119
                                                                                                                                 guint                  property_id,
 
120
                                                 const GValue          *value,
 
121
                                                 GParamSpec            *pspec);
 
122
static void    cdisplay_colorblind_get_property (GObject               *object,
 
123
                                                 guint                  property_id,
 
124
                                                 GValue                *value,
 
125
                                                 GParamSpec            *pspec);
 
126
 
 
127
 
 
128
static GimpColorDisplay * cdisplay_colorblind_clone  (GimpColorDisplay   *display);
 
129
static void    cdisplay_colorblind_convert           (GimpColorDisplay   *display,
 
130
                                                      guchar             *buf,
 
131
                                                      gint                w,
 
132
                                                      gint                h,
 
133
                                                      gint                bpp,
 
134
                                                      gint                bpl);
 
135
static void    cdisplay_colorblind_load_state        (GimpColorDisplay   *display,
 
136
                                                      GimpParasite       *state);
 
137
static GimpParasite * cdisplay_colorblind_save_state (GimpColorDisplay   *display);
 
138
static GtkWidget    * cdisplay_colorblind_configure  (GimpColorDisplay   *display);
 
139
static void    cdisplay_colorblind_configure_reset   (GimpColorDisplay   *display);
 
140
 
 
141
static void    cdisplay_colorblind_changed           (GimpColorDisplay   *display);
 
142
 
 
143
static void    cdisplay_colorblind_set_deficiency    (CdisplayColorblind   *colorblind,
 
144
                                                      ColorblindDeficiency  value);
 
145
 
 
146
static void    colorblind_deficiency_callback        (GtkWidget          *widget,
 
147
                                                      CdisplayColorblind *colorblind);
 
148
 
 
149
 
 
150
static const GimpModuleInfo cdisplay_colorblind_info =
 
151
{
 
152
  GIMP_MODULE_ABI_VERSION,
 
153
  N_("Color deficit simulation filter (Brettel-Vienot-Mollon algorithm)"),
 
154
  "Michael Natterer <mitch@gimp.org>, Bob Dougherty <bob@vischeck.com>, "
 
155
  "Alex Wade <alex@vischeck.com>",
 
156
  "v0.2",
 
157
  "(c) 2002-2004, released under the GPL",
 
158
  "January 22, 2003"
 
159
};
 
160
 
 
161
static GType                  cdisplay_colorblind_type            = 0;
 
162
static GType                  cdisplay_colorblind_deficiency_type = 0;
 
163
static GimpColorDisplayClass *parent_class                        = NULL;
 
164
 
 
165
 
 
166
G_MODULE_EXPORT const GimpModuleInfo *
 
167
gimp_module_query (GTypeModule *module)
 
168
{
 
169
  return &cdisplay_colorblind_info;
 
170
}
 
171
 
 
172
G_MODULE_EXPORT gboolean
 
173
gimp_module_register (GTypeModule *module)
 
174
{
 
175
  cdisplay_colorblind_get_type (module);
 
176
  cdisplay_colorblind_deficiency_get_type (module);
 
177
 
 
178
  return TRUE;
 
179
}
 
180
 
 
181
static GType
 
182
cdisplay_colorblind_get_type (GTypeModule *module)
 
183
{
 
184
  if (! cdisplay_colorblind_type)
 
185
    {
 
186
      static const GTypeInfo display_info =
 
187
      {
 
188
        sizeof (CdisplayColorblindClass),
 
189
        (GBaseInitFunc) NULL,
 
190
        (GBaseFinalizeFunc) NULL,
 
191
        (GClassInitFunc) cdisplay_colorblind_class_init,
 
192
        NULL,           /* class_finalize */
 
193
        NULL,           /* class_data     */
 
194
        sizeof (CdisplayColorblind),
 
195
        0,              /* n_preallocs    */
 
196
        (GInstanceInitFunc) cdisplay_colorblind_init,
 
197
      };
 
198
 
 
199
      cdisplay_colorblind_type =
 
200
        g_type_module_register_type (module,
 
201
                                     GIMP_TYPE_COLOR_DISPLAY,
 
202
                                     "CdisplayColorblind",
 
203
                                     &display_info, 0);
 
204
    }
 
205
 
 
206
  return cdisplay_colorblind_type;
 
207
}
 
208
 
 
209
 
 
210
static GType
 
211
cdisplay_colorblind_deficiency_get_type (GTypeModule *module)
 
212
{
 
213
  if (! cdisplay_colorblind_deficiency_type)
 
214
    cdisplay_colorblind_deficiency_type =
 
215
      gimp_module_register_enum (module,
 
216
                                 "CDisplayColorblindDeficiency",
 
217
                                 cdisplay_colorblind_deficiency_enum_values);
 
218
 
 
219
  return cdisplay_colorblind_deficiency_type;
 
220
}
 
221
 
 
222
static void
 
223
cdisplay_colorblind_class_init (CdisplayColorblindClass *klass)
 
224
{
 
225
  GObjectClass          *object_class  = G_OBJECT_CLASS (klass);
 
226
  GimpColorDisplayClass *display_class = GIMP_COLOR_DISPLAY_CLASS (klass);
 
227
 
 
228
  parent_class = g_type_class_peek_parent (klass);
 
229
 
 
230
  object_class->dispose          = cdisplay_colorblind_dispose;
 
231
  object_class->get_property     = cdisplay_colorblind_get_property;
 
232
  object_class->set_property     = cdisplay_colorblind_set_property;
 
233
 
 
234
  g_object_class_install_property (object_class, PROP_DEFICIENCY,
 
235
                                   g_param_spec_enum ("deficiency", NULL, NULL,
 
236
                                                      CDISPLAY_TYPE_COLORBLIND_DEFICIENCY,
 
237
                                                      DEFAULT_DEFICIENCY,
 
238
                                                      G_PARAM_READWRITE |
 
239
                                                      G_PARAM_CONSTRUCT |
 
240
                                                      GIMP_MODULE_PARAM_SERIALIZE));
 
241
 
 
242
  display_class->name            = _("Color Deficient Vision");
 
243
  display_class->help_id         = "gimp-colordisplay-colorblind";
 
244
  display_class->clone           = cdisplay_colorblind_clone;
 
245
  display_class->convert         = cdisplay_colorblind_convert;
 
246
  display_class->load_state      = cdisplay_colorblind_load_state;
 
247
  display_class->save_state      = cdisplay_colorblind_save_state;
 
248
  display_class->configure       = cdisplay_colorblind_configure;
 
249
  display_class->configure_reset = cdisplay_colorblind_configure_reset;
 
250
  display_class->changed         = cdisplay_colorblind_changed;
 
251
}
 
252
 
 
253
static void
 
254
cdisplay_colorblind_init (CdisplayColorblind *colorblind)
 
255
{
 
256
  /* For most modern Cathode-Ray Tube monitors (CRTs), the following
 
257
   * are good estimates of the RGB->LMS and LMS->RGB transform
 
258
   * matrices.  They are based on spectra measured on a typical CRT
 
259
   * with a PhotoResearch PR650 spectral photometer and the Stockman
 
260
   * human cone fundamentals. NOTE: these estimates will NOT work well
 
261
   * for LCDs!
 
262
   */
 
263
  colorblind->rgb2lms[0] = 0.05059983;
 
264
  colorblind->rgb2lms[1] = 0.08585369;
 
265
  colorblind->rgb2lms[2] = 0.00952420;
 
266
 
 
267
  colorblind->rgb2lms[3] = 0.01893033;
 
268
  colorblind->rgb2lms[4] = 0.08925308;
 
269
  colorblind->rgb2lms[5] = 0.01370054;
 
270
 
 
271
  colorblind->rgb2lms[6] = 0.00292202;
 
272
  colorblind->rgb2lms[7] = 0.00975732;
 
273
  colorblind->rgb2lms[8] = 0.07145979;
 
274
 
 
275
  colorblind->lms2rgb[0] =  30.830854;
 
276
  colorblind->lms2rgb[1] = -29.832659;
 
277
  colorblind->lms2rgb[2] =   1.610474;
 
278
 
 
279
  colorblind->lms2rgb[3] =  -6.481468;
 
280
  colorblind->lms2rgb[4] =  17.715578;
 
281
  colorblind->lms2rgb[5] =  -2.532642;
 
282
 
 
283
  colorblind->lms2rgb[6] =  -0.375690;
 
284
  colorblind->lms2rgb[7] =  -1.199062;
 
285
  colorblind->lms2rgb[8] =  14.273846;
 
286
 
 
287
  /* The RGB<->LMS transforms above are computed from the human cone
 
288
   * photo-pigment absorption spectra and the monitor phosphor
 
289
   * emission spectra. These parameters are fairly constant for most
 
290
   * humans and most montiors (at least for modern CRTs). However,
 
291
   * gamma will vary quite a bit, as it is a property of the monitor
 
292
   * (eg. amplifier gain), the video card, and even the
 
293
   * software. Further, users can adjust their gammas (either via
 
294
   * adjusting the monitor amp gains or in software). That said, the
 
295
   * following are the gamma estimates that we have used in the
 
296
   * Vischeck code. Many colorblind users have viewed our simulations
 
297
   * and told us that they "work" (simulated and original images are
 
298
   * indistinguishabled).
 
299
   */
 
300
  colorblind->gammaRGB[0] = 2.1;
 
301
  colorblind->gammaRGB[1] = 2.0;
 
302
  colorblind->gammaRGB[2] = 2.1;
 
303
}
 
304
 
 
305
static void
 
306
cdisplay_colorblind_dispose (GObject *object)
 
307
{
 
308
  CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (object);
 
309
 
 
310
  if (colorblind->hbox)
 
311
    gtk_widget_destroy (colorblind->hbox);
 
312
 
 
313
  G_OBJECT_CLASS (parent_class)->dispose (object);
 
314
}
 
315
 
 
316
static void
 
317
cdisplay_colorblind_get_property (GObject    *object,
 
318
                                  guint       property_id,
 
319
                                  GValue     *value,
 
320
                                  GParamSpec *pspec)
 
321
{
 
322
  CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (object);
 
323
 
 
324
  switch (property_id)
 
325
    {
 
326
    case PROP_DEFICIENCY:
 
327
      g_value_set_enum (value, colorblind->deficiency);
 
328
      break;
 
329
    default:
 
330
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
331
      break;
 
332
    }
 
333
}
 
334
 
 
335
static void
 
336
cdisplay_colorblind_set_property (GObject      *object,
 
337
                                  guint         property_id,
 
338
                                  const GValue *value,
 
339
                                  GParamSpec   *pspec)
 
340
{
 
341
  CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (object);
 
342
 
 
343
  switch (property_id)
 
344
    {
 
345
    case PROP_DEFICIENCY:
 
346
      cdisplay_colorblind_set_deficiency (colorblind,
 
347
                                          g_value_get_enum (value));
 
348
      break;
 
349
    default:
 
350
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
351
      break;
 
352
    }
 
353
}
 
354
 
 
355
static GimpColorDisplay *
 
356
cdisplay_colorblind_clone (GimpColorDisplay *display)
 
357
{
 
358
  CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (display);
 
359
  CdisplayColorblind *copy;
 
360
 
 
361
  copy = CDISPLAY_COLORBLIND (gimp_color_display_new (G_TYPE_FROM_INSTANCE (colorblind)));
 
362
 
 
363
  copy->deficiency = colorblind->deficiency;
 
364
 
 
365
  return GIMP_COLOR_DISPLAY (copy);
 
366
}
 
367
 
 
368
static void
 
369
cdisplay_colorblind_convert (GimpColorDisplay *display,
 
370
                             guchar           *buf,
 
371
                             gint              width,
 
372
                             gint              height,
 
373
                             gint              bpp,
 
374
                             gint              bpl)
 
375
{
 
376
  CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (display);
 
377
  guchar             *b;
 
378
  gfloat              rgb2lms[9],lms2rgb[9];
 
379
  gfloat              a1, b1, c1, a2, b2, c2;
 
380
  gfloat              tmp;
 
381
  gfloat              red, green, blue, redOld, greenOld;
 
382
  gint                x, y;
 
383
 
 
384
  /* Require 3 bytes per pixel (assume RGB) */
 
385
  if (bpp != 3)
 
386
    return;
 
387
 
 
388
  /* to improve readability, copy the parameters into local variables */
 
389
  memcpy (rgb2lms, colorblind->rgb2lms, sizeof (rgb2lms));
 
390
  memcpy (lms2rgb, colorblind->lms2rgb, sizeof (lms2rgb));
 
391
  a1 = colorblind->a1; b1 = colorblind->b1; c1 = colorblind->c1;
 
392
  a2 = colorblind->a2; b2 = colorblind->b2; c2 = colorblind->c2;
 
393
 
 
394
  for (y = 0; y < height; y++, buf += bpl)
 
395
    for (x = 0, b = buf; x < width; x++, b += bpp)
 
396
      {
 
397
        guint32 pixel;
 
398
        guint   index;
 
399
 
 
400
        /* First check our cache */
 
401
        pixel = b[0] << 16 | b[1] << 8 | b[2];
 
402
        index = pixel % COLOR_CACHE_SIZE;
 
403
 
 
404
        if (colorblind->cache[2 * index] == pixel)
 
405
          {
 
406
            pixel = colorblind->cache[2 * index + 1];
 
407
 
 
408
            b[2] = pixel & 0xFF; pixel >>= 8;
 
409
            b[1] = pixel & 0xFF; pixel >>= 8;
 
410
            b[0] = pixel & 0xFF;
 
411
 
 
412
            continue;
 
413
          }
 
414
 
 
415
        red   = b[0];
 
416
        green = b[1];
 
417
        blue  = b[2];
 
418
 
 
419
        /* Remove gamma to linearize RGB intensities */
 
420
        red   = pow (red,   1.0 / colorblind->gammaRGB[0]);
 
421
        green = pow (green, 1.0 / colorblind->gammaRGB[1]);
 
422
        blue  = pow (blue,  1.0 / colorblind->gammaRGB[2]);
 
423
 
 
424
        /* Convert to LMS (dot product with transform matrix) */
 
425
        redOld   = red;
 
426
        greenOld = green;
 
427
 
 
428
        red   = redOld * rgb2lms[0] + greenOld * rgb2lms[1] + blue * rgb2lms[2];
 
429
        green = redOld * rgb2lms[3] + greenOld * rgb2lms[4] + blue * rgb2lms[5];
 
430
        blue  = redOld * rgb2lms[6] + greenOld * rgb2lms[7] + blue * rgb2lms[8];
 
431
 
 
432
        switch (colorblind->deficiency)
 
433
          {
 
434
          case COLORBLIND_DEFICIENCY_DEUTERANOPIA:
 
435
            tmp = blue / red;
 
436
            /* See which side of the inflection line we fall... */
 
437
            if (tmp < colorblind->inflection)
 
438
            green = -(a1 * red + c1 * blue) / b1;
 
439
            else
 
440
              green = -(a2 * red + c2 * blue) / b2;
 
441
            break;
 
442
 
 
443
          case COLORBLIND_DEFICIENCY_PROTANOPIA:
 
444
            tmp = blue / green;
 
445
            /* See which side of the inflection line we fall... */
 
446
            if (tmp < colorblind->inflection)
 
447
              red = -(b1 * green + c1 * blue) / a1;
 
448
            else
 
449
              red = -(b2 * green + c2 * blue) / a2;
 
450
            break;
 
451
 
 
452
          case COLORBLIND_DEFICIENCY_TRITANOPIA:
 
453
            tmp = green / red;
 
454
            /* See which side of the inflection line we fall... */
 
455
            if (tmp < colorblind->inflection)
 
456
              blue = -(a1 * red + b1 * green) / c1;
 
457
            else
 
458
              blue = -(a2 * red + b2 * green) / c2;
 
459
            break;
 
460
 
 
461
          default:
 
462
            break;
 
463
          }
 
464
 
 
465
        /* Convert back to RGB (cross product with transform matrix) */
 
466
        redOld   = red;
 
467
        greenOld = green;
 
468
 
 
469
        red   = redOld * lms2rgb[0] + greenOld * lms2rgb[1] + blue * lms2rgb[2];
 
470
        green = redOld * lms2rgb[3] + greenOld * lms2rgb[4] + blue * lms2rgb[5];
 
471
        blue  = redOld * lms2rgb[6] + greenOld * lms2rgb[7] + blue * lms2rgb[8];
 
472
 
 
473
        /* Apply gamma to go back to non-linear intensities */
 
474
        red   = pow (red,   colorblind->gammaRGB[0]);
 
475
        green = pow (green, colorblind->gammaRGB[1]);
 
476
        blue  = pow (blue,  colorblind->gammaRGB[2]);
 
477
 
 
478
        /* Ensure that we stay within the RGB gamut */
 
479
        /* *** FIX THIS: it would be better to desaturate than blindly clip. */
 
480
        red   = CLAMP (red,   0, 255);
 
481
        green = CLAMP (green, 0, 255);
 
482
        blue  = CLAMP (blue,  0, 255);
 
483
 
 
484
        /* Stuff result back into buffer */
 
485
        b[0] = (guchar) red;
 
486
        b[1] = (guchar) green;
 
487
        b[2] = (guchar) blue;
 
488
 
 
489
        /* Put the result into our cache */
 
490
        colorblind->cache[2 * index]     = pixel;
 
491
        colorblind->cache[2 * index + 1] = b[0] << 16 | b[1] << 8 | b[2];
 
492
      }
 
493
}
 
494
 
 
495
static void
 
496
cdisplay_colorblind_load_state (GimpColorDisplay *display,
 
497
                                GimpParasite     *state)
 
498
{
 
499
  CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (display);
 
500
  const gchar        *str;
 
501
 
 
502
  str = gimp_parasite_data (state);
 
503
 
 
504
  if (str[gimp_parasite_data_size (state) - 1] == '\0')
 
505
    {
 
506
      gint value;
 
507
 
 
508
      if (sscanf (str, "%d", &value) == 1)
 
509
        cdisplay_colorblind_set_deficiency (colorblind, value);
 
510
    }
 
511
}
 
512
 
 
513
static GimpParasite *
 
514
cdisplay_colorblind_save_state (GimpColorDisplay *display)
 
515
{
 
516
  CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (display);
 
517
  gchar               buf[32];
 
518
 
 
519
  g_snprintf (buf, sizeof (buf), "%d", colorblind->deficiency);
 
520
 
 
521
  return gimp_parasite_new ("Display/Colorblind", GIMP_PARASITE_PERSISTENT,
 
522
                            strlen (buf) + 1, buf);
 
523
}
 
524
 
 
525
static GtkWidget *
 
526
cdisplay_colorblind_configure (GimpColorDisplay *display)
 
527
{
 
528
  CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (display);
 
529
  GtkWidget          *label;
 
530
 
 
531
  if (colorblind->hbox)
 
532
    gtk_widget_destroy (colorblind->hbox);
 
533
 
 
534
  colorblind->hbox = gtk_hbox_new (FALSE, 6);
 
535
 
 
536
  g_signal_connect (colorblind->hbox, "destroy",
 
537
                    G_CALLBACK (gtk_widget_destroyed),
 
538
                    &colorblind->hbox);
 
539
 
 
540
  label = gtk_label_new_with_mnemonic (_("Color _Deficiency Type:"));
 
541
  gtk_box_pack_start (GTK_BOX (colorblind->hbox), label, FALSE, FALSE, 0);
 
542
  gtk_widget_show (label);
 
543
 
 
544
  colorblind->combo =
 
545
    gimp_int_combo_box_new (_("Protanopia (insensitivity to red)"),
 
546
                            COLORBLIND_DEFICIENCY_PROTANOPIA,
 
547
                            _("Deuteranopia (insensitivity to green)"),
 
548
                            COLORBLIND_DEFICIENCY_DEUTERANOPIA,
 
549
                            _("Tritanopia (insensitivity to blue)"),
 
550
                            COLORBLIND_DEFICIENCY_TRITANOPIA,
 
551
                            NULL);
 
552
 
 
553
  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (colorblind->combo),
 
554
                                 colorblind->deficiency);
 
555
 
 
556
  g_signal_connect (colorblind->combo, "changed",
 
557
                    G_CALLBACK (colorblind_deficiency_callback),
 
558
                    colorblind);
 
559
 
 
560
  gtk_box_pack_start (GTK_BOX (colorblind->hbox), colorblind->combo,
 
561
                      TRUE, TRUE, 0);
 
562
  gtk_widget_show (colorblind->combo);
 
563
 
 
564
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), colorblind->combo);
 
565
 
 
566
  return colorblind->hbox;
 
567
}
 
568
 
 
569
static void
 
570
cdisplay_colorblind_configure_reset (GimpColorDisplay *display)
 
571
{
 
572
  CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (display);
 
573
 
 
574
  if (colorblind->combo)
 
575
    {
 
576
      gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (colorblind->combo),
 
577
                                     DEFAULT_DEFICIENCY);
 
578
      colorblind->deficiency = DEFAULT_DEFICIENCY;
 
579
 
 
580
      gimp_color_display_changed (GIMP_COLOR_DISPLAY (colorblind));
 
581
    }
 
582
}
 
583
 
 
584
static void
 
585
cdisplay_colorblind_changed (GimpColorDisplay *display)
 
586
{
 
587
  CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (display);
 
588
  gfloat              anchor_e[3];
 
589
  gfloat              anchor[12];
 
590
 
 
591
  /*  This function performs initialisations that are dependant
 
592
   *  on the type of color deficiency.
 
593
   */
 
594
 
 
595
  /* Performs protan, deutan or tritan color image simulation based on
 
596
   * Brettel, Vienot and Mollon JOSA 14/10 1997
 
597
   *  L,M,S for lambda=475,485,575,660
 
598
   *
 
599
   * Load the LMS anchor-point values for lambda = 475 & 485 nm (for
 
600
   * protans & deutans) and the LMS values for lambda = 575 & 660 nm
 
601
   * (for tritans)
 
602
   */
 
603
  anchor[0] = 0.08008;  anchor[1]  = 0.1579;    anchor[2]  = 0.5897;
 
604
  anchor[3] = 0.1284;   anchor[4]  = 0.2237;    anchor[5]  = 0.3636;
 
605
  anchor[6] = 0.9856;   anchor[7]  = 0.7325;    anchor[8]  = 0.001079;
 
606
  anchor[9] = 0.0914;   anchor[10] = 0.007009;  anchor[11] = 0.0;
 
607
 
 
608
  /* We also need LMS for RGB=(1,1,1)- the equal-energy point (one of
 
609
   * our anchors) (we can just peel this out of the rgb2lms transform
 
610
   * matrix)
 
611
   */
 
612
  anchor_e[0] =
 
613
    colorblind->rgb2lms[0] + colorblind->rgb2lms[1] + colorblind->rgb2lms[2];
 
614
  anchor_e[1] =
 
615
    colorblind->rgb2lms[3] + colorblind->rgb2lms[4] + colorblind->rgb2lms[5];
 
616
  anchor_e[2] =
 
617
    colorblind->rgb2lms[6] + colorblind->rgb2lms[7] + colorblind->rgb2lms[8];
 
618
 
 
619
  switch (colorblind->deficiency)
 
620
    {
 
621
    case COLORBLIND_DEFICIENCY_DEUTERANOPIA:
 
622
      /* find a,b,c for lam=575nm and lam=475 */
 
623
      colorblind->a1 = anchor_e[1] * anchor[8] - anchor_e[2] * anchor[7];
 
624
      colorblind->b1 = anchor_e[2] * anchor[6] - anchor_e[0] * anchor[8];
 
625
      colorblind->c1 = anchor_e[0] * anchor[7] - anchor_e[1] * anchor[6];
 
626
      colorblind->a2 = anchor_e[1] * anchor[2] - anchor_e[2] * anchor[1];
 
627
      colorblind->b2 = anchor_e[2] * anchor[0] - anchor_e[0] * anchor[2];
 
628
      colorblind->c2 = anchor_e[0] * anchor[1] - anchor_e[1] * anchor[0];
 
629
      colorblind->inflection = (anchor_e[2] / anchor_e[0]);
 
630
      break;
 
631
 
 
632
    case COLORBLIND_DEFICIENCY_PROTANOPIA:
 
633
      /* find a,b,c for lam=575nm and lam=475 */
 
634
      colorblind->a1 = anchor_e[1] * anchor[8] - anchor_e[2] * anchor[7];
 
635
      colorblind->b1 = anchor_e[2] * anchor[6] - anchor_e[0] * anchor[8];
 
636
      colorblind->c1 = anchor_e[0] * anchor[7] - anchor_e[1] * anchor[6];
 
637
      colorblind->a2 = anchor_e[1] * anchor[2] - anchor_e[2] * anchor[1];
 
638
      colorblind->b2 = anchor_e[2] * anchor[0] - anchor_e[0] * anchor[2];
 
639
      colorblind->c2 = anchor_e[0] * anchor[1] - anchor_e[1] * anchor[0];
 
640
      colorblind->inflection = (anchor_e[2] / anchor_e[1]);
 
641
      break;
 
642
 
 
643
    case COLORBLIND_DEFICIENCY_TRITANOPIA:
 
644
      /* Set 1: regions where lambda_a=575, set 2: lambda_a=475 */
 
645
      colorblind->a1 = anchor_e[1] * anchor[11] - anchor_e[2] * anchor[10];
 
646
      colorblind->b1 = anchor_e[2] * anchor[9]  - anchor_e[0] * anchor[11];
 
647
      colorblind->c1 = anchor_e[0] * anchor[10] - anchor_e[1] * anchor[9];
 
648
      colorblind->a2 = anchor_e[1] * anchor[5]  - anchor_e[2] * anchor[4];
 
649
      colorblind->b2 = anchor_e[2] * anchor[3]  - anchor_e[0] * anchor[5];
 
650
      colorblind->c2 = anchor_e[0] * anchor[4]  - anchor_e[1] * anchor[3];
 
651
      colorblind->inflection = (anchor_e[1] / anchor_e[0]);
 
652
      break;
 
653
    }
 
654
 
 
655
  /* Invalidate the cache */
 
656
  memset (colorblind->cache, 0, sizeof (colorblind->cache));
 
657
}
 
658
 
 
659
static void
 
660
cdisplay_colorblind_set_deficiency (CdisplayColorblind   *colorblind,
 
661
                                    ColorblindDeficiency  value)
 
662
{
 
663
  if (value != colorblind->deficiency)
 
664
    {
 
665
      GEnumClass *enum_class;
 
666
 
 
667
      enum_class = g_type_class_peek (CDISPLAY_TYPE_COLORBLIND_DEFICIENCY);
 
668
 
 
669
      if (! g_enum_get_value (enum_class, value))
 
670
        return;
 
671
 
 
672
      colorblind->deficiency = value;
 
673
 
 
674
      g_object_notify (G_OBJECT (colorblind), "deficiency");
 
675
      gimp_color_display_changed (GIMP_COLOR_DISPLAY (colorblind));
 
676
    }
 
677
}
 
678
 
 
679
static void
 
680
colorblind_deficiency_callback (GtkWidget          *widget,
 
681
                                CdisplayColorblind *colorblind)
 
682
{
 
683
  gint  value;
 
684
 
 
685
  gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value);
 
686
 
 
687
  cdisplay_colorblind_set_deficiency (colorblind, value);
 
688
}