[gthumb] started work on a curves tool
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb] started work on a curves tool
- Date: Sun, 25 Jan 2015 12:05:17 +0000 (UTC)
commit 938204273b7f4844977abd5833c9530ea0937250
Author: Paolo Bacchilega <paobac src gnome org>
Date: Wed Dec 24 16:54:52 2014 +0100
started work on a curves tool
data/icons/hicolor/16x16/actions/Makefile.am | 1 +
.../hicolor/16x16/actions/curves-symbolic.svg | 166 ++++
extensions/file_tools/Makefile.am | 2 +
extensions/file_tools/data/ui/Makefile.am | 1 +
extensions/file_tools/data/ui/curves-options.ui | 68 ++
extensions/file_tools/gth-file-tool-curves.c | 787 ++++++++++++++++++++
extensions/file_tools/gth-file-tool-curves.h | 54 ++
extensions/file_tools/main.c | 2 +
8 files changed, 1081 insertions(+), 0 deletions(-)
---
diff --git a/data/icons/hicolor/16x16/actions/Makefile.am b/data/icons/hicolor/16x16/actions/Makefile.am
index e7f7d25..979a236 100644
--- a/data/icons/hicolor/16x16/actions/Makefile.am
+++ b/data/icons/hicolor/16x16/actions/Makefile.am
@@ -8,6 +8,7 @@ icons_DATA = \
change-date-symbolic.svg \
comment-symbolic.svg \
convert-format-symbolic.svg \
+ curves-symbolic.svg \
edit-symbolic.svg \
emblem-flag-blue.svg \
emblem-flag-gray.svg \
diff --git a/data/icons/hicolor/16x16/actions/curves-symbolic.svg
b/data/icons/hicolor/16x16/actions/curves-symbolic.svg
new file mode 100644
index 0000000..71ef1c2
--- /dev/null
+++ b/data/icons/hicolor/16x16/actions/curves-symbolic.svg
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91+devel r13821 custom"
+ sodipodi:docname="curves-symbolic.svg"
+ shape-rendering="crispEdges">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="DiamondS"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="DiamondS"
+ style="overflow:visible">
+ <path
+ id="path8477"
+ d="M 0,-7.0710768 L -7.0710894,0 L 0,7.0710589 L 7.0710462,0 L 0,-7.0710768 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
+ transform="scale(0.2)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Send"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow2Send"
+ style="overflow:visible;">
+ <path
+ id="path8425"
+ style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469
6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="scale(0.3) rotate(180) translate(-2.3,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lend"
+ style="overflow:visible;">
+ <path
+ id="path8395"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
+ transform="scale(0.8) rotate(180) translate(12.5,0)" />
+ </marker>
+ <linearGradient
+ id="linearGradient3946">
+ <stop
+ id="stop3948"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:0.74698794;" />
+ <stop
+ id="stop3950"
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0.02409638;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3830-9">
+ <stop
+ style="stop-color:#000000;stop-opacity:0.74698794;"
+ offset="0"
+ id="stop3832-7" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.02409638;"
+ offset="1"
+ id="stop3834-5" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3984">
+ <stop
+ style="stop-color:#f4deba;stop-opacity:1;"
+ offset="0"
+ id="stop3986" />
+ <stop
+ style="stop-color:#de9625;stop-opacity:0;"
+ offset="1"
+ id="stop3988" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#555753"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="16"
+ inkscape:cx="0.081131996"
+ inkscape:cy="13.122671"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:snap-grids="true"
+ inkscape:window-width="1680"
+ inkscape:window-height="984"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ showborder="false"
+ fit-margin-top="0"
+ fit-margin-right="0"
+ fit-margin-left="0"
+ fit-margin-bottom="0"
+ showguides="false"
+ inkscape:guide-bbox="true"
+ borderlayer="false"
+ inkscape:showpageshadow="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid7044"
+ empspacing="8"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="false"
+ originx="-31.97559px"
+ originy="-816.00002px"
+ dotted="false" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Livello 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-31.97559,-220.36218)">
+ <path
+ style="fill:#bebebe;fill-opacity:1;stroke:none"
+ d="M 0.03125 0 L 0.03125 16 L 16.03125 16 L 16.03125 0 L 0.03125 0 z M 1.03125 1 L 15.03125 1 L
15.03125 15 L 1.03125 15 L 1.03125 1 z "
+ id="path3789"
+ transform="translate(31.97559,220.36218)" />
+ <path
+
style="opacity:0.108;fill:#bebebe;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 3.0253906 1 L 3.0253906 3 L 1.0253906 3 L 1.0234375 3.9941406 L 1.0273438 4.0058594 L 3.0253906
4.0058594 L 3.0253906 6 L 1.0273438 6 L 1.0273438 7 L 3.0253906 7 L 3.0253906 9 L 1.0273438 9 L 1.0273438 10
L 3.0253906 10 L 3.0253906 12 L 1.0273438 12 L 1.0273438 13 L 3.0253906 13 L 3.0253906 15 L 4.0253906 15 L
4.0253906 13 L 6.0253906 13 L 6.0253906 15 L 7.0253906 15 L 7.0253906 13 L 9.0253906 13 L 9.0253906 15 L
10.025391 15 L 10.025391 13 L 12.025391 13 L 12.025391 15 L 13.025391 15 L 13.025391 13 L 15.027344 13 L
15.027344 12 L 13.025391 12 L 13.025391 10 L 15.027344 10 L 15.027344 9 L 13.025391 9 L 13.025391 7 L
15.027344 7 L 15.027344 6 L 13.025391 6 L 13.025391 4 L 15.025391 4 L 15.025391 3 L 13.025391 3 L 13.025391 1
L 12.025391 1 L 12.025391 3 L 10.025391 3 L 10.025391 1 L 9.0253906 1 L 9.0253906 3 L 7.0253906 3 L 7.0253906
1 L 6.0253906 1 L 6.0253906 3 L 4.0253906 3 L 4.0253906 1 L 3.0253906 1 z M 10.025391 4.0019531 L 12.025391
4.0019531 L 12.025391 6 L
10.025391 6 L 10.025391 4.0019531 z M 4.0253906 4.0039062 L 6.0253906 4.0039062 L 6.0253906 6 L 4.0253906 6
L 4.0253906 4.0039062 z M 7.0253906 4.0039062 L 9.0253906 4.0039062 L 9.0253906 6 L 7.0253906 6 L 7.0253906
4.0039062 z M 4.0253906 7 L 6.0253906 7 L 6.0253906 9 L 4.0253906 9 L 4.0253906 7 z M 7.0253906 7 L 9.0253906
7 L 9.0253906 9 L 7.0253906 9 L 7.0253906 7 z M 10.025391 7 L 12.025391 7 L 12.025391 9 L 10.025391 9 L
10.025391 7 z M 4.0253906 10 L 6.0253906 10 L 6.0253906 12 L 4.0253906 12 L 4.0253906 10 z M 7.0253906 10 L
9.0253906 10 L 9.0253906 12 L 7.0253906 12 L 7.0253906 10 z M 10.025391 10 L 12.025391 10 L 12.025391 12 L
10.025391 12 L 10.025391 10 z "
+ id="path5266"
+ transform="translate(31.97559,220.36218)" />
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#bebebe;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background
:accumulate"
+ d="m 45.982422,221.85547 a 0.50005,0.50005 0 0 0 -0.47461,0.41992 c -0.670445,3.76092
-2.151193,6.28601 -4.164062,8.07227 -2.012869,1.78626 -4.58527,2.83704 -7.460938,3.52929 a 0.50005,0.50005 0
1 0 0.234376,0.97071 c 2.967072,-0.71426 5.707261,-1.8144 7.890624,-3.75196 2.183364,-1.93756 3.78249,-4.7092
4.484376,-8.64648 a 0.50005,0.50005 0 0 0 -0.509766,-0.59375 z"
+ id="path5913"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/extensions/file_tools/Makefile.am b/extensions/file_tools/Makefile.am
index 26d6a2e..bfc3dae 100644
--- a/extensions/file_tools/Makefile.am
+++ b/extensions/file_tools/Makefile.am
@@ -13,6 +13,7 @@ HEADER_FILES = \
gth-file-tool-adjust-colors.h \
gth-file-tool-adjust-contrast.h \
gth-file-tool-crop.h \
+ gth-file-tool-curves.h \
gth-file-tool-flip.h \
gth-file-tool-grayscale.h \
gth-file-tool-mirror.h \
@@ -61,6 +62,7 @@ libfile_tools_la_SOURCES = \
gth-file-tool-adjust-colors.c \
gth-file-tool-adjust-contrast.c \
gth-file-tool-crop.c \
+ gth-file-tool-curves.c \
gth-file-tool-flip.c \
gth-file-tool-grayscale.c \
gth-file-tool-mirror.c \
diff --git a/extensions/file_tools/data/ui/Makefile.am b/extensions/file_tools/data/ui/Makefile.am
index d5bfe37..3db21b9 100644
--- a/extensions/file_tools/data/ui/Makefile.am
+++ b/extensions/file_tools/data/ui/Makefile.am
@@ -3,6 +3,7 @@ ui_DATA = \
adjust-colors-options.ui \
adjust-contrast-options.ui \
crop-options.ui \
+ curves-options.ui \
grayscale-options.ui \
resize-options.ui \
rotate-options.ui \
diff --git a/extensions/file_tools/data/ui/curves-options.ui b/extensions/file_tools/data/ui/curves-options.ui
new file mode 100644
index 0000000..66ee39c
--- /dev/null
+++ b/extensions/file_tools/data/ui/curves-options.ui
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.3 -->
+<interface>
+ <requires lib="gtk+" version="3.12"/>
+ <object class="GtkAlignment" id="options">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="bottom_padding">12</property>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="curves_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="preview_checkbutton">
+ <property name="label" translatable="yes">_Preview</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/extensions/file_tools/gth-file-tool-curves.c b/extensions/file_tools/gth-file-tool-curves.c
new file mode 100644
index 0000000..b2fc692
--- /dev/null
+++ b/extensions/file_tools/gth-file-tool-curves.c
@@ -0,0 +1,787 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2014 Free Software Foundation, Inc.
+ *
+ * This program 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 2 of the License, or
+ * (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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <math.h>
+#include "gth-file-tool-curves.h"
+#include "gth-preview-tool.h"
+
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
+#define APPLY_DELAY 150
+#define PREVIEW_SIZE 0.9
+
+
+G_DEFINE_TYPE (GthFileToolCurves, gth_file_tool_curves, GTH_TYPE_IMAGE_VIEWER_PAGE_TOOL)
+
+
+typedef struct {
+ double x;
+ double y;
+} Point;
+
+
+typedef struct {
+ Point *p;
+ int n;
+} Points;
+
+
+struct _GthFileToolCurvesPrivate {
+ cairo_surface_t *destination;
+ cairo_surface_t *preview;
+ GtkBuilder *builder;
+ GthTask *image_task;
+ guint apply_event;
+ GthImageViewerTool *preview_tool;
+ gboolean view_original;
+ gboolean apply_to_original;
+ gboolean closing;
+ Points points[GTH_HISTOGRAM_N_CHANNELS];
+};
+
+
+/* -- Gauss-Jordan linear equation solver (for splines) -- */
+
+
+typedef struct {
+ double **v;
+ int r;
+ int c;
+} Matrix;
+
+
+static Matrix *
+GJ_matrix_new (int r, int c)
+{
+ Matrix *m;
+ int i, j;
+
+ m = g_new (Matrix, 1);
+ m->r = r;
+ m->c = c;
+ m->v = g_new (double *, r);
+ for (i = 0; i < r; i++) {
+ m->v[i] = g_new (double, c);
+ for (j = 0; j < c; j++)
+ m->v[i][j] = 0.0;
+ }
+
+ return m;
+}
+
+
+static void
+GJ_matrix_free (Matrix *m)
+{
+ int i;
+ for (i = 0; i < m->r; i++)
+ g_free (m->v[i]);
+ g_free (m->v);
+ g_free (m);
+}
+
+
+static void
+GJ_swap_rows (double **m, int k, int l)
+{
+ double *t = m[k];
+ m[k] = m[l];
+ m[l] = t;
+}
+
+
+static gboolean
+GJ_matrix_solve (Matrix *m, double *x)
+{
+ double **A = m->v;
+ int r = m->r;
+ int k, i, j;
+
+ for (k = 0; k < r; k++) { // column
+ // pivot for column
+ int i_max = 0;
+ double vali = 0;
+
+ for (i = k; i < r; i++) {
+ if ((i == k) || (A[i][k] > vali)) {
+ i_max = i;
+ vali = A[i][k];
+ }
+ }
+ GJ_swap_rows (A, k, i_max);
+
+ if (A[i_max][i] == 0) {
+ g_print ("matrix is singular!\n");
+ return TRUE;
+ }
+
+ // for all rows below pivot
+ for (i = k + 1; i < r; i++) {
+ for (j = k + 1; j < r + 1; j++)
+ A[i][j] = A[i][j] - A[k][j] * (A[i][k] / A[k][k]);
+ A[i][k] = 0.0;
+ }
+ }
+
+ for (i = r - 1; i >= 0; i--) { // rows = columns
+ double v = A[i][r] / A[i][i];
+
+ x[i] = v;
+ for (j = i - 1; j >= 0; j--) { // rows
+ A[j][r] -= A[j][i] * v;
+ A[j][i] = 0.0;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/* -- points -- */
+
+
+static void
+points_init (Points *p, int n)
+{
+ p->n = n;
+ p->p = g_new (Point, p->n);
+}
+
+
+static void
+points_dispose (Points *p)
+{
+ if (p->p != NULL)
+ g_free (p->p);
+ points_init (p, 0);
+}
+
+
+static void
+points_copy (Points *source, Points *dest)
+{
+ int i;
+
+ points_init (dest, source->n);
+ for (i = 0; i < source->n; i++) {
+ dest->p[i].x = source->p[i].x;
+ dest->p[i].y = source->p[i].y;
+ }
+}
+
+
+/* -- spline function -- */
+
+
+typedef struct {
+ Points points;
+ double *k;
+ gboolean is_singular;
+} Spline;
+
+
+static Spline *
+spline_new (Points *points)
+{
+ Spline *spline;
+ int i;
+
+ spline = g_new (Spline, 1);
+ points_copy (points, &spline->points);
+ spline->k = g_new (double, points->n + 1);
+ for (i = 0; i < points->n + 1; i++)
+ spline->k[i] = 1.0;
+ spline->is_singular = FALSE;
+
+ return spline;
+}
+
+
+static void
+spline_free (Spline *spline)
+{
+ g_return_if_fail (spline != NULL);
+
+ points_dispose (&spline->points);
+ g_free (spline->k);
+ g_free (spline);
+}
+
+
+static void
+spline_set_natural_ks (Spline *spline)
+{
+ int n = spline->points.n;
+ Point *p = spline->points.p;
+ Matrix *m;
+ double **A;
+ int i;
+
+ m = GJ_matrix_new (n+1, n+2);
+ A = m->v;
+ for (i = 1; i < n; i++) {
+ A[i][i-1] = 1.0 / (p[i].x - p[i-1].x);
+ A[i][i ] = 2.0 * (1.0 / (p[i].x - p[i-1].x) + 1.0 / (p[i+1].x - p[i].x));
+ A[i][i+1] = 1.0 / (p[i+1].x - p[i].x);
+ A[i][n+1] = 3.0 * ( (p[i].y - p[i-1].y) / ((p[i].x - p[i-1].x) * (p[i].x - p[i-1].x)) +
(p[i+1].y - p[i].y) / ((p[i+1].x - p[i].x) * (p[i+1].x - p[i].x)) );
+ }
+
+ A[0][0 ] = 2.0 / (p[1].x - p[0].x);
+ A[0][1 ] = 1.0 / (p[1].x - p[0].x);
+ A[0][n+1] = 3.0 * (p[1].y - p[0].y) / ((p[1].x - p[0].x) * (p[1].x - p[0].x));
+
+ A[n][n-1] = 1.0 / (p[n].x - p[n-1].x);
+ A[n][n ] = 2.0 / (p[n].x - p[n-1].x);
+ A[n][n+1] = 3.0 * (p[n].y - p[n-1].y) / ((p[n].x - p[n-1].x) * (p[n].x - p[n-1].x));
+
+ spline->is_singular = GJ_matrix_solve (m, spline->k);
+
+ GJ_matrix_free (m);
+}
+
+
+static int
+spline_eval (Spline *spline, double x)
+{
+ Point *p = spline->points.p;
+ double *k = spline->k;
+ int i;
+
+ for (i = 1; p[i].x < x; i++)
+ /* void */;
+ double t = (x - p[i-1].x) / (p[i].x - p[i-1].x);
+ double a = k[i-1] * (p[i].x - p[i-1].x) - (p[i].y - p[i-1].y);
+ double b = - k[i] * (p[i].x - p[i-1].x) + (p[i].y - p[i-1].y);
+ double y = round ( ((1-t) * p[i-1].y) + (t * p[i].y) + (t * (1-t) * (a * (1-t) + b * t)) );
+
+ return CLAMP (y, 0, 255);
+}
+
+
+/* -- apply_changes -- */
+
+
+typedef struct {
+ long *value_map[GTH_HISTOGRAM_N_CHANNELS];
+ Spline *spline[GTH_HISTOGRAM_N_CHANNELS];
+} TaskData;
+
+
+static void
+curves_setup (TaskData *task_data,
+ cairo_surface_t *source)
+{
+ long **value_map;
+ int c, v;
+
+ for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++)
+ spline_set_natural_ks (task_data->spline[c]);
+
+ for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++) {
+ task_data->value_map[c] = g_new (long, 256);
+ if (task_data->spline[c]->is_singular) {
+ for (v = 0; v <= 255; v++)
+ task_data->value_map[c][v] = v;
+ }
+ else {
+ for (v = 0; v <= 255; v++) {
+ task_data->value_map[c][v] = spline_eval (task_data->spline[c], v);
+ /*if (c == 0) FIXME
+ g_print ("%d -> %ld\n", v, task_data->value_map[c][v]);*/
+ }
+ }
+ }
+}
+
+
+static inline guchar
+value_func (TaskData *task_data,
+ int n_channel,
+ guchar value)
+{
+ return (guchar) task_data->value_map[n_channel][value];
+}
+
+
+static gpointer
+curves_exec (GthAsyncTask *task,
+ gpointer user_data)
+{
+ TaskData *task_data = user_data;
+ cairo_surface_t *source;
+ cairo_format_t format;
+ int width;
+ int height;
+ int source_stride;
+ cairo_surface_t *destination;
+ int destination_stride;
+ unsigned char *p_source_line;
+ unsigned char *p_destination_line;
+ unsigned char *p_source;
+ unsigned char *p_destination;
+ gboolean cancelled;
+ double progress;
+ int x, y;
+ unsigned char red, green, blue, alpha;
+
+ /* initialize the extra data */
+
+ source = gth_image_task_get_source_surface (GTH_IMAGE_TASK (task));
+ curves_setup (task_data, source);
+
+ /* convert the image */
+
+ format = cairo_image_surface_get_format (source);
+ width = cairo_image_surface_get_width (source);
+ height = cairo_image_surface_get_height (source);
+ source_stride = cairo_image_surface_get_stride (source);
+
+ destination = cairo_image_surface_create (format, width, height);
+ destination_stride = cairo_image_surface_get_stride (destination);
+ p_source_line = _cairo_image_surface_flush_and_get_data (source);
+ p_destination_line = _cairo_image_surface_flush_and_get_data (destination);
+ for (y = 0; y < height; y++) {
+ gth_async_task_get_data (task, NULL, &cancelled, NULL);
+ if (cancelled) {
+ cairo_surface_destroy (destination);
+ cairo_surface_destroy (source);
+ return NULL;
+ }
+
+ progress = (double) y / height;
+ gth_async_task_set_data (task, NULL, NULL, &progress);
+
+ p_source = p_source_line;
+ p_destination = p_destination_line;
+ for (x = 0; x < width; x++) {
+ CAIRO_GET_RGBA (p_source, red, green, blue, alpha);
+ red = value_func (task_data, GTH_HISTOGRAM_CHANNEL_RED, red);
+ green = value_func (task_data, GTH_HISTOGRAM_CHANNEL_GREEN, green);
+ blue = value_func (task_data, GTH_HISTOGRAM_CHANNEL_BLUE, blue);
+ CAIRO_SET_RGBA (p_destination, red, green, blue, alpha);
+
+ p_source += 4;
+ p_destination += 4;
+ }
+ p_source_line += source_stride;
+ p_destination_line += destination_stride;
+ }
+
+ cairo_surface_mark_dirty (destination);
+ gth_image_task_set_destination_surface (GTH_IMAGE_TASK (task), destination);
+
+ cairo_surface_destroy (destination);
+ cairo_surface_destroy (source);
+
+ return NULL;
+}
+
+
+static void apply_changes (GthFileToolCurves *self);
+
+
+static void
+image_task_completed_cb (GthTask *task,
+ GError *error,
+ gpointer user_data)
+{
+ GthFileToolCurves *self = user_data;
+ GthImage *destination_image;
+
+ self->priv->image_task = NULL;
+
+ if (self->priv->closing) {
+ g_object_unref (task);
+ gth_image_viewer_page_tool_reset_image (GTH_IMAGE_VIEWER_PAGE_TOOL (self));
+ return;
+ }
+
+ if (error != NULL) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ apply_changes (self);
+ g_object_unref (task);
+ return;
+ }
+
+ destination_image = gth_image_task_get_destination (GTH_IMAGE_TASK (task));
+ if (destination_image == NULL) {
+ g_object_unref (task);
+ return;
+ }
+
+ cairo_surface_destroy (self->priv->destination);
+ self->priv->destination = gth_image_get_cairo_surface (destination_image);
+
+ if (self->priv->apply_to_original) {
+ if (self->priv->destination != NULL) {
+ GtkWidget *window;
+ GtkWidget *viewer_page;
+
+ window = gth_file_tool_get_window (GTH_FILE_TOOL (self));
+ viewer_page = gth_browser_get_viewer_page (GTH_BROWSER (window));
+ gth_image_viewer_page_set_image (GTH_IMAGE_VIEWER_PAGE (viewer_page),
self->priv->destination, TRUE);
+ }
+
+ gth_file_tool_hide_options (GTH_FILE_TOOL (self));
+ }
+ else {
+ if (! self->priv->view_original)
+ gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool),
self->priv->destination);
+ }
+
+ g_object_unref (task);
+}
+
+
+static TaskData *
+task_data_new (Points *points)
+{
+ TaskData *task_data;
+ int c, n;
+
+ task_data = g_new (TaskData, 1);
+ for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++) {
+ task_data->value_map[c] = NULL;
+ task_data->spline[c] = spline_new (&points[c]);
+ }
+
+ return task_data;
+}
+
+
+static void
+task_data_destroy (gpointer user_data)
+{
+ TaskData *task_data = user_data;
+ int c;
+
+ if (task_data == NULL)
+ return;
+
+ for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++)
+ spline_free (task_data->spline[c]);
+ for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++)
+ g_free (task_data->value_map[c]);
+ g_free (task_data);
+}
+
+
+static gboolean
+apply_cb (gpointer user_data)
+{
+ GthFileToolCurves *self = user_data;
+ GtkWidget *window;
+ TaskData *task_data;
+
+ if (self->priv->apply_event != 0) {
+ g_source_remove (self->priv->apply_event);
+ self->priv->apply_event = 0;
+ }
+
+ if (self->priv->image_task != NULL) {
+ gth_task_cancel (self->priv->image_task);
+ return FALSE;
+ }
+
+ window = gth_file_tool_get_window (GTH_FILE_TOOL (self));
+
+ task_data = task_data_new (self->priv->points);
+ self->priv->image_task = gth_image_task_new (_("Applying changes"),
+ NULL,
+ curves_exec,
+ NULL,
+ task_data,
+ task_data_destroy);
+ if (self->priv->apply_to_original)
+ gth_image_task_set_source_surface (GTH_IMAGE_TASK (self->priv->image_task),
gth_image_viewer_page_tool_get_source (GTH_IMAGE_VIEWER_PAGE_TOOL (self)));
+ else
+ gth_image_task_set_source_surface (GTH_IMAGE_TASK (self->priv->image_task),
self->priv->preview);
+
+ g_signal_connect (self->priv->image_task,
+ "completed",
+ G_CALLBACK (image_task_completed_cb),
+ self);
+ gth_browser_exec_task (GTH_BROWSER (window), self->priv->image_task, FALSE);
+
+ return FALSE;
+}
+
+
+static void
+apply_changes (GthFileToolCurves *self)
+{
+ if (self->priv->apply_event != 0) {
+ g_source_remove (self->priv->apply_event);
+ self->priv->apply_event = 0;
+ }
+ self->priv->apply_event = g_timeout_add (APPLY_DELAY, apply_cb, self);
+}
+
+
+static void
+reset_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GthFileToolCurves *self = user_data;
+ int c;
+
+ for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++) {
+ points_init (&self->priv->points[c], 3);
+
+ self->priv->points[c].p[0].x = 0;
+ self->priv->points[c].p[0].y = 0;
+
+ self->priv->points[c].p[1].x = 128;
+ self->priv->points[c].p[1].y = 128;
+
+ self->priv->points[c].p[2].x = 255;
+ self->priv->points[c].p[2].y = 255;
+ }
+
+ apply_changes (self);
+}
+
+
+static void
+preview_checkbutton_toggled_cb (GtkToggleButton *togglebutton,
+ gpointer user_data)
+{
+ GthFileToolCurves *self = user_data;
+
+ self->priv->view_original = ! gtk_toggle_button_get_active (togglebutton);
+ if (self->priv->view_original)
+ gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool), self->priv->preview);
+ else
+ gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool),
self->priv->destination);
+}
+
+
+static GtkWidget *
+gth_file_tool_curves_get_options (GthFileTool *base)
+{
+ GthFileToolCurves *self;
+ GtkWidget *viewer_page;
+ GtkWidget *viewer;
+ cairo_surface_t *source;
+ GtkWidget *options;
+ int width, height;
+ GtkAllocation allocation;
+
+ self = (GthFileToolCurves *) base;
+
+ viewer_page = gth_image_viewer_page_tool_get_page (GTH_IMAGE_VIEWER_PAGE_TOOL (self));
+ if (viewer_page == NULL)
+ return NULL;
+
+ _cairo_clear_surface (&self->priv->destination);
+ _cairo_clear_surface (&self->priv->preview);
+
+ source = gth_image_viewer_page_tool_get_source (GTH_IMAGE_VIEWER_PAGE_TOOL (self));
+ if (source == NULL)
+ return NULL;
+
+ width = cairo_image_surface_get_width (source);
+ height = cairo_image_surface_get_height (source);
+ viewer = gth_image_viewer_page_get_image_viewer (GTH_IMAGE_VIEWER_PAGE (viewer_page));
+ gtk_widget_get_allocation (GTK_WIDGET (viewer), &allocation);
+ if (scale_keeping_ratio (&width, &height, PREVIEW_SIZE * allocation.width, PREVIEW_SIZE *
allocation.height, FALSE))
+ self->priv->preview = _cairo_image_surface_scale_bilinear (source, width, height);
+ else
+ self->priv->preview = cairo_surface_reference (source);
+
+ self->priv->destination = cairo_surface_reference (self->priv->preview);
+ self->priv->apply_to_original = FALSE;
+ self->priv->view_original = FALSE;
+ self->priv->closing = FALSE;
+
+ self->priv->builder = _gtk_builder_new_from_file ("curves-options.ui", "file_tools");
+ options = _gtk_builder_get_widget (self->priv->builder, "options");
+ gtk_widget_show (options);
+
+ g_signal_connect (GET_WIDGET ("preview_checkbutton"),
+ "toggled",
+ G_CALLBACK (preview_checkbutton_toggled_cb),
+ self);
+
+ self->priv->preview_tool = gth_preview_tool_new ();
+ gth_preview_tool_set_image (GTH_PREVIEW_TOOL (self->priv->preview_tool), self->priv->preview);
+ gth_image_viewer_set_tool (GTH_IMAGE_VIEWER (viewer), self->priv->preview_tool);
+ apply_changes (self);
+
+ return options;
+}
+
+
+static void
+gth_file_tool_curves_destroy_options (GthFileTool *base)
+{
+ GthFileToolCurves *self;
+ GtkWidget *viewer_page;
+
+ self = (GthFileToolCurves *) base;
+
+ if (self->priv->apply_event != 0) {
+ g_source_remove (self->priv->apply_event);
+ self->priv->apply_event = 0;
+ }
+
+ viewer_page = gth_image_viewer_page_tool_get_page (GTH_IMAGE_VIEWER_PAGE_TOOL (self));
+ gth_image_viewer_page_reset_viewer_tool (GTH_IMAGE_VIEWER_PAGE (viewer_page));
+ gth_viewer_page_update_sensitivity (GTH_VIEWER_PAGE (viewer_page));
+
+ _cairo_clear_surface (&self->priv->preview);
+ _cairo_clear_surface (&self->priv->destination);
+ _g_clear_object (&self->priv->builder);
+}
+
+
+static void
+gth_file_tool_curves_apply_options (GthFileTool *base)
+{
+ GthFileToolCurves *self;
+
+ self = (GthFileToolCurves *) base;
+ self->priv->apply_to_original = TRUE;
+ apply_changes (self);
+}
+
+
+static void
+gth_file_tool_curves_populate_headerbar (GthFileTool *base,
+ GthBrowser *browser)
+{
+ GthFileToolCurves *self;
+ gboolean rtl;
+ GtkWidget *button;
+
+ self = (GthFileToolCurves *) base;
+
+ /* reset button */
+
+ rtl = gtk_widget_get_direction (GTK_WIDGET (base)) == GTK_TEXT_DIR_RTL;
+ button = gth_browser_add_header_bar_button (browser,
+ GTH_BROWSER_HEADER_SECTION_EDITOR_COMMANDS,
+ rtl ? "edit-undo-rtl-symbolic" : "edit-undo-symbolic",
+ _("Reset"),
+ NULL,
+ NULL);
+ g_signal_connect (button,
+ "clicked",
+ G_CALLBACK (reset_button_clicked_cb),
+ self);
+}
+
+
+static void
+gth_file_tool_sharpen_reset_image (GthImageViewerPageTool *base)
+{
+ GthFileToolCurves *self = (GthFileToolCurves *) base;
+
+ if (self->priv->image_task != NULL) {
+ self->priv->closing = TRUE;
+ gth_task_cancel (self->priv->image_task);
+ return;
+ }
+
+ if (self->priv->apply_event != 0) {
+ g_source_remove (self->priv->apply_event);
+ self->priv->apply_event = 0;
+ }
+
+ gth_image_viewer_page_reset (GTH_IMAGE_VIEWER_PAGE (gth_image_viewer_page_tool_get_page
(GTH_IMAGE_VIEWER_PAGE_TOOL (self))));
+ gth_file_tool_hide_options (GTH_FILE_TOOL (self));
+}
+
+
+static void
+gth_file_tool_curves_init (GthFileToolCurves *self)
+{
+ int c;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_FILE_TOOL_CURVES, GthFileToolCurvesPrivate);
+ self->priv->preview = NULL;
+ self->priv->destination = NULL;
+ self->priv->builder = NULL;
+ self->priv->image_task = NULL;
+ self->priv->view_original = FALSE;
+
+ for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++) {
+ /* points_init (&self->priv->points[c], 0); FIXME */
+ points_init (&self->priv->points[c], 4);
+
+ self->priv->points[c].p[0].x = 0;
+ self->priv->points[c].p[0].y = 0;
+
+ self->priv->points[c].p[1].x = 100;
+ self->priv->points[c].p[1].y = 54;
+
+ self->priv->points[c].p[2].x = 161;
+ self->priv->points[c].p[2].y = 190;
+
+ self->priv->points[c].p[3].x = 255;
+ self->priv->points[c].p[3].y = 255;
+ }
+
+ gth_file_tool_construct (GTH_FILE_TOOL (self), "curves-symbolic", _("Curves"),
GTH_TOOLBOX_SECTION_COLORS);
+ gtk_widget_set_tooltip_text (GTK_WIDGET (self), _("Adjust color curves"));
+}
+
+
+static void
+gth_file_tool_curves_finalize (GObject *object)
+{
+ GthFileToolCurves *self;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GTH_IS_FILE_TOOL_CURVES (object));
+
+ self = (GthFileToolCurves *) object;
+
+ cairo_surface_destroy (self->priv->preview);
+ cairo_surface_destroy (self->priv->destination);
+ _g_object_unref (self->priv->builder);
+
+ G_OBJECT_CLASS (gth_file_tool_curves_parent_class)->finalize (object);
+}
+
+
+static void
+gth_file_tool_curves_class_init (GthFileToolCurvesClass *klass)
+{
+ GObjectClass *gobject_class;
+ GthFileToolClass *file_tool_class;
+ GthImageViewerPageToolClass *image_viewer_page_tool_class;
+
+ g_type_class_add_private (klass, sizeof (GthFileToolCurvesPrivate));
+
+ gobject_class = (GObjectClass*) klass;
+ gobject_class->finalize = gth_file_tool_curves_finalize;
+
+ file_tool_class = (GthFileToolClass *) klass;
+ file_tool_class->get_options = gth_file_tool_curves_get_options;
+ file_tool_class->destroy_options = gth_file_tool_curves_destroy_options;
+ file_tool_class->apply_options = gth_file_tool_curves_apply_options;
+ file_tool_class->populate_headerbar = gth_file_tool_curves_populate_headerbar;
+
+ image_viewer_page_tool_class = (GthImageViewerPageToolClass *) klass;
+ image_viewer_page_tool_class->reset_image = gth_file_tool_sharpen_reset_image;
+}
diff --git a/extensions/file_tools/gth-file-tool-curves.h b/extensions/file_tools/gth-file-tool-curves.h
new file mode 100644
index 0000000..af1ce8a
--- /dev/null
+++ b/extensions/file_tools/gth-file-tool-curves.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2014 Free Software Foundation, Inc.
+ *
+ * This program 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 2 of the License, or
+ * (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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_FILE_TOOL_CURVES_H
+#define GTH_FILE_TOOL_CURVES_H
+
+#include <gthumb.h>
+#include <extensions/image_viewer/image-viewer.h>
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_FILE_TOOL_CURVES (gth_file_tool_curves_get_type ())
+#define GTH_FILE_TOOL_CURVES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_FILE_TOOL_CURVES,
GthFileToolCurves))
+#define GTH_FILE_TOOL_CURVES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_FILE_TOOL_CURVES,
GthFileToolCurvesClass))
+#define GTH_IS_FILE_TOOL_CURVES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_FILE_TOOL_CURVES))
+#define GTH_IS_FILE_TOOL_CURVES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_FILE_TOOL_CURVES))
+#define GTH_FILE_TOOL_CURVES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_FILE_TOOL_CURVES,
GthFileToolCurvesClass))
+
+typedef struct _GthFileToolCurves GthFileToolCurves;
+typedef struct _GthFileToolCurvesClass GthFileToolCurvesClass;
+typedef struct _GthFileToolCurvesPrivate GthFileToolCurvesPrivate;
+
+struct _GthFileToolCurves {
+ GthImageViewerPageTool parent_instance;
+ GthFileToolCurvesPrivate *priv;
+};
+
+struct _GthFileToolCurvesClass {
+ GthImageViewerPageToolClass parent_class;
+};
+
+GType gth_file_tool_curves_get_type (void);
+
+G_END_DECLS
+
+#endif /* GTH_FILE_TOOL_CURVES_H */
diff --git a/extensions/file_tools/main.c b/extensions/file_tools/main.c
index 2a9c4bb..0051fa9 100644
--- a/extensions/file_tools/main.c
+++ b/extensions/file_tools/main.c
@@ -27,6 +27,7 @@
#include "gth-file-tool-adjust-colors.h"
#include "gth-file-tool-adjust-contrast.h"
#include "gth-file-tool-crop.h"
+#include "gth-file-tool-curves.h"
#include "gth-file-tool-flip.h"
#include "gth-file-tool-grayscale.h"
#include "gth-file-tool-mirror.h"
@@ -54,6 +55,7 @@ gthumb_extension_activate (void)
gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_ADJUST_COLORS);
gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_SHARPEN);
gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_GRAYSCALE);
+ gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_CURVES);
gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_NEGATIVE);
gth_main_register_type ("file-tools", GTH_TYPE_FILE_TOOL_ROTATE_LEFT);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]