Example #1
0
void
gimp_levels_config_stretch (GimpLevelsConfig *config,
                            GimpHistogram    *histogram,
                            gboolean          is_color)
{
  g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config));
  g_return_if_fail (histogram != NULL);

  g_object_freeze_notify (G_OBJECT (config));

  if (is_color)
    {
      GimpHistogramChannel channel;

      /*  Set the overall value to defaults  */
      channel = config->channel;
      config->channel = GIMP_HISTOGRAM_VALUE;
      gimp_levels_config_reset_channel (config);
      config->channel = channel;

      for (channel = GIMP_HISTOGRAM_RED;
           channel <= GIMP_HISTOGRAM_BLUE;
           channel++)
        {
          gimp_levels_config_stretch_channel (config, histogram, channel);
        }
    }
  else
    {
      gimp_levels_config_stretch_channel (config, histogram,
                                          GIMP_HISTOGRAM_VALUE);
    }

  g_object_thaw_notify (G_OBJECT (config));
}
Example #2
0
void
gimp_levels_config_to_cruft (GimpLevelsConfig *config,
                             Levels           *cruft,
                             gboolean          is_color)
{
  GimpHistogramChannel channel;

  g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config));
  g_return_if_fail (cruft != NULL);

  for (channel = GIMP_HISTOGRAM_VALUE;
       channel <= GIMP_HISTOGRAM_ALPHA;
       channel++)
    {
      cruft->gamma[channel]       = config->gamma[channel];
      cruft->low_input[channel]   = config->low_input[channel]   * 255.999;
      cruft->high_input[channel]  = config->high_input[channel]  * 255.999;
      cruft->low_output[channel]  = config->low_output[channel]  * 255.999;
      cruft->high_output[channel] = config->high_output[channel] * 255.999;
    }

  if (! is_color)
    {
      cruft->gamma[1]       = cruft->gamma[GIMP_HISTOGRAM_ALPHA];
      cruft->low_input[1]   = cruft->low_input[GIMP_HISTOGRAM_ALPHA];
      cruft->high_input[1]  = cruft->high_input[GIMP_HISTOGRAM_ALPHA];
      cruft->low_output[1]  = cruft->low_output[GIMP_HISTOGRAM_ALPHA];
      cruft->high_output[1] = cruft->high_output[GIMP_HISTOGRAM_ALPHA];
    }
}
Example #3
0
gboolean
gimp_levels_config_save_cruft (GimpLevelsConfig  *config,
                               gpointer           fp,
                               GError           **error)
{
  FILE *file = fp;
  gint  i;

  g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), FALSE);
  g_return_val_if_fail (file != NULL, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  fprintf (file, "# GIMP Levels File\n");

  for (i = 0; i < 5; i++)
    {
      gchar buf[G_ASCII_DTOSTR_BUF_SIZE];

      fprintf (file, "%d %d %d %d %s\n",
               (gint) (config->low_input[i]   * 255.999),
               (gint) (config->high_input[i]  * 255.999),
               (gint) (config->low_output[i]  * 255.999),
               (gint) (config->high_output[i] * 255.999),
               g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
                                config->gamma[i]));
    }

  return TRUE;
}
Example #4
0
void
gimp_levels_config_reset_channel (GimpLevelsConfig *config)
{
  g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config));

  g_object_freeze_notify (G_OBJECT (config));

  gimp_config_reset_property (G_OBJECT (config), "gamma");
  gimp_config_reset_property (G_OBJECT (config), "low-input");
  gimp_config_reset_property (G_OBJECT (config), "high-input");
  gimp_config_reset_property (G_OBJECT (config), "low-output");
  gimp_config_reset_property (G_OBJECT (config), "high-output");

  g_object_thaw_notify (G_OBJECT (config));
}
Example #5
0
gboolean
gimp_levels_config_save_cruft (GimpLevelsConfig  *config,
                               GOutputStream     *output,
                               GError           **error)
{
  GString *string;
  gsize    bytes_written;
  gint     i;

  g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), FALSE);
  g_return_val_if_fail (G_IS_OUTPUT_STREAM (output), FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  string = g_string_new ("# GIMP Levels File\n");

  for (i = 0; i < 5; i++)
    {
      gchar buf[G_ASCII_DTOSTR_BUF_SIZE];

      g_string_append_printf (string,
                              "%d %d %d %d %s\n",
                              (gint) (config->low_input[i]   * 255.999),
                              (gint) (config->high_input[i]  * 255.999),
                              (gint) (config->low_output[i]  * 255.999),
                              (gint) (config->high_output[i] * 255.999),
                              g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
                                               config->gamma[i]));
    }

  if (! g_output_stream_write_all (output, string->str, string->len,
                                   &bytes_written, NULL, error) ||
      bytes_written != string->len)
    {
      g_prefix_error (error, _("Writing levels file failed: "));
      g_string_free (string, TRUE);
      return FALSE;
    }

  g_string_free (string, TRUE);

  return TRUE;
}
Example #6
0
gboolean
gimp_levels_config_load_cruft (GimpLevelsConfig  *config,
                               GInputStream      *input,
                               GError           **error)
{
  GDataInputStream *data_input;
  gint              low_input[5];
  gint              high_input[5];
  gint              low_output[5];
  gint              high_output[5];
  gdouble           gamma[5];
  gchar            *line;
  gsize             line_len;
  gint              i;

  g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), FALSE);
  g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  data_input = g_data_input_stream_new (input);

  line_len = 64;
  line = g_data_input_stream_read_line (data_input, &line_len,
                                        NULL, error);
  if (! line)
    return FALSE;

  if (strcmp (line, "# GIMP Levels File") != 0)
    {
      g_set_error_literal (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
			   _("not a GIMP Levels file"));
      g_object_unref (data_input);
      g_free (line);
      return FALSE;
    }

  g_free (line);

  for (i = 0; i < 5; i++)
    {
      gchar  float_buf[32];
      gchar *endp;
      gint   fields;

      line_len = 64;
      line = g_data_input_stream_read_line (data_input, &line_len,
                                            NULL, error);
      if (! line)
        {
          g_object_unref (data_input);
          return FALSE;
        }

      fields = sscanf (line, "%d %d %d %d %31s",
                       &low_input[i],
                       &high_input[i],
                       &low_output[i],
                       &high_output[i],
                       float_buf);

      g_free (line);

      if (fields != 5)
        goto error;

      gamma[i] = g_ascii_strtod (float_buf, &endp);

      if (endp == float_buf || errno == ERANGE)
        goto error;
    }

  g_object_unref (data_input);

  g_object_freeze_notify (G_OBJECT (config));

  for (i = 0; i < 5; i++)
    {
      config->low_input[i]   = low_input[i]   / 255.0;
      config->high_input[i]  = high_input[i]  / 255.0;
      config->low_output[i]  = low_output[i]  / 255.0;
      config->high_output[i] = high_output[i] / 255.0;
      config->gamma[i]       = gamma[i];
    }

  g_object_notify (G_OBJECT (config), "gamma");
  g_object_notify (G_OBJECT (config), "low-input");
  g_object_notify (G_OBJECT (config), "high-input");
  g_object_notify (G_OBJECT (config), "low-output");
  g_object_notify (G_OBJECT (config), "high-output");

  g_object_thaw_notify (G_OBJECT (config));

  return TRUE;

 error:
  g_object_unref (data_input);

  g_set_error_literal (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
		       _("parse error"));
  return FALSE;
}
Example #7
0
GimpCurvesConfig *
gimp_levels_config_to_curves_config (GimpLevelsConfig *config)
{
  GimpCurvesConfig     *curves;
  GimpHistogramChannel  channel;

  g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), NULL);

  curves = g_object_new (GIMP_TYPE_CURVES_CONFIG, NULL);

  for (channel = GIMP_HISTOGRAM_VALUE;
       channel <= GIMP_HISTOGRAM_ALPHA;
       channel++)
    {
      GimpCurve  *curve    = curves->curve[channel];
      const gint  n_points = gimp_curve_get_n_points (curve);
      static const gint n  = 4;
      gint        point    = -1;
      gdouble     gamma    = config->gamma[channel];
      gdouble     delta_in;
      gdouble     delta_out;
      gdouble     x, y;

      /* clear the points set by default */
      gimp_curve_set_point (curve, 0, -1, -1);
      gimp_curve_set_point (curve, n_points - 1, -1, -1);

      delta_in  = config->high_input[channel] - config->low_input[channel];
      delta_out = config->high_output[channel] - config->low_output[channel];

      x = config->low_input[channel];
      y = config->low_output[channel];

      point = CLAMP (n_points * x, point + 1, n_points - 1 - n);
      gimp_curve_set_point (curve, point, x, y);

      if (delta_out != 0 && gamma != 1.0)
        {
          /* The Levels tool performs gamma correction, which is a
           * power law, while the Curves tool uses cubic Bézier
           * curves. Here we try to approximate this gamma correction
           * with a Bézier curve with 5 control points. Two of them
           * must be (low_input, low_output) and (high_input,
           * high_output), so we need to add 3 more control points in
           * the middle.
           */
          gint i;

          if (gamma > 1)
            {
              /* Case no. 1: γ > 1
               *
               * The curve should look like a horizontal
               * parabola. Since its curvature is greatest when x is
               * small, we add more control points there, so the
               * approximation is more accurate. I decided to set the
               * length of the consecutive segments to x₀, γ⋅x₀, γ²⋅x₀
               * and γ³⋅x₀ and I saw that the curves looked
               * good. Still, this is completely arbitrary.
               */
              gdouble dx = 0;
              gdouble x0;

              for (i = 0; i < n; ++i)
                dx = dx * gamma + 1;
              x0 = delta_in / dx;

              dx = 0;
              for (i = 1; i < n; ++i)
                {
                  dx = dx * gamma + x0;
                  x = config->low_input[channel] + dx;
                  y = config->low_output[channel] + delta_out *
                      gimp_operation_levels_map_input (config, channel, x);
                  point = CLAMP (n_points * x, point + 1, n_points - 1 - n + i);
                  gimp_curve_set_point (curve, point, x, y);
                }
            }
          else
            {
              /* Case no. 2: γ < 1
               *
               * The curve is the same as the one in case no. 1,
               * observed through a reflexion along the y = x axis. So
               * if we invert γ and swap the x and y axes we can use
               * the same method as in case no. 1.
               */
              GimpLevelsConfig *config_inv;
              gdouble           dy = 0;
              gdouble           y0;
              const gdouble     gamma_inv = 1 / gamma;

              config_inv = gimp_config_duplicate (GIMP_CONFIG (config));

              config_inv->gamma[channel]       = gamma_inv;
              config_inv->low_input[channel]   = config->low_output[channel];
              config_inv->low_output[channel]  = config->low_input[channel];
              config_inv->high_input[channel]  = config->high_output[channel];
              config_inv->high_output[channel] = config->high_input[channel];

              for (i = 0; i < n; ++i)
                dy = dy * gamma_inv + 1;
              y0 = delta_out / dy;

              dy = 0;
              for (i = 1; i < n; ++i)
                {
                  dy = dy * gamma_inv + y0;
                  y = config->low_output[channel] + dy;
                  x = config->low_input[channel] + delta_in *
                      gimp_operation_levels_map_input (config_inv, channel, y);
                  point = CLAMP (n_points * x, point + 1, n_points - 1 - n + i);
                  gimp_curve_set_point (curve, point, x, y);
                }

              g_object_unref (config_inv);
            }
        }

      x = config->high_input[channel];
      y = config->high_output[channel];

      point = CLAMP (n_points * x, point + 1, n_points - 1);
      gimp_curve_set_point (curve, point, x, y);
    }

  return curves;
}
Example #8
0
void
gimp_levels_config_adjust_by_colors (GimpLevelsConfig     *config,
                                     GimpHistogramChannel  channel,
                                     const GimpRGB        *black,
                                     const GimpRGB        *gray,
                                     const GimpRGB        *white)
{
  g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config));

  g_object_freeze_notify (G_OBJECT (config));

  if (black)
    {
      config->low_input[channel] = gimp_levels_config_input_from_color (channel,
                                                                        black);
      g_object_notify (G_OBJECT (config), "low-input");
    }


  if (white)
    {
      config->high_input[channel] = gimp_levels_config_input_from_color (channel,
                                                                         white);
      g_object_notify (G_OBJECT (config), "high-input");
    }

  if (gray)
    {
      gdouble input;
      gdouble range;
      gdouble inten;
      gdouble out_light;
      gdouble lightness;

      /* Calculate lightness value */
      lightness = GIMP_RGB_LUMINANCE (gray->r, gray->g, gray->b);

      input = gimp_levels_config_input_from_color (channel, gray);

      range = config->high_input[channel] - config->low_input[channel];
      if (range <= 0)
        goto out;

      input -= config->low_input[channel];
      if (input < 0)
        goto out;

      /* Normalize input and lightness */
      inten = input / range;
      out_light = lightness / range;

      /* See bug 622054: picking pure black or white as gamma doesn't
       * work. But we cannot compare to 0.0 or 1.0 because cpus and
       * compilers are shit. If you try to check out_light using
       * printf() it will give exact 0.0 or 1.0 anyway, probably
       * because the generated code is different and out_light doesn't
       * live in a register. That must be why the cpu/compiler mafia
       * invented epsilon and defined this shit to be the programmer's
       * responsibility.
       */
      if (out_light <= 0.0001 || out_light >= 0.9999)
        goto out;

      /* Map selected color to corresponding lightness */
      config->gamma[channel] = log (inten) / log (out_light);
      config->gamma[channel] = CLAMP (config->gamma[channel], 0.1, 10.0);
      g_object_notify (G_OBJECT (config), "gamma");
    }

 out:
  g_object_thaw_notify (G_OBJECT (config));
}
Example #9
0
void
gimp_levels_config_stretch_channel (GimpLevelsConfig     *config,
                                    GimpHistogram        *histogram,
                                    GimpHistogramChannel  channel)
{
  gdouble count;
  gdouble bias = 0.006;
  gint    n_bins;
  gint    i;

  g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config));
  g_return_if_fail (histogram != NULL);

  g_object_freeze_notify (G_OBJECT (config));

  config->gamma[channel]       = 1.0;
  config->low_output[channel]  = 0.0;
  config->high_output[channel] = 1.0;

  n_bins = gimp_histogram_n_bins (histogram);
  count  = gimp_histogram_get_count (histogram, channel, 0, n_bins - 1);

  if (count == 0.0)
    {
      config->low_input[channel]  = 0.0;
      config->high_input[channel] = 0.0;
    }
  else
    {
      gdouble new_count;
      gdouble percentage;
      gdouble next_percentage;

      /*  Set the low input  */
      new_count = 0.0;

      for (i = 0; i < (n_bins - 1); i++)
        {
          new_count += gimp_histogram_get_value (histogram, channel, i);
          percentage = new_count / count;
          next_percentage = (new_count +
                             gimp_histogram_get_value (histogram,
                                                       channel,
                                                       i + 1)) / count;

          if (fabs (percentage - bias) < fabs (next_percentage - bias))
            {
              config->low_input[channel] = (gdouble) (i + 1) / (n_bins - 1);
              break;
            }
        }

      /*  Set the high input  */
      new_count = 0.0;

      for (i = (n_bins - 1); i > 0; i--)
        {
          new_count += gimp_histogram_get_value (histogram, channel, i);
          percentage = new_count / count;
          next_percentage = (new_count +
                             gimp_histogram_get_value (histogram,
                                                       channel,
                                                       i - 1)) / count;

          if (fabs (percentage - bias) < fabs (next_percentage - bias))
            {
              config->high_input[channel] = (gdouble) (i - 1) / (n_bins - 1);
              break;
            }
        }
    }

  g_object_notify (G_OBJECT (config), "gamma");
  g_object_notify (G_OBJECT (config), "low-input");
  g_object_notify (G_OBJECT (config), "high-input");
  g_object_notify (G_OBJECT (config), "low-output");
  g_object_notify (G_OBJECT (config), "high-output");

  g_object_thaw_notify (G_OBJECT (config));
}
Example #10
0
gboolean
gimp_levels_config_load_cruft (GimpLevelsConfig  *config,
                               gpointer           fp,
                               GError           **error)
{
  FILE    *file = fp;
  gint     low_input[5];
  gint     high_input[5];
  gint     low_output[5];
  gint     high_output[5];
  gdouble  gamma[5];
  gint     i;
  gint     fields;
  gchar    buf[50];
  gchar   *nptr;

  g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), FALSE);
  g_return_val_if_fail (file != NULL, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  if (! fgets (buf, sizeof (buf), file) ||
      strcmp (buf, "# GIMP Levels File\n") != 0)
    {
      g_set_error_literal (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
			   _("not a GIMP Levels file"));
      return FALSE;
    }

  for (i = 0; i < 5; i++)
    {
      fields = fscanf (file, "%d %d %d %d ",
                       &low_input[i],
                       &high_input[i],
                       &low_output[i],
                       &high_output[i]);

      if (fields != 4)
        goto error;

      if (! fgets (buf, 50, file))
        goto error;

      gamma[i] = g_ascii_strtod (buf, &nptr);

      if (buf == nptr || errno == ERANGE)
        goto error;
    }

  g_object_freeze_notify (G_OBJECT (config));

  for (i = 0; i < 5; i++)
    {
      config->low_input[i]   = low_input[i]   / 255.0;
      config->high_input[i]  = high_input[i]  / 255.0;
      config->low_output[i]  = low_output[i]  / 255.0;
      config->high_output[i] = high_output[i] / 255.0;
      config->gamma[i]       = gamma[i];
    }

  g_object_notify (G_OBJECT (config), "gamma");
  g_object_notify (G_OBJECT (config), "low-input");
  g_object_notify (G_OBJECT (config), "high-input");
  g_object_notify (G_OBJECT (config), "low-output");
  g_object_notify (G_OBJECT (config), "high-output");

  g_object_thaw_notify (G_OBJECT (config));

  return TRUE;

 error:
  g_set_error_literal (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
		       _("parse error"));
  return FALSE;
}
Example #11
0
void
gimp_levels_config_adjust_by_colors (GimpLevelsConfig     *config,
                                     GimpHistogramChannel  channel,
                                     const GimpRGB        *black,
                                     const GimpRGB        *gray,
                                     const GimpRGB        *white)
{
  g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config));

  g_object_freeze_notify (G_OBJECT (config));

  if (black)
    {
      config->low_input[channel] = gimp_levels_config_input_from_color (channel,
                                                                        black);
      g_object_notify (G_OBJECT (config), "low-input");
    }


  if (white)
    {
      config->high_input[channel] = gimp_levels_config_input_from_color (channel,
                                                                         white);
      g_object_notify (G_OBJECT (config), "high-input");
    }

  if (gray)
    {
      gdouble input;
      gdouble range;
      gdouble inten;
      gdouble out_light;
      gdouble lightness;

      /* Calculate lightness value */
      lightness = GIMP_RGB_LUMINANCE (gray->r, gray->g, gray->b);

      input = gimp_levels_config_input_from_color (channel, gray);

      range = config->high_input[channel] - config->low_input[channel];
      if (range <= 0)
        return;

      input -= config->low_input[channel];
      if (input < 0)
        return;

      /* Normalize input and lightness */
      inten = input / range;
      out_light = lightness/ range;

      if (out_light <= 0)
        return;

      /* Map selected color to corresponding lightness */
      config->gamma[channel] = log (inten) / log (out_light);
      g_object_notify (G_OBJECT (config), "gamma");
    }

  g_object_thaw_notify (G_OBJECT (config));
}