gamma.c 10.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
    This file is part of darktable,
    copyright (c) 2009--2010 johannes hanika.

    darktable 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.

    darktable 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 darktable.  If not, see <http://www.gnu.org/licenses/>.
*/
jhanika's avatar
jhanika committed
18
#ifdef HAVE_CONFIG_H
19
#include "config.h"
jhanika's avatar
jhanika committed
20
#endif
jhanika's avatar
jhanika committed
21
#include <assert.h>
22 23
#include <math.h>
#include <stdlib.h>
jhanika's avatar
jhanika committed
24
#include <string.h>
25

26
#include "common/colorspaces_inline_conversions.h"
jhanika's avatar
jhanika committed
27
#include "control/control.h"
28
#include "develop/develop.h"
29
#include "gui/accelerators.h"
jhanika's avatar
jhanika committed
30
#include "gui/gtk.h"
31
#include "iop/iop_api.h"
jhanika's avatar
jhanika committed
32

33
DT_MODULE_INTROSPECTION(1, dt_iop_gamma_params_t)
34

35 36 37 38

typedef struct dt_iop_gamma_params_t
{
  float gamma, linear;
39
} dt_iop_gamma_params_t;
40 41 42 43 44


typedef struct dt_iop_gamma_data_t
{
  uint8_t table[0x10000];
45
} dt_iop_gamma_data_t;
46 47


johannes hanika's avatar
johannes hanika committed
48 49
const char *name()
{
50
  return C_("modulename", "gamma");
johannes hanika's avatar
johannes hanika committed
51 52
}

53
int groups()
54
{
55
  return IOP_GROUP_COLOR;
56 57
}

58 59
int flags()
{
60
  return IOP_FLAGS_HIDDEN | IOP_FLAGS_ONE_INSTANCE;
61 62
}

63 64 65 66 67 68 69
static inline float Hue_2_RGB(float v1, float v2, float vH)
{
  if(vH < 0.0f) vH += 1.0f;
  if(vH > 1.0f) vH -= 1.0f;
  if((6.0f * vH) < 1.0f) return (v1 + (v2 - v1) * 6.0f * vH);
  if((2.0f * vH) < 1.0f) return (v2);
  if((3.0f * vH) < 2.0f) return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
70
  return v1;
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
}

static inline void HSL_2_RGB(const float *HSL, float *RGB)
{
  float H = HSL[0];
  float S = HSL[1];
  float L = HSL[2];

  float var_1, var_2;

  if(S < 1e-6f)
  {
    RGB[0] = RGB[1] = RGB[2] = L;
  }
  else
  {
    if(L < 0.5f)
      var_2 = L * (1.0f + S);
    else
      var_2 = (L + S) - (S * L);

    var_1 = 2.0f * L - var_2;

    RGB[0] = Hue_2_RGB(var_1, var_2, H + (1.0f / 3.0f));
    RGB[1] = Hue_2_RGB(var_1, var_2, H);
    RGB[2] = Hue_2_RGB(var_1, var_2, H - (1.0f / 3.0f));
  }
}

static inline void LCH_2_Lab(const float *LCH, float *Lab)
{
  Lab[0] = LCH[0];
  Lab[1] = cosf(2.0f * M_PI * LCH[2]) * LCH[1];
  Lab[2] = sinf(2.0f * M_PI * LCH[2]) * LCH[1];
}

static inline void LCH_2_RGB(const float *LCH, float *RGB)
{
  float Lab[3], XYZ[3];
  LCH_2_Lab(LCH, Lab);
  dt_Lab_to_XYZ(Lab, XYZ);
112
  dt_XYZ_to_sRGB_clipped(XYZ, RGB);
113 114 115 116 117 118
}

static inline void Lab_2_RGB(const float *Lab, float *RGB)
{
  float XYZ[3];
  dt_Lab_to_XYZ(Lab, XYZ);
119
  dt_XYZ_to_sRGB_clipped(XYZ, RGB);
120 121 122 123 124 125 126 127
}

