| |
| |
| #include "toonzqt/tonecurvefield.h" |
| #include "toonzqt/fxhistogramrender.h" |
| #include "toonzqt/intpairfield.h" |
| #include "toonzqt/checkbox.h" |
| |
| #include <QPainter> |
| #include <QPainterPath> |
| #include <QStackedWidget> |
| #include <QMouseEvent> |
| #include <QApplication> |
| #include <QHBoxLayout> |
| #include <QComboBox> |
| #include <QLabel> |
| |
| using namespace DVGui; |
| |
| |
| namespace |
| { |
| |
| |
| double getPercentAtPoint(QPointF point, QPainterPath path) |
| { |
| int i; |
| for (i = 1; i < 100; i++) { |
| double p = double(i) * 0.01; |
| QPointF pathPoint = path.pointAtPercent(p); |
| if (abs(pathPoint.x() - point.x()) < 3 && abs(pathPoint.y() - point.y()) < 3) |
| return p; |
| } |
| return 0; |
| } |
| |
| |
| |
| QList<QPointF> getIntersectedPoint(QRectF rect, QPainterPath path) |
| { |
| int y0 = rect.top(); |
| int y1 = rect.bottom(); |
| QList<QPointF> points; |
| double g = 1.0 / 256.0; |
| QPointF prec = path.pointAtPercent(0); |
| QPointF point = path.pointAtPercent(g); |
| int j = 0; |
| for (j = 2; j < 256; j++) { |
| QPointF next = path.pointAtPercent(double(j) * g); |
| if (prec.y() >= y0 && prec.y() <= y1) { |
| if (point.y() < y0) |
| points.push_back(QPointF(prec.x(), y0)); |
| else if (point.y() > y1) |
| points.push_back(QPointF(prec.x(), y1)); |
| } |
| if (next.y() >= y0 && next.y() <= y1) { |
| if (point.y() < y0) |
| points.push_back(QPointF(next.x(), y0)); |
| else if (point.y() > y1) |
| points.push_back(QPointF(next.x(), y1)); |
| } |
| prec = point; |
| point = next; |
| } |
| return points; |
| } |
| |
| |
| |
| double qtNorm2(const QPointF &p) |
| { |
| return p.x() * p.x() + p.y() * p.y(); |
| } |
| |
| double qtNorm(const QPointF &p) |
| { |
| return sqrt(qtNorm2(p)); |
| } |
| |
| QPointF qtNormalize(const QPointF &p) |
| { |
| double n = qtNorm(p); |
| assert(n != 0.0); |
| return (1.0 / n) * p; |
| } |
| |
| double qtDistance2(const QPointF &p1, const QPointF &p2) |
| { |
| return qtNorm2(p2 - p1); |
| } |
| |
| double qtDistance(const QPointF &p1, const QPointF &p2) |
| { |
| return qtNorm(p2 - p1); |
| } |
| |
| |
| |
| QPointF getNewFirstHandlePoint(const QPointF &p, const QPointF &nextP, const QPointF &oldHandlePoint) |
| { |
| bool canMove = (nextP.x() - p.x() > 16); |
| int yDistance = nextP.y() - p.y(); |
| double sign = (yDistance != 0) ? yDistance / abs(yDistance) : 1; |
| double t = qtDistance(p, oldHandlePoint); |
| if (!canMove) { |
| QPointF normalizedP = (yDistance != 0) ? qtNormalize(nextP - p) : QPoint(1, 1); |
| return QPointF(p + QPointF(t * normalizedP)); |
| } else if (abs(oldHandlePoint.x() - p.x()) < 16) |
| return QPointF(p.x() + 16, p.y() + sign * sqrt(t * t - 16 * 16)); |
| return oldHandlePoint; |
| } |
| |
| |
| |
| QPointF getNewSecondHandlePoint(const QPointF &p, const QPointF &nextP, const QPointF &oldHandlePoint) |
| { |
| bool canMove = (nextP.x() - p.x() > 16); |
| int yDistance = p.y() - nextP.y(); |
| double sign = (yDistance != 0) ? yDistance / abs(yDistance) : 1; |
| double s = qtDistance(oldHandlePoint, nextP); |
| if (!canMove) { |
| QPointF normalizedP = (yDistance != 0) ? qtNormalize(nextP - p) : QPoint(1, 1); |
| return QPointF(nextP - QPointF(s * normalizedP)); |
| } else if (abs(nextP.x() - oldHandlePoint.x()) < 16) |
| return QPointF(nextP.x() - 16, nextP.y() + sign * sqrt(s * s - 16 * 16)); |
| return oldHandlePoint; |
| } |
| |
| |
| } |
| |
| |
| |
| |
| |
| |
| ChennelCurveEditor::ChennelCurveEditor(QWidget *parent, HistogramView *histogramView) |
| : QWidget(parent), m_histogramView(histogramView), m_currentControlPointIndex(-1), m_mouseButton(Qt::NoButton), m_curveHeight(256), m_LeftRightMargin(42), m_TopMargin(9), m_BottomMargin(48), m_isLinear(false) |
| { |
| setFixedSize(m_curveHeight + 2 * m_LeftRightMargin + 2, |
| m_curveHeight + m_TopMargin + m_BottomMargin); |
| setAttribute(Qt::WA_KeyCompression); |
| setFocusPolicy(Qt::StrongFocus); |
| setMouseTracking(true); |
| |
| m_histogramView->setDrawnWidget(this); |
| m_histogramView->setGraphHeight(m_curveHeight); |
| m_histogramView->setGraphAlphaMask(120); |
| m_verticalChannelBar = new ChannelBar(0, m_histogramView->getChannelBarColor(), false); |
| } |
| |
| |
| |
| void ChennelCurveEditor::setPoints(QList<TPointD> points) |
| { |
| if (!m_points.isEmpty()) |
| m_points.clear(); |
| int i; |
| for (i = 0; i < points.size(); i++) { |
| QPointF p = strokeToViewPoint(points.at(i)); |
| m_points.push_back(p); |
| } |
| |
| int firstIndex = 3; |
| int lastIndex = m_points.size() - 4; |
| emit firstLastXPostionChanged(viewToStrokePoint(m_points.at(firstIndex)).x, |
| viewToStrokePoint(m_points.at(lastIndex)).x); |
| update(); |
| } |
| |
| |
| |
| QList<TPointD> ChennelCurveEditor::getPoints() |
| { |
| QList<TPointD> points; |
| if (m_points.isEmpty()) |
| return points; |
| int i; |
| for (i = 0; i < m_points.size(); i++) |
| points.push_back(viewToStrokePoint(m_points.at(i))); |
| return points; |
| } |
| |
| |
| |
| void ChennelCurveEditor::setFirstLastXPosition(std::pair<int, int> values, bool isDragging) |
| { |
| if (!isDragging) { |
| emit controlPointChanged(false); |
| return; |
| } |
| |
| QPointF newX0 = strokeToViewPoint(TPointD(values.first, 0)); |
| QPointF newX1 = strokeToViewPoint(TPointD(values.second, 0)); |
| |
| int indexX0 = 3; |
| int indexX1 = m_points.size() - 4; |
| QPointF x0 = m_points.at(indexX0); |
| QPointF x1 = m_points.at(indexX1); |
| if (x0.x() != newX0.x()) { |
| QPointF delta(newX0.x() - x0.x(), 0); |
| moveCentralControlPoint(indexX0, delta); |
| update(); |
| } |
| if (x1.x() != newX1.x()) { |
| QPointF delta(newX1.x() - x1.x(), 0); |
| moveCentralControlPoint(indexX1, delta); |
| update(); |
| } |
| m_currentControlPointIndex = -1; |
| } |
| |
| |
| |
| void ChennelCurveEditor::setLinear(bool isLinear) |
| { |
| if (m_isLinear == isLinear) |
| return; |
| m_isLinear = isLinear; |
| update(); |
| } |
| |
| |
| |
| QPointF ChennelCurveEditor::checkPoint(const QPointF p) |
| { |
| QPointF checkedP = p; |
| int y0 = m_TopMargin + 1; |
| int y1 = m_curveHeight + m_TopMargin; |
| if (p.y() < y0) |
| checkedP = QPointF(checkedP.x(), y0); |
| if (p.y() > y1) |
| checkedP = QPointF(checkedP.x(), y1); |
| int x0 = m_LeftRightMargin + 1; |
| int x1 = m_curveHeight + m_LeftRightMargin; |
| if (p.x() < x0) |
| checkedP = QPointF(x0, checkedP.y()); |
| if (p.x() > x1) |
| checkedP = QPointF(x1, checkedP.y()); |
| return checkedP; |
| } |
| |
| |
| |
| QPointF ChennelCurveEditor::strokeToViewPoint(const TPointD p) |
| { |
| double x = p.x + m_LeftRightMargin + 1; |
| double y = height() - m_BottomMargin - p.y; |
| return QPointF(x, y); |
| } |
| |
| |
| |
| TPointD ChennelCurveEditor::viewToStrokePoint(const QPointF &p) |
| { |
| double x = p.x() - m_LeftRightMargin - 1; |
| double y = m_curveHeight - (p.y() - m_TopMargin); |
| return TThickPoint(x, y); |
| } |
| |
| |
| |
| int ChennelCurveEditor::getClosestPointIndex(const QPointF &pos, double &minDistance2) const |
| { |
| int closestPointIndex = -1; |
| minDistance2 = 0; |
| int i; |
| for (i = 0; i < (int)m_points.size(); i++) { |
| double distance2 = qtDistance2(pos, m_points.at(i)); |
| if (closestPointIndex < 0 || distance2 < minDistance2) { |
| minDistance2 = distance2; |
| closestPointIndex = i; |
| } |
| } |
| return closestPointIndex; |
| } |
| |
| |
| |
| void ChennelCurveEditor::movePoint(int index, const QPointF delta) |
| { |
| QPointF p = m_points.at(index); |
| p += delta; |
| setPoint(index, p); |
| |
| int firstIndex = 3; |
| int lastIndex = m_points.size() - 4; |
| if (index == firstIndex) |
| emit firstLastXPostionChanged(viewToStrokePoint(p).x, viewToStrokePoint(m_points.at(lastIndex)).x); |
| if (index == lastIndex) |
| emit firstLastXPostionChanged(viewToStrokePoint(m_points.at(firstIndex)).x, viewToStrokePoint(p).x); |
| } |
| |
| |
| |
| void ChennelCurveEditor::setPoint(int index, const QPointF p) |
| { |
| m_points.removeAt(index); |
| m_points.insert(index, p); |
| |
| int firstIndex = 3; |
| int lastIndex = m_points.size() - 4; |
| if (index == firstIndex) |
| emit firstLastXPostionChanged(viewToStrokePoint(p).x, viewToStrokePoint(m_points.at(lastIndex)).x); |
| if (index == lastIndex) |
| emit firstLastXPostionChanged(viewToStrokePoint(m_points.at(firstIndex)).x, viewToStrokePoint(p).x); |
| } |
| |
| |
| |
| void ChennelCurveEditor::moveCurrentControlPoint(const QPointF delta) |
| { |
| assert(m_currentControlPointIndex != -1); |
| int pointCount = m_points.size(); |
| |
| if (isCentralControlPoint(m_currentControlPointIndex)) |
| moveCentralControlPoint(m_currentControlPointIndex, delta); |
| |
| else if (isLeftControlPoint(m_currentControlPointIndex)) { |
| QPointF p0 = m_points.at(m_currentControlPointIndex) + QPointF(0, delta.y()); |
| setPoint(m_currentControlPointIndex, p0); |
| if (m_currentControlPointIndex < pointCount - 5) { |
| QPointF p2 = m_points.at(m_currentControlPointIndex + 2) - QPointF(0, delta.y()); |
| setPoint(m_currentControlPointIndex + 2, p2); |
| } |
| emit controlPointChanged(true); |
| } |
| |
| else { |
| assert(isRightControlPoint(m_currentControlPointIndex)); |
| QPointF p0 = m_points.at(m_currentControlPointIndex) + QPointF(0, delta.y()); |
| setPoint(m_currentControlPointIndex, p0); |
| if (m_currentControlPointIndex > 4) { |
| QPointF p2 = m_points.at(m_currentControlPointIndex - 2) - QPointF(0, delta.y()); |
| setPoint(m_currentControlPointIndex - 2, p2); |
| } |
| emit controlPointChanged(true); |
| } |
| update(); |
| } |
| |
| |
| |
| void ChennelCurveEditor::moveCentralControlPoint(int index, const QPointF delta) |
| { |
| int pointCount = m_points.size(); |
| |
| assert(index < pointCount - 3 && index > 2); |
| |
| QPointF p = m_points.at(index); |
| QPointF d = delta; |
| |
| int newX = p.x() + delta.x(); |
| int newY = p.y() + delta.y(); |
| QPointF newPoint = checkPoint(QPoint(newX, newY)); |
| d = newPoint - p; |
| |
| QPointF nextP = m_points.at(index + 3); |
| QPointF precP = m_points.at(index - 3); |
| double nextDistance = nextP.x() - (p.x() + d.x()); |
| double precDistance = (p.x() + d.x()) - precP.x(); |
| |
| |
| |
| if (index == 3 && index + 3 == pointCount - 4) { |
| setPoint(index + 1, getNewFirstHandlePoint(p, nextP, m_points.at(index + 1))); |
| setPoint(index + 2, getNewSecondHandlePoint(p, nextP, m_points.at(index + 2))); |
| if (nextDistance < 0) |
| d = QPointF(nextP.x() - p.x(), d.y()); |
| } |
| |
| |
| else if (index - 3 == 3 && index == pointCount - 4) { |
| setPoint(index - 2, getNewFirstHandlePoint(precP, p, m_points.at(index - 2))); |
| setPoint(index - 1, getNewSecondHandlePoint(precP, p, m_points.at(index - 1))); |
| if (precDistance < 0) |
| d = QPointF(precP.x() - p.x(), d.y()); |
| } |
| |
| else if (nextDistance < 16) |
| d = QPointF(nextP.x() - p.x() - 16, d.y()); |
| else if (precDistance < 16) |
| d = QPointF(precP.x() - p.x() + 16, d.y()); |
| |
| |
| if (index == 3) { |
| QPointF dY = QPointF(0, d.y()); |
| movePoint(index - 1, dY); |
| movePoint(index - 2, dY); |
| movePoint(index - 3, dY); |
| } |
| |
| if (index == pointCount - 4) { |
| QPointF dY = QPointF(0, d.y()); |
| movePoint(index + 1, dY); |
| movePoint(index + 2, dY); |
| movePoint(index + 3, dY); |
| } |
| if (index > 3) |
| movePoint(index - 1, d); |
| if (index < pointCount - 4) |
| movePoint(index + 1, d); |
| movePoint(index, d); |
| emit controlPointChanged(true); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void ChennelCurveEditor::addControlPoint(double percent) |
| { |
| QPainterPath path = getPainterPath(); |
| QPointF p = path.pointAtPercent(percent); |
| |
| |
| int pointCount = m_points.size(); |
| int beforeControlPointIndex; |
| for (beforeControlPointIndex = pointCount - 1; beforeControlPointIndex >= 0; beforeControlPointIndex--) { |
| QPointF point = m_points.at(beforeControlPointIndex); |
| if (isCentralControlPoint(beforeControlPointIndex) && point.x() < p.x()) |
| break; |
| } |
| |
| if (beforeControlPointIndex == 0 || beforeControlPointIndex == pointCount - 4) |
| return; |
| |
| QPointF p0 = checkPoint(m_points.at(beforeControlPointIndex)); |
| |
| if (abs(p.x() - p0.x()) <= 16) |
| return; |
| double beforeControlPointPercent = getPercentAtPoint(p0, path); |
| QPointF p1 = checkPoint(m_points.at(beforeControlPointIndex + 1)); |
| QPointF p2 = checkPoint(m_points.at(beforeControlPointIndex + 2)); |
| QPointF p3 = checkPoint(m_points.at(beforeControlPointIndex + 3)); |
| |
| if (abs(p3.x() - p.x()) <= 16) |
| return; |
| double nextControlPointPercent = getPercentAtPoint(p3, path); |
| |
| |
| double t = percent * 100 / (nextControlPointPercent - beforeControlPointPercent); |
| double s = t - 1; |
| QPointF speed = 3.0 * ((p1 - p0) * s * s + 2 * (p2 - p0) * s * t + (p3 - p2) * t * t); |
| double m = speed.y() / speed.x(); |
| |
| int newControlPointIndex = beforeControlPointIndex + 3; |
| m_points.insert(newControlPointIndex - 1, QPointF(p.x() - 16, p.y() - 16 * m)); |
| m_points.insert(newControlPointIndex, p); |
| m_points.insert(newControlPointIndex + 1, QPointF(p.x() + 16, p.y() + 16 * m)); |
| |
| m_currentControlPointIndex = newControlPointIndex; |
| emit controlPointAdded(newControlPointIndex); |
| update(); |
| } |
| |
| |
| |
| void ChennelCurveEditor::removeCurrentControlPoint() |
| { |
| removeControlPoint(m_currentControlPointIndex); |
| } |
| |
| |
| |
| void ChennelCurveEditor::removeControlPoint(int index) |
| { |
| |
| if (index <= 4) { |
| setPoint(0, strokeToViewPoint(TPointD(-40, 0))); |
| setPoint(1, strokeToViewPoint(TPointD(-20, 0))); |
| setPoint(2, strokeToViewPoint(TPointD(-20, 0))); |
| setPoint(3, strokeToViewPoint(TPointD(0, 0))); |
| setPoint(4, strokeToViewPoint(TPointD(16, 16))); |
| update(); |
| emit controlPointChanged(false); |
| return; |
| } |
| |
| if (index >= m_points.size() - 5) { |
| int i = m_points.size() - 5; |
| setPoint(i, strokeToViewPoint(TPointD(239, 239))); |
| setPoint(i + 1, strokeToViewPoint(TPointD(255, 255))); |
| setPoint(i + 2, strokeToViewPoint(TPointD(275, 255))); |
| setPoint(i + 3, strokeToViewPoint(TPointD(275, 255))); |
| setPoint(i + 4, strokeToViewPoint(TPointD(295, 255))); |
| update(); |
| emit controlPointChanged(false); |
| return; |
| } |
| |
| int firstIndex = 0; |
| if (isCentralControlPoint(index)) |
| firstIndex = index - 1; |
| else if (isLeftControlPoint(index)) |
| firstIndex = index; |
| else |
| firstIndex = index - 2; |
| |
| m_points.removeAt(firstIndex); |
| m_points.removeAt(firstIndex); |
| m_points.removeAt(firstIndex); |
| |
| emit controlPointRemoved(firstIndex + 1); |
| m_currentControlPointIndex = firstIndex - 2; |
| update(); |
| } |
| |
| |
| |
| QPainterPath ChennelCurveEditor::getPainterPath() |
| { |
| int pointCount = m_points.size(); |
| if (pointCount == 0) |
| return QPainterPath(); |
| |
| QPointF p0 = m_points.at(0); |
| QPainterPath path(p0); |
| int i; |
| for (i = 1; i < pointCount; i++) { |
| QPointF p1 = m_points.at(i); |
| QPointF p2 = m_points.at(++i); |
| QPointF p3 = m_points.at(++i); |
| path.moveTo(p0); |
| if (!m_isLinear) |
| path.cubicTo(p1, p2, p3); |
| else |
| path.lineTo(p3); |
| p0 = p3; |
| } |
| |
| |
| QRectF rect(m_LeftRightMargin, m_TopMargin, m_curveHeight, m_curveHeight); |
| QRectF r = path.boundingRect(); |
| if (!rect.contains(QRect(rect.left(), r.top(), rect.width(), r.height()))) { |
| QList<QPointF> points = getIntersectedPoint(rect, path); |
| |
| |
| int j = 0; |
| for (j = 0; j < points.size(); j++) { |
| QPointF p0 = points.at(j); |
| QPointF p1 = points.at(++j); |
| QPainterPath line(p0); |
| line.lineTo(p1); |
| path.addPath(line); |
| } |
| } |
| |
| return path; |
| } |
| |
| |
| |
| void ChennelCurveEditor::paintEvent(QPaintEvent *e) |
| { |
| QPainter painter(this); |
| |
| |
| painter.setRenderHint(QPainter::Antialiasing, false); |
| painter.setPen(QColor(250, 250, 250)); |
| int i; |
| int d = m_curveHeight * 0.25; |
| for (i = 1; i < 16; i++) { |
| |
| int delta = m_TopMargin + 16 * i; |
| int j; |
| for (j = 1; j < 4; j++) |
| painter.drawLine(QPoint((j - 1) * d + m_LeftRightMargin + 1, delta), QPoint(j * d + m_LeftRightMargin - 1, delta)); |
| painter.drawLine(QPoint((4 - 1) * d + m_LeftRightMargin + 1, delta), QPoint(4 * d + m_LeftRightMargin, delta)); |
| |
| delta = m_LeftRightMargin + 1 + 16 * i; |
| if (i % 4 == 0) |
| continue; |
| for (j = 1; j < 5; j++) |
| painter.drawLine(QPoint(delta, (j - 1) * d + m_TopMargin), QPoint(delta, j * d + m_TopMargin - 1)); |
| } |
| |
| |
| m_histogramView->draw(&painter, QPoint(m_LeftRightMargin - 10, 0)); |
| |
| |
| m_verticalChannelBar->draw(&painter, QPoint(0, -2)); |
| |
| QRectF r = rect().adjusted(m_LeftRightMargin, m_TopMargin, -m_LeftRightMargin, -m_BottomMargin); |
| |
| painter.setClipRect(r, Qt::IntersectClip); |
| QPainterPath path = getPainterPath(); |
| if (path.isEmpty()) |
| return; |
| painter.setRenderHint(QPainter::Antialiasing, true); |
| painter.setPen(Qt::black); |
| painter.setBrush(Qt::NoBrush); |
| painter.drawPath(path); |
| |
| |
| r = r.adjusted(-5, -5, 5, 5); |
| int n = m_points.size(); |
| QPointF p = m_points.at(3); |
| for (i = 3; i < n - 3; i++) { |
| QBrush brush(Qt::white); |
| int rad = 3; |
| QPointF nextP = m_points.at(i + 1); |
| if (isCentralControlPoint(i)) |
| rad = 4; |
| else if (m_isLinear) { |
| p = nextP; |
| continue; |
| } |
| if (m_currentControlPointIndex == i) |
| brush = QBrush(Qt::black); |
| painter.setBrush(brush); |
| painter.setPen(Qt::black); |
| |
| if (!m_isLinear) { |
| if (isLeftControlPoint(i)) |
| painter.drawLine(p, nextP); |
| else if (isCentralControlPoint(i) && i < n - 4) |
| painter.drawLine(p, nextP); |
| } |
| QPainterPath circle; |
| QRectF pointRect(p.x() - rad, p.y() - rad, 2 * rad, 2 * rad); |
| if (r.contains(pointRect)) |
| #if QT_VERSION >= 0x050000 |
| painter.setClipRect(pointRect.adjusted(-1, -1, 1, 1)); |
| #else |
| painter.setClipRect(pointRect.adjusted(-1, -1, 1, 1), Qt::UniteClip); |
| #endif |
| circle.addEllipse(pointRect); |
| painter.fillPath(circle, brush); |
| painter.drawPath(circle); |
| p = nextP; |
| } |
| } |
| |
| |
| |
| void ChennelCurveEditor::mouseMoveEvent(QMouseEvent *e) |
| { |
| if (m_mouseButton == Qt::LeftButton && m_currentControlPointIndex != -1) { |
| QPoint pos = e->pos(); |
| QPointF posF = QPointF(pos.x(), pos.y()); |
| moveCurrentControlPoint(posF - m_points.at(m_currentControlPointIndex)); |
| } |
| } |
| |
| |
| |
| void ChennelCurveEditor::mousePressEvent(QMouseEvent *e) |
| { |
| m_mouseButton = e->button(); |
| setFocus(); |
| if (m_mouseButton == Qt::LeftButton) { |
| QPoint pos = e->pos(); |
| QPointF posF = QPointF(pos.x(), pos.y()); |
| double minDistance; |
| int controlPointIndex = getClosestPointIndex(posF, minDistance); |
| |
| |
| if (minDistance < 20) |
| m_currentControlPointIndex = controlPointIndex; |
| else { |
| m_currentControlPointIndex = -1; |
| |
| |
| double percent = getPercentAtPoint(posF, getPainterPath()); |
| if (percent != 0 && minDistance > 20) |
| addControlPoint(percent); |
| } |
| update(); |
| } |
| } |
| |
| |
| |
| void ChennelCurveEditor::mouseReleaseEvent(QMouseEvent *e) |
| { |
| |
| if (m_mouseButton == Qt::LeftButton && |
| m_currentControlPointIndex != -1 && |
| e->button() == Qt::LeftButton) |
| emit controlPointChanged(false); |
| m_mouseButton = Qt::NoButton; |
| } |
| |
| |
| |
| void ChennelCurveEditor::keyPressEvent(QKeyEvent *e) |
| { |
| if (e->key() == Qt::Key_Delete) |
| removeCurrentControlPoint(); |
| } |
| |
| |
| |
| void ChennelCurveEditor::enterEvent(QEvent *) |
| { |
| m_mouseButton = Qt::NoButton; |
| } |
| |
| |
| |
| void ChennelCurveEditor::leaveEvent(QEvent *) |
| { |
| m_mouseButton = Qt::NoButton; |
| } |
| |
| |
| |
| bool ChennelCurveEditor::eventFilter(QObject *object, QEvent *event) |
| { |
| if (event->type() == QEvent::Shortcut || |
| event->type() == QEvent::ShortcutOverride) { |
| if (!object->inherits("FxSettings")) { |
| event->accept(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| |
| void ChennelCurveEditor::focusInEvent(QFocusEvent *fe) |
| { |
| QWidget::focusInEvent(fe); |
| qApp->installEventFilter(this); |
| } |
| |
| |
| |
| void ChennelCurveEditor::focusOutEvent(QFocusEvent *fe) |
| { |
| m_currentControlPointIndex = -1; |
| emit focusOut(); |
| QWidget::focusOutEvent(fe); |
| qApp->removeEventFilter(this); |
| update(); |
| } |
| |
| |
| |
| |
| |
| ToneCurveField::ToneCurveField(QWidget *parent, FxHistogramRender *fxHistogramRender) |
| : QWidget(parent), m_toneCurveStackedWidget(0) |
| { |
| setFixedWidth(368); |
| |
| int currentChannelIndex = 0; |
| |
| QVBoxLayout *mainLayout = new QVBoxLayout(this); |
| mainLayout->setMargin(0); |
| mainLayout->setSpacing(0); |
| |
| QStringList channels; |
| channels << "RGBA" |
| << "RGB" |
| << "Red" |
| << "Green" |
| << "Blue" |
| << "Alpha"; |
| int channelCount = channels.size(); |
| |
| |
| QWidget *channelListWidget = new QWidget(this); |
| QHBoxLayout *channelListLayout = new QHBoxLayout(channelListWidget); |
| channelListLayout->setMargin(0); |
| channelListLayout->setSpacing(0); |
| QLabel *channelLabel = new QLabel(tr("Channel:"), channelListWidget); |
| channelListLayout->addWidget(channelLabel); |
| m_channelListChooser = new QComboBox(channelListWidget); |
| m_channelListChooser->setFixedSize(100, 20); |
| m_channelListChooser->addItems(channels); |
| m_channelListChooser->setCurrentIndex(currentChannelIndex); |
| channelListLayout->addWidget(m_channelListChooser); |
| channelListWidget->setLayout(channelListLayout); |
| mainLayout->addWidget(channelListWidget, 0, Qt::AlignCenter); |
| |
| |
| m_toneCurveStackedWidget = new QStackedWidget(this); |
| Histograms *histograms = new Histograms(0, true); |
| fxHistogramRender->setHistograms(histograms); |
| int i; |
| for (i = 0; i < channelCount; i++) { |
| ChennelCurveEditor *c = new ChennelCurveEditor(this, histograms->getHistogramView(i)); |
| m_toneCurveStackedWidget->addWidget(c); |
| connect(c, SIGNAL(firstLastXPostionChanged(int, int)), this, SLOT(onFirstLastXPostionChanged(int, int))); |
| } |
| |
| QWidget *w = new QWidget(this); |
| QHBoxLayout *layout = new QHBoxLayout(w); |
| layout->setMargin(0); |
| layout->setSpacing(0); |
| layout->addWidget(m_toneCurveStackedWidget); |
| w->setLayout(layout); |
| mainLayout->addWidget(w, 0, Qt::AlignHCenter); |
| m_toneCurveStackedWidget->setCurrentIndex(currentChannelIndex); |
| |
| |
| m_sliderStackedWidget = new QStackedWidget(this); |
| for (i = 0; i < channelCount; i++) { |
| IntPairField *intPairSlider = new IntPairField(this); |
| intPairSlider->setFixedHeight(20); |
| intPairSlider->setLabelsEnabled(false); |
| intPairSlider->setRange(0, 255); |
| intPairSlider->setValues(std::make_pair(0, 255)); |
| m_sliderStackedWidget->addWidget(intPairSlider); |
| connect(intPairSlider, SIGNAL(valuesChanged(bool)), this, SLOT(sliderValueChanged(bool))); |
| } |
| mainLayout->addWidget(m_sliderStackedWidget); |
| m_sliderStackedWidget->setCurrentIndex(currentChannelIndex); |
| |
| mainLayout->addSpacing(10); |
| m_isLinearCheckBox = new CheckBox(QString("Linear"), this); |
| mainLayout->addWidget(m_isLinearCheckBox, 0, Qt::AlignHCenter); |
| connect(m_isLinearCheckBox, SIGNAL(clicked(bool)), SLOT(setLinearManually(bool))); |
| connect(m_isLinearCheckBox, SIGNAL(toggled(bool)), SLOT(setLinear(bool))); |
| |
| connect(m_channelListChooser, SIGNAL(currentIndexChanged(int)), m_toneCurveStackedWidget, SLOT(setCurrentIndex(int))); |
| connect(m_channelListChooser, SIGNAL(currentIndexChanged(int)), m_sliderStackedWidget, SLOT(setCurrentIndex(int))); |
| connect(m_channelListChooser, SIGNAL(currentIndexChanged(int)), this, SIGNAL(currentChannelIndexChanged(int))); |
| |
| setLayout(mainLayout); |
| } |
| |
| |
| |
| void ToneCurveField::setCurrentChannel(int currentChannel) |
| { |
| m_channelListChooser->setCurrentIndex(currentChannel); |
| } |
| |
| |
| |
| ChennelCurveEditor *ToneCurveField::getChannelEditor(int channel) const |
| { |
| ChennelCurveEditor *c = dynamic_cast<ChennelCurveEditor *>(m_toneCurveStackedWidget->widget(channel)); |
| assert(c); |
| return c; |
| } |
| |
| |
| |
| ChennelCurveEditor *ToneCurveField::getCurrentChannelEditor() const |
| { |
| return getChannelEditor(m_toneCurveStackedWidget->currentIndex()); |
| } |
| |
| |
| |
| IntPairField *ToneCurveField::getCurrentSlider() const |
| { |
| return dynamic_cast<IntPairField *>(m_sliderStackedWidget->currentWidget()); |
| } |
| |
| |
| |
| void ToneCurveField::sliderValueChanged(bool isDragging) |
| { |
| std::pair<int, int> values = getCurrentSlider()->getValues(); |
| getCurrentChannelEditor()->setFirstLastXPosition(values, isDragging); |
| } |
| |
| |
| |
| void ToneCurveField::onFirstLastXPostionChanged(int x0, int x1) |
| { |
| std::pair<int, int> values = getCurrentSlider()->getValues(); |
| if (values.first != x0 || values.second != x1) |
| getCurrentSlider()->setValues(std::make_pair(x0, x1)); |
| } |
| |
| |
| |
| void ToneCurveField::setIsLinearCheckBox(bool isChecked) |
| { |
| if (m_isLinearCheckBox->isChecked() == isChecked) |
| return; |
| m_isLinearCheckBox->setChecked(isChecked); |
| } |
| |
| |
| |
| void ToneCurveField::setLinear(bool isLinear) |
| { |
| int i; |
| for (i = 0; i < m_sliderStackedWidget->count(); i++) |
| getChannelEditor(i)->setLinear(isLinear); |
| } |
| |
| |
| |
| void ToneCurveField::setLinearManually(bool isLinear) |
| { |
| emit isLinearChanged(isLinear); |
| } |
| |