2 * This file is part of RawTherapee.
4 * Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
6 * RawTherapee is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * RawTherapee is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
21 #define RADIUS 3 /* radius of the control points */
22 #define MIN_DISTANCE 8 /* min distance between control points */
24 MyCurve::MyCurve () : listener(NULL
) {
26 cursor_type
= Gdk::TOP_LEFT_ARROW
;
31 add_events(Gdk::EXPOSURE_MASK
| Gdk::POINTER_MOTION_MASK
| Gdk::POINTER_MOTION_HINT_MASK
| Gdk::ENTER_NOTIFY_MASK
| Gdk::BUTTON_PRESS_MASK
| Gdk::BUTTON_RELEASE_MASK
| Gdk::BUTTON1_MOTION_MASK
);
32 signal_event().connect( sigc::mem_fun(*this, &MyCurve::handleEvents
) );
41 void MyCurve::spline_solve (int n
, double x
[], double y
[], double y2
[]) {
43 double* u
= new double[n
-1];
45 y2
[0] = u
[0] = 0.0; /* set lower boundary condition to "natural" */
47 for (int i
= 1; i
< n
- 1; ++i
)
49 double sig
= (x
[i
] - x
[i
- 1]) / (x
[i
+ 1] - x
[i
- 1]);
50 double p
= sig
* y2
[i
- 1] + 2.0;
51 y2
[i
] = (sig
- 1.0) / p
;
52 u
[i
] = ((y
[i
+ 1] - y
[i
])
53 / (x
[i
+ 1] - x
[i
]) - (y
[i
] - y
[i
- 1]) / (x
[i
] - x
[i
- 1]));
54 u
[i
] = (6.0 * u
[i
] / (x
[i
+ 1] - x
[i
- 1]) - sig
* u
[i
- 1]) / p
;
58 for (int k
= n
- 2; k
>= 0; --k
)
59 y2
[k
] = y2
[k
] * y2
[k
+ 1] + u
[k
];
64 double MyCurve::spline_eval (int n
, double x
[], double y
[], double y2
[], double val
) {
71 /* do a binary search for the right interval: */
72 int k_lo
= 0, k_hi
= n
- 1;
73 while (k_hi
- k_lo
> 1){
74 int k
= (k_hi
+ k_lo
) / 2;
81 double h
= x
[k_hi
] - x
[k_lo
];
82 double a
= (x
[k_hi
] - val
) / h
;
83 double b
= (val
- x
[k_lo
]) / h
;
84 return a
*y
[k_lo
] + b
*y
[k_hi
] + ((a
*a
*a
- a
)*y2
[k_lo
] + (b
*b
*b
- b
)*y2
[k_hi
]) * (h
*h
)/6.0;
88 std::vector
<double> MyCurve::get_vector (int veclen
) {
90 std::vector
<double> vector
;
92 int num
= curve
.x
.size();
94 /* count active points: */
98 for (int i
= 0; i
< num
; ++i
)
99 if (curve
.x
[i
] > prev
) {
105 /* handle degenerate case: */
109 ry
= curve
.y
[firstact
];
112 if (ry
< 0.0) ry
= 0.0;
113 if (ry
> 1.0) ry
= 1.0;
114 for (int x
= 0; x
< veclen
; ++x
)
115 vector
.push_back(ry
);
119 if (curve
.type
==Spline
) {
121 double* mem
= new double [3*active
];
123 double* yv
= mem
+ active
;
124 double* y2v
= mem
+ 2*active
;
128 for (int i
= 0; i
< num
; ++i
) {
129 if (curve
.x
[i
] > prev
) {
131 xv
[dst
] = curve
.x
[i
];
132 yv
[dst
] = curve
.y
[i
];
136 spline_solve (active
, xv
, yv
, y2v
);
138 double dx
= 1.0 / (veclen
- 1);
140 for (int x
= 0; x
< veclen
; ++x
, rx
+= dx
) {
141 double ry
= spline_eval (active
, xv
, yv
, y2v
, rx
);
142 if (ry
< 0.0) ry
= 0;
143 if (ry
> 1.0) ry
= 1.0;
144 vector
.push_back (ry
);
148 else if (curve
.type
==Linear
) {
149 double dx
= 1.0 / (veclen
- 1);
154 for (int x
= 0; x
< veclen
; ++x
, rx
+= dx
) {
155 if (rx
>= curve
.x
[i
]) {
160 while (next
< num
&& curve
.x
[next
] <= curve
.x
[i
])
163 double delta_x
= curve
.x
[next
] - curve
.x
[i
];
164 dy
= (curve
.y
[next
] - curve
.y
[i
]) / delta_x
;
171 vector
.push_back (curve
.y
[0]);
172 else if (rx
>curve
.x
[num
-1])
173 vector
.push_back (curve
.y
[num
-1]);
175 vector
.push_back (ry
);
182 void MyCurve::interpolate (int width
, int height
) {
184 this->height
= height
;
186 std::vector
<double> vector
= get_vector (width
);
187 this->height
= height
;
188 for (int i
= 0; i
< width
; ++i
) {
189 Gdk::Point
p (RADIUS
+ i
, RADIUS
+ height
- (int)((height
-1) * vector
[i
] + 0.5));
195 void MyCurve::draw (int width
, int height
) {
200 if (this->height
!= height
|| point
.size() != width
)
201 interpolate (width
, height
);
203 Gtk::StateType state
= Gtk::STATE_NORMAL
;
205 state
= Gtk::STATE_INSENSITIVE
;
207 Glib::RefPtr
<Gtk::Style
> style
= get_style ();
209 Cairo::RefPtr
<Cairo::Context
> cr
= pixmap
->create_cairo_context();
211 /* clear the pixmap: */
212 // gtk_paint_flat_box (style->gobj(), pixmap->gobj(), GTK_STATE_NORMAL, GTK_SHADOW_NONE,
213 // NULL, (GtkWidget*)gobj(), "curve_bg", 0, 0, , height + RADIUS * 2);
215 // pixmap->draw_rectangle (style->get_bg_gc (state), false, 0, 0, width + RADIUS*2 - 1, height + RADIUS*2 - 1);
217 Gdk::Color c
= style
->get_bg (state
);
218 cr
->set_source_rgb (c
.get_red_p(), c
.get_green_p(), c
.get_blue_p());
219 cr
->rectangle (0, 0, width
+ RADIUS
*2, height
+ RADIUS
*2);
222 /* draw the grid lines: (XXX make more meaningful) */
223 cr
->set_line_width (1.0);
224 c
= style
->get_dark (state
);
225 cr
->set_source_rgb (c
.get_red_p(), c
.get_green_p(), c
.get_blue_p());
226 cr
->set_antialias (Cairo::ANTIALIAS_NONE
);
227 for (int i
= 0; i
< 5; i
++) {
229 cr
->move_to (RADIUS
, i
* height
/ 4 + RADIUS
);
230 cr
->line_to (width
+ RADIUS
, i
* height
/ 4 + RADIUS
);
231 cr
->move_to (i
* width
/ 4 + RADIUS
, RADIUS
);
232 cr
->line_to (i
* width
/ 4 + RADIUS
, height
+ RADIUS
);
233 // pixmap->draw_line (style->get_dark_gc (state), RADIUS, i * height / 4 + RADIUS, width + RADIUS, i * height / 4 + RADIUS);
234 // pixmap->draw_line (style->get_dark_gc (state), i * width / 4 + RADIUS, RADIUS, i * width / 4 + RADIUS, height + RADIUS);
238 cr
->set_antialias (Cairo::ANTIALIAS_SUBPIXEL
);
239 cr
->set_line_width (1.0);
240 cr
->set_source_rgb (0.0, 0.0, 0.0);
241 cr
->move_to (point
[0].get_x(), point
[0].get_y());
242 for (int i
=1; i
<point
.size(); i
++)
243 cr
->line_to (point
[i
].get_x(), point
[i
].get_y());
246 for (int i
= 0; i
< curve
.x
.size(); ++i
) {
248 double x
= ((width
-1) * curve
.x
[i
] + 0.5)+RADIUS
; // project (curve.x[i], 0, 1, width);
249 double y
= height
- ((height
-1) * curve
.y
[i
] + 0.5)+RADIUS
; // project (curve.y[i], 0, 1, height);
252 cr
->arc (x
, y
, RADIUS
, 0, 2*M_PI
);
256 get_window()->draw_drawable (style
->get_fg_gc (state
), pixmap
, 0, 0, 0, 0, width
+ RADIUS
* 2, height
+ RADIUS
* 2);
259 bool MyCurve::handleEvents (GdkEvent
* event
) {
261 Gdk::CursorType new_type
= cursor_type
;
263 GdkEventMotion
*mevent
;
264 std::vector
<double>::iterator itx
, ity
;
268 int width
= get_allocation().get_width() - RADIUS
* 2;
269 int height
= get_allocation().get_height() - RADIUS
* 2;
271 if ((width
< 0) || (height
< 0))
274 /* get the pointer position */
276 Gdk::ModifierType gm
;
277 get_window()->get_pointer (tx
, ty
, gm
);
278 int x
= CLAMP ((tx
- RADIUS
), 0, width
-1);
279 int y
= CLAMP ((ty
- RADIUS
), 0, height
-1);
281 unsigned int distance
= ~0U;
282 int num
= curve
.x
.size();
283 int closest_point
= 0;
284 for (int i
= 0; i
< num
; ++i
) {
285 int cx
= (int)((width
-1) * curve
.x
[i
] + 0.5); //project (c->ctlpoint[i][0], min_x, c->max_x, width);
286 if ((unsigned int) abs (x
- cx
) < distance
) {
287 distance
= abs (x
- cx
);
292 switch (event
->type
) {
300 pixmap
= Gdk::Pixmap::create (get_window(), get_allocation().get_width(), get_allocation().get_height());
301 interpolate (width
, height
);
303 draw (width
, height
);
307 case Gdk::BUTTON_PRESS
:
309 new_type
= Gdk::PLUS
;
310 switch (curve
.type
) {
313 if (distance
> MIN_DISTANCE
) {
314 /* insert a new control point */
316 int cx
= (int)((width
-1)*curve
.x
[closest_point
]+0.5);
320 itx
= curve
.x
.begin();
321 ity
= curve
.y
.begin();
322 for (int i
=0; i
<closest_point
; i
++) { itx
++; ity
++; }
323 curve
.x
.insert (itx
, 0);
324 curve
.y
.insert (ity
, 0);
327 grab_point
= closest_point
;
328 curve
.x
[grab_point
] = (double) x
/ (width
-1);
329 curve
.y
[grab_point
] = (double) (height
-y
) / (height
-1);
331 interpolate (width
, height
);
335 draw (width
, height
);
339 case Gdk::BUTTON_RELEASE
:
340 remove_modal_grab ();
342 /* delete inactive points: */
343 itx
= curve
.x
.begin();
344 ity
= curve
.y
.begin();
345 for (src
= dst
= 0; src
< num
; ++src
) {
346 if (curve
.x
[src
] >= 0.0) {
347 curve
.x
[dst
] = curve
.x
[src
];
348 curve
.y
[dst
] = curve
.y
[src
];
355 curve
.x
.erase (itx
, curve
.x
.end());
356 curve
.y
.erase (ity
, curve
.y
.end());
357 if (curve
.x
.size() <= 0) {
358 curve
.x
.push_back (0);
359 curve
.y
.push_back (0);
360 interpolate (width
, height
);
361 draw (width
, height
);
364 new_type
= Gdk::FLEUR
;
370 case Gdk::MOTION_NOTIFY
:
371 mevent
= (GdkEventMotion
*) event
;
373 switch (curve
.type
) {
376 if (grab_point
== -1) {
377 /* if no point is grabbed... */
378 if (distance
<= MIN_DISTANCE
)
379 new_type
= Gdk::FLEUR
;
381 new_type
= Gdk::PLUS
;
384 /* drag the grabbed point */
385 new_type
= Gdk::FLEUR
;
386 int leftbound
= -MIN_DISTANCE
;
388 leftbound
= (int)((width
-1)*curve
.x
[grab_point
-1]+0.5);
390 int rightbound
= width
+ RADIUS
* 2 + MIN_DISTANCE
;
391 if (grab_point
+ 1 < num
)
392 rightbound
= (int)((width
-1)*curve
.x
[grab_point
+1]+0.5);
394 if (tx
<= leftbound
|| tx
>= rightbound
|| ty
> height
+ RADIUS
* 2 + MIN_DISTANCE
|| ty
< -MIN_DISTANCE
)
395 curve
.x
[grab_point
] = -1.0;
397 curve
.x
[grab_point
] = (double) x
/ (width
-1);
398 curve
.y
[grab_point
] = (double) (height
-y
) / (height
-1);
400 interpolate (width
, height
);
401 draw (width
, height
);
407 if (new_type
!= cursor_type
) {
408 cursor_type
= new_type
;
409 Gdk::Cursor
* cursor
= new Gdk::Cursor (get_display(), cursor_type
);
410 get_window ()->set_cursor (*cursor
);
425 std::vector
<double> MyCurve::getPoints () {
427 std::vector
<double> result
;
428 if (curve
.type
==Linear
)
429 result
.push_back (-1.0);
431 result
.push_back (+1.0);
432 for (int i
=0; i
<curve
.x
.size(); i
++)
434 result
.push_back (curve
.x
[i
]);
435 result
.push_back (curve
.y
[i
]);
440 void MyCurve::setPoints (const std::vector
<double>& p
) {
450 for (int i
=0; i
<p
.size()/2; i
++) {
451 curve
.x
.push_back (p
[ix
++]);
452 curve
.y
.push_back (p
[ix
++]);
458 void MyCurve::setType (CurveType t
) {
464 void MyCurve::notifyListener () {
466 listener
->curveChanged ();