Proper check for rawzor libraries.
[rawtherapee-fixes.git] / rtgui / mycurve.cc
blob913a398f6264cd59074554eb9d7c432397363879
1 /*
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/>.
19 #include <mycurve.h>
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;
27 curve.type = Spline;
28 height = 0;
29 grab_point = -1;
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) );
34 curve.x.push_back(0);
35 curve.y.push_back(0);
36 curve.x.push_back(1);
37 curve.y.push_back(1);
38 curve.type = Spline;
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;
57 y2[n - 1] = 0.0;
58 for (int k = n - 2; k >= 0; --k)
59 y2[k] = y2[k] * y2[k + 1] + u[k];
61 delete [] u;
64 double MyCurve::spline_eval (int n, double x[], double y[], double y2[], double val) {
66 if (val>x[n-1])
67 return y[n-1];
68 else if (val<x[0])
69 return y[0];
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;
75 if (x[k] > val)
76 k_hi = k;
77 else
78 k_lo = k;
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: */
95 double prev =- 1.0;
96 int active = 0;
97 int firstact = -1;
98 for (int i = 0; i < num; ++i)
99 if (curve.x[i] > prev) {
100 if (firstact < 0)
101 firstact = i;
102 prev = curve.x[i];
103 ++active;
105 /* handle degenerate case: */
106 if (active < 2) {
107 double ry;
108 if (active > 0)
109 ry = curve.y[firstact];
110 else
111 ry = 0.0;
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);
116 return vector;
119 if (curve.type==Spline) {
121 double* mem = new double [3*active];
122 double* xv = mem;
123 double* yv = mem + active;
124 double* y2v = mem + 2*active;
126 prev = -1.0;
127 int dst = 0;
128 for (int i = 0; i < num; ++i) {
129 if (curve.x[i] > prev) {
130 prev = curve.x[i];
131 xv[dst] = curve.x[i];
132 yv[dst] = curve.y[i];
133 dst++;
136 spline_solve (active, xv, yv, y2v);
138 double dx = 1.0 / (veclen - 1);
139 double rx = 0.0;
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);
146 delete [] mem;
148 else if (curve.type==Linear) {
149 double dx = 1.0 / (veclen - 1);
150 double rx = 0;
151 double ry = 0;
152 double dy = 0.0;
153 int i = firstact;
154 for (int x = 0; x < veclen; ++x, rx += dx) {
155 if (rx >= curve.x[i]) {
156 if (rx > curve.x[i])
157 ry = 0.0;
158 dy = 0.0;
159 int next = i + 1;
160 while (next < num && curve.x[next] <= curve.x[i])
161 ++next;
162 if (next < num) {
163 double delta_x = curve.x[next] - curve.x[i];
164 dy = (curve.y[next] - curve.y[i]) / delta_x;
165 dy *= dx;
166 ry = curve.y[i];
167 i = next;
170 if (rx<curve.x[0])
171 vector.push_back (curve.y[0]);
172 else if (rx>curve.x[num-1])
173 vector.push_back (curve.y[num-1]);
174 else
175 vector.push_back (ry);
176 ry += dy;
179 return vector;
182 void MyCurve::interpolate (int width, int height) {
184 this->height = height;
185 point.clear ();
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));
190 point.push_back (p);
195 void MyCurve::draw (int width, int height) {
197 if (!pixmap)
198 return;
200 if (this->height != height || point.size() != width)
201 interpolate (width, height);
203 Gtk::StateType state = Gtk::STATE_NORMAL;
204 if (!is_sensitive())
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);
220 cr->fill ();
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);
236 cr->stroke ();
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());
244 cr->stroke ();
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);
251 /* draw a bullet: */
252 cr->arc (x, y, RADIUS, 0, 2*M_PI);
253 cr->fill ();
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;
262 int src, dst;
263 GdkEventMotion *mevent;
264 std::vector<double>::iterator itx, ity;
266 bool retval = false;
268 int width = get_allocation().get_width() - RADIUS * 2;
269 int height = get_allocation().get_height() - RADIUS * 2;
271 if ((width < 0) || (height < 0))
272 return false;
274 /* get the pointer position */
275 int tx, ty;
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);
288 closest_point = i;
292 switch (event->type) {
293 case Gdk::CONFIGURE:
294 if (pixmap)
295 pixmap.clear ();
296 /* fall through */
298 case Gdk::EXPOSE:
299 if (!pixmap) {
300 pixmap = Gdk::Pixmap::create (get_window(), get_allocation().get_width(), get_allocation().get_height());
301 interpolate (width, height);
303 draw (width, height);
305 break;
307 case Gdk::BUTTON_PRESS:
308 add_modal_grab ();
309 new_type = Gdk::PLUS;
310 switch (curve.type) {
311 case Linear:
312 case Spline:
313 if (distance > MIN_DISTANCE) {
314 /* insert a new control point */
315 if (num > 0) {
316 int cx = (int)((width-1)*curve.x[closest_point]+0.5);
317 if (x > cx)
318 ++closest_point;
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);
325 num++;
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);
332 notifyListener ();
333 break;
335 draw (width, height);
336 retval = true;
337 break;
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];
349 ++dst;
350 ++itx;
351 ++ity;
354 if (dst < 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;
365 grab_point = -1;
366 retval = true;
367 notifyListener ();
368 break;
370 case Gdk::MOTION_NOTIFY:
371 mevent = (GdkEventMotion *) event;
373 switch (curve.type) {
374 case Linear:
375 case Spline:
376 if (grab_point == -1) {
377 /* if no point is grabbed... */
378 if (distance <= MIN_DISTANCE)
379 new_type = Gdk::FLEUR;
380 else
381 new_type = Gdk::PLUS;
383 else {
384 /* drag the grabbed point */
385 new_type = Gdk::FLEUR;
386 int leftbound = -MIN_DISTANCE;
387 if (grab_point > 0)
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;
396 else {
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);
402 notifyListener ();
404 break;
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);
411 delete cursor;
414 retval = true;
415 break;
417 default:
418 break;
421 return retval;
425 std::vector<double> MyCurve::getPoints () {
427 std::vector<double> result;
428 if (curve.type==Linear)
429 result.push_back (-1.0);
430 else
431 result.push_back (+1.0);
432 for (int i=0; i<curve.x.size(); i++)
433 if (curve.x[i]>=0) {
434 result.push_back (curve.x[i]);
435 result.push_back (curve.y[i]);
437 return result;
440 void MyCurve::setPoints (const std::vector<double>& p) {
442 int ix = 0;
443 if (p[ix++]>0)
444 curve.type = Spline;
445 else
446 curve.type = Linear;
448 curve.x.clear ();
449 curve.y.clear ();
450 for (int i=0; i<p.size()/2; i++) {
451 curve.x.push_back (p[ix++]);
452 curve.y.push_back (p[ix++]);
454 pixmap.clear ();
455 bool pi = pixmap;
458 void MyCurve::setType (CurveType t) {
460 curve.type = t;
461 pixmap.clear ();
464 void MyCurve::notifyListener () {
465 if (listener)
466 listener->curveChanged ();