1
2
3 #include "toonzqt/tonecurvefield.h"
4 #include "toonzqt/fxhistogramrender.h"
5 #include "toonzqt/doublepairfield.h"
6 #include "toonzqt/checkbox.h"
7 #include "toonzqt/doublefield.h"
8 #include <QPainter>
9 #include <QPainterPath>
10 #include <QStackedWidget>
11 #include <QMouseEvent>
12 #include <QApplication>
13 #include <QHBoxLayout>
14 #include <QComboBox>
15 #include <QLabel>
16
17 using namespace DVGui;
18
19 //-----------------------------------------------------------------------------
20 namespace {
21
22 // minimum distance between control points
23 const double cpMargin = 4.0;
24
25 //-----------------------------------------------------------------------------
26
getPercentAtPoint(QPointF point,QPainterPath path)27 double getPercentAtPoint(QPointF point, QPainterPath path) {
28 int i;
29 for (i = 1; i < 100; i++) {
30 double p = double(i) * 0.01;
31 QPointF pathPoint = path.pointAtPercent(p);
32 if (std::abs(pathPoint.x() - point.x()) < 3 &&
33 std::abs(pathPoint.y() - point.y()) < 3)
34 return p;
35 }
36 return 0;
37 }
38
39 //-----------------------------------------------------------------------------
40
getIntersectedPoint(QRectF rect,QPainterPath path)41 QList<QPointF> getIntersectedPoint(QRectF rect, QPainterPath path) {
42 int y0 = rect.top();
43 int y1 = rect.bottom();
44 QList<QPointF> points;
45 double g = 1.0 / 256.0;
46 QPointF prec = path.pointAtPercent(0);
47 QPointF point = path.pointAtPercent(g);
48 int j = 0;
49 for (j = 2; j < 256; j++) {
50 QPointF next = path.pointAtPercent(double(j) * g);
51 if (prec.y() >= y0 && prec.y() <= y1) {
52 if (point.y() < y0)
53 points.push_back(QPointF(prec.x(), y0));
54 else if (point.y() > y1)
55 points.push_back(QPointF(prec.x(), y1));
56 }
57 if (next.y() >= y0 && next.y() <= y1) {
58 if (point.y() < y0)
59 points.push_back(QPointF(next.x(), y0));
60 else if (point.y() > y1)
61 points.push_back(QPointF(next.x(), y1));
62 }
63 prec = point;
64 point = next;
65 }
66 return points;
67 }
68
69 //-----------------------------------------------------------------------------
70
qtNorm2(const QPointF & p)71 double qtNorm2(const QPointF &p) { return p.x() * p.x() + p.y() * p.y(); }
72
qtDistance2(const QPointF & p1,const QPointF & p2)73 double qtDistance2(const QPointF &p1, const QPointF &p2) {
74 return qtNorm2(p2 - p1);
75 }
76
77 //---------------------------------------------------------
78 // referred to the truncateSpeeds() in tdoubleparam.cpp
truncateSpeeds(double aFrame,double bFrame,QVector2D & aSpeedTrunc,QVector2D & bSpeedTrunc)79 void truncateSpeeds(double aFrame, double bFrame, QVector2D &aSpeedTrunc,
80 QVector2D &bSpeedTrunc) {
81 double deltaX = bFrame - aFrame;
82 if (aSpeedTrunc.x() < 0) aSpeedTrunc.setX(0);
83 if (bSpeedTrunc.x() > 0) bSpeedTrunc.setX(0);
84
85 if (aFrame + aSpeedTrunc.x() > bFrame) {
86 if (aSpeedTrunc.x() != 0) {
87 aSpeedTrunc = aSpeedTrunc * (deltaX / aSpeedTrunc.x());
88 }
89 }
90
91 if (bFrame + bSpeedTrunc.x() < aFrame) {
92 if (bSpeedTrunc.x() != 0) {
93 bSpeedTrunc = -bSpeedTrunc * (deltaX / bSpeedTrunc.x());
94 }
95 }
96 }
97
98 } // anonymous namespace
99 //-----------------------------------------------------------------------------
100
101 //=============================================================================
102 // ChennelCurveEditor
103 //=============================================================================
104
ChennelCurveEditor(QWidget * parent,HistogramView * histogramView)105 ChennelCurveEditor::ChennelCurveEditor(QWidget *parent,
106 HistogramView *histogramView)
107 : QWidget(parent)
108 , m_histogramView(histogramView)
109 , m_currentControlPointIndex(-1)
110 , m_mouseButton(Qt::NoButton)
111 , m_curveHeight(256)
112 , m_LeftRightMargin(42)
113 , m_TopMargin(9)
114 , m_BottomMargin(48)
115 , m_isLinear(false)
116 , m_isEnlarged(false) {
117 setFixedSize(m_curveHeight + 2 * m_LeftRightMargin + 2,
118 m_curveHeight + m_TopMargin + m_BottomMargin);
119 setAttribute(Qt::WA_KeyCompression);
120 setFocusPolicy(Qt::StrongFocus);
121 setMouseTracking(true);
122
123 m_histogramView->setDrawnWidget(this);
124 m_histogramView->setGraphHeight(m_curveHeight);
125 m_histogramView->setGraphAlphaMask(120);
126 m_verticalChannelBar =
127 new ChannelBar(0, m_histogramView->getChannelBarColor(), false);
128 }
129
130 //-----------------------------------------------------------------------------
131
setPoints(QList<TPointD> points)132 void ChennelCurveEditor::setPoints(QList<TPointD> points) {
133 if (!m_points.isEmpty()) m_points.clear();
134 for (const TPointD &point : points)
135 m_points.push_back(QPointF(point.x, point.y));
136 // update slider position according to the first and the last control points
137 int firstIndex = 3;
138 int lastIndex = m_points.size() - 4;
139 emit firstLastXPostionChanged(m_points.at(firstIndex).x(),
140 m_points.at(lastIndex).x());
141 update();
142 }
143
144 //-----------------------------------------------------------------------------
145
getPoints()146 QList<TPointD> ChennelCurveEditor::getPoints() {
147 QList<TPointD> points;
148 if (m_points.isEmpty()) return points;
149 for (const QPointF &point : m_points)
150 points.push_back(TPointD(point.x(), point.y()));
151 return points;
152 }
153
154 //-----------------------------------------------------------------------------
155
setFirstLastXPosition(std::pair<double,double> values,bool isDragging)156 void ChennelCurveEditor::setFirstLastXPosition(std::pair<double, double> values,
157 bool isDragging) {
158 QPointF newX0(values.first, 0);
159 QPointF newX1(values.second, 0);
160
161 int indexX0 = 3;
162 int indexX1 = m_points.size() - 4;
163 QPointF x0 = m_points.at(indexX0);
164 QPointF x1 = m_points.at(indexX1);
165 if (x0.x() != newX0.x()) {
166 QPointF delta(newX0.x() - x0.x(), 0);
167 moveCentralControlPoint(indexX0, delta);
168 update();
169 }
170 if (x1.x() != newX1.x()) {
171 QPointF delta(newX1.x() - x1.x(), 0);
172 moveCentralControlPoint(indexX1, delta);
173 update();
174 }
175 m_currentControlPointIndex = -1;
176
177 if (!isDragging) emit controlPointChanged(false);
178 }
179
180 //-----------------------------------------------------------------------------
181
setLinear(bool isLinear)182 void ChennelCurveEditor::setLinear(bool isLinear) {
183 if (m_isLinear == isLinear) return;
184 m_isLinear = isLinear;
185 update();
186 }
187
188 //-----------------------------------------------------------------------------
189
setEnlarged(bool isEnlarged)190 void ChennelCurveEditor::setEnlarged(bool isEnlarged) {
191 if (m_isEnlarged == isEnlarged) return;
192 m_isEnlarged = isEnlarged;
193 int widgetHeight = (m_isEnlarged) ? m_curveHeight * 2 : m_curveHeight;
194 setFixedSize(widgetHeight + 2 * m_LeftRightMargin + 2,
195 widgetHeight + m_TopMargin + m_BottomMargin);
196 m_histogramView->setGraphHeight(widgetHeight);
197 m_verticalChannelBar->setFixedHeight(widgetHeight + 22);
198 update();
199 }
200
201 //-----------------------------------------------------------------------------
202
setLabelRange(ChannelBar::Range range)203 void ChennelCurveEditor::setLabelRange(ChannelBar::Range range) {
204 m_histogramView->channelBar()->setLabelRange(range);
205 m_verticalChannelBar->setLabelRange(range);
206 }
207
208 //-----------------------------------------------------------------------------
209
checkPoint(const QPointF p)210 QPointF ChennelCurveEditor::checkPoint(const QPointF p) {
211 QPointF checkedP = p;
212 if (p.x() < 0)
213 checkedP.setX(0);
214 else if (p.x() > m_curveHeight)
215 checkedP.setX(m_curveHeight);
216 if (p.y() < 0)
217 checkedP.setY(0);
218 else if (p.y() > m_curveHeight)
219 checkedP.setY(m_curveHeight);
220 return checkedP;
221 }
222
223 //-----------------------------------------------------------------------------
224
getVisibleHandlePos(int index) const225 QPointF ChennelCurveEditor::getVisibleHandlePos(int index) const {
226 QRectF rect(0.0, 0.0, m_curveHeight, m_curveHeight);
227 QPointF handlePos(m_points.at(index));
228 if (isCentralControlPoint(index) || rect.contains(handlePos))
229 return handlePos;
230
231 if (isLeftControlPoint(index)) {
232 QPointF cp = m_points.at(index + 1);
233 QVector2D lHandle(handlePos - cp);
234 if (handlePos.x() < 0) {
235 float ratio = -cp.x() / lHandle.x();
236 handlePos = cp + lHandle.toPointF() * ratio;
237 }
238 if (handlePos.y() < 0) {
239 float ratio = -cp.y() / lHandle.y();
240 handlePos = cp + lHandle.toPointF() * ratio;
241 } else if (handlePos.y() > 256) {
242 float ratio = (256 - cp.y()) / lHandle.y();
243 handlePos = cp + lHandle.toPointF() * ratio;
244 }
245 } else { // isRightControlPoint
246 QPointF cp = m_points.at(index - 1);
247 QVector2D rHandle(handlePos - cp);
248 if (handlePos.x() > 256) {
249 float ratio = (256 - cp.x()) / rHandle.x();
250 handlePos = cp + rHandle.toPointF() * ratio;
251 }
252 if (handlePos.y() < 0) {
253 float ratio = -cp.y() / rHandle.y();
254 handlePos = cp + rHandle.toPointF() * ratio;
255 } else if (handlePos.y() > 256) {
256 float ratio = (256 - cp.y()) / rHandle.y();
257 handlePos = cp + rHandle.toPointF() * ratio;
258 }
259 }
260 return handlePos;
261 }
262
263 //-----------------------------------------------------------------------------
264
viewToStrokePoint(const QPointF & p)265 QPointF ChennelCurveEditor::viewToStrokePoint(const QPointF &p) {
266 double x = p.x() - m_LeftRightMargin - 1;
267 double y = p.y() - m_TopMargin;
268 if (m_isEnlarged) {
269 x *= 0.5;
270 y *= 0.5;
271 }
272 return QPointF(x, m_curveHeight - y);
273 }
274
275 //-----------------------------------------------------------------------------
276
getClosestPointIndex(const QPointF & pos,double & minDistance2) const277 int ChennelCurveEditor::getClosestPointIndex(const QPointF &pos,
278 double &minDistance2) const {
279 int closestPointIndex = -1;
280 minDistance2 = 0;
281 enum pointType { Handle = 0, ControlPoint, PseudoHandle } closestPointType;
282 QRectF rect(0, 0, m_curveHeight, m_curveHeight);
283 int i;
284 for (i = 3; i < (int)m_points.size() - 3; i++) {
285 if (m_isLinear && !isCentralControlPoint(i)) continue;
286 QPointF visiblePoint = getVisibleHandlePos(i);
287
288 pointType type =
289 (isCentralControlPoint(i))
290 ? ControlPoint
291 : (visiblePoint == m_points.at(i)) ? Handle : PseudoHandle;
292
293 double distance2 = qtDistance2(pos, visiblePoint);
294 if (closestPointIndex < 0 || distance2 < minDistance2 ||
295 (distance2 == minDistance2 && type < closestPointType)) {
296 minDistance2 = distance2;
297 closestPointIndex = i;
298 closestPointType = type;
299 }
300 }
301 return closestPointIndex;
302 }
303
304 //-----------------------------------------------------------------------------
305
movePoint(int index,const QPointF delta)306 void ChennelCurveEditor::movePoint(int index, const QPointF delta) {
307 QPointF p = m_points.at(index);
308 p += delta;
309 setPoint(index, p);
310
311 int firstIndex = 3;
312 int lastIndex = m_points.size() - 4;
313 if (index == firstIndex)
314 emit firstLastXPostionChanged(p.x(), m_points.at(lastIndex).x());
315 if (index == lastIndex)
316 emit firstLastXPostionChanged(m_points.at(firstIndex).x(), p.x());
317 }
318
319 //-----------------------------------------------------------------------------
320
setPoint(int index,const QPointF p)321 void ChennelCurveEditor::setPoint(int index, const QPointF p) {
322 m_points.removeAt(index);
323 m_points.insert(index, p);
324
325 int firstIndex = 3;
326 int lastIndex = m_points.size() - 4;
327 if (index == firstIndex)
328 emit firstLastXPostionChanged(p.x(), m_points.at(lastIndex).x());
329 if (index == lastIndex)
330 emit firstLastXPostionChanged(m_points.at(firstIndex).x(), p.x());
331 }
332
333 //-----------------------------------------------------------------------------
334
moveCurrentControlPoint(QPointF delta)335 void ChennelCurveEditor::moveCurrentControlPoint(QPointF delta) {
336 assert(m_currentControlPointIndex != -1);
337 int pointCount = m_points.size();
338 // in case moving the control point
339 if (isCentralControlPoint(m_currentControlPointIndex))
340 moveCentralControlPoint(m_currentControlPointIndex, delta);
341 // in case moving the left handle
342 else if (isLeftControlPoint(m_currentControlPointIndex)) {
343 QPointF cp = m_points.at(m_currentControlPointIndex + 1);
344 QPointF left = m_points.at(m_currentControlPointIndex);
345
346 // handle should not move across the control point
347 if (left.x() + delta.x() > cp.x()) delta.setX(cp.x() - left.x());
348
349 left += delta;
350 setPoint(m_currentControlPointIndex, left);
351
352 // rotate the opposite handle keeping the handle length unchanged
353 if (m_currentControlPointIndex < pointCount - 5) {
354 QVector2D lHandle(cp - left);
355 if (!lHandle.isNull()) {
356 QPointF right = m_points.at(m_currentControlPointIndex + 2);
357 QVector2D rHandle(right - cp);
358 QVector2D newRHandle = lHandle.normalized() * rHandle.length();
359 setPoint(m_currentControlPointIndex + 2, cp + newRHandle.toPointF());
360 }
361 }
362 emit controlPointChanged(true);
363 }
364 // in case moving the right handle
365 else {
366 assert(isRightControlPoint(m_currentControlPointIndex));
367 QPointF cp = m_points.at(m_currentControlPointIndex - 1);
368 QPointF right = m_points.at(m_currentControlPointIndex);
369
370 // handle should not move across the control point
371 if (right.x() + delta.x() < cp.x()) delta.setX(cp.x() - right.x());
372
373 right += delta;
374 setPoint(m_currentControlPointIndex, right);
375
376 // rotate the opposite handle keeping the handle length unchanged
377 if (m_currentControlPointIndex > 4) {
378 QVector2D rHandle(cp - right);
379 if (!rHandle.isNull()) {
380 QPointF left = m_points.at(m_currentControlPointIndex - 2);
381 QVector2D lHandle(left - cp);
382 QVector2D newLHandle = rHandle.normalized() * lHandle.length();
383 setPoint(m_currentControlPointIndex - 2, cp + newLHandle.toPointF());
384 }
385 }
386 emit controlPointChanged(true);
387 }
388 update();
389 emit updateCurrentPosition(m_currentControlPointIndex,
390 m_points.at(m_currentControlPointIndex));
391 }
392
393 //-----------------------------------------------------------------------------
394
moveCentralControlPoint(int index,QPointF delta)395 void ChennelCurveEditor::moveCentralControlPoint(int index, QPointF delta) {
396 int pointCount = m_points.size();
397
398 assert(index < pointCount - 3 && index > 2);
399
400 QPointF p = m_points.at(index);
401 QPointF d = delta;
402 // Trovo il valore di delta im modo tale che il punto di controllo non sia
403 // trascinato fuori dal range consentito
404 QPointF newPoint = checkPoint(p + delta);
405 d = newPoint - p;
406
407 QPointF nextP = m_points.at(index + 3);
408 QPointF precP = m_points.at(index - 3);
409 double nextDistance = nextP.x() - (p.x() + d.x());
410 double precDistance = (p.x() + d.x()) - precP.x();
411
412 if (nextDistance < cpMargin)
413 d = QPointF(nextP.x() - p.x() - cpMargin, d.y());
414 else if (precDistance < cpMargin)
415 d = QPointF(precP.x() - p.x() + cpMargin, d.y());
416
417 if (d.isNull()) return;
418
419 // Punto di controllo speciale: il primo visualizzato.
420 if (index == 3) {
421 QPointF dY = QPointF(0, d.y());
422 movePoint(index - 1, dY);
423 movePoint(index - 2, dY);
424 movePoint(index - 3, dY);
425 }
426 // Punto di controllo speciale: l'ultimo visualizzato.
427 if (index == pointCount - 4) {
428 QPointF dY = QPointF(0, d.y());
429 movePoint(index + 1, dY);
430 movePoint(index + 2, dY);
431 movePoint(index + 3, dY);
432 }
433 if (index > 3) movePoint(index - 1, d);
434 if (index < pointCount - 4) movePoint(index + 1, d);
435 movePoint(index, d);
436 emit controlPointChanged(true);
437 }
438
439 //-----------------------------------------------------------------------------
440
addControlPoint(double percent)441 void ChennelCurveEditor::addControlPoint(double percent) {
442 QPainterPath path = getPainterPath();
443 QPointF p = path.pointAtPercent(percent);
444
445 // Cerco il punto di controllo precedente
446 int pointCount = m_points.size();
447 int beforeControlPointIndex;
448 for (beforeControlPointIndex = pointCount - 1; beforeControlPointIndex >= 0;
449 beforeControlPointIndex--) {
450 QPointF point = m_points.at(beforeControlPointIndex);
451 if (isCentralControlPoint(beforeControlPointIndex) && point.x() < p.x())
452 break;
453 }
454
455 if (beforeControlPointIndex == 0 || beforeControlPointIndex == pointCount - 4)
456 return;
457
458 QPointF p0 = checkPoint(m_points.at(beforeControlPointIndex));
459 // Se sono troppo vicino al punto di controllo precedente ritorno
460 if (std::abs(p.x() - p0.x()) <= cpMargin) return;
461 double beforeControlPointPercent = getPercentAtPoint(p0, path);
462 QPointF p1 = checkPoint(m_points.at(beforeControlPointIndex + 1));
463 QPointF p2 = checkPoint(m_points.at(beforeControlPointIndex + 2));
464 QPointF p3 = checkPoint(m_points.at(beforeControlPointIndex + 3));
465 // Se sono troppo vicino al punto di controllo successivo ritorno
466 if (std::abs(p3.x() - p.x()) <= cpMargin) return;
467 double nextControlPointPercent = getPercentAtPoint(p3, path);
468
469 // Calcolo la velocita' e quindi il coiffciente angolare.
470 double t =
471 percent * 100 / (nextControlPointPercent - beforeControlPointPercent);
472 double s = t - 1;
473 QPointF speed =
474 3.0 * ((p1 - p0) * s * s + 2 * (p2 - p0) * s * t + (p3 - p2) * t * t);
475 double m = speed.y() / speed.x();
476
477 int newControlPointIndex = beforeControlPointIndex + 3;
478 m_points.insert(newControlPointIndex - 1,
479 QPointF(p.x() - 16, p.y() - 16 * m));
480 m_points.insert(newControlPointIndex, p);
481 m_points.insert(newControlPointIndex + 1,
482 QPointF(p.x() + 16, p.y() + 16 * m));
483
484 m_currentControlPointIndex = newControlPointIndex;
485 m_preMousePos = p;
486 emit controlPointAdded(newControlPointIndex);
487 update();
488 }
489
490 //-----------------------------------------------------------------------------
491
removeCurrentControlPoint()492 void ChennelCurveEditor::removeCurrentControlPoint() {
493 removeControlPoint(m_currentControlPointIndex);
494 }
495
496 //-----------------------------------------------------------------------------
497
selectNextControlPoint()498 void ChennelCurveEditor::selectNextControlPoint() {
499 int controlPointCount = (int)m_points.size();
500
501 if (controlPointCount == 0) return;
502
503 int firstVisibleControlPoint = 3;
504 int lastVisibleControlPoint = m_points.size() - 4;
505
506 m_currentControlPointIndex++;
507 if (m_currentControlPointIndex < firstVisibleControlPoint ||
508 m_currentControlPointIndex > lastVisibleControlPoint)
509 m_currentControlPointIndex = firstVisibleControlPoint;
510
511 emit updateCurrentPosition(m_currentControlPointIndex,
512 m_points.at(m_currentControlPointIndex));
513 update();
514 }
515
516 //-----------------------------------------------------------------------------
517
selectPreviousControlPoint()518 void ChennelCurveEditor::selectPreviousControlPoint() {
519 int controlPointCount = (int)m_points.size();
520
521 if (controlPointCount == 0) return;
522
523 int firstVisibleControlPoint = 3;
524 int lastVisibleControlPoint = m_points.size() - 4;
525
526 m_currentControlPointIndex--;
527 if (m_currentControlPointIndex < firstVisibleControlPoint ||
528 m_currentControlPointIndex > lastVisibleControlPoint)
529 m_currentControlPointIndex = lastVisibleControlPoint;
530
531 emit updateCurrentPosition(m_currentControlPointIndex,
532 m_points.at(m_currentControlPointIndex));
533 update();
534 }
535
536 //-----------------------------------------------------------------------------
537
removeControlPoint(int index)538 void ChennelCurveEditor::removeControlPoint(int index) {
539 // Non posso eliminare il primo punto di controllo visibile quindi lo rimetto
540 // in condizione iniziale
541 if (index <= 4) {
542 setPoint(0, QPointF(-40, 0));
543 setPoint(1, QPointF(-20, 0));
544 setPoint(2, QPointF(-20, 0));
545 setPoint(3, QPointF(0, 0));
546 setPoint(4, QPointF(16, 16));
547 update();
548 emit controlPointChanged(false);
549 return;
550 }
551 // Non posso eliminare il l'ultimo punto di controllo visibile quindi lo
552 // rimetto in condizione iniziale
553 if (index >= m_points.size() - 5) {
554 int i = m_points.size() - 5;
555 setPoint(i, QPointF(239, 239));
556 setPoint(i + 1, QPointF(255, 255));
557 setPoint(i + 2, QPointF(275, 255));
558 setPoint(i + 3, QPointF(275, 255));
559 setPoint(i + 4, QPointF(295, 255));
560 update();
561 emit controlPointChanged(false);
562 return;
563 }
564
565 int firstIndex = 0;
566 if (isCentralControlPoint(index))
567 firstIndex = index - 1;
568 else if (isLeftControlPoint(index))
569 firstIndex = index;
570 else
571 firstIndex = index - 2;
572
573 m_points.removeAt(firstIndex);
574 m_points.removeAt(firstIndex);
575 m_points.removeAt(firstIndex);
576
577 emit controlPointRemoved(firstIndex + 1);
578 m_currentControlPointIndex = firstIndex - 2;
579
580 emit updateCurrentPosition(m_currentControlPointIndex,
581 m_points.at(m_currentControlPointIndex));
582
583 update();
584 }
585
586 //-----------------------------------------------------------------------------
587
getPainterPath()588 QPainterPath ChennelCurveEditor::getPainterPath() {
589 int pointCount = m_points.size();
590 if (pointCount == 0) return QPainterPath();
591
592 QPointF p0 = m_points.at(0);
593 QPainterPath path(p0);
594 int i;
595 for (i = 1; i < pointCount; i++) {
596 QPointF p1 = m_points.at(i);
597 QPointF p2 = m_points.at(++i);
598 QPointF p3 = m_points.at(++i);
599 path.moveTo(p0);
600 if (!m_isLinear) {
601 // truncate speed
602 QVector2D aSpeed(p1 - p0);
603 QVector2D bSpeed(p2 - p3);
604 truncateSpeeds(p0.x(), p3.x(), aSpeed, bSpeed);
605 path.cubicTo(p0 + aSpeed.toPointF(), p3 + bSpeed.toPointF(), p3);
606 } else
607 path.lineTo(p3);
608 p0 = p3;
609 }
610
611 // Cerco le eventuali intersezioni con il bordo.
612 QRectF rect(0, 0, m_curveHeight, m_curveHeight);
613 // QRectF rect(m_LeftRightMargin, m_TopMargin, m_curveHeight, m_curveHeight);
614 QRectF r = path.boundingRect();
615 if (!rect.contains(QRect(rect.left(), r.top(), rect.width(), r.height()))) {
616 QList<QPointF> points = getIntersectedPoint(rect, path);
617 // Se trovo punti di intersezione (per come e' definita la curva devono
618 // essere pari)
619 // faccio l'unione del path calcolato e di nuovi path lineari.
620 int j = 0;
621 for (j = 0; j < points.size(); j++) {
622 QPointF p0 = points.at(j);
623 QPointF p1 = points.at(++j);
624 QPainterPath line(p0);
625 line.lineTo(p1);
626 path.addPath(line);
627 }
628 }
629
630 return path;
631 }
632
633 //-----------------------------------------------------------------------------
634
paintEvent(QPaintEvent * e)635 void ChennelCurveEditor::paintEvent(QPaintEvent *e) {
636 QPainter painter(this);
637
638 double scale = (m_isEnlarged) ? 2.0 : 1.0;
639 // Disegno il reticolato
640 painter.setRenderHint(QPainter::Antialiasing, false);
641 painter.setPen(QColor(250, 250, 250));
642
643 // Disegno l'histogram.
644 m_histogramView->draw(&painter, QPoint(m_LeftRightMargin - 10, 0),
645 m_curveHeight * scale);
646
647 // Disegno la barra verticale a sinistra.
648 m_verticalChannelBar->draw(
649 &painter,
650 QPoint(0, -2)); //-1 == m_topMargin- il margine della barra(=10+1).
651
652 QRectF r = rect().adjusted(m_LeftRightMargin, m_TopMargin, -m_LeftRightMargin,
653 -m_BottomMargin);
654 // Disegno la curva entro i limiti del grafo
655 painter.setClipRect(r, Qt::IntersectClip);
656
657 painter.translate(m_LeftRightMargin + 1, height() - m_BottomMargin);
658 painter.scale(scale, -scale);
659
660 QPainterPath path = getPainterPath();
661 if (path.isEmpty()) return;
662 painter.setRenderHint(QPainter::Antialiasing, true);
663 QPen blackPen(Qt::black);
664 QPen bluePen(Qt::blue);
665 blackPen.setWidthF(1.0 / scale);
666 bluePen.setWidthF(1.0 / scale);
667
668 painter.setPen(blackPen);
669 painter.setBrush(Qt::NoBrush);
670 painter.drawPath(path);
671
672 int n = m_points.size();
673 QPointF p = m_points.at(3);
674 for (int i = 3; i < n - 3; i++) {
675 QBrush brush(Qt::white);
676 int rad = (m_isEnlarged) ? 1 : 2;
677 QPointF nextP = m_points.at(i + 1);
678 if (isCentralControlPoint(i))
679 rad = (m_isEnlarged) ? 2 : 3;
680 else if (m_isLinear) {
681 p = nextP;
682 continue;
683 }
684 painter.setPen(blackPen);
685
686 QPointF handlePos = p;
687 if (!m_isLinear) {
688 if (isLeftControlPoint(i)) {
689 painter.drawLine(p, nextP);
690 } else if (isCentralControlPoint(i) && i < n - 4)
691 painter.drawLine(p, nextP);
692
693 handlePos = getVisibleHandlePos(i);
694 }
695
696 painter.setBrush((m_currentControlPointIndex != i)
697 ? Qt::white
698 : (p == handlePos) ? Qt::black : Qt::blue);
699 painter.setPen((p == handlePos) ? blackPen : bluePen);
700
701 QRectF pointRect(handlePos.x() - rad, handlePos.y() - rad, 2 * rad,
702 2 * rad);
703 painter.drawEllipse(pointRect);
704 p = nextP;
705 }
706 }
707
708 //-----------------------------------------------------------------------------
709
mouseMoveEvent(QMouseEvent * e)710 void ChennelCurveEditor::mouseMoveEvent(QMouseEvent *e) {
711 QPointF posF = viewToStrokePoint(QPointF(e->pos()));
712 if (m_mouseButton == Qt::LeftButton && m_currentControlPointIndex != -1) {
713 moveCurrentControlPoint(posF - m_preMousePos);
714 m_preMousePos = posF;
715 } else if (m_currentControlPointIndex == -1)
716 emit updateCurrentPosition(-1, posF);
717 }
718
719 //-----------------------------------------------------------------------------
720
mousePressEvent(QMouseEvent * e)721 void ChennelCurveEditor::mousePressEvent(QMouseEvent *e) {
722 m_mouseButton = e->button();
723 setFocus();
724 if (m_mouseButton == Qt::LeftButton) {
725 QPointF posF = viewToStrokePoint(QPointF(e->pos()));
726 double minDistance;
727 int controlPointIndex = getClosestPointIndex(posF, minDistance);
728
729 // Se la distanza e' piccola seleziono il control point corrente
730 if (minDistance < 20) {
731 m_currentControlPointIndex = controlPointIndex;
732 m_preMousePos = posF;
733 } else {
734 m_currentControlPointIndex = -1;
735 // Se sono sufficentemente lontano da un punto di controllo, ma abbastanza
736 // vicino alla curva
737 // aggiungo un punto di controllo
738 double percent = getPercentAtPoint(posF, getPainterPath());
739 if (percent != 0 && minDistance > 20) addControlPoint(percent);
740 }
741 QPointF currentPointPos = (m_currentControlPointIndex == -1)
742 ? posF
743 : m_points.at(m_currentControlPointIndex);
744
745 emit updateCurrentPosition(m_currentControlPointIndex, currentPointPos);
746 update();
747 }
748 }
749
750 //-----------------------------------------------------------------------------
751
mouseReleaseEvent(QMouseEvent * e)752 void ChennelCurveEditor::mouseReleaseEvent(QMouseEvent *e) {
753 // fx preview updates here ( it does not update while mouse dragging )
754 if (m_mouseButton == Qt::LeftButton && m_currentControlPointIndex != -1 &&
755 e->button() == Qt::LeftButton)
756 emit controlPointChanged(false);
757 m_mouseButton = Qt::NoButton;
758 }
759
760 //-----------------------------------------------------------------------------
761
keyPressEvent(QKeyEvent * e)762 void ChennelCurveEditor::keyPressEvent(QKeyEvent *e) {
763 if (m_currentControlPointIndex == -1) return;
764
765 if (e->key() == Qt::Key_Delete) {
766 removeCurrentControlPoint();
767 return;
768 }
769
770 bool controlPressed = e->modifiers() & Qt::ControlModifier;
771 bool shiftPressed = e->modifiers() & Qt::ShiftModifier;
772 float delta = (shiftPressed) ? 10.0 : 1.0;
773
774 if (e->key() == Qt::Key_Right) {
775 if (controlPressed)
776 selectNextControlPoint();
777 else
778 moveCurrentControlPoint(QPointF(delta, 0.0));
779 } else if (e->key() == Qt::Key_Left) {
780 if (controlPressed)
781 selectPreviousControlPoint();
782 else
783 moveCurrentControlPoint(QPointF(-delta, 0.0));
784 } else if (e->key() == Qt::Key_Up)
785 moveCurrentControlPoint(QPointF(0.0, delta));
786 else if (e->key() == Qt::Key_Down)
787 moveCurrentControlPoint(QPointF(0.0, -delta));
788 }
789
790 //--------------------------------------------------------
791
eventFilter(QObject * object,QEvent * event)792 bool ChennelCurveEditor::eventFilter(QObject *object, QEvent *event) {
793 if (event->type() == QEvent::Shortcut ||
794 event->type() == QEvent::ShortcutOverride) {
795 if (!object->inherits("FxSettings")) {
796 event->accept();
797 return true;
798 }
799 }
800 return false;
801 }
802
803 //--------------------------------------------------------
804
focusInEvent(QFocusEvent * fe)805 void ChennelCurveEditor::focusInEvent(QFocusEvent *fe) {
806 QWidget::focusInEvent(fe);
807 qApp->installEventFilter(this);
808 }
809
810 //--------------------------------------------------------
811
focusOutEvent(QFocusEvent * fe)812 void ChennelCurveEditor::focusOutEvent(QFocusEvent *fe) {
813 emit focusOut();
814 QWidget::focusOutEvent(fe);
815 qApp->removeEventFilter(this);
816 update();
817 }
818
819 //=============================================================================
820 // ToneCurveField
821 //=============================================================================
822
ToneCurveField(QWidget * parent,FxHistogramRender * fxHistogramRender)823 ToneCurveField::ToneCurveField(QWidget *parent,
824 FxHistogramRender *fxHistogramRender)
825 : QWidget(parent), m_toneCurveStackedWidget(0), m_currentPointIndex(-1) {
826 setFixedWidth(400);
827 // setFixedWidth(368);
828
829 QStringList channels;
830 channels << "RGBA"
831 << "RGB"
832 << "Red"
833 << "Green"
834 << "Blue"
835 << "Alpha";
836 int channelCount = channels.size();
837 int currentChannelIndex = 0;
838
839 m_channelListChooser = new QComboBox(this);
840 m_channelListChooser->setFixedSize(100, 20);
841 m_channelListChooser->addItems(channels);
842 m_channelListChooser->setCurrentIndex(currentChannelIndex);
843
844 m_rangeMode = new QComboBox(this);
845 m_rangeMode->addItems({"0-255", "0.0-1.0"});
846 m_rangeMode->setCurrentIndex(0);
847
848 // stack widget dei grafi
849 m_toneCurveStackedWidget = new QStackedWidget(this);
850 Histograms *histograms = new Histograms(0, true);
851 fxHistogramRender->setHistograms(histograms);
852 int i;
853 for (i = 0; i < channelCount; i++) {
854 ChennelCurveEditor *c =
855 new ChennelCurveEditor(this, histograms->getHistogramView(i));
856 m_toneCurveStackedWidget->addWidget(c);
857 connect(c, SIGNAL(firstLastXPostionChanged(double, double)), this,
858 SLOT(onFirstLastXPostionChanged(double, double)));
859 connect(c, SIGNAL(updateCurrentPosition(int, QPointF)), this,
860 SLOT(onUpdateCurrentPosition(int, QPointF)));
861 }
862 m_toneCurveStackedWidget->setCurrentIndex(currentChannelIndex);
863
864 // stack widget degli slider
865 m_sliderStackedWidget = new QStackedWidget(this);
866 for (i = 0; i < channelCount; i++) {
867 DoublePairField *doublePairSlider = new DoublePairField(this);
868 doublePairSlider->setFixedHeight(20);
869 doublePairSlider->setLabelsEnabled(false);
870 doublePairSlider->setRange(0.0, 255.0);
871 doublePairSlider->setValues(std::make_pair(0.0, 255.0));
872 m_sliderStackedWidget->addWidget(doublePairSlider);
873 connect(doublePairSlider, SIGNAL(valuesChanged(bool)), this,
874 SLOT(sliderValueChanged(bool)));
875 }
876 m_sliderStackedWidget->setCurrentIndex(currentChannelIndex);
877
878 m_isLinearCheckBox = new CheckBox(QString("Linear"), this);
879 m_isEnlargedCheckBox = new CheckBox(QString("Enlarge"), this);
880 m_isEnlargedCheckBox->setChecked(false);
881 m_currentInput = new DoubleLineEdit(this);
882 m_currentOutput = new DoubleLineEdit(this);
883 m_currentInput->setFixedWidth(60);
884 m_currentOutput->setFixedWidth(60);
885 m_currentInput->setDecimals(2);
886 m_currentOutput->setDecimals(2);
887 m_currentInput->setDisabled(true);
888 m_currentOutput->setDisabled(true);
889
890 //------ layout
891
892 QVBoxLayout *mainLayout = new QVBoxLayout(this);
893 mainLayout->setMargin(0);
894 mainLayout->setSpacing(0);
895 {
896 QHBoxLayout *channelListLayout = new QHBoxLayout();
897 channelListLayout->setMargin(0);
898 channelListLayout->setSpacing(0);
899 channelListLayout->setAlignment(Qt::AlignCenter);
900 {
901 // lista canali: label+comboBox
902 channelListLayout->addWidget(new QLabel(tr("Channel:"), this));
903 channelListLayout->addWidget(m_channelListChooser);
904 channelListLayout->addSpacing(20);
905 channelListLayout->addWidget(new QLabel(tr("Range:"), this));
906 channelListLayout->addWidget(m_rangeMode);
907 channelListLayout->addSpacing(20);
908 channelListLayout->addWidget(m_isEnlargedCheckBox);
909 }
910 mainLayout->addLayout(channelListLayout, 0);
911
912 QGridLayout *bottomLayout = new QGridLayout();
913 bottomLayout->setMargin(0);
914 bottomLayout->setHorizontalSpacing(5);
915 bottomLayout->setVerticalSpacing(0);
916 {
917 QVBoxLayout *currentValLayout = new QVBoxLayout();
918 currentValLayout->setMargin(0);
919 currentValLayout->setSpacing(0);
920 currentValLayout->setAlignment(Qt::AlignLeft);
921 {
922 currentValLayout->addStretch(1);
923 currentValLayout->addWidget(new QLabel(tr("Output:"), this));
924 currentValLayout->addWidget(m_currentOutput);
925 currentValLayout->addSpacing(10);
926 currentValLayout->addWidget(new QLabel(tr("Input:"), this));
927 currentValLayout->addWidget(m_currentInput);
928 currentValLayout->addSpacing(10);
929 }
930 bottomLayout->addLayout(currentValLayout, 0, 0);
931
932 bottomLayout->addWidget(m_toneCurveStackedWidget, 0, 1, Qt::AlignHCenter);
933 bottomLayout->addWidget(m_sliderStackedWidget, 1, 1);
934 }
935 bottomLayout->setColumnStretch(1, 1);
936 bottomLayout->setRowStretch(0, 1);
937
938 mainLayout->addLayout(bottomLayout);
939 mainLayout->addSpacing(10);
940 mainLayout->addWidget(m_isLinearCheckBox, 0, Qt::AlignHCenter);
941 }
942 setLayout(mainLayout);
943
944 connect(m_isLinearCheckBox, SIGNAL(clicked(bool)),
945 SLOT(setLinearManually(bool)));
946 connect(m_isLinearCheckBox, SIGNAL(toggled(bool)), SLOT(setLinear(bool)));
947 connect(m_isEnlargedCheckBox, SIGNAL(toggled(bool)), SLOT(setEnlarged(bool)));
948
949 connect(m_channelListChooser, SIGNAL(currentIndexChanged(int)), this,
950 SLOT(onCurrentChannelSwitched(int)));
951 connect(m_rangeMode, SIGNAL(currentIndexChanged(int)), this,
952 SLOT(onRangeModeSwitched(int)));
953 connect(m_currentInput, SIGNAL(editingFinished()), this,
954 SLOT(onCurrentPointEditted()));
955 connect(m_currentOutput, SIGNAL(editingFinished()), this,
956 SLOT(onCurrentPointEditted()));
957 }
958
959 //-----------------------------------------------------------------------------
960
setCurrentChannel(int currentChannel)961 void ToneCurveField::setCurrentChannel(int currentChannel) {
962 m_channelListChooser->setCurrentIndex(currentChannel);
963 }
964
965 //-----------------------------------------------------------------------------
966
getChannelEditor(int channel) const967 ChennelCurveEditor *ToneCurveField::getChannelEditor(int channel) const {
968 ChennelCurveEditor *c = dynamic_cast<ChennelCurveEditor *>(
969 m_toneCurveStackedWidget->widget(channel));
970 assert(c);
971 return c;
972 }
973
974 //-----------------------------------------------------------------------------
975
getCurrentChannelEditor() const976 ChennelCurveEditor *ToneCurveField::getCurrentChannelEditor() const {
977 return getChannelEditor(m_toneCurveStackedWidget->currentIndex());
978 }
979
980 //-----------------------------------------------------------------------------
981
getCurrentSlider() const982 DoublePairField *ToneCurveField::getCurrentSlider() const {
983 return dynamic_cast<DoublePairField *>(
984 m_sliderStackedWidget->currentWidget());
985 }
986
987 //-----------------------------------------------------------------------------
988
sliderValueChanged(bool isDragging)989 void ToneCurveField::sliderValueChanged(bool isDragging) {
990 std::pair<double, double> values = getCurrentSlider()->getValues();
991
992 if (m_rangeMode->currentIndex() == 1) {
993 values.first *= 255.0;
994 values.second *= 255.0;
995 }
996
997 getCurrentChannelEditor()->setFirstLastXPosition(values, isDragging);
998 }
999
1000 //-----------------------------------------------------------------------------
1001
onFirstLastXPostionChanged(double x0,double x1)1002 void ToneCurveField::onFirstLastXPostionChanged(double x0, double x1) {
1003 if (m_rangeMode->currentIndex() == 1) {
1004 x0 /= 255.0;
1005 x1 /= 255.0;
1006 }
1007 std::pair<double, double> values = getCurrentSlider()->getValues();
1008 if (values.first != x0 || values.second != x1)
1009 getCurrentSlider()->setValues(std::make_pair(x0, x1));
1010 }
1011
1012 //-----------------------------------------------------------------------------
1013 // index = -1 when no point is selected
onUpdateCurrentPosition(int index,QPointF pos)1014 void ToneCurveField::onUpdateCurrentPosition(int index, QPointF pos) {
1015 auto modify = [&](double val) {
1016 return (m_rangeMode->currentIndex() == 0) ? val : val / 255.0;
1017 };
1018
1019 QList<TPointD> points = getCurrentChannelEditor()->getPoints();
1020 assert(index == -1 || (index >= 3 && index < points.size() - 3));
1021 if (index != -1 && (index < 3 || index >= points.size() - 3)) return;
1022
1023 double maxChanVal = 255.0;
1024
1025 // if no point is selected
1026 if (index == -1) {
1027 if (m_currentPointIndex != index) {
1028 m_currentInput->setDisabled(true);
1029 m_currentOutput->setDisabled(true);
1030 m_currentInput->setRange(-(std::numeric_limits<double>::max)(),
1031 (std::numeric_limits<double>::max)());
1032 m_currentOutput->setRange(-(std::numeric_limits<double>::max)(),
1033 (std::numeric_limits<double>::max)());
1034 m_currentPointIndex = index;
1035 }
1036 if (pos.x() < 0.0 || pos.x() > maxChanVal || pos.y() < 0.0 ||
1037 pos.y() > maxChanVal) {
1038 m_currentInput->setText("");
1039 m_currentOutput->setText("");
1040 } else {
1041 m_currentInput->setValue(modify(pos.x()));
1042 m_currentOutput->setValue(modify(pos.y()));
1043 }
1044 } else { // if any point is selected
1045 if (m_currentPointIndex != index) {
1046 m_currentInput->setEnabled(true);
1047 m_currentOutput->setEnabled(true);
1048 if (index % 3 == 0) { // control point case
1049 // 前後のポイントと4離す
1050 double xmin = (index == 3) ? 0 : points.at(index - 3).x + cpMargin;
1051 double xmax = (index == points.size() - 4)
1052 ? maxChanVal
1053 : points.at(index + 3).x - cpMargin;
1054 m_currentInput->setRange(modify(xmin), modify(xmax));
1055 m_currentOutput->setRange(0.0, modify(maxChanVal));
1056 } else if (index % 3 == 2) { // left handle
1057 TPointD cp = points.at(index + 1);
1058 m_currentInput->setRange(-(std::numeric_limits<double>::max)(),
1059 modify(cp.x));
1060 m_currentOutput->setRange(-(std::numeric_limits<double>::max)(),
1061 (std::numeric_limits<double>::max)());
1062 } else { // right handle
1063 TPointD cp = points.at(index - 1);
1064 m_currentInput->setRange(modify(cp.x),
1065 (std::numeric_limits<double>::max)());
1066 m_currentOutput->setRange(-(std::numeric_limits<double>::max)(),
1067 (std::numeric_limits<double>::max)());
1068 }
1069 m_currentPointIndex = index;
1070 }
1071 m_currentInput->setValue(modify(pos.x()));
1072 m_currentOutput->setValue(modify(pos.y()));
1073 }
1074 }
1075
1076 //-----------------------------------------------------------------------------
1077
onCurrentPointEditted()1078 void ToneCurveField::onCurrentPointEditted() {
1079 TPointD oldPos =
1080 getCurrentChannelEditor()->getPoints().at(m_currentPointIndex);
1081 double factor = (m_rangeMode->currentIndex() == 0) ? 1.0 : 255.0;
1082 QPointF newPos(m_currentInput->getValue() * factor,
1083 m_currentOutput->getValue() * factor);
1084 getCurrentChannelEditor()->moveCurrentControlPoint(
1085 newPos - QPointF(oldPos.x, oldPos.y));
1086 }
1087
1088 //-----------------------------------------------------------------------------
1089
onCurrentChannelSwitched(int channelIndex)1090 void ToneCurveField::onCurrentChannelSwitched(int channelIndex) {
1091 getCurrentChannelEditor()->setCurrentControlPointIndex(-1);
1092 m_toneCurveStackedWidget->setCurrentIndex(channelIndex);
1093 m_sliderStackedWidget->setCurrentIndex(channelIndex);
1094 emit currentChannelIndexChanged(channelIndex);
1095 onUpdateCurrentPosition(-1, QPointF(-1, -1));
1096 }
1097
1098 //-----------------------------------------------------------------------------
1099
onRangeModeSwitched(int index)1100 void ToneCurveField::onRangeModeSwitched(int index) {
1101 double maxVal = (index == 0) ? 255.0 : 1.0;
1102 double factor = (index == 0) ? 255.0 : 1.0 / 255.0;
1103 ChannelBar::Range range =
1104 (index == 0) ? ChannelBar::Range_0_255 : ChannelBar::Range_0_1;
1105
1106 for (int i = 0; i < m_toneCurveStackedWidget->count(); i++) {
1107 ChennelCurveEditor *c =
1108 dynamic_cast<ChennelCurveEditor *>(m_toneCurveStackedWidget->widget(i));
1109 if (c) c->setLabelRange(range);
1110
1111 DoublePairField *doublePairSlider =
1112 dynamic_cast<DoublePairField *>(m_sliderStackedWidget->widget(i));
1113 if (doublePairSlider) {
1114 doublePairSlider->setRange(0.0, maxVal);
1115 std::pair<double, double> val = doublePairSlider->getValues();
1116 doublePairSlider->setValues(
1117 std::make_pair(val.first * factor, val.second * factor));
1118 }
1119 }
1120
1121 if (m_currentPointIndex == -1) return;
1122
1123 int pointIndex = m_currentPointIndex;
1124 // in order to force updating the value range
1125 m_currentPointIndex = -1;
1126 TPointD point = getCurrentChannelEditor()->getPoints().at(pointIndex);
1127 onUpdateCurrentPosition(pointIndex, QPointF(point.x, point.y));
1128 }
1129
1130 //-----------------------------------------------------------------------------
1131
setIsLinearCheckBox(bool isChecked)1132 void ToneCurveField::setIsLinearCheckBox(bool isChecked) {
1133 if (m_isLinearCheckBox->isChecked() == isChecked) return;
1134 m_isLinearCheckBox->setChecked(isChecked);
1135 }
1136
1137 //-----------------------------------------------------------------------------
1138
setLinear(bool isLinear)1139 void ToneCurveField::setLinear(bool isLinear) {
1140 int i;
1141 for (i = 0; i < m_sliderStackedWidget->count(); i++)
1142 getChannelEditor(i)->setLinear(isLinear);
1143 }
1144
1145 //-----------------------------------------------------------------------------
1146
setEnlarged(bool isEnlarged)1147 void ToneCurveField::setEnlarged(bool isEnlarged) {
1148 int i;
1149 for (i = 0; i < m_sliderStackedWidget->count(); i++)
1150 getChannelEditor(i)->setEnlarged(isEnlarged);
1151 setFixedWidth((isEnlarged) ? 400 + 256 : 400);
1152 emit sizeChanged();
1153 }
1154
1155 //-----------------------------------------------------------------------------
1156
setLinearManually(bool isLinear)1157 void ToneCurveField::setLinearManually(bool isLinear) {
1158 emit isLinearChanged(isLinear);
1159 }
1160
1161 //-----------------------------------------------------------------------------
1162
isEnlarged()1163 bool ToneCurveField::isEnlarged() { return m_isEnlargedCheckBox->isChecked(); }