static inline void false_color(float val, dt_dev_pixelpipe_display_mask_t channel, float *out)
{
  float in[3];

  switch((channel & DT_DEV_PIXELPIPE_DISPLAY_ANY) & ~DT_DEV_PIXELPIPE_DISPLAY_OUTPUT)
  {
128 129 130 131 132 133
    case DT_DEV_PIXELPIPE_DISPLAY_L:
      in[0] = val * 100.0f;
      in[1] = 0.0f;
      in[2] = 0.0f;
      Lab_2_RGB(in, out);
      break;
134
    case DT_DEV_PIXELPIPE_DISPLAY_a:
135
      in[0] = 80.0f;
136 137 138 139 140
      in[1] = val * 256.0f - 128.0f;
      in[2] = 0.0f;
      Lab_2_RGB(in, out);
      break;
    case DT_DEV_PIXELPIPE_DISPLAY_b:
141
      in[0] = 80.0f;
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
      in[1] = 0.0f;
      in[2] = val * 256.0f - 128.0f;
      Lab_2_RGB(in, out);
      break;
    case DT_DEV_PIXELPIPE_DISPLAY_R:
      out[0] = val;
      out[1] = out[2] = 0.0f;
      break;
    case DT_DEV_PIXELPIPE_DISPLAY_G:
      out[1] = val;
      out[0] = out[2] = 0.0f;
      break;
    case DT_DEV_PIXELPIPE_DISPLAY_B:
      out[2] = val;
      out[0] = out[1] = 0.0f;
      break;
    case DT_DEV_PIXELPIPE_DISPLAY_LCH_C:
159
      in[0] = 80.0f;
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
      in[1] = val * 128.0f * sqrtf(2.0f);
      in[2] = 0.9111f;
      LCH_2_RGB(in, out);
      break;
    case DT_DEV_PIXELPIPE_DISPLAY_LCH_h:
      in[0] = 50.0f;
      in[1] = 0.25f * 128.0f * sqrtf(2.0f);
      in[2] = val;
      LCH_2_RGB(in, out);
      break;
    case DT_DEV_PIXELPIPE_DISPLAY_HSL_H:
      in[0] = val;
      in[1] = 1.0f;
      in[2] = 0.5f;
      HSL_2_RGB(in, out);
      break;
    case DT_DEV_PIXELPIPE_DISPLAY_HSL_S:
      in[0] = 0.8333f;
      in[1] = val;
      in[2] = 0.5f;
      HSL_2_RGB(in, out);
      break;
    case DT_DEV_PIXELPIPE_DISPLAY_HSL_l:
183 184 185 186 187 188
      in[0] = 0.0f;
      in[1] = 0.0f;
      in[2] = val;
      HSL_2_RGB(in, out);
      break;
    case DT_DEV_PIXELPIPE_DISPLAY_GRAY:
189 190 191 192 193 194
    default:
      out[0] = out[1] = out[2] = val;
      break;
  }
}

195 196
void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o,
             const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
197 198
{
  dt_iop_gamma_data_t *d = (dt_iop_gamma_data_t *)piece->data;
199
  const int ch = piece->colors;
200

201
  const dt_dev_pixelpipe_display_mask_t mask_display = piece->pipe->mask_display;
202 203 204
  char *str = dt_conf_get_string("channel_display");
  const int fcolor = !strcmp(str, "false color");
  g_free(str);
205

206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
  if((mask_display & DT_DEV_PIXELPIPE_DISPLAY_CHANNEL) && (mask_display & DT_DEV_PIXELPIPE_DISPLAY_ANY) && fcolor)
  {
    const float yellow[3] = { 1.0f, 1.0f, 0.0f };
#ifdef _OPENMP
#pragma omp parallel for default(none) shared(d) schedule(static)
#endif
    for(int k = 0; k < roi_out->height; k++)
    {
      const float *in = ((float *)i) + (size_t)ch * k * roi_out->width;
      uint8_t *out = ((uint8_t *)o) + (size_t)ch * k * roi_out->width;
      for(int j = 0; j < roi_out->width; j++, in += ch, out += ch)
      {
        const float alpha = (mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) ? in[3] : 0.0f;
        float colors[3];
        false_color(in[1], mask_display, colors);
        for(int c = 0; c < 3; c++)
        {
          const float value = colors[c] * (1.0f - alpha) + yellow[c] * alpha;
224
          out[2 - c] = d->table[(uint16_t)CLAMP((int)(0x10000ul * value), 0, 0xffff)];
225 226 227 228 229
        }
      }
    }
  }
  else if((mask_display & DT_DEV_PIXELPIPE_DISPLAY_CHANNEL) && (mask_display & DT_DEV_PIXELPIPE_DISPLAY_ANY))
230 231 232 233 234 235 236 237 238 239 240 241 242 243
  {
    const float yellow[3] = { 1.0f, 1.0f, 0.0f };
#ifdef _OPENMP
#pragma omp parallel for default(none) shared(d) schedule(static)
#endif
    for(int k = 0; k < roi_out->height; k++)
    {
      const float *in = ((float *)i) + (size_t)ch * k * roi_out->width;
      uint8_t *out = ((uint8_t *)o) + (size_t)ch * k * roi_out->width;
      for(int j = 0; j < roi_out->width; j++, in += ch, out += ch)
      {
        float alpha = (mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) ? in[3] : 0.0f;
        for(int c = 0; c < 3; c++)
        {
244
          const float value = in[1] * (1.0f - alpha) + yellow[c] * alpha;
245
          out[2 - c] = d->table[(uint16_t)CLAMP((int)(0x10000ul * value), 0, 0xffff)];
246 247 248 249 250
        }
      }
    }
  }
  else if(mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK)
251 252
  {
    const float yellow[3] = { 1.0f, 1.0f, 0.0f };
253
#ifdef _OPENMP
254
#pragma omp parallel for default(none) shared(d) schedule(static)
255
#endif
256
    for(int k = 0; k < roi_out->height; k++)
257
    {
258 259 260
      const float *in = ((float *)i) + (size_t)ch * k * roi_out->width;
      uint8_t *out = ((uint8_t *)o) + (size_t)ch * k * roi_out->width;
      for(int j = 0; j < roi_out->width; j++, in += ch, out += ch)
261
      {
262 263
        const float gray = 0.3f * in[0] + 0.59f * in[1] + 0.11f * in[2];
        const float alpha = in[3];
264
        for(int c = 0; c < 3; c++)
265
        {
266
          const float value = gray * (1.0f - alpha) + yellow[c] * alpha;
267
          out[2 - c] = d->table[(uint16_t)CLAMP((int)(0x10000ul * value), 0, 0xffff)];
268 269 270 271 272
        }
      }
    }
  }
  else
273
  {
274
#ifdef _OPENMP
275
#pragma omp parallel for default(none) shared(d) schedule(static)
276
#endif
277
    for(int k = 0; k < roi_out->height; k++)
Bruce Guenter's avatar
Bruce Guenter committed
278
    {
279 280 281
      const float *in = ((float *)i) + (size_t)ch * k * roi_out->width;
      uint8_t *out = ((uint8_t *)o) + (size_t)ch * k * roi_out->width;
      for(int j = 0; j < roi_out->width; j++, in += ch, out += ch)
282
      {
283
        for(int c = 0; c < 3; c++) out[2 - c] = d->table[(uint16_t)CLAMP((int)(0x10000ul * in[c]), 0, 0xffff)];
284
      }
Bruce Guenter's avatar
Bruce Guenter committed
285
    }
286 287 288
  }
}

289 290
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe,
                   dt_dev_pixelpipe_iop_t *piece)
jhanika's avatar
jhanika committed
291 292
{
  dt_iop_gamma_params_t *p = (dt_iop_gamma_params_t *)p1;
293
  dt_iop_gamma_data_t *d = (dt_iop_gamma_data_t *)piece->data;
294 295

  // build gamma table in pipeline piece from committed params:
296
  float a, b, c, g;
297
  if(p->linear < 1.0)
298
  {
299 300 301 302
    g = p->gamma * (1.0 - p->linear) / (1.0 - p->gamma * p->linear);
    a = 1.0 / (1.0 + p->linear * (g - 1));
    b = p->linear * (g - 1) * a;
    c = powf(a * p->linear + b, g) / p->linear;
303 304 305 306 307 308
  }
  else
  {
    a = b = g = 0.0;
    c = 1.0;
  }
309
  for(int k = 0; k < 0x10000; k++)
310 311
  {
    int32_t tmp;
312 313
    if(k < 0x10000 * p->linear)
      tmp = MIN(c * k, 0xFFFF);
314 315
    else
    {
316
      const float _t = powf(a * k / 0x10000 + b, g) * 0x10000;
317 318
      tmp = MIN(_t, 0xFFFF);
    }
319
    d->table[k] = tmp >> 8;
320
  }
jhanika's avatar
jhanika committed
321 322
}

323
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
jhanika's avatar
jhanika committed
324
{
325 326
  piece->data = malloc(sizeof(dt_iop_gamma_data_t));
  self->commit_params(self, self->default_params, pipe, piece);
jhanika's avatar
jhanika committed
327 328
}

329
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
jhanika's avatar
jhanika committed
330
{
331
  free(piece->data);
332
  piece->data = NULL;
jhanika's avatar
jhanika committed
333 334 335 336 337
}

void init(dt_iop_module_t *module)
{
  // module->data = malloc(sizeof(dt_iop_gamma_data_t));
338 339
  module->params = calloc(1, sizeof(dt_iop_gamma_params_t));
  module->default_params = calloc(1, sizeof(dt_iop_gamma_params_t));
jhanika's avatar
jhanika committed
340 341
  module->params_size = sizeof(dt_iop_gamma_params_t);
  module->gui_data = NULL;
342
  module->priority = 1000; // module order created by iop_dependencies.py, do not edit!
343
  module->hide_enable_button = 1;
344
  module->default_enabled = 1;
345
  dt_iop_gamma_params_t tmp = (dt_iop_gamma_params_t){ 1.0, 1.0 };
346 347
  memcpy(module->params, &tmp, sizeof(dt_iop_gamma_params_t));
  memcpy(module->default_params, &tmp, sizeof(dt_iop_gamma_params_t));
jhanika's avatar
jhanika committed
348 349 350 351 352 353 354 355 356
}

void cleanup(dt_iop_module_t *module)
{
  free(module->params);
  module->params = NULL;
}


Richard Wonka's avatar
Richard Wonka committed
357
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
358
// vim: shiftwidth=2 expandtab tabstop=2 cindent
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
359
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;