diff --git a/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Dry.kpp b/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Dry.kpp index 20f6e114d9..a7145b4284 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Dry.kpp and b/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Dry.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Wet-Pattern.kpp b/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Wet-Pattern.kpp index 751329b996..d3ace377ab 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Wet-Pattern.kpp and b/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Wet-Pattern.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Wet.kpp b/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Wet.kpp index 516c92b93b..f980c456df 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Wet.kpp and b/krita/data/paintoppresets/j)_WaterC_Basic_Lines-Wet.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Basic_Round-Fringe_02.kpp b/krita/data/paintoppresets/j)_WaterC_Basic_Round-Fringe_02.kpp index 54be5680f0..b14e87c35b 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Basic_Round-Fringe_02.kpp and b/krita/data/paintoppresets/j)_WaterC_Basic_Round-Fringe_02.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Basic_Round-Grain.kpp b/krita/data/paintoppresets/j)_WaterC_Basic_Round-Grain.kpp index 7f51c9f2e3..18485e1e69 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Basic_Round-Grain.kpp and b/krita/data/paintoppresets/j)_WaterC_Basic_Round-Grain.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Basic_Round-Grunge.kpp b/krita/data/paintoppresets/j)_WaterC_Basic_Round-Grunge.kpp index 9393041390..b5047354e2 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Basic_Round-Grunge.kpp and b/krita/data/paintoppresets/j)_WaterC_Basic_Round-Grunge.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Flat_Big-Grain_Tilt.kpp b/krita/data/paintoppresets/j)_WaterC_Flat_Big-Grain_Tilt.kpp index a0658fc329..5a003df191 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Flat_Big-Grain_Tilt.kpp and b/krita/data/paintoppresets/j)_WaterC_Flat_Big-Grain_Tilt.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Flat_Decay_Tilt.kpp b/krita/data/paintoppresets/j)_WaterC_Flat_Decay_Tilt.kpp index e29b1fd799..e9188051f9 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Flat_Decay_Tilt.kpp and b/krita/data/paintoppresets/j)_WaterC_Flat_Decay_Tilt.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Special_Blobs.kpp b/krita/data/paintoppresets/j)_WaterC_Special_Blobs.kpp index f97294bcd7..61368d675e 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Special_Blobs.kpp and b/krita/data/paintoppresets/j)_WaterC_Special_Blobs.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Special_Splats.kpp b/krita/data/paintoppresets/j)_WaterC_Special_Splats.kpp index 4f51db44c3..28e304006f 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Special_Splats.kpp and b/krita/data/paintoppresets/j)_WaterC_Special_Splats.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Spread-Pattern.kpp b/krita/data/paintoppresets/j)_WaterC_Spread-Pattern.kpp index 34c959e352..3655b8b1f7 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Spread-Pattern.kpp and b/krita/data/paintoppresets/j)_WaterC_Spread-Pattern.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Spread.kpp b/krita/data/paintoppresets/j)_WaterC_Spread.kpp index db1d2cf506..e0034013bd 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Spread.kpp and b/krita/data/paintoppresets/j)_WaterC_Spread.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Spread_WideArea.kpp b/krita/data/paintoppresets/j)_WaterC_Spread_WideArea.kpp index 6045a760eb..920e465214 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Spread_WideArea.kpp and b/krita/data/paintoppresets/j)_WaterC_Spread_WideArea.kpp differ diff --git a/krita/data/paintoppresets/j)_WaterC_Water-Pattern.kpp b/krita/data/paintoppresets/j)_WaterC_Water-Pattern.kpp index 31783e3119..f1e8cae1cc 100644 Binary files a/krita/data/paintoppresets/j)_WaterC_Water-Pattern.kpp and b/krita/data/paintoppresets/j)_WaterC_Water-Pattern.kpp differ diff --git a/libs/image/kis_gradient_painter.cc b/libs/image/kis_gradient_painter.cc index 7e18550745..0a84561cd1 100644 --- a/libs/image/kis_gradient_painter.cc +++ b/libs/image/kis_gradient_painter.cc @@ -1,1360 +1,1299 @@ /* * Copyright (c) 2004 Adrian Page * Copyright (c) 2019 Miguel Lopez * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_gradient_painter.h" #include #include #include #include #include "kis_global.h" #include "kis_paint_device.h" #include #include "kis_selection.h" #include #include "kis_image.h" #include "kis_random_accessor_ng.h" #include "kis_gradient_shape_strategy.h" #include "kis_polygonal_gradient_shape_strategy.h" #include "kis_cached_gradient_shape_strategy.h" #include "krita_utils.h" #include "KoMixColorsOp.h" - - -class CachedGradient : public KoAbstractGradient -{ - -public: - explicit CachedGradient(const KoAbstractGradient *gradient, qint32 steps, const KoColorSpace *cs) - : KoAbstractGradient(gradient->filename()) - { - m_subject = gradient; - m_max = steps - 1; - m_colorSpace = cs; - - m_black = KoColor(cs); - - KoColor tmpColor(m_colorSpace); - for(qint32 i = 0; i < steps; i++) { - m_subject->colorAt(tmpColor, qreal(i) / m_max); - m_colors << tmpColor; - } - } - - ~CachedGradient() override {} - - KoAbstractGradient* clone() const override { - return new CachedGradient(m_subject, m_max + 1, m_colorSpace); - } - - /** - * Creates a QGradient from the gradient. - * The resulting QGradient might differ from original gradient - */ - QGradient* toQGradient() const override - { - return m_subject->toQGradient(); - } - - - /// gets the color data at position 0 <= t <= 1 - const quint8 *cachedAt(qreal t) const - { - qint32 tInt = t * m_max + 0.5; - if (m_colors.size() > tInt) { - return m_colors[tInt].data(); - } - else { - return m_black.data(); - } - } - - void setColorSpace(KoColorSpace* colorSpace) { m_colorSpace = colorSpace; } - const KoColorSpace * colorSpace() const { return m_colorSpace; } - - QByteArray generateMD5() const override { return QByteArray(); } - -private: - const KoAbstractGradient *m_subject; - const KoColorSpace *m_colorSpace; - qint32 m_max; - QVector m_colors; - KoColor m_black; -}; +#include namespace { class LinearGradientStrategy : public KisGradientShapeStrategy { public: LinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd); double valueAt(double x, double y) const override; protected: double m_normalisedVectorX; double m_normalisedVectorY; double m_vectorLength; }; LinearGradientStrategy::LinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd) : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd) { double dx = gradientVectorEnd.x() - gradientVectorStart.x(); double dy = gradientVectorEnd.y() - gradientVectorStart.y(); m_vectorLength = sqrt((dx * dx) + (dy * dy)); if (m_vectorLength < DBL_EPSILON) { m_normalisedVectorX = 0; m_normalisedVectorY = 0; } else { m_normalisedVectorX = dx / m_vectorLength; m_normalisedVectorY = dy / m_vectorLength; } } double LinearGradientStrategy::valueAt(double x, double y) const { double vx = x - m_gradientVectorStart.x(); double vy = y - m_gradientVectorStart.y(); // Project the vector onto the normalised gradient vector. double t = vx * m_normalisedVectorX + vy * m_normalisedVectorY; if (m_vectorLength < DBL_EPSILON) { t = 0; } else { // Scale to 0 to 1 over the gradient vector length. t /= m_vectorLength; } return t; } class BiLinearGradientStrategy : public LinearGradientStrategy { public: BiLinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd); double valueAt(double x, double y) const override; }; BiLinearGradientStrategy::BiLinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd) : LinearGradientStrategy(gradientVectorStart, gradientVectorEnd) { } double BiLinearGradientStrategy::valueAt(double x, double y) const { double t = LinearGradientStrategy::valueAt(x, y); // Reflect if (t < -DBL_EPSILON) { t = -t; } return t; } class RadialGradientStrategy : public KisGradientShapeStrategy { public: RadialGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd); double valueAt(double x, double y) const override; protected: double m_radius; }; RadialGradientStrategy::RadialGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd) : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd) { double dx = gradientVectorEnd.x() - gradientVectorStart.x(); double dy = gradientVectorEnd.y() - gradientVectorStart.y(); m_radius = sqrt((dx * dx) + (dy * dy)); } double RadialGradientStrategy::valueAt(double x, double y) const { double dx = x - m_gradientVectorStart.x(); double dy = y - m_gradientVectorStart.y(); double distance = sqrt((dx * dx) + (dy * dy)); double t; if (m_radius < DBL_EPSILON) { t = 0; } else { t = distance / m_radius; } return t; } class SquareGradientStrategy : public KisGradientShapeStrategy { public: SquareGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd); double valueAt(double x, double y) const override; protected: double m_normalisedVectorX; double m_normalisedVectorY; double m_vectorLength; }; SquareGradientStrategy::SquareGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd) : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd) { double dx = gradientVectorEnd.x() - gradientVectorStart.x(); double dy = gradientVectorEnd.y() - gradientVectorStart.y(); m_vectorLength = sqrt((dx * dx) + (dy * dy)); if (m_vectorLength < DBL_EPSILON) { m_normalisedVectorX = 0; m_normalisedVectorY = 0; } else { m_normalisedVectorX = dx / m_vectorLength; m_normalisedVectorY = dy / m_vectorLength; } } double SquareGradientStrategy::valueAt(double x, double y) const { double px = x - m_gradientVectorStart.x(); double py = y - m_gradientVectorStart.y(); double distance1 = 0; double distance2 = 0; if (m_vectorLength > DBL_EPSILON) { // Point to line distance is: // distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / m_vectorLength; // // Here l0 = (0, 0) and |l1 - l0| = 1 distance1 = -m_normalisedVectorY * px + m_normalisedVectorX * py; distance1 = fabs(distance1); // Rotate point by 90 degrees and get the distance to the perpendicular distance2 = -m_normalisedVectorY * -py + m_normalisedVectorX * px; distance2 = fabs(distance2); } double t = qMax(distance1, distance2) / m_vectorLength; return t; } class ConicalGradientStrategy : public KisGradientShapeStrategy { public: ConicalGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd); double valueAt(double x, double y) const override; protected: double m_vectorAngle; }; ConicalGradientStrategy::ConicalGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd) : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd) { double dx = gradientVectorEnd.x() - gradientVectorStart.x(); double dy = gradientVectorEnd.y() - gradientVectorStart.y(); // Get angle from 0 to 2 PI. m_vectorAngle = atan2(dy, dx) + M_PI; } double ConicalGradientStrategy::valueAt(double x, double y) const { double px = x - m_gradientVectorStart.x(); double py = y - m_gradientVectorStart.y(); double angle = atan2(py, px) + M_PI; angle -= m_vectorAngle; if (angle < 0) { angle += 2 * M_PI; } double t = angle / (2 * M_PI); return t; } class ConicalSymetricGradientStrategy : public KisGradientShapeStrategy { public: ConicalSymetricGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd); double valueAt(double x, double y) const override; protected: double m_vectorAngle; }; ConicalSymetricGradientStrategy::ConicalSymetricGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd) : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd) { double dx = gradientVectorEnd.x() - gradientVectorStart.x(); double dy = gradientVectorEnd.y() - gradientVectorStart.y(); // Get angle from 0 to 2 PI. m_vectorAngle = atan2(dy, dx) + M_PI; } double ConicalSymetricGradientStrategy::valueAt(double x, double y) const { double px = x - m_gradientVectorStart.x(); double py = y - m_gradientVectorStart.y(); double angle = atan2(py, px) + M_PI; angle -= m_vectorAngle; if (angle < 0) { angle += 2 * M_PI; } double t; if (angle < M_PI) { t = angle / M_PI; } else { t = 1 - ((angle - M_PI) / M_PI); } return t; } class SpiralGradientStrategy : public KisGradientShapeStrategy { public: SpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd); double valueAt(double x, double y) const override; protected: double m_vectorAngle; double m_radius; }; SpiralGradientStrategy::SpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd) : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd) { double dx = gradientVectorEnd.x() - gradientVectorStart.x(); double dy = gradientVectorEnd.y() - gradientVectorStart.y(); // Get angle from 0 to 2 PI. m_vectorAngle = atan2(dy, dx) + M_PI; m_radius = sqrt((dx * dx) + (dy * dy)); }; double SpiralGradientStrategy::valueAt(double x, double y) const { double dx = x - m_gradientVectorStart.x(); double dy = y - m_gradientVectorStart.y(); double distance = sqrt((dx * dx) + (dy * dy)); double angle = atan2(dy, dx) + M_PI; double t; angle -= m_vectorAngle; if (m_radius < DBL_EPSILON) { t = 0; } else { t = distance / m_radius; } if (angle < 0) { angle += 2 * M_PI; } t += angle / (2 * M_PI); return t; }; class ReverseSpiralGradientStrategy : public KisGradientShapeStrategy { public: ReverseSpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd); double valueAt(double x, double y) const override; protected: double m_vectorAngle; double m_radius; }; ReverseSpiralGradientStrategy::ReverseSpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd) : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd) { double dx = gradientVectorEnd.x() - gradientVectorStart.x(); double dy = gradientVectorEnd.y() - gradientVectorStart.y(); // Get angle from 0 to 2 PI. m_vectorAngle = atan2(dy, dx) + M_PI; m_radius = sqrt((dx * dx) + (dy * dy)); }; double ReverseSpiralGradientStrategy::valueAt(double x, double y) const { double dx = x - m_gradientVectorStart.x(); double dy = y - m_gradientVectorStart.y(); double distance = sqrt((dx * dx) + (dy * dy)); double angle = atan2(dy, dx) + M_PI; double t; angle -= m_vectorAngle; if (m_radius < DBL_EPSILON) { t = 0; } else { t = distance / m_radius; } if (angle < 0) { angle += 2 * M_PI; } //Reverse direction of spiral gradient t += 1 - (angle / (2 * M_PI)); return t; }; class GradientRepeatStrategy { public: GradientRepeatStrategy() {} virtual ~GradientRepeatStrategy() {} virtual double valueAt(double t) const = 0; }; class GradientRepeatNoneStrategy : public GradientRepeatStrategy { public: static GradientRepeatNoneStrategy *instance(); double valueAt(double t) const override; private: GradientRepeatNoneStrategy() {} static GradientRepeatNoneStrategy *m_instance; }; GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::m_instance = 0; GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::instance() { if (m_instance == 0) { m_instance = new GradientRepeatNoneStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } // Output is clamped to 0 to 1. double GradientRepeatNoneStrategy::valueAt(double t) const { double value = t; if (t < DBL_EPSILON) { value = 0; } else if (t > 1 - DBL_EPSILON) { value = 1; } return value; } class GradientRepeatForwardsStrategy : public GradientRepeatStrategy { public: static GradientRepeatForwardsStrategy *instance(); double valueAt(double t) const override; private: GradientRepeatForwardsStrategy() {} static GradientRepeatForwardsStrategy *m_instance; }; GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::m_instance = 0; GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::instance() { if (m_instance == 0) { m_instance = new GradientRepeatForwardsStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } // Output is 0 to 1, 0 to 1, 0 to 1... double GradientRepeatForwardsStrategy::valueAt(double t) const { int i = static_cast(t); if (t < DBL_EPSILON) { i--; } double value = t - i; return value; } class GradientRepeatAlternateStrategy : public GradientRepeatStrategy { public: static GradientRepeatAlternateStrategy *instance(); double valueAt(double t) const override; private: GradientRepeatAlternateStrategy() {} static GradientRepeatAlternateStrategy *m_instance; }; GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::m_instance = 0; GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::instance() { if (m_instance == 0) { m_instance = new GradientRepeatAlternateStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } // Output is 0 to 1, 1 to 0, 0 to 1, 1 to 0... double GradientRepeatAlternateStrategy::valueAt(double t) const { if (t < 0) { t = -t; } int i = static_cast(t); double value = t - i; if (i % 2 == 1) { value = 1 - value; } return value; } //Had to create this class to solve alternating mode for cases where values should be repeated for every HalfValues like for example, spirals... class GradientRepeatModuloDivisiveContinuousHalfStrategy : public GradientRepeatStrategy { public: static GradientRepeatModuloDivisiveContinuousHalfStrategy *instance(); double valueAt(double t) const override; private: GradientRepeatModuloDivisiveContinuousHalfStrategy() {} static GradientRepeatModuloDivisiveContinuousHalfStrategy *m_instance; }; GradientRepeatModuloDivisiveContinuousHalfStrategy *GradientRepeatModuloDivisiveContinuousHalfStrategy::m_instance = 0; GradientRepeatModuloDivisiveContinuousHalfStrategy *GradientRepeatModuloDivisiveContinuousHalfStrategy::instance() { if (m_instance == 0) { m_instance = new GradientRepeatModuloDivisiveContinuousHalfStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } // Output is 0 to 1, 1 to 0, 0 to 1, 1 to 0 per HalfValues double GradientRepeatModuloDivisiveContinuousHalfStrategy::valueAt(double t) const { if (t < 0) { t = -t; } int i = static_cast(t*2); int ti = static_cast(t); double value = t - ti; if (i % 2 == 1) { value = 1 - value; } return value*2; } class RepeatForwardsPaintPolicy { public: RepeatForwardsPaintPolicy(KisGradientPainter::enumGradientShape shape); void setup(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd, const QSharedPointer &shapeStrategy, const GradientRepeatStrategy *repeatStrategy, qreal antiAliasThreshold, bool reverseGradient, - const CachedGradient * cachedGradient); + const KoCachedGradient * cachedGradient); const quint8 *colorAt(qreal x, qreal y) const; private: KisGradientPainter::enumGradientShape m_shape; qreal m_antiAliasThresholdNormalized; qreal m_antiAliasThresholdNormalizedRev; qreal m_antiAliasThresholdNormalizedDbl; QSharedPointer m_shapeStrategy; const GradientRepeatStrategy *m_repeatStrategy; bool m_reverseGradient; - const CachedGradient *m_cachedGradient; + const KoCachedGradient *m_cachedGradient; const quint8 *m_extremeColors[2]; const KoColorSpace *m_colorSpace; mutable QVector m_resultColor; }; RepeatForwardsPaintPolicy::RepeatForwardsPaintPolicy(KisGradientPainter::enumGradientShape shape) : m_shape(shape) {} void RepeatForwardsPaintPolicy::setup(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd, const QSharedPointer &shapeStrategy, const GradientRepeatStrategy *repeatStrategy, qreal antiAliasThreshold, bool reverseGradient, - const CachedGradient * cachedGradient) + const KoCachedGradient * cachedGradient) { qreal dx = gradientVectorEnd.x() - gradientVectorStart.x(); qreal dy = gradientVectorEnd.y() - gradientVectorStart.y(); qreal distanceInPixels = sqrt(dx * dx + dy * dy); // Compute the area to be be smoothed // based on the length of the gradient m_antiAliasThresholdNormalized = antiAliasThreshold / distanceInPixels; m_antiAliasThresholdNormalizedRev = 1. - m_antiAliasThresholdNormalized; m_antiAliasThresholdNormalizedDbl = 2. * m_antiAliasThresholdNormalized; m_shapeStrategy = shapeStrategy; m_repeatStrategy = repeatStrategy; m_reverseGradient = reverseGradient; m_cachedGradient = cachedGradient; m_extremeColors[0] = m_cachedGradient->cachedAt(1.); m_extremeColors[1] = m_cachedGradient->cachedAt(0.); m_colorSpace = m_cachedGradient->colorSpace(); m_resultColor = QVector(m_colorSpace->pixelSize()); } const quint8 *RepeatForwardsPaintPolicy::colorAt(qreal x, qreal y) const { qreal t = m_shapeStrategy->valueAt(x, y); // Early return if the pixel is near the center of the gradient if // the shape is radial or square. // This prevents applying smoothing since there are // no aliasing artifacts in these gradient shapes at the center if (t <= m_antiAliasThresholdNormalized && (m_shape == KisGradientPainter::GradientShapeBiLinear || m_shape == KisGradientPainter::GradientShapeRadial || m_shape == KisGradientPainter::GradientShapeSquare)) { if (m_reverseGradient) { t = 1 - t; } return m_cachedGradient->cachedAt(t); } t = m_repeatStrategy->valueAt(t); if (m_reverseGradient) { t = 1 - t; } // If this pixel is in the area of the smoothing, // then perform bilinear interpolation between the extreme colors. if (t <= m_antiAliasThresholdNormalized || t >= m_antiAliasThresholdNormalizedRev) { qreal s; if (t <= m_antiAliasThresholdNormalized) { s = .5 + t / m_antiAliasThresholdNormalizedDbl; } else { s = (t - m_antiAliasThresholdNormalizedRev) / m_antiAliasThresholdNormalizedDbl; } qint16 colorWeights[2]; colorWeights[0] = static_cast((1.0 - s) * 255 + 0.5); colorWeights[1] = 255 - colorWeights[0]; m_colorSpace->mixColorsOp()->mixColors(m_extremeColors, colorWeights, 2, m_resultColor.data()); return m_resultColor.data(); } return m_cachedGradient->cachedAt(t); } class ConicalGradientPaintPolicy { public: void setup(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd, const QSharedPointer &shapeStrategy, const GradientRepeatStrategy *repeatStrategy, qreal antiAliasThreshold, bool reverseGradient, - const CachedGradient * cachedGradient); + const KoCachedGradient * cachedGradient); const quint8 *colorAt(qreal x, qreal y) const; private: QPointF m_gradientVectorStart; QSharedPointer m_shapeStrategy; const GradientRepeatStrategy *m_repeatStrategy; qreal m_singularityThreshold; qreal m_antiAliasThreshold; bool m_reverseGradient; - const CachedGradient *m_cachedGradient; + const KoCachedGradient *m_cachedGradient; const quint8 *m_extremeColors[2]; const KoColorSpace *m_colorSpace; mutable QVector m_resultColor; }; void ConicalGradientPaintPolicy::setup(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd, const QSharedPointer &shapeStrategy, const GradientRepeatStrategy *repeatStrategy, qreal antiAliasThreshold, bool reverseGradient, - const CachedGradient * cachedGradient) + const KoCachedGradient * cachedGradient) { Q_UNUSED(gradientVectorEnd); m_gradientVectorStart = gradientVectorStart; m_shapeStrategy = shapeStrategy; m_repeatStrategy = repeatStrategy; m_singularityThreshold = 8.; m_antiAliasThreshold = antiAliasThreshold; m_reverseGradient = reverseGradient; m_cachedGradient = cachedGradient; m_extremeColors[0] = m_cachedGradient->cachedAt(1.); m_extremeColors[1] = m_cachedGradient->cachedAt(0.); m_colorSpace = m_cachedGradient->colorSpace(); m_resultColor = QVector(m_colorSpace->pixelSize()); } const quint8 *ConicalGradientPaintPolicy::colorAt(qreal x, qreal y) const { // Compute the distance from the center of the gradient to thecurrent pixel qreal dx = x - m_gradientVectorStart.x(); qreal dy = y - m_gradientVectorStart.y(); qreal distanceInPixels = sqrt(dx * dx + dy * dy); // Compute the perimeter for this distance qreal perimeter = 2. * M_PI * distanceInPixels; // The smoothing is applied in the vicinity of the aliased border. // The width of the vicinity is an area antiAliasThreshold pixels wide // to each side of the border, but in this case the area is scaled down // if it is too close to the center qreal antiAliasThresholdNormalized; if (distanceInPixels < m_singularityThreshold){ antiAliasThresholdNormalized = distanceInPixels * m_antiAliasThreshold / m_singularityThreshold; } else { antiAliasThresholdNormalized = m_antiAliasThreshold; } antiAliasThresholdNormalized = antiAliasThresholdNormalized / perimeter; qreal antiAliasThresholdNormalizedRev = 1. - antiAliasThresholdNormalized; qreal antiAliasThresholdNormalizedDbl = 2. * antiAliasThresholdNormalized; qreal t = m_shapeStrategy->valueAt(x, y); t = m_repeatStrategy->valueAt(t); if (m_reverseGradient) { t = 1 - t; } // If this pixel is in the area of the smoothing, // then perform bilinear interpolation between the extreme colors. if (t <= antiAliasThresholdNormalized || t >= antiAliasThresholdNormalizedRev) { qreal s; if (t <= antiAliasThresholdNormalized) { s = .5 + t / antiAliasThresholdNormalizedDbl; } else { s = (t - antiAliasThresholdNormalizedRev) / antiAliasThresholdNormalizedDbl; } qint16 colorWeights[2]; colorWeights[0] = static_cast((1.0 - s) * 255 + 0.5); colorWeights[1] = 255 - colorWeights[0]; m_colorSpace->mixColorsOp()->mixColors(m_extremeColors, colorWeights, 2, m_resultColor.data()); return m_resultColor.data(); } return m_cachedGradient->cachedAt(t); } class SpyralGradientRepeatNonePaintPolicy { public: SpyralGradientRepeatNonePaintPolicy(bool isReverseSpiral = false); void setup(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd, const QSharedPointer &shapeStrategy, const GradientRepeatStrategy *repeatStrategy, qreal antiAliasThreshold, bool reverseGradient, - const CachedGradient * cachedGradient); + const KoCachedGradient * cachedGradient); const quint8 *colorAt(qreal x, qreal y) const; private: QPointF m_gradientVectorStart; qreal m_distanceInPixels; qreal m_singularityThreshold; qreal m_angle; QSharedPointer m_shapeStrategy; const GradientRepeatStrategy *m_repeatStrategy; qreal m_antiAliasThreshold; bool m_reverseGradient; - const CachedGradient *m_cachedGradient; + const KoCachedGradient *m_cachedGradient; mutable const quint8 *m_extremeColors[2]; const KoColorSpace *m_colorSpace; mutable QVector m_resultColor; bool m_isReverseSpiral; }; SpyralGradientRepeatNonePaintPolicy::SpyralGradientRepeatNonePaintPolicy(bool isReverseSpiral) : m_isReverseSpiral(isReverseSpiral) { } void SpyralGradientRepeatNonePaintPolicy::setup(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd, const QSharedPointer &shapeStrategy, const GradientRepeatStrategy *repeatStrategy, qreal antiAliasThreshold, bool reverseGradient, - const CachedGradient * cachedGradient) + const KoCachedGradient * cachedGradient) { m_gradientVectorStart = gradientVectorStart; qreal dx = gradientVectorEnd.x() - gradientVectorStart.x(); qreal dy = gradientVectorEnd.y() - gradientVectorStart.y(); m_distanceInPixels = sqrt(dx * dx + dy * dy); m_singularityThreshold = m_distanceInPixels / 32.; m_angle = atan2(dy, dx) + M_PI; m_shapeStrategy = shapeStrategy; m_repeatStrategy = repeatStrategy; m_antiAliasThreshold = antiAliasThreshold; m_reverseGradient = reverseGradient; m_cachedGradient = cachedGradient; m_colorSpace = m_cachedGradient->colorSpace(); m_resultColor = QVector(m_colorSpace->pixelSize()); } const quint8 *SpyralGradientRepeatNonePaintPolicy::colorAt(qreal x, qreal y) const { // Compute the distance from the center of the gradient to thecurrent pixel qreal dx = x - m_gradientVectorStart.x(); qreal dy = y - m_gradientVectorStart.y(); qreal distanceInPixels = sqrt(dx * dx + dy * dy); // Compute the perimeter for this distance qreal perimeter = 2. * M_PI * distanceInPixels; // The smoothing is applied in the vicinity of the aliased border. // The width of the vicinity is an area antiAliasThreshold pixels wide // to each side of the border, but in this case the area is scaled down // if it is too close to the center qreal antiAliasThresholdNormalized; if (distanceInPixels < m_singularityThreshold) { antiAliasThresholdNormalized = distanceInPixels * m_antiAliasThreshold / m_singularityThreshold; } else { antiAliasThresholdNormalized = m_antiAliasThreshold; } antiAliasThresholdNormalized = antiAliasThresholdNormalized / perimeter; qreal antiAliasThresholdNormalizedRev = 1. - antiAliasThresholdNormalized; qreal antiAliasThresholdNormalizedDbl = 2. * antiAliasThresholdNormalized; qreal t = m_shapeStrategy->valueAt(x, y); t = m_repeatStrategy->valueAt(t); if (m_reverseGradient) { t = 1 - t; } // Compute the area to be be smoothed based on the angle of the gradient // and the angle of the current pixel to the center of the gradient qreal angle = atan2(dy, dx) + M_PI; angle -= m_angle; if (angle < 0.) { angle += 2. * M_PI; } angle /= (2. * M_PI); angle = m_repeatStrategy->valueAt(angle); // If this pixel is in the area of the smoothing, // then perform bilinear interpolation between the extreme colors. if (distanceInPixels < m_distanceInPixels && (angle <= antiAliasThresholdNormalized || angle >= antiAliasThresholdNormalizedRev)) { qreal s; if (angle <= antiAliasThresholdNormalized) { s = .5 + angle / antiAliasThresholdNormalizedDbl; } else { s = (angle - antiAliasThresholdNormalizedRev) / antiAliasThresholdNormalizedDbl; } if (m_reverseGradient) { distanceInPixels = m_distanceInPixels - distanceInPixels; m_extremeColors[0] = m_cachedGradient->cachedAt(0.); } else { m_extremeColors[0] = m_cachedGradient->cachedAt(1.); } if (m_isReverseSpiral) { m_extremeColors[1] = m_extremeColors[0]; m_extremeColors[0] = (m_cachedGradient->cachedAt(distanceInPixels / m_distanceInPixels)); } else { m_extremeColors[1] = (m_cachedGradient->cachedAt(distanceInPixels / m_distanceInPixels)); } qint16 colorWeights[2]; colorWeights[0] = static_cast((1.0 - s) * 255 + 0.5); colorWeights[1] = 255 - colorWeights[0]; m_colorSpace->mixColorsOp()->mixColors(m_extremeColors, colorWeights, 2, m_resultColor.data()); return m_resultColor.data(); } return m_cachedGradient->cachedAt(t); } class NoAntialiasPaintPolicy { public: void setup(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd, const QSharedPointer &shapeStrategy, const GradientRepeatStrategy *repeatStrategy, qreal antiAliasThreshold, bool reverseGradient, - const CachedGradient * cachedGradient); + const KoCachedGradient * cachedGradient); const quint8 *colorAt(qreal x, qreal y) const; private: QSharedPointer m_shapeStrategy; const GradientRepeatStrategy *m_repeatStrategy; bool m_reverseGradient; - const CachedGradient *m_cachedGradient; + const KoCachedGradient *m_cachedGradient; }; void NoAntialiasPaintPolicy::setup(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd, const QSharedPointer &shapeStrategy, const GradientRepeatStrategy *repeatStrategy, qreal antiAliasThreshold, bool reverseGradient, - const CachedGradient * cachedGradient) + const KoCachedGradient * cachedGradient) { Q_UNUSED(gradientVectorStart); Q_UNUSED(gradientVectorEnd); Q_UNUSED(antiAliasThreshold); m_shapeStrategy = shapeStrategy; m_repeatStrategy = repeatStrategy; m_reverseGradient = reverseGradient; m_cachedGradient = cachedGradient; } const quint8 *NoAntialiasPaintPolicy::colorAt(qreal x, qreal y) const { qreal t = m_shapeStrategy->valueAt(x, y); t = m_repeatStrategy->valueAt(t); if (m_reverseGradient) { t = 1 - t; } return m_cachedGradient->cachedAt(t); } } struct Q_DECL_HIDDEN KisGradientPainter::Private { enumGradientShape shape; struct ProcessRegion { ProcessRegion() {} ProcessRegion(QSharedPointer _precalculatedShapeStrategy, const QRect &_processRect) : precalculatedShapeStrategy(_precalculatedShapeStrategy), processRect(_processRect) {} QSharedPointer precalculatedShapeStrategy; QRect processRect; }; QVector processRegions; }; KisGradientPainter::KisGradientPainter() : m_d(new Private()) { } KisGradientPainter::KisGradientPainter(KisPaintDeviceSP device) : KisPainter(device), m_d(new Private()) { } KisGradientPainter::KisGradientPainter(KisPaintDeviceSP device, KisSelectionSP selection) : KisPainter(device, selection), m_d(new Private()) { } KisGradientPainter::~KisGradientPainter() { } void KisGradientPainter::setGradientShape(enumGradientShape shape) { m_d->shape = shape; } KisGradientShapeStrategy* createPolygonShapeStrategy(const QPainterPath &path, const QRect &boundingRect) { // TODO: implement UI for exponent option const qreal exponent = 2.0; KisGradientShapeStrategy *strategy = new KisPolygonalGradientShapeStrategy(path, exponent); KIS_ASSERT_RECOVER_NOOP(boundingRect.width() >= 3 && boundingRect.height() >= 3); const qreal step = qMin(qreal(8.0), KritaUtils::maxDimensionPortion(boundingRect, 0.01, 2)); return new KisCachedGradientShapeStrategy(boundingRect, step, step, strategy); } /** * TODO: make this call happen asynchronously when the user does nothing */ void KisGradientPainter::precalculateShape() { if (!m_d->processRegions.isEmpty()) return; QPainterPath path; if (selection()) { if (!selection()->outlineCacheValid()) { selection()->recalculateOutlineCache(); } KIS_ASSERT_RECOVER_RETURN(selection()->outlineCacheValid()); KIS_ASSERT_RECOVER_RETURN(!selection()->outlineCache().isEmpty()); path = selection()->outlineCache(); } else { path.addRect(device()->defaultBounds()->bounds()); } QList splitPaths = KritaUtils::splitDisjointPaths(path); Q_FOREACH (const QPainterPath &subpath, splitPaths) { QRect boundingRect = subpath.boundingRect().toAlignedRect(); if (boundingRect.width() < 3 || boundingRect.height() < 3) { boundingRect = kisGrowRect(boundingRect, 2); } Private::ProcessRegion r(toQShared(createPolygonShapeStrategy(subpath, boundingRect)), boundingRect); m_d->processRegions << r; } } bool KisGradientPainter::paintGradient(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd, enumGradientRepeat repeat, double antiAliasThreshold, bool reverseGradient, qint32 startx, qint32 starty, qint32 width, qint32 height) { return paintGradient(gradientVectorStart, gradientVectorEnd, repeat, antiAliasThreshold, reverseGradient, QRect(startx, starty, width, height)); } bool KisGradientPainter::paintGradient(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd, enumGradientRepeat repeat, double antiAliasThreshold, bool reverseGradient, const QRect &applyRect) { // The following combinations of options have aliasing artifacts // where the first color meets the last color of the gradient. // so antialias threshold is used to compute if the pixel is in // the smothing area. Then linear interpolation is used to blend // between the first and last colors if (antiAliasThreshold > DBL_EPSILON) { if ((m_d->shape == GradientShapeLinear || m_d->shape == GradientShapeBiLinear || m_d->shape == GradientShapeRadial || m_d->shape == GradientShapeSquare || m_d->shape == GradientShapeSpiral || m_d->shape == GradientShapeReverseSpiral) && repeat == GradientRepeatForwards) { RepeatForwardsPaintPolicy paintPolicy(m_d->shape); return paintGradient(gradientVectorStart, gradientVectorEnd, repeat, antiAliasThreshold, reverseGradient, applyRect, paintPolicy); } else if (m_d->shape == GradientShapeConical) { ConicalGradientPaintPolicy paintPolicy; return paintGradient(gradientVectorStart, gradientVectorEnd, repeat, antiAliasThreshold, reverseGradient, applyRect, paintPolicy); } else if ((m_d->shape == GradientShapeSpiral || m_d->shape == GradientShapeReverseSpiral) && repeat == GradientRepeatNone) { SpyralGradientRepeatNonePaintPolicy paintPolicy(m_d->shape == GradientShapeReverseSpiral); return paintGradient(gradientVectorStart, gradientVectorEnd, repeat, antiAliasThreshold, reverseGradient, applyRect, paintPolicy); } } // Default behavior: no antialiasing required NoAntialiasPaintPolicy paintPolicy; return paintGradient(gradientVectorStart, gradientVectorEnd, repeat, antiAliasThreshold, reverseGradient, applyRect, paintPolicy); } template bool KisGradientPainter::paintGradient(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd, enumGradientRepeat repeat, double antiAliasThreshold, bool reverseGradient, const QRect &applyRect, T & paintPolicy) { if (!gradient()) return false; QRect requestedRect = applyRect; //If the device has a selection only iterate over that selection united with our area of interest if (selection()) { requestedRect &= selection()->selectedExactRect(); } QSharedPointer shapeStrategy; switch (m_d->shape) { case GradientShapeLinear: { Private::ProcessRegion r(toQShared(new LinearGradientStrategy(gradientVectorStart, gradientVectorEnd)), requestedRect); m_d->processRegions.clear(); m_d->processRegions << r; break; } case GradientShapeBiLinear: { Private::ProcessRegion r(toQShared(new BiLinearGradientStrategy(gradientVectorStart, gradientVectorEnd)), requestedRect); m_d->processRegions.clear(); m_d->processRegions << r; break; } case GradientShapeRadial: { Private::ProcessRegion r(toQShared(new RadialGradientStrategy(gradientVectorStart, gradientVectorEnd)), requestedRect); m_d->processRegions.clear(); m_d->processRegions << r; break; } case GradientShapeSquare: { Private::ProcessRegion r(toQShared(new SquareGradientStrategy(gradientVectorStart, gradientVectorEnd)), requestedRect); m_d->processRegions.clear(); m_d->processRegions << r; break; } case GradientShapeConical: { Private::ProcessRegion r(toQShared(new ConicalGradientStrategy(gradientVectorStart, gradientVectorEnd)), requestedRect); m_d->processRegions.clear(); m_d->processRegions << r; break; } case GradientShapeConicalSymetric: { Private::ProcessRegion r(toQShared(new ConicalSymetricGradientStrategy(gradientVectorStart, gradientVectorEnd)), requestedRect); m_d->processRegions.clear(); m_d->processRegions << r; break; } case GradientShapeSpiral: { Private::ProcessRegion r(toQShared(new SpiralGradientStrategy(gradientVectorStart, gradientVectorEnd)), requestedRect); m_d->processRegions.clear(); m_d->processRegions << r; break; } case GradientShapeReverseSpiral: { Private::ProcessRegion r(toQShared(new ReverseSpiralGradientStrategy(gradientVectorStart, gradientVectorEnd)), requestedRect); m_d->processRegions.clear(); m_d->processRegions << r; break; } case GradientShapePolygonal: precalculateShape(); repeat = GradientRepeatNone; break; } GradientRepeatStrategy *repeatStrategy = 0; switch (repeat) { case GradientRepeatNone: repeatStrategy = GradientRepeatNoneStrategy::instance(); break; case GradientRepeatForwards: repeatStrategy = GradientRepeatForwardsStrategy::instance(); break; case GradientRepeatAlternate: if (m_d->shape == GradientShapeSpiral || m_d->shape == GradientShapeReverseSpiral) {repeatStrategy = GradientRepeatModuloDivisiveContinuousHalfStrategy::instance();} else {repeatStrategy = GradientRepeatAlternateStrategy::instance();} break; } Q_ASSERT(repeatStrategy != 0); KisPaintDeviceSP dev = device()->createCompositionSourceDevice(); const KoColorSpace * colorSpace = dev->colorSpace(); const qint32 pixelSize = colorSpace->pixelSize(); Q_FOREACH (const Private::ProcessRegion &r, m_d->processRegions) { QRect processRect = r.processRect; QSharedPointer shapeStrategy = r.precalculatedShapeStrategy; - CachedGradient cachedGradient(gradient(), qMax(processRect.width(), processRect.height()), colorSpace); + KoCachedGradient cachedGradient(gradient(), qMax(processRect.width(), processRect.height()), colorSpace); KisSequentialIteratorProgress it(dev, processRect, progressUpdater()); paintPolicy.setup(gradientVectorStart, gradientVectorEnd, shapeStrategy, repeatStrategy, antiAliasThreshold, reverseGradient, &cachedGradient); while (it.nextPixel()) { memcpy(it.rawData(), paintPolicy.colorAt(it.x(), it.y()), pixelSize); } bitBlt(processRect.topLeft(), dev, processRect); } return true; } diff --git a/libs/image/kis_perspectivetransform_worker.cpp b/libs/image/kis_perspectivetransform_worker.cpp index eb03d96ad8..0938a4ad41 100644 --- a/libs/image/kis_perspectivetransform_worker.cpp +++ b/libs/image/kis_perspectivetransform_worker.cpp @@ -1,205 +1,206 @@ /* * This file is part of Krita * * Copyright (c) 2006 Cyrille Berger * Copyright (c) 2009 Edward Apap * Copyright (c) 2010 Marc Pegon * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_perspectivetransform_worker.h" #include #include #include #include #include #include #include #include "kis_paint_device.h" #include "kis_perspective_math.h" #include "kis_random_accessor_ng.h" #include "kis_random_sub_accessor.h" #include "kis_selection.h" #include #include "krita_utils.h" #include "kis_progress_update_helper.h" #include "kis_painter.h" #include "kis_image.h" KisPerspectiveTransformWorker::KisPerspectiveTransformWorker(KisPaintDeviceSP dev, QPointF center, double aX, double aY, double distance, KoUpdaterPtr progress) : m_dev(dev), m_progressUpdater(progress) { QMatrix4x4 m; m.rotate(180. * aX / M_PI, QVector3D(1, 0, 0)); m.rotate(180. * aY / M_PI, QVector3D(0, 1, 0)); QTransform project = m.toTransform(distance); QTransform t = QTransform::fromTranslate(center.x(), center.y()); QTransform forwardTransform = t.inverted() * project * t; init(forwardTransform); } KisPerspectiveTransformWorker::KisPerspectiveTransformWorker(KisPaintDeviceSP dev, const QTransform &transform, KoUpdaterPtr progress) : m_dev(dev), m_progressUpdater(progress) { init(transform); } void KisPerspectiveTransformWorker::fillParams(const QRectF &srcRect, const QRect &dstBaseClipRect, KisRegion *dstRegion, QPolygonF *dstClipPolygon) { QPolygonF bounds = srcRect; QPolygonF newBounds = m_forwardTransform.map(bounds); newBounds = newBounds.intersected(QRectF(dstBaseClipRect)); QPainterPath path; path.addPolygon(newBounds); *dstRegion = KritaUtils::splitPath(path); *dstClipPolygon = newBounds; } void KisPerspectiveTransformWorker::init(const QTransform &transform) { m_isIdentity = transform.isIdentity(); m_forwardTransform = transform; m_backwardTransform = transform.inverted(); if (m_dev) { m_srcRect = m_dev->exactBounds(); QPolygonF dstClipPolygonUnused; fillParams(m_srcRect, m_dev->defaultBounds()->bounds(), &m_dstRegion, &dstClipPolygonUnused); } } KisPerspectiveTransformWorker::~KisPerspectiveTransformWorker() { } void KisPerspectiveTransformWorker::setForwardTransform(const QTransform &transform) { init(transform); } void KisPerspectiveTransformWorker::run() { KIS_ASSERT_RECOVER_RETURN(m_dev); if (m_isIdentity) return; KisPaintDeviceSP cloneDevice = new KisPaintDevice(*m_dev.data()); // Clear the destination device, since all the tiles are already // shared with cloneDevice m_dev->clear(); KIS_ASSERT_RECOVER_NOOP(!m_isIdentity); KisProgressUpdateHelper progressHelper(m_progressUpdater, 100, m_dstRegion.rectCount()); KisRandomSubAccessorSP srcAcc = cloneDevice->createRandomSubAccessor(); KisRandomAccessorSP accessor = m_dev->createRandomAccessorNG(); Q_FOREACH (const QRect &rect, m_dstRegion.rects()) { for (int y = rect.y(); y < rect.y() + rect.height(); ++y) { for (int x = rect.x(); x < rect.x() + rect.width(); ++x) { QPointF dstPoint(x, y); QPointF srcPoint = m_backwardTransform.map(dstPoint); if (m_srcRect.contains(srcPoint)) { accessor->moveTo(dstPoint.x(), dstPoint.y()); srcAcc->moveTo(srcPoint.x(), srcPoint.y()); srcAcc->sampledOldRawData(accessor->rawData()); } } } progressHelper.step(); } } void KisPerspectiveTransformWorker::runPartialDst(KisPaintDeviceSP srcDev, KisPaintDeviceSP dstDev, const QRect &dstRect) { - QRectF srcClipRect = srcDev->exactBounds(); + QRectF srcClipRect = srcDev->defaultBounds()->imageBorderRect(); if (srcClipRect.isEmpty()) return; if (m_isIdentity) { if (srcDev->defaultBounds()->wrapAroundMode()) { - KisProgressUpdateHelper progressHelper(m_progressUpdater, 100, dstRect.height()/ srcClipRect.height()); - for (int y = dstRect.y(); y < dstRect.y() + dstRect.height(); y+=srcClipRect.height()) { - for (int x = dstRect.x(); x < dstRect.x() + dstRect.width(); x+=srcClipRect.width()) { - KisPainter::copyAreaOptimizedOldData(QPoint(x, y), srcDev, dstDev, srcClipRect.toRect()); + QRect srcRect = srcClipRect.toRect(); + KisProgressUpdateHelper progressHelper(m_progressUpdater, 100, dstRect.height()/ srcRect.height()); + for (int y = dstRect.y(); y < dstRect.y() + dstRect.height(); y+= srcRect.height()) { + for (int x = dstRect.x(); x < dstRect.x() + dstRect.width(); x+= srcRect.width()) { + KisPainter::copyAreaOptimizedOldData(QPoint(x, y), srcDev, dstDev, srcRect); } progressHelper.step(); } return; } else { KisPainter::copyAreaOptimizedOldData(dstRect.topLeft(), srcDev, dstDev, dstRect); return; } } KisProgressUpdateHelper progressHelper(m_progressUpdater, 100, dstRect.height()); KisRandomSubAccessorSP srcAcc = srcDev->createRandomSubAccessor(); KisRandomAccessorSP accessor = dstDev->createRandomAccessorNG(); for (int y = dstRect.y(); y < dstRect.y() + dstRect.height(); ++y) { for (int x = dstRect.x(); x < dstRect.x() + dstRect.width(); ++x) { QPointF dstPoint(x, y); QPointF srcPoint = m_backwardTransform.map(dstPoint); if (srcClipRect.contains(srcPoint) || srcDev->defaultBounds()->wrapAroundMode()) { accessor->moveTo(dstPoint.x(), dstPoint.y()); srcAcc->moveTo(srcPoint.x(), srcPoint.y()); srcAcc->sampledOldRawData(accessor->rawData()); } } progressHelper.step(); } } QTransform KisPerspectiveTransformWorker::forwardTransform() const { return m_forwardTransform; } QTransform KisPerspectiveTransformWorker::backwardTransform() const { return m_backwardTransform; } diff --git a/libs/image/tests/kis_asl_parser_test.cpp b/libs/image/tests/kis_asl_parser_test.cpp index 9f3f6e8857..1b4d95af5e 100644 --- a/libs/image/tests/kis_asl_parser_test.cpp +++ b/libs/image/tests/kis_asl_parser_test.cpp @@ -1,331 +1,331 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_parser_test.h" #include #include "testutil.h" #include #include #include #include #include #include #include void KisAslParserTest::test() { QString fileName(TestUtil::fetchDataFileLazy("asl/freebie.asl")); QFile aslFile(fileName); aslFile.open(QIODevice::ReadOnly); KisAslReader reader; QDomDocument doc = reader.readFile(&aslFile); dbgKrita << ppVar(doc.toString()); KisAslObjectCatcher trivialCatcher; KisAslXmlParser parser; parser.parseXML(doc, trivialCatcher); } struct CallbackVerifier { CallbackVerifier() : m_numCallsHappened(0) {} void setColor(const QColor &color) { QVERIFY(color == QColor(Qt::white)); m_numCallsHappened++; } void setOpacity(double opacity) { QVERIFY(qFuzzyCompare(opacity, 75)); m_numCallsHappened++; } void setBlendingMode(const QString &mode) { QVERIFY(mode == "Scrn"); m_numCallsHappened++; } void setEnabled(bool value) { QVERIFY(value); m_numCallsHappened++; } void setCurve(const QString &name, const QVector &points) { QCOMPARE(name, QString("Linear")); QCOMPARE(points[0], QPointF()); QCOMPARE(points[1], QPointF(255.0, 255.0)); m_numCallsHappened++; } void setText(const QString &text) { QCOMPARE(text, QString("11adf7a2-a120-11e1-957c-d1ee226781a4")); m_numCallsHappened++; } void setPattern(const KoPattern *pattern) { dbgKrita << ppVar(pattern->name()); dbgKrita << ppVar(pattern->filename()); //QCOMPARE(text, QString("11adf7a2-a120-11e1-957c-d1ee226781a4")); m_numCallsHappened++; } int m_numCallsHappened; }; void KisAslParserTest::testWithCallbacks() { using namespace std::placeholders; QString fileName(TestUtil::fetchDataFileLazy("asl/freebie.asl")); QFile aslFile(fileName); aslFile.open(QIODevice::ReadOnly); KisAslReader reader; QDomDocument doc = reader.readFile(&aslFile); KisAslCallbackObjectCatcher c; CallbackVerifier verifier; c.subscribeColor("/Styl/Lefx/IrGl/Clr ", std::bind(&CallbackVerifier::setColor, &verifier, _1)); c.subscribeUnitFloat("/Styl/Lefx/IrGl/Opct", "#Prc", std::bind(&CallbackVerifier::setOpacity, &verifier, _1)); c.subscribeEnum("/Styl/Lefx/IrGl/Md ", "BlnM", std::bind(&CallbackVerifier::setBlendingMode, &verifier, _1)); c.subscribeBoolean("/Styl/Lefx/IrGl/enab", std::bind(&CallbackVerifier::setEnabled, &verifier, _1)); c.subscribeCurve("/Styl/Lefx/OrGl/TrnS", std::bind(&CallbackVerifier::setCurve, &verifier, _1, _2)); c.subscribeText("/null/Idnt", std::bind(&CallbackVerifier::setText, &verifier, _1)); KisAslXmlParser parser; parser.parseXML(doc, c); QCOMPARE(verifier.m_numCallsHappened, 6); } #include void KisAslParserTest::testASLXMLWriter() { KisAslXmlWriter w; QImage testImage(QSize(16, 16), QImage::Format_ARGB32); KoPattern testPattern1(testImage, "Some very nice name ;)", ""); KoPattern testPattern2(testImage, "Another very nice name ;P", ""); w.enterList("Patterns"); w.writePattern("", &testPattern1); w.writePattern("", &testPattern2); w.leaveList(); w.enterDescriptor("", "", "null"); w.writeText("Nm ", "www.designpanoply.com - Freebie 5"); w.writeText("Idnt", "11adf7a2-a120-11e1-957c-d1ee226781a4"); w.leaveDescriptor(); w.enterDescriptor("", "", "Styl"); w.enterDescriptor("documentMode", "", "documentMode"); w.leaveDescriptor(); w.enterDescriptor("Lefx", "", "Lefx"); w.writeUnitFloat("Scl ", "#Prc", 100); w.writeBoolean("masterFxSwitch", true); w.enterDescriptor("DrSh", "", "DrSh"); w.writeBoolean("enab", true); w.writeEnum("Md ", "BlnM", "Mltp"); w.writeColor("Clr ", Qt::green); w.writeUnitFloat("Opct", "#Prc", 16); w.writeBoolean("uglg", false); w.writeUnitFloat("lagl", "#Prc", 100); w.writeUnitFloat("Dstn", "#Pxl", 100); w.writeUnitFloat("Ckmt", "#Pxl", 100); w.writeUnitFloat("blur", "#Pxl", 100); w.writeUnitFloat("Nose", "#Prc", 100); w.writeBoolean("anta", true); w.writeCurve("TrnS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.writeBoolean("layerConceals", true); w.leaveDescriptor(); w.leaveDescriptor(); w.leaveDescriptor(); dbgKrita << ppVar(w.document().toString()); } #include #include void KisAslParserTest::testWritingGradients() { KisAslXmlWriter w1; KoSegmentGradient segmentGradient; segmentGradient.createSegment(INTERP_LINEAR, COLOR_INTERP_RGB, 0.0, 0.3, 0.15, Qt::black, Qt::red); segmentGradient.createSegment(INTERP_LINEAR, COLOR_INTERP_RGB, 0.3, 0.6, 0.45, Qt::red, Qt::green); segmentGradient.createSegment(INTERP_LINEAR, COLOR_INTERP_RGB, 0.6, 1.0, 0.8, Qt::green, Qt::white); w1.writeSegmentGradient("tstG", &segmentGradient); //dbgKrita << "==="; //dbgKrita << ppVar(w1.document().toString()); KisAslXmlWriter w2; const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QList stops; - stops << KoGradientStop(0.0, KoColor(Qt::black, cs)); - stops << KoGradientStop(0.3, KoColor(Qt::red, cs)); - stops << KoGradientStop(0.6, KoColor(Qt::green, cs)); - stops << KoGradientStop(1.0, KoColor(Qt::white, cs)); + stops << KoGradientStop(0.0, KoColor(Qt::black, cs), COLORSTOP); + stops << KoGradientStop(0.3, KoColor(Qt::red, cs), COLORSTOP); + stops << KoGradientStop(0.6, KoColor(Qt::green, cs), COLORSTOP); + stops << KoGradientStop(1.0, KoColor(Qt::white, cs), COLORSTOP); KoStopGradient stopGradient; stopGradient.setStops(stops); w2.writeStopGradient("tstG", &stopGradient); //dbgKrita << "==="; //dbgKrita << ppVar(w2.document().toString()); QCOMPARE(w1.document().toString(), w2.document().toString()); } #include void KisAslParserTest::testASLWriter() { //QString srcFileName(TestUtil::fetchDataFileLazy("asl/testset/freebie_with_pattern.asl")); QString srcFileName(TestUtil::fetchDataFileLazy("asl/freebie.asl")); QDomDocument srcDoc; { QFile srcAslFile(srcFileName); srcAslFile.open(QIODevice::ReadOnly); KisAslReader reader; srcDoc = reader.readFile(&srcAslFile); QFile tfile("src_parsed.xml"); tfile.open(QIODevice::WriteOnly); tfile.write(srcDoc.toByteArray()); tfile.close(); } QString dstFileName("test.asl"); { QFile dstAslFile(dstFileName); dstAslFile.open(QIODevice::WriteOnly); KisAslWriter writer; writer.writeFile(&dstAslFile, srcDoc); dstAslFile.flush(); dstAslFile.close(); } QDomDocument dstDoc; { QFile roundTripAslFile(dstFileName); roundTripAslFile.open(QIODevice::ReadOnly); KisAslReader reader; dstDoc = reader.readFile(&roundTripAslFile); QFile tfile("dst_parsed.xml"); tfile.open(QIODevice::WriteOnly); tfile.write(dstDoc.toByteArray()); tfile.close(); } QCOMPARE(srcDoc.toByteArray(), dstDoc.toByteArray()); } void KisAslParserTest::testParserWithPatterns() { QDir dir(QString(FILES_DATA_DIR) + QDir::separator() + "testset"); QFileInfoList files = dir.entryInfoList(QStringList() << "*.asl", QDir::Files); int index = 0; Q_FOREACH (const QFileInfo &fileInfo, files) { //if (index != 12) {index++; continue;} dbgKrita << "===" << index << "==="; dbgKrita << ppVar(fileInfo.fileName()); QFile aslFile(fileInfo.absoluteFilePath()); aslFile.open(QIODevice::ReadOnly); KisAslReader reader; QDomDocument doc = reader.readFile(&aslFile); QFile xmlFile("mydata.xml"); xmlFile.open(QIODevice::WriteOnly); xmlFile.write(doc.toByteArray()); //dbgKrita << ppVar(doc.toString()); CallbackVerifier verifier; KisAslCallbackObjectCatcher c; c.subscribePattern("/Patterns/KisPattern", std::bind(&CallbackVerifier::setPattern, &verifier, std::placeholders::_1)); KisAslXmlParser parser; parser.parseXML(doc, c); //QCOMPARE(verifier.m_numCallsHappened, 7); index++; //break; } } QTEST_MAIN(KisAslParserTest) diff --git a/libs/pigment/CMakeLists.txt b/libs/pigment/CMakeLists.txt index 7e38c6cb00..e85cd1d944 100644 --- a/libs/pigment/CMakeLists.txt +++ b/libs/pigment/CMakeLists.txt @@ -1,131 +1,132 @@ project(kritapigment) # we have to repeat platform specifics from top-level if (WIN32) include_directories(${CMAKE_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif () include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/resources ${CMAKE_CURRENT_SOURCE_DIR}/compositeops) set(FILE_OPENEXR_SOURCES) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIRS}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() set(LINK_VC_LIB) if(HAVE_VC) include_directories(SYSTEM ${Vc_INCLUDE_DIR}) set(LINK_VC_LIB ${Vc_LIBRARIES}) ko_compile_for_all_implementations_no_scalar(__per_arch_factory_objs compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp) ko_compile_for_all_implementations(__per_arch_alpha_applicator_factory_objs KoAlphaMaskApplicatorFactoryImpl.cpp) message("Following objects are generated from the per-arch lib") message("${__per_arch_factory_objs}") else() set(__per_arch_alpha_applicator_factory_objs KoAlphaMaskApplicatorFactoryImpl.cpp) endif() add_subdirectory(tests) add_subdirectory(benchmarks) set(kritapigment_SRCS DebugPigment.cpp KoBasicHistogramProducers.cpp KoAlphaMaskApplicatorBase.cpp KoColor.cpp KoColorDisplayRendererInterface.cpp KoColorConversionAlphaTransformation.cpp KoColorConversionCache.cpp KoColorConversions.cpp KoColorConversionSystem.cpp KoColorConversionTransformation.cpp KoColorProofingConversionTransformation.cpp KoColorConversionTransformationFactory.cpp KoColorModelStandardIds.cpp KoColorProfile.cpp KoColorSpace.cpp KoColorSpaceEngine.cpp KoColorSpaceFactory.cpp KoColorSpaceMaths.cpp KoCmykColorSpaceMaths.cpp KoLabColorSpaceMaths.cpp KoColorSpaceRegistry.cpp KoColorProfileStorage.cpp KoColorTransformation.cpp KoColorTransformationFactory.cpp KoColorTransformationFactoryRegistry.cpp KoCompositeColorTransformation.cpp KoCompositeOp.cpp KoCompositeOpRegistry.cpp KoCopyColorConversionTransformation.cpp KoFallBackColorTransformation.cpp KoHistogramProducer.cpp KoMultipleColorConversionTransformation.cpp KoUniqueNumberForIdServer.cpp colorspaces/KoAlphaColorSpace.cpp colorspaces/KoLabColorSpace.cpp colorspaces/KoRgbU16ColorSpace.cpp colorspaces/KoRgbU8ColorSpace.cpp colorspaces/KoSimpleColorSpaceEngine.cpp compositeops/KoOptimizedCompositeOpFactory.cpp compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp compositeops/KoAlphaDarkenParamsWrapper.cpp ${__per_arch_factory_objs} ${__per_arch_alpha_applicator_factory_objs} KoAlphaMaskApplicatorFactory.cpp colorprofiles/KoDummyColorProfile.cpp resources/KoAbstractGradient.cpp resources/KoColorSet.cpp resources/KisSwatch.cpp resources/KisSwatchGroup.cpp resources/KoPattern.cpp resources/KoResource.cpp resources/KoMD5Generator.cpp resources/KoHashGeneratorProvider.cpp resources/KoStopGradient.cpp resources/KoSegmentGradient.cpp + resources/KoCachedGradient.h ) set (EXTRA_LIBRARIES ${LINK_OPENEXR_LIB} ${LINK_VC_LIB}) if(MSVC OR (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) # avoid "cannot open file 'LIBC.lib'" error set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:LIBC.LIB") endif() add_library(kritapigment SHARED ${kritapigment_SRCS}) generate_export_header(kritapigment) target_include_directories( kritapigment PUBLIC $ $ ) target_link_libraries( kritapigment PUBLIC kritaplugin kritastore kritaglobal ${EXTRA_LIBRARIES} KF5::I18n KF5::ConfigCore Qt5::Core Qt5::Gui Qt5::Xml ${WIN32_PLATFORM_NET_LIBS} ) set_target_properties(kritapigment PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritapigment ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/pigment/KoColorSpace.cpp b/libs/pigment/KoColorSpace.cpp index 5198378459..23fd346d1c 100644 --- a/libs/pigment/KoColorSpace.cpp +++ b/libs/pigment/KoColorSpace.cpp @@ -1,831 +1,836 @@ /* * Copyright (c) 2005 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoColorSpace.h" #include "KoColorSpace_p.h" #include "KoChannelInfo.h" #include "DebugPigment.h" #include "KoCompositeOp.h" #include "KoColorTransformation.h" #include "KoColorTransformationFactory.h" #include "KoColorTransformationFactoryRegistry.h" #include "KoColorConversionCache.h" #include "KoColorConversionSystem.h" #include "KoColorSpaceRegistry.h" #include "KoColorProfile.h" #include "KoCopyColorConversionTransformation.h" #include "KoFallBackColorTransformation.h" #include "KoUniqueNumberForIdServer.h" #include "KoMixColorsOp.h" #include "KoConvolutionOp.h" #include "KoCompositeOpRegistry.h" #include "KoColorSpaceEngine.h" #include #include #include #include #include #include #include #include KoColorSpace::KoColorSpace() : d(new Private()) { } KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp) : d(new Private()) { d->id = id; d->idNumber = KoUniqueNumberForIdServer::instance()->numberForId(d->id); d->name = name; d->mixColorsOp = mixColorsOp; d->convolutionOp = convolutionOp; d->transfoToRGBA16 = 0; d->transfoFromRGBA16 = 0; d->transfoToLABA16 = 0; d->transfoFromLABA16 = 0; d->gamutXYY = QPolygonF(); d->TRCXYY = QPolygonF(); d->colorants = QVector (0); d->lumaCoefficients = QVector (0); d->iccEngine = 0; d->deletability = NotOwnedByRegistry; } KoColorSpace::~KoColorSpace() { Q_ASSERT(d->deletability != OwnedByRegistryDoNotDelete); qDeleteAll(d->compositeOps); Q_FOREACH (KoChannelInfo * channel, d->channels) { delete channel; } if (d->deletability == NotOwnedByRegistry) { KoColorConversionCache* cache = KoColorSpaceRegistry::instance()->colorConversionCache(); if (cache) { cache->colorSpaceIsDestroyed(this); } } delete d->mixColorsOp; delete d->convolutionOp; delete d->transfoToRGBA16; delete d->transfoFromRGBA16; delete d->transfoToLABA16; delete d->transfoFromLABA16; delete d; } bool KoColorSpace::operator==(const KoColorSpace& rhs) const { const KoColorProfile* p1 = rhs.profile(); const KoColorProfile* p2 = profile(); return d->idNumber == rhs.d->idNumber && ((p1 == p2) || (*p1 == *p2)); } QString KoColorSpace::id() const { return d->id; } QString KoColorSpace::name() const { return d->name; } //Color space info stuff. QPolygonF KoColorSpace::gamutXYY() const { if (d->gamutXYY.empty()) { //now, let's decide on the boundary. This is a bit tricky because icc profiles can be both matrix-shaper and cLUT at once if the maker so pleases. //first make a list of colors. qreal max = 1.0; if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") { //boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general. max = this->channels()[0]->getUIMax(); } int samples = 5;//amount of samples in our color space. const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F32"); quint8 *data = new quint8[pixelSize()]; quint8 data2[16]; // xyza f32 is 4 floats, that is 16 bytes per pixel. //QVector sampleCoordinates(pow(colorChannelCount(),samples)); //sampleCoordinates.fill(0.0); // This is fixed to 5 since the maximum number of channels are 5 for CMYKA QVector channelValuesF(5);//for getting the coordinates. for(int x=0;xnormalisedChannelsValue(data2, channelValuesF); qreal x = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); qreal y = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->gamutXYY << QPointF(x,y); } else { for(int y=0;ynormalisedChannelsValue(data2, channelValuesF); qreal x = channelValuesF[0] / (channelValuesF[0] + channelValuesF[1] + channelValuesF[2]); qreal y = channelValuesF[1] / (channelValuesF[0] + channelValuesF[1] + channelValuesF[2]); d->gamutXYY<< QPointF(x,y); } } else { channelValuesF[0]=(max/(samples-1))*(x); channelValuesF[1]=(max/(samples-1))*(y); channelValuesF[2]=(max/(samples-1))*(z); channelValuesF[3]=max; if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz. fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); } qreal x = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); qreal y = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->gamutXYY<< QPointF(x,y); } } } } } delete[] data; //if we ever implement a boundary-checking thing I'd add it here. return d->gamutXYY; } else { return d->gamutXYY; } } QPolygonF KoColorSpace::estimatedTRCXYY() const { if (d->TRCXYY.empty()){ qreal max = 1.0; if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") { //boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general. max = this->channels()[0]->getUIMax(); } const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F32"); quint8 *data = new quint8[pixelSize()]; quint8 *data2 = new quint8[xyzColorSpace->pixelSize()]; // This is fixed to 5 since the maximum number of channels are 5 for CMYKA QVector channelValuesF(5);//for getting the coordinates. d->colorants.resize(3*colorChannelCount()); const int segments = 10; for (quint32 i=0; idisplayPosition()] = ((max/segments)*(segments-j)); if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz. fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); } if (j==0) { colorantY = channelValuesF[1]; d->colorants[3*i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[3*i+1] = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[3*i+2] = channelValuesF[1]; } d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/segments)*(segments-j))); } } else { for (int j = 0; j <= segments; j++) { channelValuesF.fill(0.0); channelValuesF[i] = ((max/segments)*(j)); fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); if (j==0) { colorantY = channelValuesF[1]; d->colorants[3*i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[3*i+1] = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[3*i+2] = channelValuesF[1]; } d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/segments)*(j))); } } } delete[] data; delete[] data2; return d->TRCXYY; } else { return d->TRCXYY; } } QVector KoColorSpace::lumaCoefficients() const { if (d->lumaCoefficients.size()>1){ return d->lumaCoefficients; } else { d->lumaCoefficients.resize(3); if (colorModelId().id()!="RGBA") { d->lumaCoefficients.fill(0.33); } else { if (d->colorants.size() <= 0) { if (profile() && profile()->hasColorants()) { d->colorants.resize(3 * colorChannelCount()); d->colorants = profile()->getColorantsxyY(); } else { QPolygonF p = estimatedTRCXYY(); Q_UNUSED(p); } } if (d->colorants[2]<0 || d->colorants[5]<0 || d->colorants[8]<0) { d->lumaCoefficients[0]=0.2126; d->lumaCoefficients[1]=0.7152; d->lumaCoefficients[2]=0.0722; } else { // luma coefficients need to add up to 1.0 qreal sum = d->colorants[2] + d->colorants[5] + d->colorants[8]; d->lumaCoefficients[0] = d->colorants[2] / sum; d->lumaCoefficients[1] = d->colorants[5] / sum; d->lumaCoefficients[2] = d->colorants[8] / sum; } } return d->lumaCoefficients; } } QList KoColorSpace::channels() const { return d->channels; } QBitArray KoColorSpace::channelFlags(bool color, bool alpha) const { QBitArray ba(d->channels.size()); if (!color && !alpha) return ba; for (int i = 0; i < d->channels.size(); ++i) { KoChannelInfo * channel = d->channels.at(i); if ((color && channel->channelType() == KoChannelInfo::COLOR) || (alpha && channel->channelType() == KoChannelInfo::ALPHA)) ba.setBit(i, true); } return ba; } void KoColorSpace::addChannel(KoChannelInfo * ci) { d->channels.push_back(ci); } bool KoColorSpace::hasCompositeOp(const QString& id) const { return d->compositeOps.contains(id); } QList KoColorSpace::compositeOps() const { return d->compositeOps.values(); } KoMixColorsOp* KoColorSpace::mixColorsOp() const { return d->mixColorsOp; } KoConvolutionOp* KoColorSpace::convolutionOp() const { return d->convolutionOp; } const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const { const QHash::ConstIterator it = d->compositeOps.constFind(id); if (it != d->compositeOps.constEnd()) { return it.value(); } else { warnPigment << "Asking for non-existent composite operation " << id << ", returning " << COMPOSITE_OVER; return d->compositeOps.value(COMPOSITE_OVER); } } void KoColorSpace::addCompositeOp(const KoCompositeOp * op) { if (op->colorSpace()->id() == id()) { d->compositeOps.insert(op->id(), const_cast(op)); } } const KoColorConversionTransformation* KoColorSpace::toLabA16Converter() const { if (!d->transfoToLABA16) { d->transfoToLABA16 = KoColorSpaceRegistry::instance()->createColorConverter(this, KoColorSpaceRegistry::instance()->lab16(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoToLABA16; } const KoColorConversionTransformation* KoColorSpace::fromLabA16Converter() const { if (!d->transfoFromLABA16) { d->transfoFromLABA16 = KoColorSpaceRegistry::instance()->createColorConverter(KoColorSpaceRegistry::instance()->lab16(), this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoFromLABA16; } const KoColorConversionTransformation* KoColorSpace::toRgbA16Converter() const { if (!d->transfoToRGBA16) { d->transfoToRGBA16 = KoColorSpaceRegistry::instance()->createColorConverter(this, KoColorSpaceRegistry::instance()->rgb16(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoToRGBA16; } const KoColorConversionTransformation* KoColorSpace::fromRgbA16Converter() const { if (!d->transfoFromRGBA16) { d->transfoFromRGBA16 = KoColorSpaceRegistry::instance()->createColorConverter(KoColorSpaceRegistry::instance()->rgb16() , this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoFromRGBA16; } void KoColorSpace::toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { toLabA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { fromLabA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { toRgbA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { fromRgbA16Converter()->transform(src, dst, nPixels); } KoColorConversionTransformation* KoColorSpace::createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (*this == *dstColorSpace) { return new KoCopyColorConversionTransformation(this); } else { return KoColorSpaceRegistry::instance()->createColorConverter(this, dstColorSpace, renderingIntent, conversionFlags); } } bool KoColorSpace::convertPixelsTo(const quint8 * src, quint8 * dst, const KoColorSpace * dstColorSpace, quint32 numPixels, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (*this == *dstColorSpace) { if (src != dst) { memcpy(dst, src, numPixels * sizeof(quint8) * pixelSize()); } } else { KoCachedColorConversionTransformation cct = KoColorSpaceRegistry::instance()->colorConversionCache()->cachedConverter(this, dstColorSpace, renderingIntent, conversionFlags); cct.transformation()->transform(src, dst, numPixels); } return true; } KoColorConversionTransformation * KoColorSpace::createProofingTransform(const KoColorSpace *dstColorSpace, const KoColorSpace *proofingSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::Intent proofingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, quint8 *gamutWarning, double adaptationState) const { if (!d->iccEngine) { d->iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); } if (!d->iccEngine) return 0; return d->iccEngine->createColorProofingTransformation(this, dstColorSpace, proofingSpace, renderingIntent, proofingIntent, conversionFlags, gamutWarning, adaptationState); } bool KoColorSpace::proofPixelsTo(const quint8 *src, quint8 *dst, quint32 numPixels, KoColorConversionTransformation *proofingTransform) const { proofingTransform->transform(src, dst, numPixels); //the transform is deleted in the destructor. return true; } void KoColorSpace::bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { Q_ASSERT_X(*op->colorSpace() == *this, "KoColorSpace::bitBlt", QString("Composite op is for color space %1 (%2) while this is %3 (%4)").arg(op->colorSpace()->id()).arg(op->colorSpace()->profile()->name()).arg(id()).arg(profile()->name()).toLatin1()); if(params.rows <= 0 || params.cols <= 0) return; if(!(*this == *srcSpace)) { if (preferCompositionInSourceColorSpace() && srcSpace->hasCompositeOp(op->id())) { quint32 conversionDstBufferStride = params.cols * srcSpace->pixelSize(); QVector * conversionDstCache = threadLocalConversionCache(params.rows * conversionDstBufferStride); quint8* conversionDstData = conversionDstCache->data(); for(qint32 row=0; rowcompositeOp(op->id()); KoCompositeOp::ParameterInfo paramInfo(params); paramInfo.dstRowStart = conversionDstData; paramInfo.dstRowStride = conversionDstBufferStride; otherOp->composite(paramInfo); for(qint32 row=0; rowconvertPixelsTo(conversionDstData + row * conversionDstBufferStride, params.dstRowStart + row * params.dstRowStride, this, params.cols, renderingIntent, conversionFlags); } } else { quint32 conversionBufferStride = params.cols * pixelSize(); QVector * conversionCache = threadLocalConversionCache(params.rows * conversionBufferStride); quint8* conversionData = conversionCache->data(); for(qint32 row=0; rowconvertPixelsTo(params.srcRowStart + row * params.srcRowStride, conversionData + row * conversionBufferStride, this, params.cols, renderingIntent, conversionFlags); } KoCompositeOp::ParameterInfo paramInfo(params); paramInfo.srcRowStart = conversionData; paramInfo.srcRowStride = conversionBufferStride; op->composite(paramInfo); } } else { op->composite(params); } } QVector * KoColorSpace::threadLocalConversionCache(quint32 size) const { QVector * ba = 0; if (!d->conversionCache.hasLocalData()) { ba = new QVector(size, '0'); d->conversionCache.setLocalData(ba); } else { ba = d->conversionCache.localData(); if ((quint8)ba->size() < size) ba->resize(size); } return ba; } KoColorTransformation* KoColorSpace::createColorTransformation(const QString & id, const QHash & parameters) const { KoColorTransformationFactory* factory = KoColorTransformationFactoryRegistry::instance()->get(id); if (!factory) return 0; QPair model(colorModelId(), colorDepthId()); QList< QPair > models = factory->supportedModels(); if (models.isEmpty() || models.contains(model)) { return factory->createTransformation(this, parameters); } else { // Find the best solution // TODO use the color conversion cache KoColorConversionTransformation* csToFallBack = 0; KoColorConversionTransformation* fallBackToCs = 0; KoColorSpaceRegistry::instance()->createColorConverters(this, models, csToFallBack, fallBackToCs); Q_ASSERT(csToFallBack); Q_ASSERT(fallBackToCs); KoColorTransformation* transfo = factory->createTransformation(fallBackToCs->srcColorSpace(), parameters); return new KoFallBackColorTransformation(csToFallBack, fallBackToCs, transfo); } } void KoColorSpace::increaseLuminosity(quint8 * pixel, qreal step) const{ int channelnumber = channelCount(); QVector channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ihasTRC()){ //only linearise and crunch the luma if there's a TRC profile()->linearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = pow(luma, 1/2.2); luma = qMin(1.0, luma + step); luma = pow(luma, 2.2); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); } else { qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = qMin(1.0, luma + step); channelValues = fromHSY(&hue, &sat, &luma); } for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ihasTRC()){ //only linearise and crunch the luma if there's a TRC profile()->linearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = pow(luma, 1/2.2); if (luma-step<0.0) { luma=0.0; } else { luma -= step; } luma = pow(luma, 2.2); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); } else { qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (luma-step<0.0) { luma=0.0; } else { luma -= step; } channelValues = fromHSY(&hue, &sat, &luma); } for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); sat += step; sat = qBound(0.0, sat, 1.0); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); sat -= step; sat = qBound(0.0, sat, 1.0); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (hue+step>1.0){ hue=(hue+step)- 1.0; } else { hue += step; } channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (hue-step<0.0){ hue=1.0-(step-hue); } else { hue -= step; } channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); u += step; u = qBound(0.0, u, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); u -= step; u = qBound(0.0, u, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); v += step; v = qBound(0.0, v, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); v -= step; v = qBound(0.0, v, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;irgb8(dstProfile); if (data) this->convertPixelsTo(const_cast(data), img.bits(), dstCS, width * height, renderingIntent, conversionFlags); return img; } bool KoColorSpace::preferCompositionInSourceColorSpace() const { return false; } void KoColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const +{ + fillGrayBrushWithColorAndLightnessWithStrength(dst, brush, brushColor, 1.0, nPixels); +} + +void KoColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const { /// Fallback implementation. All RGB color spaces have their own /// implementation without any conversions. const int rgbPixelSize = sizeof(KoBgrU16Traits::Pixel); QScopedArrayPointer rgbBuffer(new quint8[(nPixels + 1) * rgbPixelSize]); - quint8 *rgbBrushColorBuffer = rgbBuffer.data() + nPixels * rgbPixelSize; + quint8* rgbBrushColorBuffer = rgbBuffer.data() + nPixels * rgbPixelSize; this->toRgbA16(dst, rgbBuffer.data(), nPixels); this->toRgbA16(brushColor, rgbBrushColorBuffer, 1); - fillGrayBrushWithColorPreserveLightnessRGB(rgbBuffer.data(), brush, rgbBrushColorBuffer, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(rgbBuffer.data(), brush, rgbBrushColorBuffer, strength, nPixels); this->fromRgbA16(rgbBuffer.data(), dst, nPixels); } diff --git a/libs/pigment/KoColorSpace.h b/libs/pigment/KoColorSpace.h index b83fcb4a09..8a9b176611 100644 --- a/libs/pigment/KoColorSpace.h +++ b/libs/pigment/KoColorSpace.h @@ -1,715 +1,717 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2006-2007 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOCOLORSPACE_H #define KOCOLORSPACE_H #include #include #include #include #include #include #include "KoColorSpaceConstants.h" #include "KoColorConversionTransformation.h" #include "KoColorProofingConversionTransformation.h" #include "KoCompositeOp.h" #include #include "kritapigment_export.h" class QDomDocument; class QDomElement; class KoChannelInfo; class KoColorProfile; class KoColorTransformation; class QBitArray; enum Deletability { OwnedByRegistryDoNotDelete, OwnedByRegistryRegistryDeletes, NotOwnedByRegistry }; enum ColorSpaceIndependence { FULLY_INDEPENDENT, TO_LAB16, TO_RGBA8, TO_RGBA16 }; class KoMixColorsOp; class KoConvolutionOp; /** * A KoColorSpace is the definition of a certain color space. * * A color model and a color space are two related concepts. A color * model is more general in that it describes the channels involved and * how they in broad terms combine to describe a color. Examples are * RGB, HSV, CMYK. * * A color space is more specific in that it also describes exactly how * the channels are combined. So for each color model there can be a * number of specific color spaces. So RGB is the model and sRGB, * adobeRGB, etc are colorspaces. * * In Pigment KoColorSpace acts as both a color model and a color space. * You can think of the class definition as the color model, but the * instance of the class as representing a colorspace. * * A third concept is the profile represented by KoColorProfile. It * represents the info needed to specialize a color model into a color * space. * * KoColorSpace is an abstract class serving as an interface. * * Subclasses implement actual color spaces * Some subclasses implement only some parts and are named Traits * */ class KRITAPIGMENT_EXPORT KoColorSpace : public boost::equality_comparable { friend class KoColorSpaceRegistry; friend class KoColorSpaceFactory; protected: /// Only for use by classes that serve as baseclass for real color spaces KoColorSpace(); public: /// Should be called by real color spaces KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp); virtual bool operator==(const KoColorSpace& rhs) const; protected: virtual ~KoColorSpace(); public: //========== Gamut and other basic info ===================================// /* * @returns QPolygonF with 5*channel samples converted to xyY. * maybe convert to 3d space in future? */ QPolygonF gamutXYY() const; /* * @returns a polygon with 5 samples per channel converted to xyY, but unlike * gamutxyY it focuses on the luminance. This then can be used to visualise * the approximate trc of a given colorspace. */ QPolygonF estimatedTRCXYY() const; QVector lumaCoefficients() const; //========== Channels =====================================================// /// Return a list describing all the channels this color model has. The order /// of the channels in the list is the order of channels in the pixel. To find /// out the preferred display position, use KoChannelInfo::displayPosition. QList channels() const; /** * The total number of channels for a single pixel in this color model */ virtual quint32 channelCount() const = 0; /** * Position of the alpha channel in a pixel */ virtual quint32 alphaPos() const = 0; /** * The total number of color channels (excludes alpha) for a single * pixel in this color model. */ virtual quint32 colorChannelCount() const = 0; /** * returns a QBitArray that contains true for the specified * channel types: * * @param color if true, set all color channels to true * @param alpha if true, set all alpha channels to true * * The order of channels is the colorspace descriptive order, * not the pixel order. */ QBitArray channelFlags(bool color = true, bool alpha = false) const; /** * The size in bytes of a single pixel in this color model */ virtual quint32 pixelSize() const = 0; /** * Return a string with the channel's value suitable for display in the gui. */ virtual QString channelValueText(const quint8 *pixel, quint32 channelIndex) const = 0; /** * Return a string with the channel's value with integer * channels normalised to the floating point range 0 to 1, if * appropriate. */ virtual QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const = 0; /** * Return a QVector of floats with channels' values normalized * to floating point range 0 to 1. */ virtual void normalisedChannelsValue(const quint8 *pixel, QVector &channels) const = 0; /** * Write in the pixel the value from the normalized vector. */ virtual void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) const = 0; /** * Convert the value of the channel at the specified position into * an 8-bit value. The position is not the number of bytes, but * the position of the channel as defined in the channel info list. */ virtual quint8 scaleToU8(const quint8 * srcPixel, qint32 channelPos) const = 0; /** * Set dstPixel to the pixel containing only the given channel of srcPixel. The remaining channels * should be set to whatever makes sense for 'empty' channels of this color space, * with the intent being that the pixel should look like it only has the given channel. */ virtual void singleChannelPixel(quint8 *dstPixel, const quint8 *srcPixel, quint32 channelIndex) const = 0; //========== Identification ===============================================// /** * ID for use in files and internally: unchanging name. As the id must be unique * it is usually the concatenation of the id of the color model and of the color * depth, for instance "RGBA8" or "CMYKA16" or "XYZA32f". */ QString id() const; /** * User visible name which contains the name of the color model and of the color depth. * For instance "RGBA (8-bits)" or "CMYKA (16-bits)". */ QString name() const; /** * @return a string that identify the color model (for instance "RGB" or "CMYK" ...) * @see KoColorModelStandardIds.h */ virtual KoID colorModelId() const = 0; /** * @return a string that identify the bit depth (for instance "U8" or "F16" ...) * @see KoColorModelStandardIds.h */ virtual KoID colorDepthId() const = 0; /** * @return true if the profile given in argument can be used by this color space */ virtual bool profileIsCompatible(const KoColorProfile* profile) const = 0; /** * If false, images in this colorspace will degrade considerably by * functions, tools and filters that have the given measure of colorspace * independence. * * @param independence the measure to which this colorspace will suffer * from the manipulations of the tool or filter asking * @return false if no degradation will take place, true if degradation will * take place */ virtual bool willDegrade(ColorSpaceIndependence independence) const = 0; //========== Capabilities =================================================// /** * Tests if the colorspace offers the specific composite op. */ virtual bool hasCompositeOp(const QString & id) const; /** * Returns the list of user-visible composite ops supported by this colorspace. */ virtual QList compositeOps() const; /** * Retrieve a single composite op from the ones this colorspace offers. * If the requeste composite op does not exist, COMPOSITE_OVER is returned. */ const KoCompositeOp * compositeOp(const QString & id) const; /** * add a composite op to this colorspace. */ virtual void addCompositeOp(const KoCompositeOp * op); /** * Returns true if the colorspace supports channel values outside the * (normalised) range 0 to 1. */ virtual bool hasHighDynamicRange() const = 0; //========== Display profiles =============================================// /** * Return the profile of this color space. */ virtual const KoColorProfile * profile() const = 0; //================= Conversion functions ==================================// /** * The fromQColor methods take a given color defined as an RGB QColor * and fills a byte array with the corresponding color in the * the colorspace managed by this strategy. * * @param color the QColor that will be used to fill dst * @param dst a pointer to a pixel * @param profile the optional profile that describes the color values of QColor */ virtual void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const = 0; /** * The toQColor methods take a byte array that is at least pixelSize() long * and converts the contents to a QColor, using the given profile as a source * profile and the optional profile as a destination profile. * * @param src a pointer to the source pixel * @param c the QColor that will be filled with the color at src * @param profile the optional profile that describes the color in c, for instance the monitor profile */ virtual void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const = 0; /** * Convert the pixels in data to (8-bit BGRA) QImage using the specified profiles. * * @param data A pointer to a contiguous memory region containing width * height pixels * @param width in pixels * @param height in pixels * @param dstProfile destination profile * @param renderingIntent the rendering intent * @param conversionFlags conversion flags */ virtual QImage convertToQImage(const quint8 *data, qint32 width, qint32 height, const KoColorProfile * dstProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * Convert the specified data to Lab (D50). All colorspaces are guaranteed to support this * * @param src the source data * @param dst the destination data * @param nPixels the number of source pixels */ virtual void toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Convert the specified data from Lab (D50). to this colorspace. All colorspaces are * guaranteed to support this. * * @param src the pixels in 16 bit lab format * @param dst the destination data * @param nPixels the number of pixels in the array */ virtual void fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Convert the specified data to sRGB 16 bits. All colorspaces are guaranteed to support this * * @param src the source data * @param dst the destination data * @param nPixels the number of source pixels */ virtual void toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Convert the specified data from sRGB 16 bits. to this colorspace. All colorspaces are * guaranteed to support this. * * @param src the pixels in 16 bit rgb format * @param dst the destination data * @param nPixels the number of pixels in the array */ virtual void fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Create a color conversion transformation. */ virtual KoColorConversionTransformation* createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * Convert a byte array of srcLen pixels *src to the specified color space * and put the converted bytes into the prepared byte array *dst. * * Returns false if the conversion failed, true if it succeeded * * This function is not thread-safe. If you want to apply multiple conversion * in different threads at the same time, you need to create one color converter * per-thread using createColorConverter. */ virtual bool convertPixelsTo(const quint8 * src, quint8 * dst, const KoColorSpace * dstColorSpace, quint32 numPixels, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; virtual KoColorConversionTransformation *createProofingTransform(const KoColorSpace * dstColorSpace, const KoColorSpace * proofingSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::Intent proofingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, quint8 *gamutWarning, double adaptationState) const; /** * @brief proofPixelsTo * @param src source * @param dst destination * @param numPixels the amount of pixels. * @param proofingTransform the intent used for proofing. * @return */ virtual bool proofPixelsTo(const quint8 * src, quint8 * dst, quint32 numPixels, KoColorConversionTransformation *proofingTransform) const; /** * Convert @p nPixels pixels in @p src into their human-visible * visual representation. The channel is shown as grayscale. * * Both buffers are in the same color space. * * @param src source buffer in (*this) color space * @param dst destination buffer in the same color space as @p src * @param nPixels length of the buffers in number of pixels * @param pixelSize stride of each pixel in the destination buffer * @param selectedChannelIndex Index of the selected channel. */ virtual void convertChannelToVisualRepresentation(const quint8 *src, quint8 *dst, quint32 nPixels, const qint32 selectedChannelIndex) const = 0; /** * Convert @p nPixels pixels in @p src into their human-visible * visual representation. The channels are shown as if other channels were null (or, if Lab, L = 1.0, *a = *b = 0.0). * * Both buffers are in the same color space. * * @param src source buffer in (*this) color space * @param dst destination buffer in the same color space as @p src * @param nPixels length of the buffers in number of pixels * @param pixelSize stride of each pixel in the destination buffer * @param selectedChannels Bitmap of selected channels */ virtual void convertChannelToVisualRepresentation(const quint8 *src, quint8 *dst, quint32 nPixels, const QBitArray selectedChannels) const = 0; //============================== Manipulation functions ==========================// // // The manipulation functions have default implementations that _convert_ the pixel // to a QColor and back. Reimplement these methods in your color strategy! // /** * Get the alpha value of the given pixel, downscaled to an 8-bit value. */ virtual quint8 opacityU8(const quint8 * pixel) const = 0; virtual qreal opacityF(const quint8 * pixel) const = 0; /** * Set the alpha channel of the given run of pixels to the given value. * * pixels -- a pointer to the pixels that will have their alpha set to this value * alpha -- a downscaled 8-bit value for opacity * nPixels -- the number of pixels * */ virtual void setOpacity(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0; virtual void setOpacity(quint8 * pixels, qreal alpha, qint32 nPixels) const = 0; /** * Multiply the alpha channel of the given run of pixels by the given value. * * pixels -- a pointer to the pixels that will have their alpha set to this value * alpha -- a downscaled 8-bit value for opacity * nPixels -- the number of pixels * */ virtual void multiplyAlpha(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0; /** * Applies the specified 8-bit alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; the alpha values * are assumed to be 8-bits. */ virtual void applyAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0; /** * Applies the inverted 8-bit alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; the alpha values * are assumed to be 8-bits. */ virtual void applyInverseAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0; /** * Applies the specified float alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0 */ virtual void applyAlphaNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0; /** * Applies the inverted specified float alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0 */ virtual void applyInverseNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0; /** * Fills \p pixels with specified \p brushColor and then applies inverted brush * mask specified in \p alpha. */ virtual void fillInverseAlphaNormedFloatMaskWithColor(quint8 * pixels, const float * alpha, const quint8 *brushColor, qint32 nPixels) const = 0; /** * Fills \p dst with specified \p brushColor and then applies inverted brush * mask specified in \p brush. Premultiplied red channel of the brush is * used as an alpha channel for destination pixels. * * The equation is: * * dstC = colorC; * dstA = qAlpha(brush) * (255 - qRed(brush)) / 255; */ virtual void fillGrayBrushWithColor(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const = 0; /** * Fills \p dst with specified \p brushColor and then applies inverted brush * mask specified in \p brush. Inverted red channel of the brush is used * as lightness of the destination. Alpha channel of the brush is used as * alpha of the destination. * * The equation is: * * dstL_hsl = preserveLightness(colorL_hsl, lightFactor); * dstA = qAlpha(brush); * * For details on preserveLightness() formula, * see KoColorSpacePreserveLightnessUtils.h */ virtual void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const; + //Same as above, but with contrast adjusted by strength. Strength == 1 -> full contrast. Allows softer lightness adjustments. + virtual void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const; /** * Create an adjustment object for adjusting the brightness and contrast * transferValues is a 256 bins array with values from 0 to 0xFFFF * This function is thread-safe, but you need to create one KoColorTransformation per thread. */ virtual KoColorTransformation *createBrightnessContrastAdjustment(const quint16 *transferValues) const = 0; /** * Create an adjustment object for adjusting individual channels * transferValues is an array of colorChannelCount number of 256 bins array with values from 0 to 0xFFFF * This function is thread-safe, but you need to create one KoColorTransformation per thread. * * The layout of the channels must be the following: * * 0..N-2 - color channels of the pixel; * N-1 - alpha channel of the pixel (if exists) */ virtual KoColorTransformation *createPerChannelAdjustment(const quint16 * const* transferValues) const = 0; /** * Darken all color channels with the given amount. If compensate is true, * the compensation factor will be used to limit the darkening. * */ virtual KoColorTransformation *createDarkenAdjustment(qint32 shade, bool compensate, qreal compensation) const = 0; /** * Invert color channels of the given pixels * This function is thread-safe, but you need to create one KoColorTransformation per thread. */ virtual KoColorTransformation *createInvertTransformation() const = 0; /** * Get the difference between 2 colors, normalized in the range (0,255). Only completely * opaque and completely transparent are taken into account when computing the difference; * other transparency levels are not regarded when finding the difference. * * Completely transparent pixels are treated as if they are completely * different from any non-transparent pixels. */ virtual quint8 difference(const quint8* src1, const quint8* src2) const = 0; /** * Get the difference between 2 colors, normalized in the range (0,255). This function * takes the Alpha channel of the pixel into account. Alpha channel has the same * weight as Lightness channel. * * Completely transparent pixels are treated as if their color channels are * the same as ones of the other pixel. In other words, only alpha channel * difference is compared. */ virtual quint8 differenceA(const quint8* src1, const quint8* src2) const = 0; /** * @return the mix color operation of this colorspace (do not delete it locally, it's deleted by the colorspace). */ virtual KoMixColorsOp* mixColorsOp() const; /** * @return the convolution operation of this colorspace (do not delete it locally, it's deleted by the colorspace). */ virtual KoConvolutionOp* convolutionOp() const; /** * Calculate the intensity of the given pixel, scaled down to the range 0-255. XXX: Maybe this should be more flexible */ virtual quint8 intensity8(const quint8 * src) const = 0; /* *increase luminosity by step */ virtual void increaseLuminosity(quint8 * pixel, qreal step) const; virtual void decreaseLuminosity(quint8 * pixel, qreal step) const; virtual void increaseSaturation(quint8 * pixel, qreal step) const; virtual void decreaseSaturation(quint8 * pixel, qreal step) const; virtual void increaseHue(quint8 * pixel, qreal step) const; virtual void decreaseHue(quint8 * pixel, qreal step) const; virtual void increaseRed(quint8 * pixel, qreal step) const; virtual void increaseGreen(quint8 * pixel, qreal step) const; virtual void increaseBlue(quint8 * pixel, qreal step) const; virtual void increaseYellow(quint8 * pixel, qreal step) const; virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const = 0; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const = 0; virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const = 0; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const = 0; /** * Compose two arrays of pixels together. If source and target * are not the same color model, the source pixels will be * converted to the target model. We're "dst" -- "dst" pixels are always in _this_ * colorspace. * * @param srcSpace the colorspace of the source pixels that will be composited onto "us" * @param params the information needed for blitting e.g. the source and destination pixel data, * the opacity and flow, ... * @param op the composition operator to use, e.g. COPY_OVER * @param renderingIntent the rendering intent * @param conversionFlags the conversion flags. * */ virtual void bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * Serialize this color following Create's swatch color specification available * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_colour_file_format/Draft * * This function doesn't create the \ element but rather the \, * \, \ ... elements. It is assumed that colorElt is the \ * element. * * @param pixel buffer to serialized * @param colorElt root element for the serialization, it is assumed that this * element is \ * @param doc is the document containing colorElt */ virtual void colorToXML(const quint8* pixel, QDomDocument& doc, QDomElement& colorElt) const = 0; /** * Unserialize a color following Create's swatch color specification available * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_colour_file_format/Draft * * @param pixel buffer where the color will be unserialized * @param elt the element to unserialize (\, \, \) * @return the unserialize color, or an empty color object if the function failed * to unserialize the color */ virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const = 0; KoColorTransformation* createColorTransformation(const QString & id, const QHash & parameters) const; protected: /** * Use this function in the constructor of your colorspace to add the information about a channel. * @param ci a pointer to the information about a channel */ virtual void addChannel(KoChannelInfo * ci); const KoColorConversionTransformation* toLabA16Converter() const; const KoColorConversionTransformation* fromLabA16Converter() const; const KoColorConversionTransformation* toRgbA16Converter() const; const KoColorConversionTransformation* fromRgbA16Converter() const; /** * Returns the thread-local conversion cache. If it doesn't exist * yet, it is created. If it is currently too small, it is resized. */ QVector * threadLocalConversionCache(quint32 size) const; /** * This function defines the behavior of the bitBlt function * when the composition of pixels in different colorspaces is * requested, that is in case: * * srcCS == any * dstCS == this * * 1) preferCompositionInSourceColorSpace() == false, * * the source pixels are first converted to *this color space * and then composition is performed. * * 2) preferCompositionInSourceColorSpace() == true, * * the destination pixels are first converted into *srcCS color * space, then the composition is done, and the result is finally * converted into *this colorspace. * * This is used by alpha8() color space mostly, because it has * weaker representation of the color, so the composition * should be done in CS with richer functionality. */ virtual bool preferCompositionInSourceColorSpace() const; struct Private; Private * const d; }; inline QDebug operator<<(QDebug dbg, const KoColorSpace *cs) { if (cs) { dbg.nospace() << cs->name() << " (" << cs->colorModelId().id() << "," << cs->colorDepthId().id() << " )"; } else { dbg.nospace() << "0x0"; } return dbg.space(); } #endif // KOCOLORSPACE_H diff --git a/libs/pigment/KoColorSpacePreserveLightnessUtils.h b/libs/pigment/KoColorSpacePreserveLightnessUtils.h index 4739d9a361..ce4dbf1388 100644 --- a/libs/pigment/KoColorSpacePreserveLightnessUtils.h +++ b/libs/pigment/KoColorSpacePreserveLightnessUtils.h @@ -1,79 +1,82 @@ /* * Copyright (c) 2020 Peter Schatz * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KOCOLORSPACEPRESERVELIGHTNESSUTILS_H #define KOCOLORSPACEPRESERVELIGHTNESSUTILS_H #include #include "kis_global.h" template -inline static void fillGrayBrushWithColorPreserveLightnessRGB(quint8 *pixels, const QRgb *brush, quint8 *brushColor, qint32 nPixels) { +inline static void fillGrayBrushWithColorPreserveLightnessRGB(quint8 *pixels, const QRgb *brush, quint8 *brushColor, qreal strength, qint32 nPixels) { using RGBPixel = typename CSTraits::Pixel; using channels_type = typename CSTraits::channels_type; static const quint32 pixelSize = CSTraits::pixelSize; const RGBPixel *brushColorRGB = reinterpret_cast(brushColor); const float brushColorR = KoColorSpaceMaths::scaleToA(brushColorRGB->red); const float brushColorG = KoColorSpaceMaths::scaleToA(brushColorRGB->green); const float brushColorB = KoColorSpaceMaths::scaleToA(brushColorRGB->blue); + const float brushColorA = KoColorSpaceMaths::scaleToA(brushColorRGB->alpha); /** * Lightness mixing algorithm is developed by Peter Schatz * * We use a formula f(x) where f(0) = 0, f(1) = 1, and f(.5) = z, * where z is the lightness of the brush color. This can’t be linear unless * the color chosen is also .5. So we use a quadratic equation: * * f(x) = ax^2 + b^x +c * 0,0 -> 0 = a0^2 + b0 + c -> c = 0 * 1,1 -> 1 = a1^2 +b1 + c -> 1 = a + b + 0 -> a = 1 - b * .5,z -> z = a*.5^2 + b*.5 + c -> z = * = a/4 + b/2 + 0 -> z = * = 1/4 - b/4 + b/2 -> z = 1/4 + b/4 -> b = 4z - 1 * * f(x) = (1 - (4z - 1)) * x^2 + (4z - 1) * x */ const float brushColorL = getLightness(brushColorR, brushColorG, brushColorB); const float lightnessB = 4 * brushColorL - 1; const float lightnessA = 1 - lightnessB; for (; nPixels > 0; --nPixels, pixels += pixelSize, ++brush) { RGBPixel *pixelRGB = reinterpret_cast(pixels); - const float brushMaskL = qRed(*brush) / 255.0f; + float brushMaskL = qRed(*brush) / 255.0f; + brushMaskL = (brushMaskL - 0.5) * strength + 0.5; const float finalLightness = lightnessA * pow2(brushMaskL) + lightnessB * brushMaskL; + const float finalAlpha = qMin(qAlpha(*brush) / 255.0f, brushColorA); float pixelR = brushColorR; float pixelG = brushColorG; float pixelB = brushColorB; setLightness(pixelR, pixelG, pixelB, finalLightness); pixelRGB->red = KoColorSpaceMaths::scaleToA(pixelR); pixelRGB->green = KoColorSpaceMaths::scaleToA(pixelG); pixelRGB->blue = KoColorSpaceMaths::scaleToA(pixelB); - pixelRGB->alpha = KoColorSpaceMaths::scaleToA(quint8(qAlpha(*brush))); + pixelRGB->alpha = KoColorSpaceMaths::scaleToA(quint8(finalAlpha * 255)); } } #endif // KOCOLORSPACEPRESERVELIGHTNESSUTILS_H diff --git a/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp b/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp index 68ab387f20..6a2ff2406e 100644 --- a/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp +++ b/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp @@ -1,104 +1,109 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KoRgbU16ColorSpace.h" #include #include #include #include #include #include "KoChannelInfo.h" #include "KoID.h" #include "KoIntegerMaths.h" #include "KoColorConversions.h" #include KoRgbU16ColorSpace::KoRgbU16ColorSpace() : KoSimpleColorSpace(colorSpaceId(), i18n("RGB (16-bit integer/channel, unmanaged)"), RGBAColorModelID, Integer16BitsColorDepthID) { } KoRgbU16ColorSpace::~KoRgbU16ColorSpace() { } QString KoRgbU16ColorSpace::colorSpaceId() { return QString("RGBA16"); } KoColorSpace* KoRgbU16ColorSpace::clone() const { return new KoRgbU16ColorSpace(); } void KoRgbU16ColorSpace::fromQColor(const QColor& c, quint8 *dst, const KoColorProfile * /*profile*/) const { QVector channelValues; channelValues << c.blueF() << c.greenF() << c.redF() << c.alphaF(); fromNormalisedChannelsValue(dst, channelValues); } void KoRgbU16ColorSpace::toQColor(const quint8 * src, QColor *c, const KoColorProfile * /*profile*/) const { QVector channelValues(4); normalisedChannelsValue(src, channelValues); c->setRgbF(channelValues[2], channelValues[1], channelValues[0], channelValues[3]); } void KoRgbU16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma); } QVector KoRgbU16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } void KoRgbU16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v); } QVector KoRgbU16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } void KoRgbU16ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); } + +void KoRgbU16ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); +} \ No newline at end of file diff --git a/libs/pigment/colorspaces/KoRgbU16ColorSpace.h b/libs/pigment/colorspaces/KoRgbU16ColorSpace.h index 0a0b3ecbc6..537339b11c 100644 --- a/libs/pigment/colorspaces/KoRgbU16ColorSpace.h +++ b/libs/pigment/colorspaces/KoRgbU16ColorSpace.h @@ -1,77 +1,78 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KORGBU16COLORSPACE_H #define KORGBU16COLORSPACE_H #include #include "KoSimpleColorSpace.h" #include "KoSimpleColorSpaceFactory.h" #include "KoColorModelStandardIds.h" struct KoBgrU16Traits; /** * The alpha mask is a special color strategy that treats all pixels as * alpha value with a color common to the mask. The default color is white. */ class KoRgbU16ColorSpace : public KoSimpleColorSpace { public: KoRgbU16ColorSpace(); ~KoRgbU16ColorSpace() override; static QString colorSpaceId(); virtual KoColorSpace* clone() const; void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const override; void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class KoRgbU16ColorSpaceFactory : public KoSimpleColorSpaceFactory { public: KoRgbU16ColorSpaceFactory() : KoSimpleColorSpaceFactory(KoRgbU16ColorSpace::colorSpaceId(), i18n("RGB (16-bit integer/channel, unmanaged)"), true, RGBAColorModelID, Integer16BitsColorDepthID) { } KoColorSpace *createColorSpace(const KoColorProfile *) const override { return new KoRgbU16ColorSpace(); } }; #endif diff --git a/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp b/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp index 4b92725c07..b8ba5dbeb9 100644 --- a/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp +++ b/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp @@ -1,117 +1,122 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KoRgbU8ColorSpace.h" #include #include #include #include #include #include "KoChannelInfo.h" #include "KoID.h" #include "KoIntegerMaths.h" #include "compositeops/KoCompositeOps.h" #include "KoColorConversions.h" #include KoRgbU8ColorSpace::KoRgbU8ColorSpace() : KoSimpleColorSpace(colorSpaceId(), i18n("RGB (8-bit integer/channel, unmanaged)"), RGBAColorModelID, Integer8BitsColorDepthID) { addChannel(new KoChannelInfo(i18n("Blue"), 0, 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Green"), 1, 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Red"), 2, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Alpha"), 3, 3, KoChannelInfo::ALPHA, KoChannelInfo::UINT8)); // ADD, ALPHA_DARKEN, BURN, DIVIDE, DODGE, ERASE, MULTIPLY, OVER, OVERLAY, SCREEN, SUBTRACT addStandardCompositeOps(this); } KoRgbU8ColorSpace::~KoRgbU8ColorSpace() { } QString KoRgbU8ColorSpace::colorSpaceId() { return QString("RGBA"); } KoColorSpace* KoRgbU8ColorSpace::clone() const { return new KoRgbU8ColorSpace(); } void KoRgbU8ColorSpace::fromQColor(const QColor& c, quint8 *dst, const KoColorProfile * /*profile*/) const { QVector channelValues; channelValues << c.blueF() << c.greenF() << c.redF() << c.alphaF(); fromNormalisedChannelsValue(dst, channelValues); } void KoRgbU8ColorSpace::toQColor(const quint8 * src, QColor *c, const KoColorProfile * /*profile*/) const { QVector channelValues(4); normalisedChannelsValue(src, channelValues); c->setRgbF(channelValues[2], channelValues[1], channelValues[0], channelValues[3]); } void KoRgbU8ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma); } QVector KoRgbU8ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } void KoRgbU8ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v); } QVector KoRgbU8ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } void KoRgbU8ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); +} + +void KoRgbU8ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); } diff --git a/libs/pigment/colorspaces/KoRgbU8ColorSpace.h b/libs/pigment/colorspaces/KoRgbU8ColorSpace.h index 70071219b1..4a6e2a156d 100644 --- a/libs/pigment/colorspaces/KoRgbU8ColorSpace.h +++ b/libs/pigment/colorspaces/KoRgbU8ColorSpace.h @@ -1,77 +1,78 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KORGBU8COLORSPACE_H #define KORGBU8COLORSPACE_H #include #include "KoSimpleColorSpace.h" #include "KoSimpleColorSpaceFactory.h" #include "KoColorModelStandardIds.h" struct KoBgrU8Traits; /** * The alpha mask is a special color strategy that treats all pixels as * alpha value with a color common to the mask. The default color is white. */ class KoRgbU8ColorSpace : public KoSimpleColorSpace { public: KoRgbU8ColorSpace(); ~KoRgbU8ColorSpace() override; static QString colorSpaceId(); virtual KoColorSpace* clone() const; void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const override; void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class KoRgbU8ColorSpaceFactory : public KoSimpleColorSpaceFactory { public: KoRgbU8ColorSpaceFactory() : KoSimpleColorSpaceFactory("RGBA", i18n("RGB (8-bit integer/channel, unmanaged)"), true, RGBAColorModelID, Integer8BitsColorDepthID) { } KoColorSpace *createColorSpace(const KoColorProfile *) const override { return new KoRgbU8ColorSpace(); } }; #endif diff --git a/libs/pigment/resources/KoAbstractGradient.h b/libs/pigment/resources/KoAbstractGradient.h index 435ce2b64d..ab055ccc27 100644 --- a/libs/pigment/resources/KoAbstractGradient.h +++ b/libs/pigment/resources/KoAbstractGradient.h @@ -1,93 +1,103 @@ /* Copyright (c) 2007 Sven Langkamp This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KOABSTRACTGRADIENT_H #define KOABSTRACTGRADIENT_H #include #include #include "KoColorSpace.h" #include #include class KoAbstractGradient; typedef QSharedPointer KoAbstractGradientSP; class KoColor; /** * KoAbstractGradient is the base class of all gradient resources */ class KRITAPIGMENT_EXPORT KoAbstractGradient : public KoResource { public: explicit KoAbstractGradient(const QString &filename); ~KoAbstractGradient() override; virtual KoAbstractGradient* clone() const = 0; bool load() override { return false; } bool loadFromDevice(QIODevice *) override { return false; } bool save() override { return false; } bool saveToDevice(QIODevice*) const override { return false; } /** * Creates a QGradient from the gradient. * The resulting QGradient might differ from original gradient */ virtual QGradient* toQGradient() const { return new QGradient(); } /// gets the color at position 0 <= t <= 1 virtual void colorAt(KoColor&, qreal t) const; void setColorSpace(KoColorSpace* colorSpace); const KoColorSpace * colorSpace() const; void setSpread(QGradient::Spread spreadMethod); QGradient::Spread spread() const; void setType(QGradient::Type repeatType); QGradient::Type type() const; + ///tell whether there are any foreground or background color stops + virtual bool hasVariableColors() const { + return false; + } + ///Set the colors for stops that use the foreground or background color. + virtual void setVariableColors(const KoColor& foreground, const KoColor& background) { + //Do nothing... Override if gradient type supports variable colors. + Q_UNUSED(foreground); Q_UNUSED(background); + } + void updatePreview(); QImage generatePreview(int width, int height) const; KoAbstractGradient(const KoAbstractGradient &rhs); private: struct Private; Private* const d; }; Q_DECLARE_METATYPE(KoAbstractGradient*) #endif // KOABSTRACTGRADIENT_H diff --git a/libs/pigment/resources/KoCachedGradient.h b/libs/pigment/resources/KoCachedGradient.h new file mode 100644 index 0000000000..815298d438 --- /dev/null +++ b/libs/pigment/resources/KoCachedGradient.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2004 Adrian Page + * Copyright (c) 2019 Miguel Lopez + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + + //Adapted from CachedGradient in libs/image/kis_gradient_painter.cc. TODO: fix kis_gradient_painter to use this instead + +class KoCachedGradient : public KoAbstractGradient +{ + +public: + KoCachedGradient() : KoAbstractGradient("") + {} + + KoCachedGradient(const KoAbstractGradient* gradient, qint32 steps, const KoColorSpace* cs) + : KoAbstractGradient(gradient->filename()) + { + setGradient(gradient, steps, cs); + } + + ~KoCachedGradient() override {} + + KoAbstractGradient* clone() const override { + return new KoCachedGradient(m_subject, m_max + 1, m_colorSpace); + } + + /** + * Creates a QGradient from the gradient. + * The resulting QGradient might differ from original gradient + */ + QGradient* toQGradient() const override + { + return m_subject->toQGradient(); + } + + void setGradient(const KoAbstractGradient* gradient, qint32 steps, const KoColorSpace* cs) { + m_subject = gradient; + m_max = steps - 1; + m_colorSpace = cs; + + m_black = KoColor(cs); + + KoColor tmpColor(m_colorSpace); + for (qint32 i = 0; i < steps; i++) { + m_subject->colorAt(tmpColor, qreal(i) / m_max); + m_colors << tmpColor; + } + } + + void setGradient(const KoAbstractGradient* gradient, qint32 steps) { + setGradient(gradient, steps, gradient->colorSpace()); + } + + /// gets the color data at position 0 <= t <= 1 + const quint8* cachedAt(qreal t) const + { + qint32 tInt = t * m_max + 0.5; + if (m_colors.size() > tInt) { + return m_colors[tInt].data(); + } + else { + return m_black.data(); + } + } + + /// allow access to the actual color at position 0 <= t <= 1, instead of the cached color + void colorAt(KoColor& color, qreal t) const override + { + m_subject->colorAt(color, t); + } + void setColorSpace(KoColorSpace* colorSpace) { m_colorSpace = colorSpace; } + const KoColorSpace* colorSpace() const { return m_colorSpace; } + + QByteArray generateMD5() const override { return QByteArray(); } + +private: + + const KoAbstractGradient* m_subject; + const KoColorSpace* m_colorSpace; + qint32 m_max; + QVector m_colors; + KoColor m_black; +}; \ No newline at end of file diff --git a/libs/pigment/resources/KoPattern.cpp b/libs/pigment/resources/KoPattern.cpp index e87cd51591..419187fab8 100644 --- a/libs/pigment/resources/KoPattern.cpp +++ b/libs/pigment/resources/KoPattern.cpp @@ -1,399 +1,415 @@ /* This file is part of the KDE project Copyright (c) 2000 Matthias Elter Copyright (c) 2004 Boudewijn Rempt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { struct GimpPatternHeader { quint32 header_size; /* header_size = sizeof (PatternHeader) + brush name */ quint32 version; /* pattern file version # */ quint32 width; /* width of pattern */ quint32 height; /* height of pattern */ quint32 bytes; /* depth of pattern in bytes : 1, 2, 3 or 4*/ quint32 magic_number; /* GIMP brush magic number */ }; // Yes! This is _NOT_ what my pat.txt file says. It's really not 'GIMP', but 'GPAT' quint32 const GimpPatternMagic = (('G' << 24) + ('P' << 16) + ('A' << 8) + ('T' << 0)); } KoPattern::KoPattern(const QString& file) : KoResource(file) { } KoPattern::KoPattern(const QImage &image, const QString &name, const QString &folderName) : KoResource(QString()) { setPatternImage(image); setName(name); QFileInfo fileInfo(folderName + QDir::separator() + name + defaultFileExtension()); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(folderName + QDir::separator() + name + QString::number(i) + defaultFileExtension()); i++; } setFilename(fileInfo.filePath()); } KoPattern::~KoPattern() { } bool KoPattern::load() { QFile file(filename()); if (file.size() == 0) return false; bool result; if (!file.open(QIODevice::ReadOnly)) { qWarning() << "Can't open file " << filename(); return false; } result = loadFromDevice(&file); file.close(); return result; } bool KoPattern::loadPatFromDevice(QIODevice *dev) { QByteArray data = dev->readAll(); return init(data); } bool KoPattern::savePatToDevice(QIODevice* dev) const { // Header: header_size (24+name length),version,width,height,colordepth of brush,magic,name // depth: 1 = greyscale, 2 = greyscale + A, 3 = RGB, 4 = RGBA // magic = "GPAT", as a single uint32, the docs are wrong here! // name is UTF-8 (\0-terminated! The docs say nothing about this!) // _All_ data in network order, it seems! (not mentioned in gimp-2.2.8/devel-docs/pat.txt!!) // We only save RGBA at the moment // Version is 1 for now... GimpPatternHeader ph; QByteArray utf8Name = name().toUtf8(); char const* name = utf8Name.data(); int nameLength = qstrlen(name); ph.header_size = qToBigEndian((quint32)sizeof(GimpPatternHeader) + nameLength + 1); // trailing 0 ph.version = qToBigEndian((quint32)1); ph.width = qToBigEndian((quint32)width()); ph.height = qToBigEndian((quint32)height()); ph.bytes = qToBigEndian((quint32)4); ph.magic_number = qToBigEndian((quint32)GimpPatternMagic); QByteArray bytes = QByteArray::fromRawData(reinterpret_cast(&ph), sizeof(GimpPatternHeader)); int wrote = dev->write(bytes); bytes.clear(); if (wrote == -1) return false; wrote = dev->write(name, nameLength + 1); // Trailing 0 apparently! if (wrote == -1) return false; int k = 0; bytes.resize(width() * height() * 4); for (qint32 y = 0; y < height(); ++y) { for (qint32 x = 0; x < width(); ++x) { // RGBA only QRgb pixel = m_pattern.pixel(x, y); bytes[k++] = static_cast(qRed(pixel)); bytes[k++] = static_cast(qGreen(pixel)); bytes[k++] = static_cast(qBlue(pixel)); bytes[k++] = static_cast(qAlpha(pixel)); } } wrote = dev->write(bytes); if (wrote == -1) return false; KoResource::saveToDevice(dev); return true; } bool KoPattern::loadFromDevice(QIODevice *dev) { QString fileExtension; int index = filename().lastIndexOf('.'); if (index != -1) fileExtension = filename().mid(index + 1).toLower(); bool result; if (fileExtension == "pat") { result = loadPatFromDevice(dev); } else { QImage image; // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice // fails with "libpng error: IDAT: CRC error" QByteArray data = dev->readAll(); QBuffer buffer(&data); result = image.load(&buffer, fileExtension.toUpper().toLatin1()); setPatternImage(image); } return result; } bool KoPattern::save() { QFile file(filename()); file.open(QIODevice::WriteOnly | QIODevice::Truncate); bool res = saveToDevice(&file); file.close(); return res; } bool KoPattern::saveToDevice(QIODevice *dev) const { QString fileExtension; int index = filename().lastIndexOf('.'); if (index != -1) fileExtension = filename().mid(index + 1).toLower(); if (fileExtension == "pat") { return savePatToDevice(dev); } else { return m_pattern.save(dev, fileExtension.toUpper().toLatin1()); } return true; } bool KoPattern::init(QByteArray& bytes) { int dataSize = bytes.size(); const char* data = bytes.constData(); // load Gimp patterns GimpPatternHeader bh; qint32 k; char* name; if ((int)sizeof(GimpPatternHeader) > dataSize) { return false; } memcpy(&bh, data, sizeof(GimpPatternHeader)); bh.header_size = qFromBigEndian(bh.header_size); bh.version = qFromBigEndian(bh.version); bh.width = qFromBigEndian(bh.width); bh.height = qFromBigEndian(bh.height); bh.bytes = qFromBigEndian(bh.bytes); bh.magic_number = qFromBigEndian(bh.magic_number); if ((int)bh.header_size > dataSize || bh.header_size == 0) { return false; } int size = bh.header_size - sizeof(GimpPatternHeader); name = new char[size]; memcpy(name, data + sizeof(GimpPatternHeader), size); if (name[size - 1]) { delete[] name; return false; } // size -1 so we don't add the end 0 to the QString... setName(QString::fromLatin1(name, size -1)); delete[] name; if (bh.width == 0 || bh.height == 0) { return false; } QImage::Format imageFormat; if (bh.bytes == 1 || bh.bytes == 3) { imageFormat = QImage::Format_RGB32; } else { imageFormat = QImage::Format_ARGB32; } QImage pattern = QImage(bh.width, bh.height, imageFormat); if (pattern.isNull()) { return false; } k = bh.header_size; if (bh.bytes == 1) { // Grayscale qint32 val; for (quint32 y = 0; y < bh.height; ++y) { QRgb* pixels = reinterpret_cast( pattern.scanLine(y) ); for (quint32 x = 0; x < bh.width; ++x, ++k) { if (k > dataSize) { qWarning() << "failed to load grayscale pattern" << filename(); return false; } val = data[k]; pixels[x] = qRgb(val, val, val); } } // It was grayscale, so make the pattern as small as possible // by converting it to Indexed8 pattern = pattern.convertToFormat(QImage::Format_Indexed8); } else if (bh.bytes == 2) { // Grayscale + A qint32 val; qint32 alpha; for (quint32 y = 0; y < bh.height; ++y) { QRgb* pixels = reinterpret_cast( pattern.scanLine(y) ); for (quint32 x = 0; x < bh.width; ++x, ++k) { if (k + 2 > dataSize) { qWarning() << "failed to load grayscale +_ alpha pattern" << filename(); return false; } val = data[k]; alpha = data[k++]; pixels[x] = qRgba(val, val, val, alpha); } } } else if (bh.bytes == 3) { // RGB without alpha for (quint32 y = 0; y < bh.height; ++y) { QRgb* pixels = reinterpret_cast( pattern.scanLine(y) ); for (quint32 x = 0; x < bh.width; ++x) { if (k + 3 > dataSize) { qWarning() << "failed to load RGB pattern" << filename(); return false; } pixels[x] = qRgb(data[k], data[k + 1], data[k + 2]); k += 3; } } } else if (bh.bytes == 4) { // Has alpha for (quint32 y = 0; y < bh.height; ++y) { QRgb* pixels = reinterpret_cast( pattern.scanLine(y) ); for (quint32 x = 0; x < bh.width; ++x) { if (k + 4 > dataSize) { qWarning() << "failed to load RGB + Alpha pattern" << filename(); return false; } pixels[x] = qRgba(data[k], data[k + 1], data[k + 2], data[k + 3]); k += 4; } } } else { return false; } if (pattern.isNull()) { return false; } setPatternImage(pattern); setValid(true); return true; } qint32 KoPattern::width() const { return m_pattern.width(); } qint32 KoPattern::height() const { return m_pattern.height(); } void KoPattern::setPatternImage(const QImage& image) { m_pattern = image; + checkForAlpha(image); setImage(image); setValid(true); } KoPattern& KoPattern::operator=(const KoPattern & pattern) { setFilename(pattern.filename()); setPatternImage(pattern.pattern()); setValid(true); return *this; } QString KoPattern::defaultFileExtension() const { return QString(".pat"); } KoPattern* KoPattern::clone() const { KoPattern* pat = new KoPattern(filename()); pat->setPatternImage(pattern()); pat->setName(name()); return pat; } QImage KoPattern::pattern() const { return m_pattern; } +void KoPattern::checkForAlpha(const QImage& image) { + m_hasAlpha = false; + for (int y = 0; y < image.height(); y++) { + for (int x = 0; x < image.width(); x++) { + if (qAlpha(image.pixel(x, y)) != 255) { + m_hasAlpha = true; + break; + } + } + } +} + +bool KoPattern::hasAlpha() { + return m_hasAlpha; +} \ No newline at end of file diff --git a/libs/pigment/resources/KoPattern.h b/libs/pigment/resources/KoPattern.h index cd89cbaa17..742dcdbba5 100644 --- a/libs/pigment/resources/KoPattern.h +++ b/libs/pigment/resources/KoPattern.h @@ -1,80 +1,84 @@ /* Copyright (c) 2000 Matthias Elter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KOPATTERN_H #define KOPATTERN_H #include #include #include /// Write API docs here class KRITAPIGMENT_EXPORT KoPattern : public KoResource { public: /** * Creates a new KoPattern object using @p filename. No file is opened * in the constructor, you have to call load. * * @param filename the file name to save and load from. */ explicit KoPattern(const QString &filename); KoPattern(const QImage &image, const QString &name, const QString &folderName); ~KoPattern() override; public: bool load() override; bool loadFromDevice(QIODevice *dev) override; bool save() override; bool saveToDevice(QIODevice* dev) const override; bool loadPatFromDevice(QIODevice *dev); bool savePatToDevice(QIODevice* dev) const; qint32 width() const; qint32 height() const; QString defaultFileExtension() const override; KoPattern& operator=(const KoPattern& pattern); KoPattern* clone() const; /** * @brief pattern the actual pattern image * @return a valid QImage. There are no guarantees to the image format. */ QImage pattern() const; + bool hasAlpha(); + private: bool init(QByteArray& data); void setPatternImage(const QImage& image); + void checkForAlpha(const QImage& image); private: QImage m_pattern; + bool m_hasAlpha = false; mutable QByteArray m_md5; }; Q_DECLARE_METATYPE(KoPattern*) #endif // KOPATTERN_H diff --git a/libs/pigment/resources/KoSegmentGradient.cpp b/libs/pigment/resources/KoSegmentGradient.cpp index a72da66bc8..53a8ec0e41 100644 --- a/libs/pigment/resources/KoSegmentGradient.cpp +++ b/libs/pigment/resources/KoSegmentGradient.cpp @@ -1,982 +1,1104 @@ /* Copyright (c) 2000 Matthias Elter 2001 John Califf 2004 Boudewijn Rempt 2004 Adrian Page 2004, 2007 Sven Langkamp This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include "KoColorSpaceRegistry.h" #include "KoColorSpace.h" #include "KoMixColorsOp.h" #include #include #include KoGradientSegment::RGBColorInterpolationStrategy *KoGradientSegment::RGBColorInterpolationStrategy::m_instance = 0; KoGradientSegment::HSVCWColorInterpolationStrategy *KoGradientSegment::HSVCWColorInterpolationStrategy::m_instance = 0; KoGradientSegment::HSVCCWColorInterpolationStrategy *KoGradientSegment::HSVCCWColorInterpolationStrategy::m_instance = 0; KoGradientSegment::LinearInterpolationStrategy *KoGradientSegment::LinearInterpolationStrategy::m_instance = 0; KoGradientSegment::CurvedInterpolationStrategy *KoGradientSegment::CurvedInterpolationStrategy::m_instance = 0; KoGradientSegment::SineInterpolationStrategy *KoGradientSegment::SineInterpolationStrategy::m_instance = 0; KoGradientSegment::SphereIncreasingInterpolationStrategy *KoGradientSegment::SphereIncreasingInterpolationStrategy::m_instance = 0; KoGradientSegment::SphereDecreasingInterpolationStrategy *KoGradientSegment::SphereDecreasingInterpolationStrategy::m_instance = 0; KoSegmentGradient::KoSegmentGradient(const QString& file) : KoAbstractGradient(file) { } KoSegmentGradient::~KoSegmentGradient() { for (int i = 0; i < m_segments.count(); i++) { delete m_segments[i]; m_segments[i] = 0; } } KoSegmentGradient::KoSegmentGradient(const KoSegmentGradient &rhs) : KoAbstractGradient(rhs) { Q_FOREACH (KoGradientSegment *segment, rhs.m_segments) { pushSegment(new KoGradientSegment(*segment)); } } KoAbstractGradient* KoSegmentGradient::clone() const { return new KoSegmentGradient(*this); } bool KoSegmentGradient::load() { QFile file(filename()); if (!file.open(QIODevice::ReadOnly)) { warnPigment << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&file); file.close(); return res; } bool KoSegmentGradient::loadFromDevice(QIODevice *dev) { QByteArray data = dev->readAll(); QTextStream fileContent(data, QIODevice::ReadOnly); fileContent.setAutoDetectUnicode(true); QString header = fileContent.readLine(); if (header != "GIMP Gradient") { return false; } QString nameDefinition = fileContent.readLine(); QString numSegmentsText; if (nameDefinition.startsWith("Name: ")) { QString nameText = nameDefinition.right(nameDefinition.length() - 6); setName(nameText); numSegmentsText = fileContent.readLine(); } else { // Older format without name. numSegmentsText = nameDefinition; } dbgPigment << "Loading gradient: " << name(); int numSegments; bool ok; numSegments = numSegmentsText.toInt(&ok); if (!ok || numSegments < 1) { return false; } dbgPigment << "Number of segments = " << numSegments; const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); for (int i = 0; i < numSegments; i++) { QString segmentText = fileContent.readLine(); QTextStream segmentFields(&segmentText); QStringList values = segmentText.split(' '); qreal leftOffset = values[0].toDouble(); qreal middleOffset = values[1].toDouble(); qreal rightOffset = values[2].toDouble(); qreal leftRed = values[3].toDouble(); qreal leftGreen = values[4].toDouble(); qreal leftBlue = values[5].toDouble(); qreal leftAlpha = values[6].toDouble(); qreal rightRed = values[7].toDouble(); qreal rightGreen = values[8].toDouble(); qreal rightBlue = values[9].toDouble(); qreal rightAlpha = values[10].toDouble(); int interpolationType = values[11].toInt(); int colorInterpolationType = values[12].toInt(); + KoGradientSegmentEndpointType startType, endType; + if (values.count() >= 15) { //file supports FG/BG colors + startType = static_cast(values[13].toInt()); + endType = static_cast(values[14].toInt()); + } + else { + startType = endType = COLOR_ENDPOINT; + } quint8 data[4]; data[2] = static_cast(leftRed * 255 + 0.5); data[1] = static_cast(leftGreen * 255 + 0.5); data[0] = static_cast(leftBlue * 255 + 0.5); data[3] = static_cast(leftAlpha * OPACITY_OPAQUE_U8 + 0.5); KoColor leftColor(data, rgbColorSpace); data[2] = static_cast(rightRed * 255 + 0.5); data[1] = static_cast(rightGreen * 255 + 0.5); data[0] = static_cast(rightBlue * 255 + 0.5); data[3] = static_cast(rightAlpha * OPACITY_OPAQUE_U8 + 0.5); KoColor rightColor(data, rgbColorSpace); + KoGradientSegmentEndpoint left(leftOffset, leftColor, startType); + KoGradientSegmentEndpoint right(rightOffset, rightColor, endType); - KoGradientSegment *segment = new KoGradientSegment(interpolationType, colorInterpolationType, leftOffset, middleOffset, rightOffset, leftColor, rightColor); + KoGradientSegment *segment = new KoGradientSegment(interpolationType, colorInterpolationType, left, right, middleOffset); Q_CHECK_PTR(segment); if (!segment -> isValid()) { delete segment; return false; } m_segments.push_back(segment); } if (!m_segments.isEmpty()) { updatePreview(); setValid(true); return true; } else { return false; } } bool KoSegmentGradient::save() { QFile file(filename()); if (!file.open(QIODevice::WriteOnly)) { return false; } saveToDevice(&file); file.close(); return true; } bool KoSegmentGradient::saveToDevice(QIODevice *dev) const { QTextStream fileContent(dev); fileContent << "GIMP Gradient\n"; fileContent << "Name: " << name() << "\n"; fileContent << m_segments.count() << "\n"; Q_FOREACH (KoGradientSegment* segment, m_segments) { fileContent << QString::number(segment->startOffset(), 'f') << " " << QString::number(segment->middleOffset(), 'f') << " " << QString::number(segment->endOffset(), 'f') << " "; QColor startColor = segment->startColor().toQColor(); QColor endColor = segment->endColor().toQColor(); fileContent << QString::number(startColor.redF(), 'f') << " " << QString::number(startColor.greenF(), 'f') << " " << QString::number(startColor.blueF(), 'f') << " " << QString::number(startColor.alphaF(), 'f') << " "; fileContent << QString::number(endColor.redF(), 'f') << " " << QString::number(endColor.greenF(), 'f') << " " << QString::number(endColor.blueF(), 'f') << " " << QString::number(endColor.alphaF(), 'f') << " "; - fileContent << (int)segment->interpolation() << " " << (int)segment->colorInterpolation() << "\n"; + fileContent << (int)segment->interpolation() << " " << (int)segment->colorInterpolation() << " "; + + fileContent << (int)segment->startType() << " " << (int)segment->endType() << "\n"; + } KoResource::saveToDevice(dev); return true; } KoGradientSegment *KoSegmentGradient::segmentAt(qreal t) const { if (t < 0.0) return 0; if (t > 1.0) return 0; if (m_segments.isEmpty()) return 0; for (QList::const_iterator it = m_segments.begin(); it != m_segments.end(); ++it) { if (t > (*it)->startOffset() - DBL_EPSILON && t < (*it)->endOffset() + DBL_EPSILON) { return *it; } } return 0; } void KoSegmentGradient::colorAt(KoColor& dst, qreal t) const { const KoGradientSegment *segment = segmentAt(t); if (segment) { segment->colorAt(dst, t); } } QGradient* KoSegmentGradient::toQGradient() const { QGradient* gradient = new QLinearGradient(); QColor color; Q_FOREACH (KoGradientSegment* segment, m_segments) { segment->startColor().toQColor(&color); gradient->setColorAt(segment->startOffset() , color); segment->endColor().toQColor(&color); gradient->setColorAt(segment->endOffset() , color); } return gradient; } QString KoSegmentGradient::defaultFileExtension() const { return QString(".ggr"); } void KoSegmentGradient::toXML(QDomDocument &doc, QDomElement &gradientElt) const { gradientElt.setAttribute("type", "segment"); Q_FOREACH(KoGradientSegment *segment, this->segments()) { QDomElement segmentElt = doc.createElement("segment"); QDomElement start = doc.createElement("start"); QDomElement end = doc.createElement("end"); segmentElt.setAttribute("start-offset", KisDomUtils::toString(segment->startOffset())); const KoColor startColor = segment->startColor(); segmentElt.setAttribute("start-bitdepth", startColor.colorSpace()->colorDepthId().id()); segmentElt.setAttribute("start-alpha", KisDomUtils::toString(startColor.opacityF())); + segmentElt.setAttribute("start-type", KisDomUtils::toString(segment->startType())); startColor.toXML(doc, start); segmentElt.setAttribute("middle-offset", KisDomUtils::toString(segment->middleOffset())); segmentElt.setAttribute("end-offset", KisDomUtils::toString(segment->endOffset())); const KoColor endColor = segment->endColor(); segmentElt.setAttribute("end-bitdepth", endColor.colorSpace()->colorDepthId().id()); segmentElt.setAttribute("end-alpha", KisDomUtils::toString(endColor.opacityF())); + segmentElt.setAttribute("end-type", KisDomUtils::toString(segment->endType())); endColor.toXML(doc, end); segmentElt.setAttribute("interpolation", KisDomUtils::toString(segment->interpolation())); segmentElt.setAttribute("color-interpolation", KisDomUtils::toString(segment->colorInterpolation())); segmentElt.appendChild(start); segmentElt.appendChild(end); gradientElt.appendChild(segmentElt); } } KoSegmentGradient KoSegmentGradient::fromXML(const QDomElement &elt) { KoSegmentGradient gradient; QDomElement segmentElt = elt.firstChildElement("segment"); while (!segmentElt.isNull()) { int interpolation = KisDomUtils::toInt(segmentElt.attribute("interpolation", "0.0")); int colorInterpolation = KisDomUtils::toInt(segmentElt.attribute("color-interpolation", "0.0")); double startOffset = KisDomUtils::toDouble(segmentElt.attribute("start-offset", "0.0")); qreal middleOffset = KisDomUtils::toDouble(segmentElt.attribute("middle-offset", "0.0")); qreal endOffset = KisDomUtils::toDouble(segmentElt.attribute("end-offset", "0.0")); QDomElement start = segmentElt.firstChildElement("start"); QString startBitdepth = segmentElt.attribute("start-bitdepth", Integer8BitsColorDepthID.id()); QColor left = KoColor::fromXML(start.firstChildElement(), startBitdepth).toQColor(); left.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("start-alpha", "1.0"))); QString endBitdepth = segmentElt.attribute("end-bitdepth", Integer8BitsColorDepthID.id()); QDomElement end = segmentElt.firstChildElement("end"); QColor right = KoColor::fromXML(end.firstChildElement(), endBitdepth).toQColor(); right.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("end-alpha", "1.0"))); - gradient.createSegment(interpolation, colorInterpolation, startOffset, endOffset, middleOffset, left, right); + KoGradientSegmentEndpointType leftType = static_cast(KisDomUtils::toInt(segmentElt.attribute("start-type", "0"))); + KoGradientSegmentEndpointType rightType = static_cast(KisDomUtils::toInt(segmentElt.attribute("end-type", "0"))); + gradient.createSegment(interpolation, colorInterpolation, startOffset, endOffset, middleOffset, left, right, leftType, rightType); segmentElt = segmentElt.nextSiblingElement("segment"); } return gradient; } -KoGradientSegment::KoGradientSegment(int interpolationType, int colorInterpolationType, qreal startOffset, qreal middleOffset, qreal endOffset, const KoColor& startColor, const KoColor& endColor) +KoGradientSegment::KoGradientSegment(int interpolationType, int colorInterpolationType, KoGradientSegmentEndpoint start, KoGradientSegmentEndpoint end, qreal middleOffset) + : m_start(start), m_end(end) { m_interpolator = 0; switch (interpolationType) { case INTERP_LINEAR: m_interpolator = LinearInterpolationStrategy::instance(); break; case INTERP_CURVED: m_interpolator = CurvedInterpolationStrategy::instance(); break; case INTERP_SINE: m_interpolator = SineInterpolationStrategy::instance(); break; case INTERP_SPHERE_INCREASING: m_interpolator = SphereIncreasingInterpolationStrategy::instance(); break; case INTERP_SPHERE_DECREASING: m_interpolator = SphereDecreasingInterpolationStrategy::instance(); break; } m_colorInterpolator = 0; switch (colorInterpolationType) { case COLOR_INTERP_RGB: m_colorInterpolator = RGBColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CCW: m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CW: m_colorInterpolator = HSVCWColorInterpolationStrategy::instance(); break; } - if (startOffset < DBL_EPSILON) { - m_startOffset = 0; - } else if (startOffset > 1 - DBL_EPSILON) { - m_startOffset = 1; - } else { - m_startOffset = startOffset; - } - if (middleOffset < m_startOffset + DBL_EPSILON) { - m_middleOffset = m_startOffset; + + if (m_start.offset < DBL_EPSILON) { + m_start.offset = 0; + } else if (m_start.offset > 1 - DBL_EPSILON) { + m_start.offset = 1; + } + + if (middleOffset < m_start.offset + DBL_EPSILON) { + m_middleOffset = m_start.offset; } else if (middleOffset > 1 - DBL_EPSILON) { m_middleOffset = 1; } else { m_middleOffset = middleOffset; } - if (endOffset < m_middleOffset + DBL_EPSILON) { - m_endOffset = m_middleOffset; - } else if (endOffset > 1 - DBL_EPSILON) { - m_endOffset = 1; - } else { - m_endOffset = endOffset; + if (m_end.offset < m_middleOffset + DBL_EPSILON) { + m_end.offset = m_middleOffset; + } else if (m_end.offset > 1 - DBL_EPSILON) { + m_end.offset = 1; } - m_length = m_endOffset - m_startOffset; + m_length = m_end.offset - m_start.offset; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { - m_middleT = (m_middleOffset - m_startOffset) / m_length; + m_middleT = (m_middleOffset - m_start.offset) / m_length; } - m_startColor = startColor; - m_endColor = endColor; + m_hasVariableColors = m_start.type != COLOR_ENDPOINT || m_end.type != COLOR_ENDPOINT; + } const KoColor& KoGradientSegment::startColor() const { - return m_startColor; + return m_start.color; } const KoColor& KoGradientSegment::endColor() const { - return m_endColor; + return m_end.color; } qreal KoGradientSegment::startOffset() const { - return m_startOffset; + return m_start.offset; } qreal KoGradientSegment::middleOffset() const { return m_middleOffset; } qreal KoGradientSegment::endOffset() const { - return m_endOffset; + return m_end.offset; +} + +KoGradientSegmentEndpointType KoGradientSegment::startType() const +{ + return m_start.type; +} + +KoGradientSegmentEndpointType KoGradientSegment::endType() const +{ + return m_end.type; +} + +void KoGradientSegment::setStartType(KoGradientSegmentEndpointType type) { + m_start.type = type; + if (type != COLOR_ENDPOINT) { + m_hasVariableColors = true; + } + else if (m_end.type == COLOR_ENDPOINT) { + m_hasVariableColors = false; + } +} + +void KoGradientSegment::setEndType(KoGradientSegmentEndpointType type) { + m_end.type = type; + if (type != COLOR_ENDPOINT) { + m_hasVariableColors = true; + } + else if (m_start.type == COLOR_ENDPOINT) { + m_hasVariableColors = false; + } } void KoGradientSegment::setStartOffset(qreal t) { - m_startOffset = t; - m_length = m_endOffset - m_startOffset; + m_start.offset = t; + m_length = m_end.offset - m_start.offset; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { - m_middleT = (m_middleOffset - m_startOffset) / m_length; + m_middleT = (m_middleOffset - m_start.offset) / m_length; } } void KoGradientSegment::setMiddleOffset(qreal t) { m_middleOffset = t; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { - m_middleT = (m_middleOffset - m_startOffset) / m_length; + m_middleT = (m_middleOffset - m_start.offset) / m_length; } } void KoGradientSegment::setEndOffset(qreal t) { - m_endOffset = t; - m_length = m_endOffset - m_startOffset; + m_end.offset = t; + m_length = m_end.offset - m_start.offset; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { - m_middleT = (m_middleOffset - m_startOffset) / m_length; + m_middleT = (m_middleOffset - m_start.offset) / m_length; } } +void KoGradientSegment::setVariableColors(const KoColor& foreground, const KoColor& background) { + switch (m_start.type) { + case COLOR_ENDPOINT: + break; + case FOREGROUND_ENDPOINT: + m_start.color = foreground; + break; + case FOREGROUND_TRANSPARENT_ENDPOINT: //TODO: add Transparent options to gradient editor... + m_start.color = foreground; + m_start.color.setOpacity(quint8(0)); + break; + case BACKGROUND_ENDPOINT: + m_start.color = background; + break; + case BACKGROUND_TRANSPARENT_ENDPOINT: + m_start.color = background; + m_start.color.setOpacity(quint8(0)); + break; + } + + switch (m_end.type) { + case COLOR_ENDPOINT: + break; + case FOREGROUND_ENDPOINT: + m_end.color = foreground; + break; + case FOREGROUND_TRANSPARENT_ENDPOINT: + m_end.color = foreground; + m_end.color.setOpacity(quint8(0)); + break; + case BACKGROUND_ENDPOINT: + m_end.color = background; + break; + case BACKGROUND_TRANSPARENT_ENDPOINT: + m_end.color = background; + m_end.color.setOpacity(quint8(0)); + break; + } +} + +bool KoGradientSegment::hasVariableColors() { + return m_hasVariableColors; +} + int KoGradientSegment::interpolation() const { return m_interpolator->type(); } void KoGradientSegment::setInterpolation(int interpolationType) { switch (interpolationType) { case INTERP_LINEAR: m_interpolator = LinearInterpolationStrategy::instance(); break; case INTERP_CURVED: m_interpolator = CurvedInterpolationStrategy::instance(); break; case INTERP_SINE: m_interpolator = SineInterpolationStrategy::instance(); break; case INTERP_SPHERE_INCREASING: m_interpolator = SphereIncreasingInterpolationStrategy::instance(); break; case INTERP_SPHERE_DECREASING: m_interpolator = SphereDecreasingInterpolationStrategy::instance(); break; } } int KoGradientSegment::colorInterpolation() const { return m_colorInterpolator->type(); } void KoGradientSegment::setColorInterpolation(int colorInterpolationType) { switch (colorInterpolationType) { case COLOR_INTERP_RGB: m_colorInterpolator = RGBColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CCW: m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CW: m_colorInterpolator = HSVCWColorInterpolationStrategy::instance(); break; } } void KoGradientSegment::colorAt(KoColor& dst, qreal t) const { - Q_ASSERT(t > m_startOffset - DBL_EPSILON && t < m_endOffset + DBL_EPSILON); + Q_ASSERT(t > m_start.offset - DBL_EPSILON && t < m_end.offset + DBL_EPSILON); qreal segmentT; if (m_length < DBL_EPSILON) { segmentT = 0.5; } else { - segmentT = (t - m_startOffset) / m_length; + segmentT = (t - m_start.offset) / m_length; } qreal colorT = m_interpolator->valueAt(segmentT, m_middleT); - m_colorInterpolator->colorAt(dst, colorT, m_startColor, m_endColor); + m_colorInterpolator->colorAt(dst, colorT, m_start.color, m_end.color); + +} +void KoGradientSegment::mirrorSegment() +{ + KoColor tmpColor = startColor(); + setStartColor(endColor()); + setEndColor(tmpColor); + KoGradientSegmentEndpointType tmpType = startType(); + setStartType(endType()); + setEndType(tmpType); + + setMiddleOffset(endOffset() - (middleOffset() - startOffset())); + + if (interpolation() == INTERP_SPHERE_INCREASING) { + setInterpolation(INTERP_SPHERE_DECREASING); + } + else if (interpolation() == INTERP_SPHERE_DECREASING) { + setInterpolation(INTERP_SPHERE_INCREASING); + } + if (colorInterpolation() == COLOR_INTERP_HSV_CW) { + setColorInterpolation(COLOR_INTERP_HSV_CCW); + } + else if (colorInterpolation() == COLOR_INTERP_HSV_CCW) { + setColorInterpolation(COLOR_INTERP_HSV_CW); + } } bool KoGradientSegment::isValid() const { if (m_interpolator == 0 || m_colorInterpolator == 0) return false; return true; } KoGradientSegment::RGBColorInterpolationStrategy::RGBColorInterpolationStrategy() : m_colorSpace(KoColorSpaceRegistry::instance()->rgb8()) { } KoGradientSegment::RGBColorInterpolationStrategy *KoGradientSegment::RGBColorInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new RGBColorInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } void KoGradientSegment::RGBColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& _start, const KoColor& _end) const { KoColor buffer(m_colorSpace); KoColor start(m_colorSpace); KoColor end(m_colorSpace); KoColor startDummy, endDummy; //hack to get a color space with the bitdepth of the gradients(8bit), but with the colour profile of the image// const KoColorSpace* mixSpace = KoColorSpaceRegistry::instance()->rgb8(dst.colorSpace()->profile()); //convert to the right colorspace for the start and end if we have our mixSpace. if (mixSpace){ startDummy = KoColor(_start, mixSpace); endDummy = KoColor(_end, mixSpace); } else { startDummy = _start; endDummy = _end; } start.fromKoColor(_start); end.fromKoColor(_end); const quint8 *colors[2]; colors[0] = startDummy.data(); colors[1] = endDummy.data(); qint16 colorWeights[2]; colorWeights[0] = static_cast((1.0 - t) * 255 + 0.5); colorWeights[1] = 255 - colorWeights[0]; //check if our mixspace exists, it doesn't at startup. if (mixSpace){ if (*buffer.colorSpace() != *mixSpace) { buffer = KoColor(mixSpace); } mixSpace->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data()); } else { buffer = KoColor(m_colorSpace); m_colorSpace->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data()); } dst.fromKoColor(buffer); } KoGradientSegment::HSVCWColorInterpolationStrategy::HSVCWColorInterpolationStrategy() : m_colorSpace(KoColorSpaceRegistry::instance()->rgb8()) { } KoGradientSegment::HSVCWColorInterpolationStrategy *KoGradientSegment::HSVCWColorInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new HSVCWColorInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } void KoGradientSegment::HSVCWColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const { QColor sc; QColor ec; start.toQColor(&sc); end.toQColor(&ec); int s = static_cast(sc.saturation() + t * (ec.saturation() - sc.saturation()) + 0.5); int v = static_cast(sc.value() + t * (ec.value() - sc.value()) + 0.5); int h; if (ec.hue() < sc.hue()) { h = static_cast(ec.hue() + (1 - t) * (sc.hue() - ec.hue()) + 0.5); } else { h = static_cast(ec.hue() + (1 - t) * (360 - ec.hue() + sc.hue()) + 0.5); if (h > 359) { h -= 360; } } // XXX: added an explicit cast. Is this correct? quint8 opacity = static_cast(sc.alpha() + t * (ec.alpha() - sc.alpha())); QColor result; result.setHsv(h, s, v); result.setAlpha(opacity); dst.fromQColor(result); } KoGradientSegment::HSVCCWColorInterpolationStrategy::HSVCCWColorInterpolationStrategy() : m_colorSpace(KoColorSpaceRegistry::instance()->rgb8()) { } KoGradientSegment::HSVCCWColorInterpolationStrategy *KoGradientSegment::HSVCCWColorInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new HSVCCWColorInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } void KoGradientSegment::HSVCCWColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const { QColor sc; QColor se; start.toQColor(&sc); end.toQColor(&se); int s = static_cast(sc.saturation() + t * (se.saturation() - sc.saturation()) + 0.5); int v = static_cast(sc.value() + t * (se.value() - sc.value()) + 0.5); int h; if (sc.hue() < se.hue()) { h = static_cast(sc.hue() + t * (se.hue() - sc.hue()) + 0.5); } else { h = static_cast(sc.hue() + t * (360 - sc.hue() + se.hue()) + 0.5); if (h > 359) { h -= 360; } } // XXX: Added an explicit static cast quint8 opacity = static_cast(sc.alpha() + t * (se.alpha() - sc.alpha())); QColor result; result.setHsv(h, s, v); result.setAlpha(opacity); dst.fromQColor(result); } KoGradientSegment::LinearInterpolationStrategy *KoGradientSegment::LinearInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new LinearInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::LinearInterpolationStrategy::calcValueAt(qreal t, qreal middle) { Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON); qreal value = 0; if (t <= middle) { if (middle < DBL_EPSILON) { value = 0; } else { value = (t / middle) * 0.5; } } else { if (middle > 1 - DBL_EPSILON) { value = 1; } else { value = ((t - middle) / (1 - middle)) * 0.5 + 0.5; } } return value; } qreal KoGradientSegment::LinearInterpolationStrategy::valueAt(qreal t, qreal middle) const { return calcValueAt(t, middle); } KoGradientSegment::CurvedInterpolationStrategy::CurvedInterpolationStrategy() { m_logHalf = log(0.5); } KoGradientSegment::CurvedInterpolationStrategy *KoGradientSegment::CurvedInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new CurvedInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::CurvedInterpolationStrategy::valueAt(qreal t, qreal middle) const { Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON); qreal value = 0; if (middle < DBL_EPSILON) { middle = DBL_EPSILON; } value = pow(t, m_logHalf / log(middle)); return value; } KoGradientSegment::SineInterpolationStrategy *KoGradientSegment::SineInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new SineInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::SineInterpolationStrategy::valueAt(qreal t, qreal middle) const { qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle); qreal value = (sin(-M_PI_2 + M_PI * lt) + 1.0) / 2.0; return value; } KoGradientSegment::SphereIncreasingInterpolationStrategy *KoGradientSegment::SphereIncreasingInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new SphereIncreasingInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::SphereIncreasingInterpolationStrategy::valueAt(qreal t, qreal middle) const { qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle) - 1; qreal value = sqrt(1 - lt * lt); return value; } KoGradientSegment::SphereDecreasingInterpolationStrategy *KoGradientSegment::SphereDecreasingInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new SphereDecreasingInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::SphereDecreasingInterpolationStrategy::valueAt(qreal t, qreal middle) const { qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle); qreal value = 1 - sqrt(1 - lt * lt); return value; } -void KoSegmentGradient::createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, const QColor & left, const QColor & right) +void KoSegmentGradient::createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, const QColor & leftColor, const QColor & rightColor, + KoGradientSegmentEndpointType leftType, KoGradientSegmentEndpointType rightType) { - pushSegment(new KoGradientSegment(interpolation, colorInterpolation, startOffset, middleOffset, endOffset, KoColor(left, colorSpace()), KoColor(right, colorSpace()))); + KoGradientSegmentEndpoint left(startOffset, KoColor(leftColor, colorSpace()), leftType); + KoGradientSegmentEndpoint right(endOffset, KoColor(rightColor, colorSpace()), rightType); + pushSegment(new KoGradientSegment(interpolation, colorInterpolation, left, right, middleOffset)); } const QList KoSegmentGradient::getHandlePositions() const { QList handlePositions; handlePositions.push_back(m_segments[0]->startOffset()); for (int i = 0; i < m_segments.count(); i++) { handlePositions.push_back(m_segments[i]->endOffset()); } return handlePositions; } const QList KoSegmentGradient::getMiddleHandlePositions() const { QList middleHandlePositions; for (int i = 0; i < m_segments.count(); i++) { middleHandlePositions.push_back(m_segments[i]->middleOffset()); } return middleHandlePositions; } void KoSegmentGradient::moveSegmentStartOffset(KoGradientSegment* segment, double t) { QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { if (it == m_segments.begin()) { segment->setStartOffset(0.0); return; } KoGradientSegment* previousSegment = (*(it - 1)); if (t > segment->startOffset()) { if (t > segment->middleOffset()) t = segment->middleOffset(); } else { if (t < previousSegment->middleOffset()) t = previousSegment->middleOffset(); } previousSegment->setEndOffset(t); segment->setStartOffset(t); } } void KoSegmentGradient::moveSegmentEndOffset(KoGradientSegment* segment, double t) { QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { if (it + 1 == m_segments.end()) { segment->setEndOffset(1.0); return; } KoGradientSegment* followingSegment = (*(it + 1)); if (t < segment->endOffset()) { if (t < segment->middleOffset()) t = segment->middleOffset(); } else { if (t > followingSegment->middleOffset()) t = followingSegment->middleOffset(); } followingSegment->setStartOffset(t); segment->setEndOffset(t); } } void KoSegmentGradient::moveSegmentMiddleOffset(KoGradientSegment* segment, double t) { if (segment) { if (t > segment->endOffset()) segment->setMiddleOffset(segment->endOffset()); else if (t < segment->startOffset()) segment->setMiddleOffset(segment->startOffset()); else segment->setMiddleOffset(t); } } void KoSegmentGradient::splitSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { KoColor midleoffsetColor(segment->endColor().colorSpace()); segment->colorAt(midleoffsetColor, segment->middleOffset()); + KoGradientSegmentEndpoint left(segment->startOffset(), segment->startColor(), segment->startType()); + KoGradientSegmentEndpoint right(segment->middleOffset(), midleoffsetColor, COLOR_ENDPOINT); KoGradientSegment* newSegment = new KoGradientSegment( segment->interpolation(), segment->colorInterpolation(), - segment ->startOffset(), - (segment->middleOffset() - segment->startOffset()) / 2 + segment->startOffset(), - segment->middleOffset(), - segment->startColor(), - midleoffsetColor); + left, right, + (segment->middleOffset() - segment->startOffset()) / 2 + segment->startOffset()); m_segments.insert(it, newSegment); segment->setStartColor(midleoffsetColor); segment->setStartOffset(segment->middleOffset()); segment->setMiddleOffset((segment->endOffset() - segment->startOffset()) / 2 + segment->startOffset()); } } void KoSegmentGradient::duplicateSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { double middlePostionPercentage = (segment->middleOffset() - segment->startOffset()) / segment->length(); double center = segment->startOffset() + segment->length() / 2; + KoGradientSegmentEndpoint left(segment->startOffset(), segment->startColor(), segment->startType()); + KoGradientSegmentEndpoint right(center, segment->endColor(), segment->endType()); KoGradientSegment* newSegment = new KoGradientSegment( segment->interpolation(), segment->colorInterpolation(), - segment ->startOffset(), - segment->length() / 2 * middlePostionPercentage + segment->startOffset(), - center, segment->startColor(), - segment->endColor()); + left, right, + segment->length() / 2 * middlePostionPercentage + segment->startOffset()); m_segments.insert(it, newSegment); segment->setStartOffset(center); segment->setMiddleOffset(segment->length() * middlePostionPercentage + segment->startOffset()); } } void KoSegmentGradient::mirrorSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); - KoColor tmpColor = segment->startColor(); - segment->setStartColor(segment->endColor()); - segment->setEndColor(tmpColor); - segment->setMiddleOffset(segment->endOffset() - (segment->middleOffset() - segment->startOffset())); - - if (segment->interpolation() == INTERP_SPHERE_INCREASING) - segment->setInterpolation(INTERP_SPHERE_DECREASING); - else if (segment->interpolation() == INTERP_SPHERE_DECREASING) - segment->setInterpolation(INTERP_SPHERE_INCREASING); - if (segment->colorInterpolation() == COLOR_INTERP_HSV_CW) - segment->setColorInterpolation(COLOR_INTERP_HSV_CCW); - else if (segment->colorInterpolation() == COLOR_INTERP_HSV_CCW) - segment->setColorInterpolation(COLOR_INTERP_HSV_CW); + segment->mirrorSegment(); } KoGradientSegment* KoSegmentGradient::removeSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); if (m_segments.count() < 2) return 0; QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { double middlePostionPercentage; KoGradientSegment* nextSegment; if (it == m_segments.begin()) { nextSegment = (*(it + 1)); middlePostionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length(); nextSegment->setStartOffset(segment->startOffset()); nextSegment->setMiddleOffset(middlePostionPercentage * nextSegment->length() + nextSegment->startOffset()); } else { nextSegment = (*(it - 1)); middlePostionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length(); nextSegment->setEndOffset(segment->endOffset()); nextSegment->setMiddleOffset(middlePostionPercentage * nextSegment->length() + nextSegment->startOffset()); } delete segment; m_segments.erase(it); return nextSegment; } return 0; } bool KoSegmentGradient::removeSegmentPossible() const { if (m_segments.count() < 2) return false; return true; } const QList& KoSegmentGradient::segments() const { return m_segments; } + +bool KoSegmentGradient::hasVariableColors() const +{ + for (int i = 0; i < m_segments.count(); i++) { + if (m_segments[i]->hasVariableColors()) { + return true; + } + } + return false; +} + +void KoSegmentGradient::setVariableColors(const KoColor& foreground, const KoColor& background) +{ + for (int i = 0; i < m_segments.count(); i++) { + m_segments[i]->setVariableColors(foreground, background); + } +} diff --git a/libs/pigment/resources/KoSegmentGradient.h b/libs/pigment/resources/KoSegmentGradient.h index 3bd9bb5019..21aca152fd 100644 --- a/libs/pigment/resources/KoSegmentGradient.h +++ b/libs/pigment/resources/KoSegmentGradient.h @@ -1,431 +1,482 @@ /* Copyright (c) 2000 Matthias Elter 2004 Boudewijn Rempt 2004 Adrian Page 2004, 2007 Sven Langkamp 2017 Wolthera van Hövell tot Westerflier This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KOSEGMENTGRADIENT_H #define KOSEGMENTGRADIENT_H #include #include #include #include #include "KoColor.h" #include enum { INTERP_LINEAR = 0, INTERP_CURVED, INTERP_SINE, INTERP_SPHERE_INCREASING, INTERP_SPHERE_DECREASING }; enum { COLOR_INTERP_RGB, COLOR_INTERP_HSV_CCW, COLOR_INTERP_HSV_CW }; +//For saving to .ggr to match GIMP format, we also have Foreground (transparent) and Background (transparent) modes, currently unused... +enum KoGradientSegmentEndpointType { + COLOR_ENDPOINT, + FOREGROUND_ENDPOINT, + FOREGROUND_TRANSPARENT_ENDPOINT, + BACKGROUND_ENDPOINT, + BACKGROUND_TRANSPARENT_ENDPOINT +}; + +struct KoGradientSegmentEndpoint { + KoGradientSegmentEndpoint(qreal _off, KoColor _color, KoGradientSegmentEndpointType _type) : + offset(_off), color(_color), type(_type) + { + + } + + qreal offset; + KoColor color; + KoGradientSegmentEndpointType type; + + +}; + /// Write API docs here class KRITAPIGMENT_EXPORT KoGradientSegment { public: - KoGradientSegment(int interpolationType, int colorInterpolationType, qreal startOffset, qreal middleOffset, qreal endOffset, const KoColor& startColor, const KoColor& endColor); + KoGradientSegment(int interpolationType, int colorInterpolationType, KoGradientSegmentEndpoint start, KoGradientSegmentEndpoint end, qreal middleOffset); // startOffset <= t <= endOffset void colorAt(KoColor&, qreal t) const; const KoColor& startColor() const; const KoColor& endColor() const; + KoGradientSegmentEndpointType startType() const; + KoGradientSegmentEndpointType endType() const; void setStartColor(const KoColor& color) { - m_startColor = color; + m_start.color = color; + if (m_start.type == FOREGROUND_TRANSPARENT_ENDPOINT || m_start.type == BACKGROUND_TRANSPARENT_ENDPOINT) { + m_start.color.setOpacity(quint8(0)); + } else if (m_start.type == FOREGROUND_ENDPOINT || m_start.type == BACKGROUND_ENDPOINT) { + m_start.color.setOpacity(quint8(255)); + } } void setEndColor(const KoColor& color) { - m_endColor = color; + m_end.color = color; + if (m_end.type == FOREGROUND_TRANSPARENT_ENDPOINT || m_end.type == BACKGROUND_TRANSPARENT_ENDPOINT) { + m_end.color.setOpacity(quint8(0)); + } else if (m_end.type == FOREGROUND_ENDPOINT || m_end.type == BACKGROUND_ENDPOINT) { + m_end.color.setOpacity(quint8(255)); + } } + void setStartType(KoGradientSegmentEndpointType type); + void setEndType(KoGradientSegmentEndpointType type); + qreal startOffset() const; qreal middleOffset() const; qreal endOffset() const; void setStartOffset(qreal t); void setMiddleOffset(qreal t); void setEndOffset(qreal t); + void setVariableColors(const KoColor& foreground, const KoColor& background); + bool hasVariableColors(); + qreal length() { return m_length; } int interpolation() const; int colorInterpolation() const; void setInterpolation(int interpolationType); void setColorInterpolation(int colorInterpolationType); + void mirrorSegment(); + bool isValid() const; protected: class ColorInterpolationStrategy { public: ColorInterpolationStrategy() {} virtual ~ColorInterpolationStrategy() {} virtual void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const = 0; virtual int type() const = 0; }; class RGBColorInterpolationStrategy : public ColorInterpolationStrategy { public: static RGBColorInterpolationStrategy *instance(); void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override; int type() const override { return COLOR_INTERP_RGB; } private: RGBColorInterpolationStrategy(); static RGBColorInterpolationStrategy *m_instance; const KoColorSpace * const m_colorSpace; }; class HSVCWColorInterpolationStrategy : public ColorInterpolationStrategy { public: static HSVCWColorInterpolationStrategy *instance(); void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override; int type() const override { return COLOR_INTERP_HSV_CW; } private: HSVCWColorInterpolationStrategy(); static HSVCWColorInterpolationStrategy *m_instance; const KoColorSpace * const m_colorSpace; }; class HSVCCWColorInterpolationStrategy : public ColorInterpolationStrategy { public: static HSVCCWColorInterpolationStrategy *instance(); void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override; int type() const override { return COLOR_INTERP_HSV_CCW; } private: HSVCCWColorInterpolationStrategy(); static HSVCCWColorInterpolationStrategy *m_instance; const KoColorSpace * const m_colorSpace; }; class InterpolationStrategy { public: InterpolationStrategy() {} virtual ~InterpolationStrategy() {} virtual qreal valueAt(qreal t, qreal middle) const = 0; virtual int type() const = 0; }; class LinearInterpolationStrategy : public InterpolationStrategy { public: static LinearInterpolationStrategy *instance(); qreal valueAt(qreal t, qreal middle) const override; int type() const override { return INTERP_LINEAR; } // This does the actual calculation and is made // static as an optimization for the other // strategies that need this for their own calculation. static qreal calcValueAt(qreal t, qreal middle); private: LinearInterpolationStrategy() {} static LinearInterpolationStrategy *m_instance; }; class CurvedInterpolationStrategy : public InterpolationStrategy { public: static CurvedInterpolationStrategy *instance(); qreal valueAt(qreal t, qreal middle) const override; int type() const override { return INTERP_CURVED; } private: CurvedInterpolationStrategy(); static CurvedInterpolationStrategy *m_instance; qreal m_logHalf; }; class SphereIncreasingInterpolationStrategy : public InterpolationStrategy { public: static SphereIncreasingInterpolationStrategy *instance(); qreal valueAt(qreal t, qreal middle) const override; int type() const override { return INTERP_SPHERE_INCREASING; } private: SphereIncreasingInterpolationStrategy() {} static SphereIncreasingInterpolationStrategy *m_instance; }; class SphereDecreasingInterpolationStrategy : public InterpolationStrategy { public: static SphereDecreasingInterpolationStrategy *instance(); qreal valueAt(qreal t, qreal middle) const override; int type() const override { return INTERP_SPHERE_DECREASING; } private: SphereDecreasingInterpolationStrategy() {} static SphereDecreasingInterpolationStrategy *m_instance; }; class SineInterpolationStrategy : public InterpolationStrategy { public: static SineInterpolationStrategy *instance(); qreal valueAt(qreal t, qreal middle) const override; int type() const override { return INTERP_SINE; } private: SineInterpolationStrategy() {} static SineInterpolationStrategy *m_instance; }; private: InterpolationStrategy *m_interpolator; ColorInterpolationStrategy *m_colorInterpolator; - qreal m_startOffset; qreal m_middleOffset; - qreal m_endOffset; qreal m_length; qreal m_middleT; - KoColor m_startColor; - KoColor m_endColor; + KoGradientSegmentEndpoint m_start, m_end; + bool m_hasVariableColors = false; + }; /** * KoSegmentGradient stores a segment based gradients like Gimp gradients */ class KRITAPIGMENT_EXPORT KoSegmentGradient : public KoAbstractGradient { public: explicit KoSegmentGradient(const QString &file = QString()); ~KoSegmentGradient() override; KoAbstractGradient* clone() const override; /// reimplemented bool load() override; bool loadFromDevice(QIODevice *dev) override; /// not implemented bool save() override; bool saveToDevice(QIODevice* dev) const override; /// reimplemented void colorAt(KoColor& dst, qreal t) const override; + /// reimplemented + bool hasVariableColors() const override; + /// reimplemented + void setVariableColors(const KoColor& foreground, const KoColor& background) override; + /** * Returns the segment at a given position * @param t position inside the gradient, with 0 <= t <= 1 * @return the segment the position, 0 if no segment is found */ KoGradientSegment *segmentAt(qreal t) const; /// reimplemented QGradient* toQGradient() const override; /// reimplemented QString defaultFileExtension() const override; /** * @brief toXML * convert the gradient to xml. */ void toXML(QDomDocument& doc, QDomElement& gradientElt) const; /** * @brief fromXML * get a segment gradient from xml. * @return gradient */ static KoSegmentGradient fromXML(const QDomElement& elt); /** * a gradient colour picker can consist of one or more segments. * A segment has two end points - each colour in the gradient * colour picker represents a segment end point. * @param interpolation * @param colorInterpolation * @param startOffset * @param endOffset * @param middleOffset * @param left * @param right + * @param leftType + * @param rightType * @return void */ - void createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, const QColor & left, const QColor & right); + void createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, + const QColor & leftColor, const QColor & rightColor, + KoGradientSegmentEndpointType leftType = COLOR_ENDPOINT, KoGradientSegmentEndpointType rightType = COLOR_ENDPOINT); /** * gets a list of end points of the segments in the gradient * colour picker. If two colours, one segment then two end * points, and if three colours, then two segments with four * endpoints. * @return a list of double values */ const QList getHandlePositions() const; /** * gets a list of middle points of the segments in the gradient * colour picker. * @return a list of double values */ const QList getMiddleHandlePositions() const; /** * Moves the StartOffset of the specified segment to the * specified value and corrects the endoffset of the previous * segment. If the segment is the first Segment the startoffset * will be set to 0.0 . The offset will maximally be moved till * the middle of the current or the previous segment. This is * useful if someone clicks to move the handler for a segment, * to set the half the segment to the right and half the segment * to the left of the handler. * @param segment the segment for which to move the relative * offset within the gradient colour picker. * @param t the new startoff position for the segment * @return void */ void moveSegmentStartOffset(KoGradientSegment* segment, double t); /** * Moves the endoffset of the specified segment to the specified * value and corrects the startoffset of the following segment. * If the segment is the last segment the endoffset will be set * to 1.0 . The offset will maximally be moved till the middle * of the current or the following segment. This is useful if * someone moves the segment handler in the gradient colour * picker, and needs the segment to move with it. Sets the end * position of the segment to the correct new position. * @param segment the segment for which to move the relative * end position within the gradient colour picker. * @param t the new end position for the segment * @return void */ void moveSegmentEndOffset(KoGradientSegment* segment, double t); /** * moves the Middle of the specified segment to the specified * value. The offset will maximally be moved till the endoffset * or startoffset of the segment. This sets the middle of the * segment to the same position as the handler of the gradient * colour picker. * @param segment the segment for which to move the relative * middle position within the gradient colour picker. * @param t the new middle position for the segment * @return void */ void moveSegmentMiddleOffset(KoGradientSegment* segment, double t); /** * splits the specified segment into two equal parts * @param segment the segment to split * @return void */ void splitSegment(KoGradientSegment* segment); /** * duplicate the specified segment * @param segment the segment to duplicate * @return void */ void duplicateSegment(KoGradientSegment* segment); /** * create a segment horizontally reversed to the specified one. * @param segment the segment to reverse * @return void */ void mirrorSegment(KoGradientSegment* segment); /** * removes the specific segment from the gradient colour picker. * @param segment the segment to remove * @return the segment which will be at the place of the old * segment. 0 if the segment is not in the gradient or it is * not possible to remove the segment. */ KoGradientSegment* removeSegment(KoGradientSegment* segment); /** * checks if it's possible to remove a segment (at least two * segments in the gradient) * @return true if it's possible to remove an segment */ bool removeSegmentPossible() const; const QList& segments() const; protected: KoSegmentGradient(const KoSegmentGradient &rhs); inline void pushSegment(KoGradientSegment* segment) { m_segments.push_back(segment); } QList m_segments; private: bool init(); }; #endif // KOSEGMENTGRADIENT_H diff --git a/libs/pigment/resources/KoStopGradient.cpp b/libs/pigment/resources/KoStopGradient.cpp index ac583e733e..cc43c2b0e6 100644 --- a/libs/pigment/resources/KoStopGradient.cpp +++ b/libs/pigment/resources/KoStopGradient.cpp @@ -1,604 +1,644 @@ /* Copyright (C) 2005 Tim Beaulen Copyright (C) 2007 Jan Hambrecht Copyright (c) 2007 Sven Langkamp This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "KoColorSpaceRegistry.h" #include "KoMixColorsOp.h" #include "kis_dom_utils.h" #include #include +#include KoStopGradient::KoStopGradient(const QString& filename) : KoAbstractGradient(filename) { } KoStopGradient::~KoStopGradient() { } -bool KoStopGradient::operator==(const KoStopGradient &rhs) const +bool KoStopGradient::operator==(const KoStopGradient& rhs) const { return *colorSpace() == *rhs.colorSpace() && spread() == rhs.spread() && type() == rhs.type() && m_start == rhs.m_start && m_stop == rhs.m_stop && m_focalPoint == rhs.m_focalPoint && m_stops == rhs.m_stops; } KoAbstractGradient* KoStopGradient::clone() const { return new KoStopGradient(*this); } bool KoStopGradient::load() { QFile f(filename()); if (!f.open(QIODevice::ReadOnly)) { warnPigment << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&f); f.close(); return res; } -bool KoStopGradient::loadFromDevice(QIODevice *dev) +bool KoStopGradient::loadFromDevice(QIODevice* dev) { QString strExt; const int result = filename().lastIndexOf('.'); if (result >= 0) { strExt = filename().mid(result).toLower(); } QByteArray ba = dev->readAll(); QBuffer buf(&ba); loadSvgGradient(&buf); if (m_stops.count() >= 2) { setValid(true); } updatePreview(); return true; } bool KoStopGradient::save() { QFile fileOut(filename()); - if (! fileOut.open(QIODevice::WriteOnly)) + if (!fileOut.open(QIODevice::WriteOnly)) return false; bool retval = saveToDevice(&fileOut); fileOut.close(); return retval; } QGradient* KoStopGradient::toQGradient() const { QGradient* gradient; switch (type()) { case QGradient::LinearGradient: { gradient = new QLinearGradient(m_start, m_stop); break; } case QGradient::RadialGradient: { QPointF diff = m_stop - m_start; qreal radius = sqrt(diff.x() * diff.x() + diff.y() * diff.y()); gradient = new QRadialGradient(m_start, radius, m_focalPoint); break; } case QGradient::ConicalGradient: { qreal angle = atan2(m_start.y(), m_start.x()) * 180.0 / M_PI; if (angle < 0.0) angle += 360.0; gradient = new QConicalGradient(m_start, angle); break; } default: return 0; } QColor color; for (QList::const_iterator i = m_stops.begin(); i != m_stops.end(); ++i) { - i->second.toQColor(&color); - gradient->setColorAt(i->first , color); + i->color.toQColor(&color); + gradient->setColorAt(i->position, color); } gradient->setCoordinateMode(QGradient::ObjectBoundingMode); gradient->setSpread(this->spread()); return gradient; } -bool KoStopGradient::stopsAt(KoGradientStop &leftStop, KoGradientStop &rightStop, qreal t) const +bool KoStopGradient::stopsAt(KoGradientStop& leftStop, KoGradientStop& rightStop, qreal t) const { - if (! m_stops.count()) + if (!m_stops.count()) return false; - if (t <= m_stops.first().first || m_stops.count() == 1) { + if (t <= m_stops.first().position || m_stops.count() == 1) { // we have only one stop or t is before the first stop leftStop = m_stops.first(); - rightStop = KoGradientStop(-std::numeric_limits::infinity(), leftStop.second); + rightStop = KoGradientStop(-std::numeric_limits::infinity(), leftStop.color, leftStop.type); return true; - } else if (t >= m_stops.last().first) { + } else if (t >= m_stops.last().position) { // t is after the last stop rightStop = m_stops.last(); - leftStop = KoGradientStop(std::numeric_limits::infinity(), rightStop.second); + leftStop = KoGradientStop(std::numeric_limits::infinity(), rightStop.color, rightStop.type); return true; } else { // we have at least two color stops // -> find the two stops which frame our t - auto it = std::lower_bound(m_stops.begin(), m_stops.end(), KoGradientStop(t, KoColor()), [](const KoGradientStop &a, const KoGradientStop &b){ - return a.first < b.first; - }); + auto it = std::lower_bound(m_stops.begin(), m_stops.end(), KoGradientStop(t, KoColor(), COLORSTOP), [](const KoGradientStop& a, const KoGradientStop& b) { + return a.position < b.position; + }); leftStop = *(it - 1); rightStop = *(it); return true; } } void KoStopGradient::colorAt(KoColor& dst, qreal t) const { KoColor buffer; KoGradientStop leftStop, rightStop; if (!stopsAt(leftStop, rightStop, t)) return; const KoColorSpace* mixSpace = KoColorSpaceRegistry::instance()->rgb8(dst.colorSpace()->profile()); KoColor startDummy, endDummy; - if (mixSpace){ - startDummy = KoColor(leftStop.second, mixSpace); - endDummy = KoColor(rightStop.second, mixSpace); + if (mixSpace) { + startDummy = KoColor(leftStop.color, mixSpace); + endDummy = KoColor(rightStop.color, mixSpace); } else { - startDummy = leftStop.second; - endDummy = rightStop.second; + startDummy = leftStop.color; + endDummy = rightStop.color; } - const quint8 *colors[2]; + const quint8* colors[2]; colors[0] = startDummy.data(); colors[1] = endDummy.data(); qreal localT; - qreal stopDistance = rightStop.first - leftStop.first; + qreal stopDistance = rightStop.position - leftStop.position; if (stopDistance < DBL_EPSILON) { localT = 0.5; } else { - localT = (t - leftStop.first) / stopDistance; + localT = (t - leftStop.position) / stopDistance; } qint16 colorWeights[2]; colorWeights[0] = static_cast((1.0 - localT) * 255 + 0.5); colorWeights[1] = 255 - colorWeights[0]; //check if our mixspace exists, it doesn't at startup. - if (mixSpace){ + if (mixSpace) { if (*buffer.colorSpace() != *mixSpace) { buffer = KoColor(mixSpace); } mixSpace->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data()); - } - else { + } else { buffer = KoColor(colorSpace()); colorSpace()->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data()); } dst.fromKoColor(buffer); } -KoStopGradient * KoStopGradient::fromQGradient(const QGradient * gradient) +KoStopGradient* KoStopGradient::fromQGradient(const QGradient* gradient) { - if (! gradient) + if (!gradient) return 0; - KoStopGradient * newGradient = new KoStopGradient(QString()); + KoStopGradient* newGradient = new KoStopGradient(QString()); newGradient->setType(gradient->type()); newGradient->setSpread(gradient->spread()); switch (gradient->type()) { case QGradient::LinearGradient: { - const QLinearGradient * g = static_cast(gradient); + const QLinearGradient* g = static_cast(gradient); newGradient->m_start = g->start(); newGradient->m_stop = g->finalStop(); newGradient->m_focalPoint = g->start(); break; } case QGradient::RadialGradient: { - const QRadialGradient * g = static_cast(gradient); + const QRadialGradient* g = static_cast(gradient); newGradient->m_start = g->center(); newGradient->m_stop = g->center() + QPointF(g->radius(), 0); newGradient->m_focalPoint = g->focalPoint(); break; } case QGradient::ConicalGradient: { - const QConicalGradient * g = static_cast(gradient); + const QConicalGradient* g = static_cast(gradient); qreal radian = g->angle() * M_PI / 180.0; newGradient->m_start = g->center(); newGradient->m_stop = QPointF(100.0 * cos(radian), 100.0 * sin(radian)); newGradient->m_focalPoint = g->center(); break; } default: delete newGradient; return 0; } - Q_FOREACH (const QGradientStop & stop, gradient->stops()) { + Q_FOREACH(const QGradientStop & stop, gradient->stops()) { KoColor color(newGradient->colorSpace()); color.fromQColor(stop.second); - newGradient->m_stops.append(KoGradientStop(stop.first, color)); + newGradient->m_stops.append(KoGradientStop(stop.first, color, COLORSTOP)); } newGradient->setValid(true); return newGradient; } void KoStopGradient::setStops(QList< KoGradientStop > stops) { m_stops.clear(); + m_hasVariableStops = false; KoColor color; - Q_FOREACH (const KoGradientStop & stop, stops) { - color = stop.second; + Q_FOREACH(const KoGradientStop & stop, stops) { + color = stop.color; color.convertTo(colorSpace()); - m_stops.append(KoGradientStop(stop.first, color)); + m_stops.append(KoGradientStop(stop.position, color, stop.type)); + if (stop.type != COLORSTOP) { + m_hasVariableStops = true; + } } updatePreview(); } QList KoStopGradient::stops() const { return m_stops; } -void KoStopGradient::loadSvgGradient(QIODevice *file) +bool KoStopGradient::hasVariableColors() const { + return m_hasVariableStops; +} + +void KoStopGradient::setVariableColors(const KoColor& foreground, const KoColor& background) { + KoColor color; + for (int i = 0; i < m_stops.count(); i++){ + if (m_stops[i].type == FOREGROUNDSTOP) { + color = foreground; + } else if (m_stops[i].type == BACKGROUNDSTOP) { + color = background; + } else continue; + color.convertTo(colorSpace()); + m_stops[i].color = color; + } + updatePreview(); +} + +void KoStopGradient::loadSvgGradient(QIODevice* file) { QDomDocument doc; - if (!(doc.setContent(file))) + if (!(doc.setContent(file))) { file->close(); - else { + } else { for (QDomNode n = doc.documentElement().firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if (e.isNull()) continue; if (e.tagName() == "linearGradient" || e.tagName() == "radialGradient") { parseSvgGradient(e); return; } // Inkscape gradients are in another defs if (e.tagName() == "defs") { for (QDomNode defnode = e.firstChild(); !defnode.isNull(); defnode = defnode.nextSibling()) { QDomElement defelement = defnode.toElement(); if (defelement.isNull()) continue; if (defelement.tagName() == "linearGradient" || defelement.tagName() == "radialGradient") { parseSvgGradient(defelement); return; } } } } } } void KoStopGradient::parseSvgGradient(const QDomElement& element) { m_stops.clear(); + m_hasVariableStops = false; setSpread(QGradient::PadSpread); /*QString href = e.attribute( "xlink:href" ).mid( 1 ); if( !href.isEmpty() ) { }*/ setName(element.attribute("id", i18n("SVG Gradient"))); const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); bool bbox = element.attribute("gradientUnits") != "userSpaceOnUse"; if (element.tagName() == "linearGradient") { if (bbox) { QString s; s = element.attribute("x1", "0%"); qreal xOrigin; if (s.endsWith('%')) xOrigin = s.remove('%').toDouble(); else xOrigin = s.toDouble() * 100.0; s = element.attribute("y1", "0%"); qreal yOrigin; if (s.endsWith('%')) yOrigin = s.remove('%').toDouble(); else yOrigin = s.toDouble() * 100.0; s = element.attribute("x2", "100%"); qreal xVector; if (s.endsWith('%')) xVector = s.remove('%').toDouble(); else xVector = s.toDouble() * 100.0; s = element.attribute("y2", "0%"); qreal yVector; if (s.endsWith('%')) yVector = s.remove('%').toDouble(); else yVector = s.toDouble() * 100.0; m_start = QPointF(xOrigin, yOrigin); m_stop = QPointF(xVector, yVector); - } else { + } + else { m_start = QPointF(element.attribute("x1").toDouble(), element.attribute("y1").toDouble()); m_stop = QPointF(element.attribute("x2").toDouble(), element.attribute("y2").toDouble()); } setType(QGradient::LinearGradient); - } else { + } + else { if (bbox) { QString s; s = element.attribute("cx", "50%"); qreal xOrigin; if (s.endsWith('%')) xOrigin = s.remove('%').toDouble(); else xOrigin = s.toDouble() * 100.0; s = element.attribute("cy", "50%"); qreal yOrigin; if (s.endsWith('%')) yOrigin = s.remove('%').toDouble(); else yOrigin = s.toDouble() * 100.0; s = element.attribute("cx", "50%"); qreal xVector; if (s.endsWith('%')) xVector = s.remove('%').toDouble(); else xVector = s.toDouble() * 100.0; s = element.attribute("r", "50%"); if (s.endsWith('%')) xVector += s.remove('%').toDouble(); else xVector += s.toDouble() * 100.0; s = element.attribute("cy", "50%"); qreal yVector; if (s.endsWith('%')) yVector = s.remove('%').toDouble(); else yVector = s.toDouble() * 100.0; s = element.attribute("fx", "50%"); qreal xFocal; if (s.endsWith('%')) xFocal = s.remove('%').toDouble(); else xFocal = s.toDouble() * 100.0; s = element.attribute("fy", "50%"); qreal yFocal; if (s.endsWith('%')) yFocal = s.remove('%').toDouble(); else yFocal = s.toDouble() * 100.0; m_start = QPointF(xOrigin, yOrigin); m_stop = QPointF(xVector, yVector); m_focalPoint = QPointF(xFocal, yFocal); - } else { + } + else { m_start = QPointF(element.attribute("cx").toDouble(), element.attribute("cy").toDouble()); m_stop = QPointF(element.attribute("cx").toDouble() + element.attribute("r").toDouble(), - element.attribute("cy").toDouble()); + element.attribute("cy").toDouble()); m_focalPoint = QPointF(element.attribute("fx").toDouble(), element.attribute("fy").toDouble()); } setType(QGradient::RadialGradient); } // handle spread method QString spreadMethod = element.attribute("spreadMethod"); if (!spreadMethod.isEmpty()) { if (spreadMethod == "reflect") setSpread(QGradient::ReflectSpread); else if (spreadMethod == "repeat") setSpread(QGradient::RepeatSpread); } for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement colorstop = n.toElement(); if (colorstop.tagName() == "stop") { qreal opacity = 0.0; QColor c; float off; QString temp = colorstop.attribute("offset"); if (temp.contains('%')) { temp = temp.left(temp.length() - 1); off = temp.toFloat() / 100.0; - } else + } + else off = temp.toFloat(); if (!colorstop.attribute("stop-color").isEmpty()) parseSvgColor(c, colorstop.attribute("stop-color")); else { // try style attr QString style = colorstop.attribute("style").simplified(); QStringList substyles = style.split(';', QString::SkipEmptyParts); - Q_FOREACH (const QString & s, substyles) { + Q_FOREACH(const QString & s, substyles) { QStringList substyle = s.split(':'); QString command = substyle[0].trimmed(); QString params = substyle[1].trimmed(); if (command == "stop-color") parseSvgColor(c, params); if (command == "stop-opacity") opacity = params.toDouble(); } } if (!colorstop.attribute("stop-opacity").isEmpty()) opacity = colorstop.attribute("stop-opacity").toDouble(); KoColor color(rgbColorSpace); color.fromQColor(c); color.setOpacity(static_cast(opacity * OPACITY_OPAQUE_U8 + 0.5)); - + QString stopTypeStr = colorstop.attribute("krita:stop-type", "color-stop"); + KoGradientStopType stopType = KoGradientStop::typeFromString(stopTypeStr); + if (stopType != COLORSTOP) { + m_hasVariableStops = true; + } //According to the SVG spec each gradient offset has to be equal to or greater than the previous one //if not it needs to be adjusted to be equal - if (m_stops.count() > 0 && m_stops.last().first >= off) { - off = m_stops.last().first; + if (m_stops.count() > 0 && m_stops.last().position >= off) { + off = m_stops.last().position; } - m_stops.append(KoGradientStop(off, color)); + m_stops.append(KoGradientStop(off, color, stopType)); } } } -void KoStopGradient::parseSvgColor(QColor &color, const QString &s) +void KoStopGradient::parseSvgColor(QColor& color, const QString& s) { if (s.startsWith("rgb(")) { QString parse = s.trimmed(); QStringList colors = parse.split(','); QString r = colors[0].right((colors[0].length() - 4)); QString g = colors[1]; QString b = colors[2].left((colors[2].length() - 1)); if (r.contains('%')) { r = r.left(r.length() - 1); r = QString::number(int((qreal(255 * r.toDouble()) / 100.0))); } if (g.contains('%')) { g = g.left(g.length() - 1); g = QString::number(int((qreal(255 * g.toDouble()) / 100.0))); } if (b.contains('%')) { b = b.left(b.length() - 1); b = QString::number(int((qreal(255 * b.toDouble()) / 100.0))); } color = QColor(r.toInt(), g.toInt(), b.toInt()); - } else { + } + else { QString rgbColor = s.trimmed(); QColor c; if (rgbColor.startsWith('#')) c.setNamedColor(rgbColor); else { c = QColor(rgbColor); } color = c; } } QString KoStopGradient::defaultFileExtension() const { return QString(".svg"); } -void KoStopGradient::toXML(QDomDocument &doc, QDomElement &gradientElt) const +void KoStopGradient::toXML(QDomDocument& doc, QDomElement& gradientElt) const { gradientElt.setAttribute("type", "stop"); for (int s = 0; s < m_stops.size(); s++) { KoGradientStop stop = m_stops.at(s); QDomElement stopElt = doc.createElement("stop"); - stopElt.setAttribute("offset", KisDomUtils::toString(stop.first)); - stopElt.setAttribute("bitdepth", stop.second.colorSpace()->colorDepthId().id()); - stopElt.setAttribute("alpha", KisDomUtils::toString(stop.second.opacityF())); - stop.second.toXML(doc, stopElt); + stopElt.setAttribute("offset", KisDomUtils::toString(stop.position)); + stopElt.setAttribute("bitdepth", stop.color.colorSpace()->colorDepthId().id()); + stopElt.setAttribute("alpha", KisDomUtils::toString(stop.color.opacityF())); + stopElt.setAttribute("stoptype", KisDomUtils::toString(stop.type)); + stop.color.toXML(doc, stopElt); gradientElt.appendChild(stopElt); } } -KoStopGradient KoStopGradient::fromXML(const QDomElement &elt) +KoStopGradient KoStopGradient::fromXML(const QDomElement& elt) { KoStopGradient gradient; QList stops; QDomElement stopElt = elt.firstChildElement("stop"); while (!stopElt.isNull()) { qreal offset = KisDomUtils::toDouble(stopElt.attribute("offset", "0.0")); QString bitDepth = stopElt.attribute("bitdepth", Integer8BitsColorDepthID.id()); KoColor color = KoColor::fromXML(stopElt.firstChildElement(), bitDepth); color.setOpacity(KisDomUtils::toDouble(stopElt.attribute("alpha", "1.0"))); - stops.append(KoGradientStop(offset, color)); + KoGradientStopType stoptype = static_cast(KisDomUtils::toInt(stopElt.attribute("stoptype", "0"))); + stops.append(KoGradientStop(offset, color, stoptype)); stopElt = stopElt.nextSiblingElement("stop"); } gradient.setStops(stops); return gradient; } -bool KoStopGradient::saveToDevice(QIODevice *dev) const +bool KoStopGradient::saveToDevice(QIODevice* dev) const { QTextStream stream(dev); const QString spreadMethod[3] = { QString("spreadMethod=\"pad\" "), QString("spreadMethod=\"reflect\" "), QString("spreadMethod=\"repeat\" ") }; const QString indent = " "; - stream << "" << endl; + stream << "" << endl; + stream << indent; stream << "" << endl; QColor color; // color stops - Q_FOREACH (const KoGradientStop & stop, m_stops) { - stop.second.toQColor(&color); + Q_FOREACH(const KoGradientStop & stop, m_stops) { + stop.color.toQColor(&color); stream << indent << indent; stream << "(color.alpha()) / 255.0f << "\"" << " />" << endl; + stream << "\" offset=\"" << QString().setNum(stop.position); + stream << "\" stop-opacity=\"" << static_cast(color.alpha()) / 255.0f; + stream << "\" krita:stop-type=\"" << stop.typeString() << "\""; + + stream << " />" << endl; } stream << indent; stream << "" << endl; stream << "" << endl; KoResource::saveToDevice(dev); return true; -} +} \ No newline at end of file diff --git a/libs/pigment/resources/KoStopGradient.h b/libs/pigment/resources/KoStopGradient.h index 4c4d653809..8b7377263f 100644 --- a/libs/pigment/resources/KoStopGradient.h +++ b/libs/pigment/resources/KoStopGradient.h @@ -1,104 +1,163 @@ /* Copyright (c) 2007 Sven Langkamp This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KOSTOPGRADIENT_H #define KOSTOPGRADIENT_H #include #include +#include #include "KoColor.h" #include #include #include #include -typedef QPair KoGradientStop; +enum KoGradientStopType +{ + COLORSTOP, + FOREGROUNDSTOP, + BACKGROUNDSTOP +}; + +struct KoGradientStop : public boost::equality_comparable +{ + KoGradientStopType type; + KoColor color; + qreal position; + + KoGradientStop(qreal _position = 0.0, KoColor _color = KoColor(), KoGradientStopType _type = COLORSTOP) + { + type = _type; + color = _color; + position = _position; + } + + bool operator == (const KoGradientStop& other) + { + return this->type == other.type && this->color == other.color && this->position == other.position; + } + + + + QString typeString() const + { + switch (type) { + case COLORSTOP: + return "color-stop"; + case FOREGROUNDSTOP: + return "foreground-stop"; + case BACKGROUNDSTOP: + return "background-stop"; + default: + return "color-stop"; + } + } + + static KoGradientStopType typeFromString(QString typestring) { + if (typestring == "foreground-stop") { + return FOREGROUNDSTOP; + } else if (typestring == "background-stop") { + return BACKGROUNDSTOP; + } else { + return COLORSTOP; + } + } +}; + struct KoGradientStopValueSort { inline bool operator() (const KoGradientStop& a, const KoGradientStop& b) { - return (a.second.toQColor().valueF() < b.second.toQColor().valueF()); + return (a.color.toQColor().valueF() < b.color.toQColor().valueF()); } }; /** * Resource for colorstop based gradients like SVG gradients */ class KRITAPIGMENT_EXPORT KoStopGradient : public KoAbstractGradient, public boost::equality_comparable { public: + explicit KoStopGradient(const QString &filename = QString()); ~KoStopGradient() override; bool operator==(const KoStopGradient &rhs) const; KoAbstractGradient* clone() const override; bool load() override; bool loadFromDevice(QIODevice *dev) override; bool save() override; bool saveToDevice(QIODevice* dev) const override; /// reimplemented QGradient* toQGradient() const override; /// Find stops surrounding position, returns false if position outside gradient bool stopsAt(KoGradientStop& leftStop, KoGradientStop& rightStop, qreal t) const; /// reimplemented void colorAt(KoColor&, qreal t) const override; /// Creates KoStopGradient from a QGradient static KoStopGradient * fromQGradient(const QGradient * gradient); /// Sets the gradient stops void setStops(QList stops); - QList stops() const; + QList stops() const; + + /// reimplemented + bool hasVariableColors() const override; + /// reimplemented + void setVariableColors(const KoColor& foreground, const KoColor& background) override; /// reimplemented QString defaultFileExtension() const override; /** * @brief toXML * Convert the gradient to an XML string. */ void toXML(QDomDocument& doc, QDomElement& gradientElt) const; /** * @brief fromXML * convert a gradient from xml. * @return a gradient. */ static KoStopGradient fromXML(const QDomElement& elt); protected: QList m_stops; + bool m_hasVariableStops = false; QPointF m_start; QPointF m_stop; QPointF m_focalPoint; private: void loadSvgGradient(QIODevice *file); void parseSvgGradient(const QDomElement& element); void parseSvgColor(QColor &color, const QString &s); }; #endif // KOSTOPGRADIENT_H diff --git a/libs/psd/asl/kis_asl_xml_parser.cpp b/libs/psd/asl/kis_asl_xml_parser.cpp index 6aa0b4a65b..3b9a6ecaed 100644 --- a/libs/psd/asl/kis_asl_xml_parser.cpp +++ b/libs/psd/asl/kis_asl_xml_parser.cpp @@ -1,578 +1,591 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_xml_parser.h" #include #include #include #include #include #include #include #include #include #include #include "kis_dom_utils.h" #include "kis_debug.h" #include "psd_utils.h" #include "psd.h" #include "compression.h" #include "kis_asl_object_catcher.h" namespace Private { void parseElement(const QDomElement &el, const QString &parentPath, KisAslObjectCatcher &catcher); class CurveObjectCatcher : public KisAslObjectCatcher { public: void addText(const QString &path, const QString &value) override { if (path == "/Nm ") { m_name = value; } else { warnKrita << "XML (ASL): failed to parse curve object" << path << value; } } void addPoint(const QString &path, const QPointF &value) override { if (!m_arrayMode) { warnKrita << "XML (ASL): failed to parse curve object (array fault)" << path << value << ppVar(m_arrayMode); } m_points.append(value); } public: QVector m_points; QString m_name; }; QColor parseRGBColorObject(QDomElement parent) { QColor color(Qt::black); QDomNode child = parent.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type != "Double") { warnKrita << "Unknown color component type:" << ppVar(type) << ppVar(key); return Qt::red; } double value = KisDomUtils::toDouble(childEl.attribute("value", "0")); if (key == "Rd ") { color.setRed(value); } else if (key == "Grn ") { color.setGreen(value); } else if (key == "Bl ") { color.setBlue(value); } else { warnKrita << "Unknown color key value:" << ppVar(key); return Qt::red; } child = child.nextSibling(); } return color; } void parseColorStopsList(QDomElement parent, QVector &startLocations, QVector &middleOffsets, - QVector &colors) + QVector &colors, + QVector types) { QDomNode child = parent.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString classId = childEl.attribute("classId", ""); if (type == "Descriptor" && classId == "Clrt") { // sorry for naming... QDomNode child = childEl.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString classId = childEl.attribute("classId", ""); if (type == "Integer" && key == "Lctn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); startLocations.append(qreal(value) / 4096.0); } else if (type == "Integer" && key == "Mdpn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); middleOffsets.append(qreal(value) / 100.0); } else if (type == "Descriptor" && key == "Clr ") { colors.append(parseRGBColorObject(childEl)); } else if (type == "Enum" && key == "Type") { QString typeId = childEl.attribute("typeId", ""); if (typeId != "Clry") { warnKrita << "WARNING: Invalid typeId of a gradient stop type" << typeId; } QString value = childEl.attribute("value", ""); - if (value == "BckC" || value == "FrgC") { - warnKrita << "WARNING: Using foreground/background colors in ASL gradients is not yet supported"; + if (value == "BckC"){ + types.append(BACKGROUND_ENDPOINT); + } else if (value == "FrgC") { + types.append(FOREGROUND_ENDPOINT); + } else { + types.append(COLOR_ENDPOINT); } } child = child.nextSibling(); } } else { warnKrita << "WARNING: Unrecognized object in color stops list" << ppVar(type) << ppVar(key) << ppVar(classId); } child = child.nextSibling(); } } void parseTransparencyStopsList(QDomElement parent, QVector &startLocations, QVector &middleOffsets, QVector &transparencies) { QDomNode child = parent.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString classId = childEl.attribute("classId", ""); if (type == "Descriptor" && classId == "TrnS") { // sorry for naming again... QDomNode child = childEl.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Integer" && key == "Lctn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); startLocations.append(qreal(value) / 4096.0); } else if (type == "Integer" && key == "Mdpn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); middleOffsets.append(qreal(value) / 100.0); } else if (type == "UnitFloat" && key == "Opct") { QString unit = childEl.attribute("unit", ""); if (unit != "#Prc") { warnKrita << "WARNING: Invalid unit of a gradient stop transparency" << unit; } qreal value = KisDomUtils::toDouble(childEl.attribute("value", "100")); transparencies.append(value / 100.0); } child = child.nextSibling(); } } else { warnKrita << "WARNING: Unrecognized object in transparency stops list" << ppVar(type) << ppVar(key) << ppVar(classId); } child = child.nextSibling(); } } inline QString buildPath(const QString &parent, const QString &key) { return parent + "/" + key; } bool tryParseDescriptor(const QDomElement &el, const QString &path, const QString &classId, KisAslObjectCatcher &catcher) { bool retval = true; if (classId == "null") { catcher.newStyleStarted(); // here we just notify that a new style is started, we haven't // processed the whole block yet, so return false. retval = false; } else if (classId == "RGBC") { catcher.addColor(path, parseRGBColorObject(el)); } else if (classId == "ShpC") { CurveObjectCatcher curveCatcher; QDomNode child = el.firstChild(); while (!child.isNull()) { parseElement(child.toElement(), "", curveCatcher); child = child.nextSibling(); } catcher.addCurve(path, curveCatcher.m_name, curveCatcher.m_points); } else if (classId == "CrPt") { QPointF point; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Boolean" && key == "Cnty") { warnKrita << "WARNING: tryParseDescriptor: The points of the curve object contain \'Cnty\' flag which is unsupported by Krita"; warnKrita << " " << ppVar(type) << ppVar(key) << ppVar(path); child = child.nextSibling(); continue; } if (type != "Double") { warnKrita << "Unknown point component type:" << ppVar(type) << ppVar(key) << ppVar(path); return false; } double value = KisDomUtils::toDouble(childEl.attribute("value", "0")); if (key == "Hrzn") { point.setX(value); } else if (key == "Vrtc") { point.setY(value); } else { warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path); return false; } child = child.nextSibling(); } catcher.addPoint(path, point); } else if (classId == "Pnt ") { QPointF point; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString unit = childEl.attribute("unit", ""); if (type != "Double" && !(type == "UnitFloat" && unit == "#Prc")) { warnKrita << "Unknown point component type:" << ppVar(unit) << ppVar(type) << ppVar(key) << ppVar(path); return false; } double value = KisDomUtils::toDouble(childEl.attribute("value", "0")); if (key == "Hrzn") { point.setX(value); } else if (key == "Vrtc") { point.setY(value); } else { warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path); return false; } child = child.nextSibling(); } catcher.addPoint(path, point); } else if (classId == "KisPattern") { QByteArray patternData; QString patternUuid; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Text" && key == "Idnt") { patternUuid = childEl.attribute("value", ""); } if (type == "KisPatternData" && key == "Data") { QDomNode dataNode = child.firstChild(); if (!dataNode.isCDATASection()) { warnKrita << "WARNING: failed to parse KisPatternData XML section!"; continue; } QDomCDATASection dataSection = dataNode.toCDATASection(); QByteArray data = dataSection.data().toLatin1(); data = QByteArray::fromBase64(data); data = qUncompress(data); if (data.isEmpty()) { warnKrita << "WARNING: failed to parse KisPatternData XML section!"; continue; } patternData = data; } child = child.nextSibling(); } if (!patternUuid.isEmpty() && !patternData.isEmpty()) { QString fileName = QString("%1.pat").arg(patternUuid); QScopedPointer pattern(new KoPattern(fileName)); QBuffer buffer(&patternData); buffer.open(QIODevice::ReadOnly); pattern->loadPatFromDevice(&buffer); catcher.addPattern(path, pattern.data()); } else { warnKrita << "WARNING: failed to load KisPattern XML section!" << ppVar(patternUuid); } } else if (classId == "Ptrn") { // reference to an existing pattern QString patternUuid; QString patternName; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Text" && key == "Idnt") { patternUuid = childEl.attribute("value", ""); } else if (type == "Text" && key == "Nm ") { patternName = childEl.attribute("value", ""); } else { warnKrita << "WARNING: unrecognized pattern-ref section key:" << ppVar(type) << ppVar(key); } child = child.nextSibling(); } catcher.addPatternRef(path, patternUuid, patternName); } else if (classId == "Grdn") { QString gradientName; qreal gradientSmoothness = 100.0; QVector startLocations; QVector middleOffsets; QVector colors; + QVector types; QVector transpStartLocations; QVector transpMiddleOffsets; QVector transparencies; + QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Text" && key == "Nm ") { gradientName = childEl.attribute("value", ""); } else if (type == "Enum" && key == "GrdF") { QString typeId = childEl.attribute("typeId", ""); QString value = childEl.attribute("value", ""); if (typeId != "GrdF" || value != "CstS") { warnKrita << "WARNING: Unsupported gradient type (porbably, noise-based):" << value; return true; } } else if (type == "Double" && key == "Intr") { double value = KisDomUtils::toDouble(childEl.attribute("value", "4096")); gradientSmoothness = 100.0 * value / 4096.0; } else if (type == "List" && key == "Clrs") { - parseColorStopsList(childEl, startLocations, middleOffsets, colors); + parseColorStopsList(childEl, startLocations, middleOffsets, colors, types); } else if (type == "List" && key == "Trns") { parseTransparencyStopsList(childEl, transpStartLocations, transpMiddleOffsets, transparencies); } child = child.nextSibling(); } if (colors.size() < transparencies.size()) { const QColor lastColor = !colors.isEmpty() ? colors.last() : QColor(Qt::black); while (colors.size() != transparencies.size()) { const int index = colors.size(); colors.append(lastColor); startLocations.append(transpStartLocations[index]); middleOffsets.append(transpMiddleOffsets[index]); } } if (colors.size() > transparencies.size()) { const qreal lastTransparency = !transparencies.isEmpty() ? transparencies.last() : 1.0; while (colors.size() != transparencies.size()) { const int index = transparencies.size(); transparencies.append(lastTransparency); transpStartLocations.append(startLocations[index]); transpMiddleOffsets.append(middleOffsets[index]); } } if (colors.size() == 1) { colors.append(colors.last()); startLocations.append(1.0); middleOffsets.append(0.5); + types.append(COLOR_ENDPOINT); transparencies.append(transparencies.last()); transpStartLocations.append(1.0); transpMiddleOffsets.append(0.5); } QString fileName = gradientName + ".ggr"; QSharedPointer gradient(new KoSegmentGradient(fileName)); Q_UNUSED(gradientSmoothness); gradient->setName(gradientName); if (colors.size() >= 2) { for (int i = 1; i < colors.size(); i++) { QColor startColor = colors[i-1]; QColor endColor = colors[i]; startColor.setAlphaF(transparencies[i-1]); endColor.setAlphaF(transparencies[i]); qreal start = startLocations[i-1]; qreal end = startLocations[i]; qreal middle = start + middleOffsets[i-1] * (end - start); + KoGradientSegmentEndpointType startType = types[i - 1]; + KoGradientSegmentEndpointType endType = types[i]; + + gradient->createSegment(INTERP_LINEAR, COLOR_INTERP_RGB, start, end, middle, startColor, - endColor); + endColor, + startType, endType); } gradient->setValid(true); } else { gradient->setValid(false); } catcher.addGradient(path, gradient); } else { retval = false; } return retval; } void parseElement(const QDomElement &el, const QString &parentPath, KisAslObjectCatcher &catcher) { KIS_ASSERT_RECOVER_RETURN(el.tagName() == "node"); QString type = el.attribute("type", ""); QString key = el.attribute("key", ""); if (type == "Descriptor") { QString classId = el.attribute("classId", ""); QString containerName = key.isEmpty() ? classId : key; QString containerPath = buildPath(parentPath, containerName); if (!tryParseDescriptor(el, containerPath, classId, catcher)) { QDomNode child = el.firstChild(); while (!child.isNull()) { parseElement(child.toElement(), containerPath, catcher); child = child.nextSibling(); } } } else if (type == "List") { catcher.setArrayMode(true); QString containerName = key; QString containerPath = buildPath(parentPath, containerName); QDomNode child = el.firstChild(); while (!child.isNull()) { parseElement(child.toElement(), containerPath, catcher); child = child.nextSibling(); } catcher.setArrayMode(false); } else if (type == "Double") { double v = KisDomUtils::toDouble(el.attribute("value", "0")); catcher.addDouble(buildPath(parentPath, key), v); } else if (type == "UnitFloat") { QString unit = el.attribute("unit", ""); double v = KisDomUtils::toDouble(el.attribute("value", "0")); catcher.addUnitFloat(buildPath(parentPath, key), unit, v); } else if (type == "Text") { QString v = el.attribute("value", ""); catcher.addText(buildPath(parentPath, key), v); } else if (type == "Enum") { QString v = el.attribute("value", ""); QString typeId = el.attribute("typeId", ""); catcher.addEnum(buildPath(parentPath, key), typeId, v); } else if (type == "Integer") { int v = KisDomUtils::toInt(el.attribute("value", "0")); catcher.addInteger(buildPath(parentPath, key), v); } else if (type == "Boolean") { int v = KisDomUtils::toInt(el.attribute("value", "0")); catcher.addBoolean(buildPath(parentPath, key), v); } else { warnKrita << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(parentPath) << ppVar(key); } } } // namespace void KisAslXmlParser::parseXML(const QDomDocument &doc, KisAslObjectCatcher &catcher) { QDomElement root = doc.documentElement(); if (root.tagName() != "asl") { return; } QDomNode child = root.firstChild(); while (!child.isNull()) { Private::parseElement(child.toElement(), "", catcher); child = child.nextSibling(); } } diff --git a/libs/psd/asl/kis_asl_xml_writer.cpp b/libs/psd/asl/kis_asl_xml_writer.cpp index a902e76769..d54fa555a9 100644 --- a/libs/psd/asl/kis_asl_xml_writer.cpp +++ b/libs/psd/asl/kis_asl_xml_writer.cpp @@ -1,399 +1,438 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_xml_writer.h" #include #include #include #include #include #include #include #include #include #include "kis_dom_utils.h" #include "kis_asl_writer_utils.h" struct KisAslXmlWriter::Private { QDomDocument document; QDomElement currentElement; }; KisAslXmlWriter::KisAslXmlWriter() : m_d(new Private) { QDomElement el = m_d->document.createElement("asl"); m_d->document.appendChild(el); m_d->currentElement = el; } KisAslXmlWriter::~KisAslXmlWriter() { } QDomDocument KisAslXmlWriter::document() const { if (m_d->document.documentElement() != m_d->currentElement) { warnKrita << "KisAslXmlWriter::document(): unbalanced enter/leave descriptor/array"; } return m_d->document; } void KisAslXmlWriter::enterDescriptor(const QString &key, const QString &name, const QString &classId) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Descriptor"); el.setAttribute("name", name); el.setAttribute("classId", classId); m_d->currentElement.appendChild(el); m_d->currentElement = el; } void KisAslXmlWriter::leaveDescriptor() { if (!m_d->currentElement.parentNode().toElement().isNull()) { m_d->currentElement = m_d->currentElement.parentNode().toElement(); } else { warnKrita << "KisAslXmlWriter::leaveDescriptor(): unbalanced enter/leave descriptor"; } } void KisAslXmlWriter::enterList(const QString &key) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "List"); m_d->currentElement.appendChild(el); m_d->currentElement = el; } void KisAslXmlWriter::leaveList() { if (!m_d->currentElement.parentNode().toElement().isNull()) { m_d->currentElement = m_d->currentElement.parentNode().toElement(); } else { warnKrita << "KisAslXmlWriter::leaveList(): unbalanced enter/leave list"; } } void KisAslXmlWriter::writeDouble(const QString &key, double value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Double"); el.setAttribute("value", KisDomUtils::toString(value)); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeInteger(const QString &key, int value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Integer"); el.setAttribute("value", KisDomUtils::toString(value)); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeEnum(const QString &key, const QString &typeId, const QString &value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Enum"); el.setAttribute("typeId", typeId); el.setAttribute("value", value); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeUnitFloat(const QString &key, const QString &unit, double value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "UnitFloat"); el.setAttribute("unit", unit); el.setAttribute("value", KisDomUtils::toString(value)); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeText(const QString &key, const QString &value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Text"); el.setAttribute("value", value); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeBoolean(const QString &key, bool value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Boolean"); el.setAttribute("value", KisDomUtils::toString(value)); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeColor(const QString &key, const QColor &value) { enterDescriptor(key, "", "RGBC"); writeDouble("Rd ", value.red()); writeDouble("Grn ", value.green()); writeDouble("Bl ", value.blue()); leaveDescriptor(); } void KisAslXmlWriter::writePoint(const QString &key, const QPointF &value) { enterDescriptor(key, "", "CrPt"); writeDouble("Hrzn", value.x()); writeDouble("Vrtc", value.y()); leaveDescriptor(); } void KisAslXmlWriter::writePhasePoint(const QString &key, const QPointF &value) { enterDescriptor(key, "", "Pnt "); writeDouble("Hrzn", value.x()); writeDouble("Vrtc", value.y()); leaveDescriptor(); } void KisAslXmlWriter::writeOffsetPoint(const QString &key, const QPointF &value) { enterDescriptor(key, "", "Pnt "); writeUnitFloat("Hrzn", "#Prc", value.x()); writeUnitFloat("Vrtc", "#Prc", value.y()); leaveDescriptor(); } void KisAslXmlWriter::writeCurve(const QString &key, const QString &name, const QVector &points) { enterDescriptor(key, "", "ShpC"); writeText("Nm ", name); enterList("Crv "); Q_FOREACH (const QPointF &pt, points) { writePoint("", pt); } leaveList(); leaveDescriptor(); } QString KisAslXmlWriter::writePattern(const QString &key, const KoPattern *pattern) { enterDescriptor(key, "", "KisPattern"); writeText("Nm ", pattern->name()); QString uuid = KisAslWriterUtils::getPatternUuidLazy(pattern); writeText("Idnt", uuid); // Write pattern data QBuffer buffer; buffer.open(QIODevice::WriteOnly); pattern->savePatToDevice(&buffer); QDomCDATASection dataSection = m_d->document.createCDATASection(qCompress(buffer.buffer()).toBase64()); QDomElement dataElement = m_d->document.createElement("node"); dataElement.setAttribute("type", "KisPatternData"); dataElement.setAttribute("key", "Data"); dataElement.appendChild(dataSection); m_d->currentElement.appendChild(dataElement); leaveDescriptor(); return uuid; } void KisAslXmlWriter::writePatternRef(const QString &key, const KoPattern *pattern, const QString &uuid) { enterDescriptor(key, "", "Ptrn"); writeText("Nm ", pattern->name()); writeText("Idnt", uuid); leaveDescriptor(); } void KisAslXmlWriter::writeGradientImpl(const QString &key, const QString &name, QVector colors, QVector transparencies, QVector positions, + QVector types, QVector middleOffsets) { enterDescriptor(key, "Gradient", "Grdn"); writeText("Nm ", name); writeEnum("GrdF", "GrdF", "CstS"); writeDouble("Intr", 4096); enterList("Clrs"); for (int i = 0; i < colors.size(); i++) { enterDescriptor("", "", "Clrt"); writeColor("Clr ", colors[i]); - writeEnum("Type", "Clry", "UsrS"); // NOTE: we do not support BG/FG color tags + writeEnum("Type", "Clry", types[i]); writeInteger("Lctn", positions[i] * 4096.0); writeInteger("Mdpn", middleOffsets[i] * 100.0); leaveDescriptor(); }; leaveList(); enterList("Trns"); for (int i = 0; i < colors.size(); i++) { enterDescriptor("", "", "TrnS"); writeUnitFloat("Opct", "#Prc", transparencies[i] * 100.0); writeInteger("Lctn", positions[i] * 4096.0); writeInteger("Mdpn", middleOffsets[i] * 100.0); leaveDescriptor(); }; leaveList(); leaveDescriptor(); } +QString KisAslXmlWriter::getSegmentEndpointTypeString(KoGradientSegmentEndpointType segtype) { + switch (segtype) { + case COLOR_ENDPOINT: + return "UsrS"; + break; + case FOREGROUND_ENDPOINT: + case FOREGROUND_TRANSPARENT_ENDPOINT: + return "FrgC"; + break; + case BACKGROUND_ENDPOINT: + case BACKGROUND_TRANSPARENT_ENDPOINT: + return "BckC"; + break; + default: + return "UsrS"; + } +} + void KisAslXmlWriter::writeSegmentGradient(const QString &key, const KoSegmentGradient *gradient) { const QList&segments = gradient->segments(); KIS_SAFE_ASSERT_RECOVER_RETURN(!segments.isEmpty()); QVector colors; QVector transparencies; QVector positions; + QVector types; QVector middleOffsets; Q_FOREACH (const KoGradientSegment *seg, segments) { const qreal start = seg->startOffset(); const qreal end = seg->endOffset(); const qreal mid = (end - start) > DBL_EPSILON ? (seg->middleOffset() - start) / (end - start) : 0.5; QColor color = seg->startColor().toQColor(); qreal transparency = color.alphaF(); color.setAlphaF(1.0); + QString type = getSegmentEndpointTypeString(seg->startType()); + colors << color; transparencies << transparency; positions << start; + types << type; middleOffsets << mid; } // last segment if (!segments.isEmpty()) { const KoGradientSegment *lastSeg = segments.last(); QColor color = lastSeg->endColor().toQColor(); qreal transparency = color.alphaF(); color.setAlphaF(1.0); + QString type = getSegmentEndpointTypeString(lastSeg->endType()); colors << color; transparencies << transparency; positions << lastSeg->endOffset(); + types << type; middleOffsets << 0.5; } - writeGradientImpl(key, gradient->name(), colors, transparencies, positions, middleOffsets); + writeGradientImpl(key, gradient->name(), colors, transparencies, positions, types, middleOffsets); } void KisAslXmlWriter::writeStopGradient(const QString &key, const KoStopGradient *gradient) { QVector colors; QVector transparencies; QVector positions; + QVector types; QVector middleOffsets; Q_FOREACH (const KoGradientStop &stop, gradient->stops()) { - QColor color = stop.second.toQColor(); + QColor color = stop.color.toQColor(); qreal transparency = color.alphaF(); color.setAlphaF(1.0); + QString type; + switch (stop.type) { + case COLORSTOP: + type = "UsrS"; + break; + case FOREGROUNDSTOP: + type = "FrgC"; + break; + case BACKGROUNDSTOP: + type = "BckC"; + break; + } + colors << color; transparencies << transparency; - positions << stop.first; + positions << stop.position; middleOffsets << 0.5; } - writeGradientImpl(key, gradient->name(), colors, transparencies, positions, middleOffsets); + writeGradientImpl(key, gradient->name(), colors, transparencies, positions, types, middleOffsets); } diff --git a/libs/psd/asl/kis_asl_xml_writer.h b/libs/psd/asl/kis_asl_xml_writer.h index 46b8746d06..82f092b79d 100644 --- a/libs/psd/asl/kis_asl_xml_writer.h +++ b/libs/psd/asl/kis_asl_xml_writer.h @@ -1,79 +1,83 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_ASL_XML_WRITER_H #define __KIS_ASL_XML_WRITER_H #include #include +#include + #include "kritapsd_export.h" class QString; class QColor; class QPointF; class QDomDocument; class KoPattern; class KoStopGradient; class KoSegmentGradient; class KRITAPSD_EXPORT KisAslXmlWriter { public: KisAslXmlWriter(); ~KisAslXmlWriter(); QDomDocument document() const; void enterDescriptor(const QString &key, const QString &name, const QString &classId); void leaveDescriptor(); void enterList(const QString &key); void leaveList(); void writeDouble(const QString &key, double value); void writeInteger(const QString &key, int value); void writeEnum(const QString &key, const QString &typeId, const QString &value); void writeUnitFloat(const QString &key, const QString &unit, double value); void writeText(const QString &key, const QString &value); void writeBoolean(const QString &key, bool value); void writeColor(const QString &key, const QColor &value); void writePoint(const QString &key, const QPointF &value); void writePhasePoint(const QString &key, const QPointF &value); void writeOffsetPoint(const QString &key, const QPointF &value); void writeCurve(const QString &key, const QString &name, const QVector &points); QString writePattern(const QString &key, const KoPattern *pattern); void writePatternRef(const QString &key, const KoPattern *pattern, const QString &uuid); void writeSegmentGradient(const QString &key, const KoSegmentGradient *gradient); void writeStopGradient(const QString &key, const KoStopGradient *gradient); private: + QString getSegmentEndpointTypeString(KoGradientSegmentEndpointType segtype); void writeGradientImpl(const QString &key, const QString &name, QVector colors, QVector transparencies, QVector positions, + QVector types, QVector middleOffsets); private: struct Private; const QScopedPointer m_d; }; #endif /* __KIS_ASL_XML_WRITER_H */ diff --git a/libs/ui/forms/wdgautogradient.ui b/libs/ui/forms/wdgautogradient.ui index f8478b930a..cf600f7cd7 100644 --- a/libs/ui/forms/wdgautogradient.ui +++ b/libs/ui/forms/wdgautogradient.ui @@ -1,375 +1,473 @@ KisWdgAutogradient 0 0 500 250 500 250 Name: 0 1 Qt::ClickFocus - - + + Sans Serif 9 50 false false false false - Segment Color + Left: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + + 0 + 0 + + + + + 0 + 30 + + Sans Serif 9 50 false false false false + + Qt::ClickFocus + + + + + - Opacity: + Color + + true + + + leftBtnGroup + - - + + Sans Serif 9 50 false false false false - Left: + Right: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + Color + + + true + + + rightBtnGroup + + + + + + + Foreground + + + rightBtnGroup + + + + + 0 0 0 30 Sans Serif 9 50 false false false false Qt::ClickFocus - - - - - 0 - 0 - - + + Sans Serif 9 50 false false false false - - Qt::ClickFocus - - - 100 + + Opacity: - - 100 + + + + + + Background + + leftBtnGroup + - - + + + + + 0 + 0 + + Sans Serif 9 50 false false false false - - Right: + + Qt::ClickFocus - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + 100 + + + 100 - - - - 0 - 0 - - - - - 0 - 30 - + + + Foreground + + leftBtnGroup + + + + + Sans Serif 9 50 false false false false - - Qt::ClickFocus + + Segment Color - - + + + + Background + + + rightBtnGroup + + + + + - + 0 0 Sans Serif 9 50 false false false false Qt::ClickFocus 100 100 + + + + Transparent + + + + + + + Transparent + + + + + + + Transparent + + + + + + + Transparent + + + 0 0 0 0 Qt::Horizontal 40 20 Sans Serif 9 50 false false false false Qt::ClickFocus Linear Curved Sine Sphere Inc. Sphere Dec. Sans Serif 9 50 false false false false Qt::ClickFocus RGB HSV CW HSV CCW - - KisIntParseSpinBox - QSpinBox -
kis_int_parse_spin_box.h
-
KisColorButton QPushButton
kis_color_button.h
+ + KisIntParseSpinBox + QSpinBox +
kis_int_parse_spin_box.h
+
KisGradientSliderWidget
KisGradientSliderWidget.h
+ + + +
diff --git a/libs/ui/forms/wdgstopgradienteditor.ui b/libs/ui/forms/wdgstopgradienteditor.ui index 32e61154b8..1b61db4d59 100644 --- a/libs/ui/forms/wdgstopgradienteditor.ui +++ b/libs/ui/forms/wdgstopgradienteditor.ui @@ -1,150 +1,171 @@ KisWdgStopGradientEditor 0 0 368 - 167 + 231 Name: 0 1 Qt::ClickFocus Qt::NoFocus true Qt::Vertical 0 0 - - - + + + - Stop: + Foreground - + 0 0 Sans Serif 9 50 false false false false Qt::ClickFocus - + 0 0 + + + + Color + + + + + + + Stop: + + + + + + + Background + + + KisDoubleSliderSpinBox QWidget
kis_slider_spin_box.h
1
KisColorButton QPushButton
kis_color_button.h
KisStopGradientSliderWidget QWidget
kis_stopgradient_slider_widget.h
1
diff --git a/libs/ui/kis_autogradient.cc b/libs/ui/kis_autogradient.cc index 5d33501c5b..957833d593 100644 --- a/libs/ui/kis_autogradient.cc +++ b/libs/ui/kis_autogradient.cc @@ -1,172 +1,358 @@ /* * Copyright (c) 2004 Cyrille Berger * 2004 Sven Langkamp * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_autogradient.h" #include #include #include #include #include #include #include "kis_debug.h" #include "KisGradientSliderWidget.h" /****************************** KisAutogradient ******************************/ -KisAutogradientEditor::KisAutogradientEditor(KoSegmentGradient* gradient, QWidget *parent, const char* name, const QString& caption) +KisAutogradientEditor::KisAutogradientEditor(KoSegmentGradient* gradient, QWidget *parent, const char* name, const QString& caption, KoColor fgColor, KoColor bgColor) : QWidget(parent) - , m_autogradientResource(gradient) + , m_autogradientResource(gradient), + m_fgColor(fgColor), + m_bgColor(bgColor) { setObjectName(name); setupUi(this); setWindowTitle(caption); gradientSlider->setGradientResource(m_autogradientResource); nameedit->setText(gradient->name()); KoGradientSegment* segment = gradientSlider->selectedSegment(); if (segment) { slotSelectedSegment(segment); } connect(nameedit, SIGNAL(editingFinished()), this, SLOT(slotChangedName())); connect(gradientSlider, SIGNAL(sigSelectedSegment(KoGradientSegment*)), SLOT(slotSelectedSegment(KoGradientSegment*))); connect(gradientSlider, SIGNAL(sigChangedSegment(KoGradientSegment*)), SLOT(slotChangedSegment(KoGradientSegment*))); connect(comboBoxColorInterpolationType, SIGNAL(activated(int)), SLOT(slotChangedColorInterpolation(int))); connect(comboBoxInterpolationType, SIGNAL(activated(int)), SLOT(slotChangedInterpolation(int))); connect(leftColorButton, SIGNAL(changed(KoColor)), SLOT(slotChangedLeftColor(KoColor))); connect(rightColorButton, SIGNAL(changed(KoColor)), SLOT(slotChangedRightColor(KoColor))); connect(intNumInputLeftOpacity, SIGNAL(valueChanged(int)), SLOT(slotChangedLeftOpacity(int))); connect(intNumInputRightOpacity, SIGNAL(valueChanged(int)), SLOT(slotChangedRightOpacity(int))); + connect(leftBtnGroup, SIGNAL(buttonToggled(QAbstractButton*, bool)), this, SLOT(slotChangedLeftType(QAbstractButton*, bool))); + connect(rightBtnGroup, SIGNAL(buttonToggled(QAbstractButton*, bool)), this, SLOT(slotChangedRightType(QAbstractButton*, bool))); + + connect(leftForegroundTransparent, SIGNAL(toggled(bool)), this, SLOT(slotChangedLeftTypeTransparent(bool))); + connect(leftBackgroundTransparent, SIGNAL(toggled(bool)), this, SLOT(slotChangedLeftTypeTransparent(bool))); + connect(rightForegroundTransparent, SIGNAL(toggled(bool)), this, SLOT(slotChangedRightTypeTransparent(bool))); + connect(rightBackgroundTransparent, SIGNAL(toggled(bool)), this, SLOT(slotChangedRightTypeTransparent(bool))); } void KisAutogradientEditor::activate() { paramChanged(); } +void KisAutogradientEditor::disableTransparentCheckboxes() { + leftForegroundTransparent->setEnabled(false); + leftBackgroundTransparent->setEnabled(false); + rightForegroundTransparent->setEnabled(false); + rightBackgroundTransparent->setEnabled(false); +} + void KisAutogradientEditor::slotSelectedSegment(KoGradientSegment* segment) { leftColorButton->setColor(segment->startColor()); rightColorButton->setColor(segment->endColor()); comboBoxColorInterpolationType->setCurrentIndex(segment->colorInterpolation()); comboBoxInterpolationType->setCurrentIndex(segment->interpolation()); int leftOpacity = segment->startColor().opacityF(); intNumInputLeftOpacity->setValue(leftOpacity * 100); intNumInputLeftOpacity->setSuffix(i18n(" %")); int rightOpacity = segment->endColor().opacityF(); intNumInputRightOpacity->setValue(rightOpacity * 100); intNumInputRightOpacity->setSuffix(i18n(" %")); + KoGradientSegmentEndpointType leftType = segment->startType(); + KoGradientSegmentEndpointType rightType = segment->endType(); + disableTransparentCheckboxes(); //disable all of them, then enable the correct ones + switch (leftType) { + case COLOR_ENDPOINT: + leftColorRadioButton->setChecked(true); break; + case FOREGROUND_TRANSPARENT_ENDPOINT: + leftForegroundTransparent->setChecked(true); + case FOREGROUND_ENDPOINT: + leftForegroundTransparent->setEnabled(true); + leftForegroundRadioButton->setChecked(true); break; + case BACKGROUND_TRANSPARENT_ENDPOINT: + leftBackgroundTransparent->setChecked(true); + case BACKGROUND_ENDPOINT: + leftBackgroundTransparent->setEnabled(true); + leftBackgroundRadioButton->setChecked(true); break; + } + switch (rightType) { + case COLOR_ENDPOINT: + rightColorRadioButton->setChecked(true); break; + case FOREGROUND_TRANSPARENT_ENDPOINT: + rightForegroundTransparent->setChecked(true); + case FOREGROUND_ENDPOINT: + rightForegroundTransparent->setEnabled(true); + rightForegroundRadioButton->setChecked(true); break; + case BACKGROUND_TRANSPARENT_ENDPOINT: + rightBackgroundTransparent->setChecked(true); + case BACKGROUND_ENDPOINT: + rightBackgroundTransparent->setEnabled(true); + rightBackgroundRadioButton->setChecked(true); break; + } + + paramChanged(); } void KisAutogradientEditor::slotChangedSegment(KoGradientSegment*) { paramChanged(); } void KisAutogradientEditor::slotChangedInterpolation(int type) { KoGradientSegment* segment = gradientSlider->selectedSegment(); if (segment) segment->setInterpolation(type); gradientSlider->update(); paramChanged(); } void KisAutogradientEditor::slotChangedColorInterpolation(int type) { KoGradientSegment* segment = gradientSlider->selectedSegment(); if (segment) segment->setColorInterpolation(type); gradientSlider->update(); paramChanged(); } void KisAutogradientEditor::slotChangedLeftColor(const KoColor& color) { KoGradientSegment* segment = gradientSlider->selectedSegment(); if (segment) { KoColor c(color, segment->startColor().colorSpace()); c.setOpacity(segment->startColor().opacityU8()); segment->setStartColor(c); } gradientSlider->update(); paramChanged(); } void KisAutogradientEditor::slotChangedRightColor(const KoColor& color) { KoGradientSegment* segment = gradientSlider->selectedSegment(); if (segment) { KoColor c(color, segment->endColor().colorSpace()); c.setOpacity(segment->endColor().opacityU8()); segment->setEndColor(c); } gradientSlider->repaint(); paramChanged(); } void KisAutogradientEditor::slotChangedLeftOpacity(int value) { KoGradientSegment* segment = gradientSlider->selectedSegment(); if (segment) { KoColor c(segment->startColor(), segment->startColor().colorSpace()); c.setOpacity(qreal(value) / qreal(100.0)); segment->setStartColor(c); } gradientSlider->repaint(); paramChanged(); } void KisAutogradientEditor::slotChangedRightOpacity(int value) { KoGradientSegment* segment = gradientSlider->selectedSegment(); if (segment) { KoColor c(segment->endColor(), segment->endColor().colorSpace()); c.setOpacity(quint8((value *OPACITY_OPAQUE_U8) / 100)); segment->setEndColor(c); } gradientSlider->repaint(); paramChanged(); } +void KisAutogradientEditor::slotChangedLeftType(QAbstractButton* button, bool checked) +{ + if (!checked) { //Radio buttons, so we only care about the one that was checked, not the one unchecked + return; + } + KoGradientSegmentEndpointType type; + KoColor color; + const KoColorSpace* colorSpace = m_autogradientResource->colorSpace(); + if (button == leftForegroundRadioButton) { + color = KoColor(m_fgColor, colorSpace); + leftForegroundTransparent->setEnabled(true); + leftBackgroundTransparent->setEnabled(false); + if (leftForegroundTransparent->isChecked()) { + type = FOREGROUND_TRANSPARENT_ENDPOINT; + } else { + type = FOREGROUND_ENDPOINT; + } + } else if (button == leftBackgroundRadioButton) { + color = KoColor(m_bgColor, colorSpace); + leftBackgroundTransparent->setEnabled(true); + leftForegroundTransparent->setEnabled(false); + if (leftBackgroundTransparent->isChecked()) { + type = BACKGROUND_TRANSPARENT_ENDPOINT; + } else { + type = BACKGROUND_ENDPOINT; + } + } + else { + type = COLOR_ENDPOINT; + leftForegroundTransparent->setEnabled(false); + leftBackgroundTransparent->setEnabled(false); + color = KoColor(leftColorButton->color(), colorSpace); + } + KoGradientSegment* segment = gradientSlider->selectedSegment(); + if (segment) { + segment->setStartType(type); + } + slotChangedLeftColor(color); + +} + +void KisAutogradientEditor::slotChangedRightType(QAbstractButton* button, bool checked) +{ + if (!checked) { //Radio buttons, so we only care about the one that was checked, not the one unchecked + return; + } + KoGradientSegmentEndpointType type; + KoColor color; + const KoColorSpace* colorSpace = m_autogradientResource->colorSpace(); + if (button == rightForegroundRadioButton) { + color = KoColor(m_fgColor, colorSpace); + rightForegroundTransparent->setEnabled(true); + rightBackgroundTransparent->setEnabled(false); + if (rightForegroundTransparent->isChecked()) { + type = FOREGROUND_TRANSPARENT_ENDPOINT; + } else { + type = FOREGROUND_ENDPOINT; + } + } else if (button == rightBackgroundRadioButton) { + color = KoColor(m_bgColor, colorSpace); + rightBackgroundTransparent->setEnabled(true); + rightForegroundTransparent->setEnabled(false); + if (rightBackgroundTransparent->isChecked()) { + type = BACKGROUND_TRANSPARENT_ENDPOINT; + } else { + type = BACKGROUND_ENDPOINT; + } + } + else { + type = COLOR_ENDPOINT; + rightForegroundTransparent->setEnabled(false); + rightBackgroundTransparent->setEnabled(false); + color = KoColor(rightColorButton->color(), colorSpace); + } + KoGradientSegment* segment = gradientSlider->selectedSegment(); + if (segment) { + segment->setEndType(type); + } + slotChangedRightColor(color); +} + +void KisAutogradientEditor::slotChangedLeftTypeTransparent(bool checked) +{ + if (leftColorRadioButton->isChecked()) { //shouldn't be able to check/uncheck in this state, but just in case + return; + } + + KoGradientSegmentEndpointType type; + if (leftForegroundRadioButton->isChecked()) { + if (checked) { + type = FOREGROUND_TRANSPARENT_ENDPOINT; + } else { + type = FOREGROUND_ENDPOINT; + } + } else { + if (checked) { + type = FOREGROUND_TRANSPARENT_ENDPOINT; + } else { + type = FOREGROUND_ENDPOINT; + } + } + + KoGradientSegment* segment = gradientSlider->selectedSegment(); + if (segment) { + segment->setStartType(type); + slotChangedLeftColor(segment->startColor()); + } +} + +void KisAutogradientEditor::slotChangedRightTypeTransparent(bool checked) +{ + if (rightColorRadioButton->isChecked()) { //shouldn't be able to check/uncheck in this state, but just in case + return; + } + + KoGradientSegmentEndpointType type; + if (rightForegroundRadioButton->isChecked()) { + if (checked) { + type = FOREGROUND_TRANSPARENT_ENDPOINT; + } else { + type = FOREGROUND_ENDPOINT; + } + } else { + if (checked) { + type = FOREGROUND_TRANSPARENT_ENDPOINT; + } else { + type = FOREGROUND_ENDPOINT; + } + } + + KoGradientSegment* segment = gradientSlider->selectedSegment(); + if (segment) { + segment->setEndType(type); + slotChangedRightColor(segment->endColor()); + } +} + void KisAutogradientEditor::slotChangedName() { m_autogradientResource->setName(nameedit->text()); } void KisAutogradientEditor::paramChanged() { m_autogradientResource->updatePreview(); } diff --git a/libs/ui/kis_autogradient.h b/libs/ui/kis_autogradient.h index 7ba6b680a4..eb1f3ea8ba 100644 --- a/libs/ui/kis_autogradient.h +++ b/libs/ui/kis_autogradient.h @@ -1,50 +1,60 @@ /* * Copyright (c) 2004 Cyrille Berger * 2004 Sven Langkamp * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_AUTOGRADIENT_H_ #define _KIS_AUTOGRADIENT_H_ #include "ui_wdgautogradient.h" class KoGradientSegment; class KoSegmentGradient; class KisAutogradientEditor : public QWidget, public Ui::KisWdgAutogradient { Q_OBJECT public: - KisAutogradientEditor(KoSegmentGradient* gradient, QWidget *parent, const char* name, const QString& caption); + KisAutogradientEditor(KoSegmentGradient* gradient, QWidget *parent, const char* name, const QString& caption, KoColor fgColor, KoColor bgColor); void activate(); + +private: + void disableTransparentCheckboxes(); + private: KoSegmentGradient* m_autogradientResource; + KoColor m_fgColor, m_bgColor; private Q_SLOTS: void slotSelectedSegment(KoGradientSegment* segment); void slotChangedSegment(KoGradientSegment* segment); void slotChangedInterpolation(int type); void slotChangedColorInterpolation(int type); void slotChangedLeftColor(const KoColor& color); void slotChangedRightColor(const KoColor& color); void slotChangedLeftOpacity(int value); void slotChangedRightOpacity(int value); + void slotChangedLeftType(QAbstractButton* button, bool checked); + void slotChangedRightType(QAbstractButton* button, bool checked); + void slotChangedLeftTypeTransparent(bool checked); + void slotChangedRightTypeTransparent(bool checked); + void slotChangedName(); void paramChanged(); }; #endif diff --git a/libs/ui/kis_canvas_resource_provider.cpp b/libs/ui/kis_canvas_resource_provider.cpp index df5f283528..c0896aafd1 100644 --- a/libs/ui/kis_canvas_resource_provider.cpp +++ b/libs/ui/kis_canvas_resource_provider.cpp @@ -1,567 +1,559 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_canvas_resource_provider.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_favorite_resource_manager.h" #include "kis_config.h" #include "KisViewManager.h" #include "canvas/kis_canvas2.h" KisCanvasResourceProvider::KisCanvasResourceProvider(KisViewManager * view) : m_view(view) { m_fGChanged = true; } KisCanvasResourceProvider::~KisCanvasResourceProvider() { disconnect(); // in case Qt gets confused } KoCanvasResourceProvider* KisCanvasResourceProvider::resourceManager() { return m_resourceManager; } void KisCanvasResourceProvider::setResourceManager(KoCanvasResourceProvider *resourceManager) { m_resourceManager = resourceManager; QVariant v; v.setValue(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8())); m_resourceManager->setResource(KoCanvasResourceProvider::ForegroundColor, v); v.setValue(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8())); m_resourceManager->setResource(KoCanvasResourceProvider::BackgroundColor, v); setCurrentCompositeOp(COMPOSITE_OVER); setMirrorHorizontal(false); setMirrorVertical(false); m_resourceManager->setResource(HdrExposure, 0.0); m_resourceManager->setResource(HdrGamma, 1.0); m_resourceManager->setResource(EffectiveZoom, 1.0); connect(m_resourceManager, SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(slotCanvasResourceChanged(int,QVariant))); m_resourceManager->setResource(KoCanvasResourceProvider::ApplicationSpeciality, KoCanvasResourceProvider::NoAdvancedText); m_resourceManager->setResource(GamutMaskActive, false); } KoCanvasBase * KisCanvasResourceProvider::canvas() const { return m_view->canvasBase(); } KoColor KisCanvasResourceProvider::bgColor() const { return m_resourceManager->resource(KoCanvasResourceProvider::BackgroundColor).value(); } KoColor KisCanvasResourceProvider::fgColor() const { return m_resourceManager->resource(KoCanvasResourceProvider::ForegroundColor).value(); } float KisCanvasResourceProvider::HDRExposure() const { return static_cast(m_resourceManager->resource(HdrExposure).toDouble()); } void KisCanvasResourceProvider::setHDRExposure(float exposure) { m_resourceManager->setResource(HdrExposure, static_cast(exposure)); } float KisCanvasResourceProvider::HDRGamma() const { return static_cast(m_resourceManager->resource(HdrGamma).toDouble()); } void KisCanvasResourceProvider::setHDRGamma(float gamma) { m_resourceManager->setResource(HdrGamma, static_cast(gamma)); } KoPattern * KisCanvasResourceProvider::currentPattern() const { if (m_resourceManager->hasResource(CurrentPattern)) { return m_resourceManager->resource(CurrentPattern).value(); } else { return 0; } } KoAbstractGradient* KisCanvasResourceProvider::currentGradient() const { if (m_resourceManager->hasResource(CurrentGradient)) { return m_resourceManager->resource(CurrentGradient).value(); } else { return 0; } } KisImageWSP KisCanvasResourceProvider::currentImage() const { return m_view->image(); } KisNodeSP KisCanvasResourceProvider::currentNode() const { return m_view->activeNode(); } KoGamutMask *KisCanvasResourceProvider::currentGamutMask() const { if (m_resourceManager->hasResource(CurrentGamutMask)) { return m_resourceManager->resource(CurrentGamutMask).value(); } else { return nullptr; } } bool KisCanvasResourceProvider::gamutMaskActive() const { return m_resourceManager->resource(GamutMaskActive).toBool(); } KisPaintOpPresetSP KisCanvasResourceProvider::currentPreset() const { KisPaintOpPresetSP preset = m_resourceManager->resource(CurrentPaintOpPreset).value(); return preset; } void KisCanvasResourceProvider::setPaintOpPreset(const KisPaintOpPresetSP preset) { Q_ASSERT(preset->valid()); Q_ASSERT(!preset->paintOp().id().isEmpty()); Q_ASSERT(preset->settings()); if (!preset) return; dbgUI << "setPaintOpPreset" << preset->paintOp(); QVariant v; v.setValue(preset); m_resourceManager->setResource(CurrentPaintOpPreset, v); } KisPaintOpPresetSP KisCanvasResourceProvider::previousPreset() const { KisPaintOpPresetSP preset = m_resourceManager->resource(PreviousPaintOpPreset).value(); return preset; } void KisCanvasResourceProvider::setPreviousPaintOpPreset(const KisPaintOpPresetSP preset) { Q_ASSERT(preset->valid()); Q_ASSERT(!preset->paintOp().id().isEmpty()); Q_ASSERT(preset->settings()); if (!preset) return; dbgUI << "setPreviousPaintOpPreset" << preset->paintOp(); QVariant v; v.setValue(preset); m_resourceManager->setResource(PreviousPaintOpPreset, v); } void KisCanvasResourceProvider::slotPatternActivated(KoResource * res) { KoPattern *pattern = dynamic_cast(res); QVariant v; v.setValue(pattern); m_resourceManager->setResource(CurrentPattern, v); emit sigPatternChanged(pattern); } void KisCanvasResourceProvider::slotGradientActivated(KoResource *res) { KoAbstractGradient * gradient = dynamic_cast(res); QVariant v; v.setValue(gradient); m_resourceManager->setResource(CurrentGradient, v); emit sigGradientChanged(gradient); } void KisCanvasResourceProvider::setBGColor(const KoColor& c) { QVariant v; v.setValue(c); m_resourceManager->setResource(KoCanvasResourceProvider::BackgroundColor, v); emit sigBGColorChanged(c); } void KisCanvasResourceProvider::setFGColor(const KoColor& c) { m_fGChanged = true; QVariant v; v.setValue(c); m_resourceManager->setResource(KoCanvasResourceProvider::ForegroundColor, v); emit sigFGColorChanged(c); } void KisCanvasResourceProvider::slotSetFGColor(const KoColor& c) { setFGColor(c); } void KisCanvasResourceProvider::slotSetBGColor(const KoColor& c) { setBGColor(c); } void KisCanvasResourceProvider::slotNodeActivated(const KisNodeSP node) { QVariant v; v.setValue(KisNodeWSP(node)); m_resourceManager->setResource(CurrentKritaNode, v); emit sigNodeChanged(currentNode()); } void KisCanvasResourceProvider::slotImageSizeChanged() { if (KisImageWSP image = m_view->image()) { float fw = image->width() / image->xRes(); float fh = image->height() / image->yRes(); QSizeF postscriptSize(fw, fh); m_resourceManager->setResource(KoCanvasResourceProvider::PageSize, postscriptSize); } } void KisCanvasResourceProvider::slotOnScreenResolutionChanged() { KisImageWSP image = m_view->image(); KisCanvas2 *canvas = m_view->canvasBase(); if(!image || !canvas) return; qreal zoomX, zoomY; canvas->coordinatesConverter()->zoom(&zoomX, &zoomY); qreal scaleX = zoomX / image->xRes(); qreal scaleY = zoomY / image->yRes(); emit sigOnScreenResolutionChanged(scaleX, scaleY); } void KisCanvasResourceProvider::slotCanvasResourceChanged(int key, const QVariant & res) { if(key == KoCanvasResourceProvider::ForegroundColor || key == KoCanvasResourceProvider::BackgroundColor) { - KoAbstractGradient* resource = KoResourceServerProvider::instance()->gradientServer()->resources()[0]; - KoStopGradient* stopGradient = dynamic_cast(resource); - if(stopGradient) { - QList stops; - stops << KoGradientStop(0.0, fgColor()) << KoGradientStop(1.0, KoColor(QColor(0, 0, 0, 0), fgColor().colorSpace())); - stopGradient->setStops(stops); - KoResourceServerProvider::instance()->gradientServer()->updateResource(resource); - } - resource = KoResourceServerProvider::instance()->gradientServer()->resources()[1]; - stopGradient = dynamic_cast(resource); - if(stopGradient) { - QList stops; - stops << KoGradientStop(0.0, fgColor()) << KoGradientStop(1.0, bgColor()); - stopGradient->setStops(stops); - KoResourceServerProvider::instance()->gradientServer()->updateResource(resource); + QList resources = KoResourceServerProvider::instance()->gradientServer()->resources(); + for (int i = 0; i < resources.count(); i++) { + KoAbstractGradient* gradient = resources[i]; + if(gradient->hasVariableColors()){ + gradient->setVariableColors(fgColor(), bgColor()); + KoResourceServerProvider::instance()->gradientServer()->updateResource(gradient); + } } } switch (key) { case(KoCanvasResourceProvider::ForegroundColor): m_fGChanged = true; emit sigFGColorChanged(res.value()); break; case(KoCanvasResourceProvider::BackgroundColor): emit sigBGColorChanged(res.value()); break; case(CurrentPattern): emit sigPatternChanged(static_cast(res.value())); break; case(CurrentGradient): emit sigGradientChanged(static_cast(res.value())); break; case(CurrentKritaNode) : emit sigNodeChanged(currentNode()); break; case (Opacity): { emit sigOpacityChanged(res.toDouble()); } default: ; // Do nothing }; } void KisCanvasResourceProvider::setCurrentCompositeOp(const QString& compositeOp) { m_resourceManager->setResource(CurrentCompositeOp, QVariant::fromValue(compositeOp)); } QString KisCanvasResourceProvider::currentCompositeOp() const { return m_resourceManager->resource(CurrentCompositeOp).value(); } bool KisCanvasResourceProvider::eraserMode() const { return m_resourceManager->resource(EraserMode).toBool(); } void KisCanvasResourceProvider::setEraserMode(bool value) { m_resourceManager->setResource(EraserMode, QVariant::fromValue(value)); } void KisCanvasResourceProvider::slotPainting() { if (m_fGChanged) { emit sigFGColorUsed(fgColor()); m_fGChanged = false; } } void KisCanvasResourceProvider::slotGamutMaskActivated(KoGamutMask *mask) { QVariant v; v.setValue(mask); m_resourceManager->setResource(CurrentGamutMask, v); m_resourceManager->setResource(GamutMaskActive, QVariant::fromValue(true)); emit sigGamutMaskChanged(mask); } void KisCanvasResourceProvider::slotGamutMaskUnset() { m_resourceManager->setResource(GamutMaskActive, QVariant::fromValue(false)); m_resourceManager->clearResource(CurrentGamutMask); emit sigGamutMaskUnset(); } void KisCanvasResourceProvider::slotGamutMaskPreviewUpdate() { emit sigGamutMaskPreviewUpdate(); } void KisCanvasResourceProvider::slotGamutMaskDeactivate() { m_resourceManager->setResource(GamutMaskActive, QVariant::fromValue(false)); emit sigGamutMaskDeactivated(); } QList > KisCanvasResourceProvider::perspectiveGrids() const { return m_perspectiveGrids; } void KisCanvasResourceProvider::addPerspectiveGrid(KisAbstractPerspectiveGrid* grid) { m_perspectiveGrids.append(grid); } void KisCanvasResourceProvider::removePerspectiveGrid(KisAbstractPerspectiveGrid* grid) { m_perspectiveGrids.removeOne(grid); } void KisCanvasResourceProvider::clearPerspectiveGrids() { m_perspectiveGrids.clear(); } void KisCanvasResourceProvider::setMirrorHorizontal(bool mirrorHorizontal) { m_resourceManager->setResource(MirrorHorizontal, mirrorHorizontal); emit mirrorModeChanged(); } bool KisCanvasResourceProvider::mirrorHorizontal() const { return m_resourceManager->resource(MirrorHorizontal).toBool(); } void KisCanvasResourceProvider::setMirrorVertical(bool mirrorVertical) { m_resourceManager->setResource(MirrorVertical, mirrorVertical); emit mirrorModeChanged(); } bool KisCanvasResourceProvider::mirrorVertical() const { return m_resourceManager->resource(MirrorVertical).toBool(); } void KisCanvasResourceProvider::setMirrorHorizontalLock(bool isLocked) { m_resourceManager->setResource(MirrorHorizontalLock, isLocked); emit mirrorModeChanged(); } bool KisCanvasResourceProvider::mirrorHorizontalLock() { return m_resourceManager->resource(MirrorHorizontalLock).toBool(); } void KisCanvasResourceProvider::setMirrorVerticalLock(bool isLocked) { m_resourceManager->setResource(MirrorVerticalLock, isLocked); emit mirrorModeChanged(); } bool KisCanvasResourceProvider::mirrorVerticalHideDecorations() { return m_resourceManager->resource(MirrorVerticalHideDecorations).toBool(); } void KisCanvasResourceProvider::setMirrorVerticalHideDecorations(bool hide) { m_resourceManager->setResource(MirrorVerticalHideDecorations, hide); emit mirrorModeChanged(); } bool KisCanvasResourceProvider::mirrorHorizontalHideDecorations() { return m_resourceManager->resource(MirrorHorizontalHideDecorations).toBool(); } void KisCanvasResourceProvider::setMirrorHorizontalHideDecorations(bool hide) { m_resourceManager->setResource(MirrorHorizontalHideDecorations, hide); emit mirrorModeChanged(); } bool KisCanvasResourceProvider::mirrorVerticalLock() { return m_resourceManager->resource(MirrorVerticalLock).toBool(); } void KisCanvasResourceProvider::mirrorVerticalMoveCanvasToCenter() { emit moveMirrorVerticalCenter(); } void KisCanvasResourceProvider::mirrorHorizontalMoveCanvasToCenter() { emit moveMirrorHorizontalCenter(); } void KisCanvasResourceProvider::setOpacity(qreal opacity) { m_resourceManager->setResource(Opacity, opacity); } qreal KisCanvasResourceProvider::opacity() const { return m_resourceManager->resource(Opacity).toReal(); } void KisCanvasResourceProvider::setFlow(qreal flow) { m_resourceManager->setResource(Flow, flow); } qreal KisCanvasResourceProvider::flow() const { return m_resourceManager->resource(Flow).toReal(); } void KisCanvasResourceProvider::setSize(qreal size) { m_resourceManager->setResource(Size, size); } qreal KisCanvasResourceProvider::size() const { return m_resourceManager->resource(Size).toReal(); } void KisCanvasResourceProvider::setPatternSize(qreal size) { m_resourceManager->setResource(PatternSize, size); } qreal KisCanvasResourceProvider::patternSize() const { return m_resourceManager->resource(PatternSize).toReal(); } void KisCanvasResourceProvider::setGlobalAlphaLock(bool lock) { m_resourceManager->setResource(GlobalAlphaLock, lock); } bool KisCanvasResourceProvider::globalAlphaLock() const { return m_resourceManager->resource(GlobalAlphaLock).toBool(); } void KisCanvasResourceProvider::setDisablePressure(bool value) { m_resourceManager->setResource(DisablePressure, value); } bool KisCanvasResourceProvider::disablePressure() const { return m_resourceManager->resource(DisablePressure).toBool(); } void KisCanvasResourceProvider::notifyLoadingWorkspace(KisWorkspaceResource* workspace) { emit sigLoadingWorkspace(workspace); } void KisCanvasResourceProvider::notifySavingWorkspace(KisWorkspaceResource* workspace) { emit sigSavingWorkspace(workspace); } diff --git a/libs/ui/kis_control_frame.cpp b/libs/ui/kis_control_frame.cpp index 765bc074fa..7e27eea62d 100644 --- a/libs/ui/kis_control_frame.cpp +++ b/libs/ui/kis_control_frame.cpp @@ -1,239 +1,243 @@ /* * kis_control_frame.cc - part of Krita * * Copyright (c) 1999 Matthias Elter * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004 Sven Langkamp * Copyright (c) 2006 Boudewijn Rempt * * 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.g * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_control_frame.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisResourceServerProvider.h" #include "kis_canvas_resource_provider.h" #include "widgets/kis_iconwidget.h" #include "widgets/kis_gradient_chooser.h" #include "KisViewManager.h" #include "kis_config.h" #include "kis_paintop_box.h" #include "kis_custom_pattern.h" #include "widgets/kis_pattern_chooser.h" #include "kis_favorite_resource_manager.h" #include "kis_display_color_converter.h" #include KisControlFrame::KisControlFrame(KisViewManager *view, QWidget *parent, const char* name) : QObject(view) , m_viewManager(view) , m_patternWidget(0) , m_gradientWidget(0) , m_patternChooserPopup(0) , m_gradientChooserPopup(0) , m_paintopBox(0) { setObjectName(name); m_font = QFontDatabase::systemFont(QFontDatabase::GeneralFont); m_patternWidget = new KisIconWidget(parent, "patterns"); m_patternWidget->setToolTip(i18n("Fill Patterns")); m_patternWidget->setFixedSize(32, 32); m_gradientWidget = new KisIconWidget(parent, "gradients"); m_gradientWidget->setToolTip(i18n("Gradients")); m_gradientWidget->setFixedSize(32, 32); } void KisControlFrame::setup(QWidget *parent) { createPatternsChooser(m_viewManager); createGradientsChooser(m_viewManager); QWidgetAction *action = new QWidgetAction(this); action->setText(i18n("&Patterns")); m_viewManager->actionCollection()->addAction("patterns", action); action->setDefaultWidget(m_patternWidget); action = new QWidgetAction(this); action->setText(i18n("&Gradients")); m_viewManager->actionCollection()->addAction("gradients", action); action->setDefaultWidget(m_gradientWidget); // XXX: KOMVC we don't have a canvas here yet, needs a setImageView const KoColorDisplayRendererInterface *displayRenderer = \ KisDisplayColorConverter::dumbConverterInstance()->displayRendererInterface(); m_dual = new KoDualColorButton(m_viewManager->canvasResourceProvider()->fgColor(), m_viewManager->canvasResourceProvider()->bgColor(), displayRenderer, m_viewManager->mainWindow(), m_viewManager->mainWindow()); m_dual->setPopDialog(true); action = new QWidgetAction(this); action->setText(i18n("&Color")); m_viewManager->actionCollection()->addAction("dual", action); action->setDefaultWidget(m_dual); connect(m_dual, SIGNAL(foregroundColorChanged(KoColor)), m_viewManager->canvasResourceProvider(), SLOT(slotSetFGColor(KoColor))); connect(m_dual, SIGNAL(backgroundColorChanged(KoColor)), m_viewManager->canvasResourceProvider(), SLOT(slotSetBGColor(KoColor))); connect(m_viewManager->canvasResourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), m_dual, SLOT(setForegroundColor(KoColor))); connect(m_viewManager->canvasResourceProvider(), SIGNAL(sigBGColorChanged(KoColor)), m_dual, SLOT(setBackgroundColor(KoColor))); connect(m_viewManager->canvasResourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), m_gradientWidget, SLOT(update())); connect(m_viewManager->canvasResourceProvider(), SIGNAL(sigBGColorChanged(KoColor)), m_gradientWidget, SLOT(update())); m_dual->setFixedSize(28, 28); connect(m_viewManager, SIGNAL(viewChanged()), SLOT(slotUpdateDisplayRenderer())); m_paintopBox = new KisPaintopBox(m_viewManager, parent, "paintopbox"); action = new QWidgetAction(this); action->setText(i18n("&Painter's Tools")); m_viewManager->actionCollection()->addAction("paintops", action); action->setDefaultWidget(m_paintopBox); } void KisControlFrame::slotUpdateDisplayRenderer() { if (m_viewManager->canvasBase()){ m_dual->setDisplayRenderer(m_viewManager->canvasBase()->displayColorConverter()->displayRendererInterface()); m_dual->setColorSpace(m_viewManager->canvasBase()->image()->colorSpace()); m_viewManager->canvasBase()->image()->disconnect(m_dual); connect(m_viewManager->canvasBase()->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), m_dual, SLOT(setColorSpace(const KoColorSpace*)), Qt::UniqueConnection); } else if (m_viewManager->viewCount()==0) { m_dual->setDisplayRenderer(); } } void KisControlFrame::slotSetPattern(KoPattern * pattern) { m_patternWidget->setResource(pattern); m_patternChooser->setCurrentPattern(pattern); } void KisControlFrame::slotSetGradient(KoAbstractGradient * gradient) { m_gradientWidget->setResource(gradient); } void KisControlFrame::createPatternsChooser(KisViewManager * view) { if (m_patternChooserPopup) delete m_patternChooserPopup; m_patternChooserPopup = new QWidget(m_patternWidget); m_patternChooserPopup->setObjectName("pattern_chooser_popup"); QHBoxLayout * l2 = new QHBoxLayout(m_patternChooserPopup); l2->setObjectName("patternpopuplayout"); m_patternsTab = new QTabWidget(m_patternChooserPopup); m_patternsTab->setObjectName("patternstab"); m_patternsTab->setFocusPolicy(Qt::NoFocus); m_patternsTab->setFont(m_font); l2->addWidget(m_patternsTab); m_patternChooser = new KisPatternChooser(m_patternChooserPopup); m_patternChooser->setFont(m_font); QWidget *patternChooserPage = new QWidget(m_patternChooserPopup); QHBoxLayout *patternChooserPageLayout = new QHBoxLayout(patternChooserPage); patternChooserPageLayout->addWidget(m_patternChooser); m_patternsTab->addTab(patternChooserPage, i18n("Patterns")); KisCustomPattern* customPatterns = new KisCustomPattern(0, "custompatterns", i18n("Custom Pattern"), m_viewManager); customPatterns->setFont(m_font); m_patternsTab->addTab(customPatterns, i18n("Custom Pattern")); connect(m_patternChooser, SIGNAL(resourceSelected(KoResource*)), view->canvasResourceProvider(), SLOT(slotPatternActivated(KoResource*))); connect(customPatterns, SIGNAL(activatedResource(KoResource*)), view->canvasResourceProvider(), SLOT(slotPatternActivated(KoResource*))); connect(view->canvasResourceProvider(), SIGNAL(sigPatternChanged(KoPattern*)), this, SLOT(slotSetPattern(KoPattern*))); m_patternChooser->setCurrentItem(0, 0); if (m_patternChooser->currentResource() && view->canvasResourceProvider()) { view->canvasResourceProvider()->slotPatternActivated(m_patternChooser->currentResource()); } m_patternWidget->setPopupWidget(m_patternChooserPopup); } void KisControlFrame::createGradientsChooser(KisViewManager * view) { if (m_gradientChooserPopup) { delete m_gradientChooserPopup; m_gradientChooserPopup = 0; } m_gradientChooserPopup = new QWidget(m_gradientWidget); m_gradientChooserPopup->setObjectName("gradient_chooser_popup"); QHBoxLayout * l2 = new QHBoxLayout(m_gradientChooserPopup); l2->setObjectName("gradientpopuplayout"); m_gradientTab = new QTabWidget(m_gradientChooserPopup); m_gradientTab->setObjectName("gradientstab"); m_gradientTab->setFocusPolicy(Qt::NoFocus); m_gradientTab->setFont(m_font); l2->addWidget(m_gradientTab); m_gradientChooser = new KisGradientChooser(m_gradientChooserPopup); m_gradientChooser->setFont(m_font); m_gradientTab->addTab(m_gradientChooser, i18n("Gradients")); connect(m_gradientChooser, SIGNAL(resourceSelected(KoResource*)), view->canvasResourceProvider(), SLOT(slotGradientActivated(KoResource*))); connect (view->mainWindow(), SIGNAL(themeChanged()), m_gradientChooser, SLOT(slotUpdateIcons())); connect(view->canvasResourceProvider(), SIGNAL(sigGradientChanged(KoAbstractGradient*)), this, SLOT(slotSetGradient(KoAbstractGradient*))); + connect(view->canvasResourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), m_gradientChooser, SLOT(setForegroundColor(KoColor))); + connect(view->canvasResourceProvider(), SIGNAL(sigBGColorChanged(KoColor)), m_gradientChooser, SLOT(setBackgroundColor(KoColor))); + + m_gradientChooser->setCurrentItem(0, 0); if (m_gradientChooser->currentResource() && view->canvasResourceProvider()) view->canvasResourceProvider()->slotGradientActivated(m_gradientChooser->currentResource()); m_gradientWidget->setPopupWidget(m_gradientChooserPopup); } diff --git a/libs/ui/kis_stopgradient_editor.cpp b/libs/ui/kis_stopgradient_editor.cpp index 51867bd096..78d4805b78 100644 --- a/libs/ui/kis_stopgradient_editor.cpp +++ b/libs/ui/kis_stopgradient_editor.cpp @@ -1,257 +1,318 @@ /* * Copyright (c) 2004 Cyrille Berger * 2016 Sven Langkamp * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_stopgradient_editor.h" #include #include #include #include #include #include #include #include "kis_debug.h" #include /****************************** KisStopGradientEditor ******************************/ KisStopGradientEditor::KisStopGradientEditor(QWidget *parent) : QWidget(parent), - m_gradient(0) + m_gradient(0), + m_fgColor(KoColor()), + m_bgColor(KoColor()) { setupUi(this); connect(gradientSlider, SIGNAL(sigSelectedStop(int)), this, SLOT(stopChanged(int))); connect(nameedit, SIGNAL(editingFinished()), this, SLOT(nameChanged())); connect(colorButton, SIGNAL(changed(KoColor)), SLOT(colorChanged(KoColor))); + + connect(colorRadioButton, SIGNAL(toggled(bool)), this, SLOT(stopTypeChanged())); + connect(foregroundRadioButton, SIGNAL(toggled(bool)), this, SLOT(stopTypeChanged())); + connect(backgroundRadioButton, SIGNAL(toggled(bool)), this, SLOT(stopTypeChanged())); + opacitySlider->setPrefix(i18n("Opacity: ")); opacitySlider->setRange(0.0, 1.0, 2); connect(opacitySlider, SIGNAL(valueChanged(qreal)), this, SLOT(opacityChanged(qreal))); buttonReverse->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_x")); buttonReverse->setToolTip(i18n("Flip Gradient")); KisIconUtils::updateIcon(buttonReverse); connect(buttonReverse, SIGNAL(pressed()), SLOT(reverse())); buttonReverseSecond->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_x")); buttonReverseSecond->setToolTip(i18n("Flip Gradient")); KisIconUtils::updateIcon(buttonReverseSecond); connect(buttonReverseSecond, SIGNAL(clicked()), SLOT(reverse())); this->setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showContextMenu(const QPoint &))); setCompactMode(false); setGradient(0); stopChanged(-1); } -KisStopGradientEditor::KisStopGradientEditor(KoStopGradient* gradient, QWidget *parent, const char* name, const QString& caption) +KisStopGradientEditor::KisStopGradientEditor(KoStopGradient* gradient, QWidget *parent, const char* name, const QString& caption, + const KoColor &fgColor, const KoColor &bgColor) : KisStopGradientEditor(parent) { + m_fgColor = fgColor; + m_bgColor = bgColor; setObjectName(name); setWindowTitle(caption); setGradient(gradient); } void KisStopGradientEditor::setCompactMode(bool value) { lblName->setVisible(!value); buttonReverse->setVisible(!value); nameedit->setVisible(!value); buttonReverseSecond->setVisible(value); } void KisStopGradientEditor::setGradient(KoStopGradient *gradient) { m_gradient = gradient; setEnabled(m_gradient); if (m_gradient) { gradientSlider->setGradientResource(m_gradient); nameedit->setText(gradient->name()); stopChanged(gradientSlider->selectedStop()); } emit sigGradientChanged(); } void KisStopGradientEditor::notifyGlobalColorChanged(const KoColor &color) { if (colorButton->isEnabled() && color != colorButton->color()) { colorButton->setColor(color); } } boost::optional KisStopGradientEditor::currentActiveStopColor() const { if (!colorButton->isEnabled()) return boost::none; return colorButton->color(); } void KisStopGradientEditor::stopChanged(int stop) { if (!m_gradient) return; const bool hasStopSelected = stop >= 0; opacitySlider->setEnabled(hasStopSelected); colorButton->setEnabled(hasStopSelected); stopLabel->setEnabled(hasStopSelected); + foregroundRadioButton->setEnabled(hasStopSelected); + backgroundRadioButton->setEnabled(hasStopSelected); + colorRadioButton->setEnabled(hasStopSelected); if (hasStopSelected) { - KoColor color = m_gradient->stops()[stop].second; + KoColor color; + KoGradientStopType type = m_gradient->stops()[stop].type; + if (type == FOREGROUNDSTOP) { + foregroundRadioButton->setChecked(true); + opacitySlider->setEnabled(false); + color = m_fgColor; + } + else if (type == BACKGROUNDSTOP) { + backgroundRadioButton->setChecked(true); + opacitySlider->setEnabled(false); + color = m_bgColor; + } + else { + colorRadioButton->setChecked(true); + opacitySlider->setEnabled(true); + color = m_gradient->stops()[stop].color; + } + opacitySlider->setValue(color.opacityF()); - + color.setOpacity(1.0); colorButton->setColor(color); + + } + + emit sigGradientChanged(); +} + +void KisStopGradientEditor::stopTypeChanged() { + QList stops = m_gradient->stops(); + int currentStop = gradientSlider->selectedStop(); + double t = stops[currentStop].position; + KoColor color = stops[currentStop].color; + + KoGradientStopType type; + if (foregroundRadioButton->isChecked()) { + type = FOREGROUNDSTOP; + color = KoColor(m_fgColor, color.colorSpace()); + opacitySlider->setEnabled(false); + } else if (backgroundRadioButton->isChecked()) { + type = BACKGROUNDSTOP; + color = KoColor(m_bgColor, color.colorSpace()); + opacitySlider->setEnabled(false); + } + else { + type = COLORSTOP; + opacitySlider->setEnabled(true); } + stops.removeAt(currentStop); + stops.insert(currentStop, KoGradientStop(t, color, type)); + m_gradient->setStops(stops); + gradientSlider->update(); //setSelectedStopType(type); emit sigGradientChanged(); } void KisStopGradientEditor::colorChanged(const KoColor& color) { if (!m_gradient) return; QList stops = m_gradient->stops(); int currentStop = gradientSlider->selectedStop(); - double t = stops[currentStop].first; + double t = stops[currentStop].position; - KoColor c(color, stops[currentStop].second.colorSpace()); - c.setOpacity(stops[currentStop].second.opacityU8()); + KoColor c(color, stops[currentStop].color.colorSpace()); + c.setOpacity(stops[currentStop].color.opacityU8()); + + KoGradientStopType type = stops[currentStop].type; stops.removeAt(currentStop); - stops.insert(currentStop, KoGradientStop(t, c)); - + stops.insert(currentStop, KoGradientStop(t, c, type)); m_gradient->setStops(stops); gradientSlider->update(); emit sigGradientChanged(); } void KisStopGradientEditor::opacityChanged(qreal value) { if (!m_gradient) return; QList stops = m_gradient->stops(); int currentStop = gradientSlider->selectedStop(); - double t = stops[currentStop].first; + double t = stops[currentStop].position; - KoColor c = stops[currentStop].second; + KoColor c = stops[currentStop].color; c.setOpacity(value); + KoGradientStopType type = stops[currentStop].type; + stops.removeAt(currentStop); - stops.insert(currentStop, KoGradientStop(t, c)); - + stops.insert(currentStop, KoGradientStop(t, c, type)); m_gradient->setStops(stops); gradientSlider->update(); emit sigGradientChanged(); } void KisStopGradientEditor::nameChanged() { if (!m_gradient) return; m_gradient->setName(nameedit->text()); emit sigGradientChanged(); } void KisStopGradientEditor::reverse() { if (!m_gradient) return; QList stops = m_gradient->stops(); QList reversedStops; for(const KoGradientStop& stop : stops) { - reversedStops.push_front(KoGradientStop(1 - stop.first, stop.second)); + reversedStops.push_front(KoGradientStop(1 - stop.position, stop.color, stop.type)); } m_gradient->setStops(reversedStops); gradientSlider->setSelectedStop(stops.size() - 1 - gradientSlider->selectedStop()); emit sigGradientChanged(); } void KisStopGradientEditor::sortByValue( SortFlags flags = SORT_ASCENDING ) { if (!m_gradient) return; bool ascending = (flags & SORT_ASCENDING) > 0; bool evenDistribution = (flags & EVEN_DISTRIBUTION) > 0; QList stops = m_gradient->stops(); const int stopCount = stops.size(); QList sortedStops; std::sort(stops.begin(), stops.end(), KoGradientStopValueSort()); int stopIndex = 0; for (const KoGradientStop& stop : stops) { - const float value = evenDistribution ? (float)stopIndex / (float)(stopCount - 1) : stop.second.toQColor().valueF(); + const float value = evenDistribution ? (float)stopIndex / (float)(stopCount - 1) : stop.color.toQColor().valueF(); const float position = ascending ? value : 1.f - value; if (ascending) { - sortedStops.push_back(KoGradientStop(position, stop.second)); + sortedStops.push_back(KoGradientStop(position, stop.color, stop.type)); } else { - sortedStops.push_front(KoGradientStop(position, stop.second)); + sortedStops.push_front(KoGradientStop(position, stop.color, stop.type)); } stopIndex++; } m_gradient->setStops(sortedStops); gradientSlider->setSelectedStop(stopCount - 1); gradientSlider->update(); emit sigGradientChanged(); } void KisStopGradientEditor::showContextMenu(const QPoint &origin) { QMenu contextMenu(i18n("Options"), this); QAction reverseValues(i18n("Reverse Values"), this); connect(&reverseValues, &QAction::triggered, this, &KisStopGradientEditor::reverse); QAction sortAscendingValues(i18n("Sort by Value"), this); connect(&sortAscendingValues, &QAction::triggered, this, [this]{ this->sortByValue(SORT_ASCENDING); } ); QAction sortAscendingDistributed(i18n("Sort by Value (Even Distribution)"), this); connect(&sortAscendingDistributed, &QAction::triggered, this, [this]{ this->sortByValue(SORT_ASCENDING | EVEN_DISTRIBUTION);} ); contextMenu.addAction(&reverseValues); contextMenu.addSeparator(); contextMenu.addAction(&sortAscendingValues); contextMenu.addAction(&sortAscendingDistributed); contextMenu.exec(mapToGlobal(origin)); } diff --git a/libs/ui/kis_stopgradient_editor.h b/libs/ui/kis_stopgradient_editor.h index 7560e5feda..4a0fa76c3c 100644 --- a/libs/ui/kis_stopgradient_editor.h +++ b/libs/ui/kis_stopgradient_editor.h @@ -1,68 +1,71 @@ /* * Copyright (c) 2004 Cyrille Berger * 2016 Sven Langkamp * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_STOPGRADIENT_EDITOR_H_ #define _KIS_STOPGRADIENT_EDITOR_H_ #include "kritaui_export.h" #include "ui_wdgstopgradienteditor.h" #include class KoStopGradient; class KRITAUI_EXPORT KisStopGradientEditor : public QWidget, public Ui::KisWdgStopGradientEditor { Q_OBJECT public: enum SortFlag { SORT_ASCENDING = 1 << 0, EVEN_DISTRIBUTION = 1 << 1 }; Q_DECLARE_FLAGS( SortFlags, SortFlag); KisStopGradientEditor(QWidget *parent); - KisStopGradientEditor(KoStopGradient* gradient, QWidget *parent, const char* name, const QString& caption); + KisStopGradientEditor(KoStopGradient* gradient, QWidget *parent, const char* name, const QString& caption, const KoColor &fgColor, const KoColor &bgColor); void setCompactMode(bool value); void setGradient(KoStopGradient* gradient); void notifyGlobalColorChanged(const KoColor &color); boost::optional currentActiveStopColor() const; Q_SIGNALS: void sigGradientChanged(); private: KoStopGradient* m_gradient; + KoColor m_fgColor, m_bgColor; + private Q_SLOTS: void stopChanged(int stop); + void stopTypeChanged(); void colorChanged(const KoColor& color); void opacityChanged(qreal value); void nameChanged(); void reverse(); void sortByValue(SortFlags flags); void showContextMenu( const class QPoint& origin ); }; Q_DECLARE_OPERATORS_FOR_FLAGS(KisStopGradientEditor::SortFlags); #endif diff --git a/libs/ui/tests/kis_asl_layer_style_serializer_test.cpp b/libs/ui/tests/kis_asl_layer_style_serializer_test.cpp index 73fb569553..651b4e4a9e 100644 --- a/libs/ui/tests/kis_asl_layer_style_serializer_test.cpp +++ b/libs/ui/tests/kis_asl_layer_style_serializer_test.cpp @@ -1,425 +1,425 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_layer_style_serializer_test.h" #include #include #include #include #include #include #include "kis_global.h" #include "testutil.h" #include "kis_psd_layer_style.h" #include "kis_asl_layer_style_serializer.h" #include #define CMP(object, method, value) QCOMPARE(style->object()->method(), value) void KisAslLayerStyleSerializerTest::testReading() { KisAslLayerStyleSerializer s; // QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_style.asl")); QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_and_pattern.asl")); QFile aslFile(srcFileName); aslFile.open(QIODevice::ReadOnly); s.readFromDevice(&aslFile); QVector styles = s.styles(); QVERIFY(styles.size() == 1); KisPSDLayerStyleSP style = styles.first(); CMP(dropShadow, effectEnabled, true); CMP(dropShadow, blendMode, COMPOSITE_MULT); CMP(dropShadow, color, QColor(Qt::black)); CMP(dropShadow, opacity, 15); CMP(dropShadow, angle, -120); CMP(dropShadow, useGlobalLight, false); CMP(dropShadow, distance, 2); CMP(dropShadow, spread, 1); CMP(dropShadow, size, 7); CMP(dropShadow, antiAliased, true); CMP(dropShadow, noise, 3); // CMP(dropShadow, contourLookupTable,); CMP(innerShadow, effectEnabled, true); CMP(innerShadow, blendMode, COMPOSITE_DARKEN); CMP(innerShadow, color, QColor(Qt::black)); CMP(innerShadow, opacity, 28); CMP(innerShadow, angle, 120); CMP(innerShadow, useGlobalLight, true); CMP(innerShadow, distance, 8); CMP(innerShadow, spread, 15); CMP(innerShadow, size, 27); CMP(innerShadow, antiAliased, false); CMP(innerShadow, noise, 10); // CMP(innerShadow, contourLookupTable,); CMP(outerGlow, effectEnabled, true); CMP(outerGlow, blendMode, COMPOSITE_SCREEN); CMP(outerGlow, color, QColor(255, 255, 189)); CMP(outerGlow, opacity, 43); CMP(outerGlow, spread, 23); CMP(outerGlow, size, 109); CMP(outerGlow, antiAliased, true); CMP(outerGlow, noise, 29); // CMP(outerGlow, contourLookupTable,); // CMP(outerGlow, gradient,); CMP(outerGlow, fillType, psd_fill_solid_color); CMP(outerGlow, technique, psd_technique_precise); CMP(outerGlow, range, 69); CMP(outerGlow, jitter, 18); CMP(innerGlow, effectEnabled, true); CMP(innerGlow, blendMode, COMPOSITE_SCREEN); CMP(innerGlow, color, QColor(255, 255, 189)); CMP(innerGlow, opacity, 55); CMP(innerGlow, spread, 21); CMP(innerGlow, size, 128); CMP(innerGlow, antiAliased, true); CMP(innerGlow, noise, 33); // CMP(innerGlow, contourLookupTable,); // CMP(innerGlow, gradient,); CMP(innerGlow, fillType, psd_fill_solid_color); CMP(innerGlow, technique, psd_technique_softer); CMP(innerGlow, range, 32); CMP(innerGlow, jitter, 22); CMP(innerGlow, source, psd_glow_edge); CMP(satin, effectEnabled, true); CMP(satin, blendMode, COMPOSITE_MULT); CMP(satin, color, QColor(Qt::black)); CMP(satin, opacity, 68); CMP(satin, angle, 19); CMP(satin, distance, 11); CMP(satin, size, 14); CMP(satin, antiAliased, false); CMP(satin, invert, true); // CMP(satin, contourLookupTable,); CMP(colorOverlay, effectEnabled, true); CMP(colorOverlay, blendMode, COMPOSITE_OVER); CMP(colorOverlay, color, QColor(Qt::red)); CMP(colorOverlay, opacity, 63); CMP(gradientOverlay, effectEnabled, true); CMP(gradientOverlay, blendMode, COMPOSITE_OVER); CMP(gradientOverlay, opacity, 100); CMP(gradientOverlay, angle, 90); CMP(gradientOverlay, style, psd_gradient_style_linear); CMP(gradientOverlay, reverse, false); CMP(gradientOverlay, alignWithLayer, true); CMP(gradientOverlay, scale, 100); CMP(gradientOverlay, gradientXOffset, 0); CMP(gradientOverlay, gradientYOffset, 0); //CMP(gradientOverlay, dither, ); CMP(gradientOverlay, gradient()->name, QString("Two Color")); CMP(stroke, effectEnabled, true); CMP(stroke, blendMode, COMPOSITE_OVER); CMP(stroke, opacity, 67); CMP(stroke, size, 13); CMP(stroke, fillType, psd_fill_solid_color); CMP(stroke, position, psd_stroke_outside); CMP(stroke, color, QColor(210, 33, 87)); CMP(bevelAndEmboss, effectEnabled, true); CMP(bevelAndEmboss, highlightBlendMode, COMPOSITE_SCREEN); CMP(bevelAndEmboss, highlightOpacity, 75); CMP(bevelAndEmboss, highlightColor, QColor(255, 255, 255)); CMP(bevelAndEmboss, shadowBlendMode, COMPOSITE_MULT); CMP(bevelAndEmboss, shadowOpacity, 75); CMP(bevelAndEmboss, shadowColor, QColor(Qt::black)); CMP(bevelAndEmboss, technique, psd_technique_softer); CMP(bevelAndEmboss, style, psd_bevel_inner_bevel); CMP(bevelAndEmboss, useGlobalLight, true); CMP(bevelAndEmboss, angle, 120); CMP(bevelAndEmboss, altitude, 30); CMP(bevelAndEmboss, depth, 83); CMP(bevelAndEmboss, size, 49); CMP(bevelAndEmboss, direction, psd_direction_up); // FIXME: contour CMP(bevelAndEmboss, glossAntiAliased, false); CMP(bevelAndEmboss, soften, 2); CMP(bevelAndEmboss, contourEnabled, true); // FIXME: contour curve CMP(bevelAndEmboss, antiAliased, true); CMP(bevelAndEmboss, contourRange, 60); CMP(bevelAndEmboss, textureEnabled, false); CMP(patternOverlay, effectEnabled, true); CMP(patternOverlay, blendMode, COMPOSITE_OVER); CMP(patternOverlay, opacity, 100); CMP(patternOverlay, alignWithLayer, true); CMP(patternOverlay, scale, 100); CMP(patternOverlay, horizontalPhase, 201); CMP(patternOverlay, verticalPhase, 162); CMP(patternOverlay, pattern()->name, QString("$$$/Presets/Patterns/Patterns_pat/Bubbles=Bubbles")); CMP(patternOverlay, pattern()->filename, QString("b7334da0-122f-11d4-8bb5-e27e45023b5f.pat")); } void KisAslLayerStyleSerializerTest::testWriting() { QVector styles; QByteArray refXMLDoc; { KisAslLayerStyleSerializer s; QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_and_pattern.asl")); QFile aslFile(srcFileName); aslFile.open(QIODevice::ReadOnly); s.readFromDevice(&aslFile); styles = s.styles(); { aslFile.seek(0); KisAslReader reader; QDomDocument doc = reader.readFile(&aslFile); refXMLDoc = doc.toByteArray(); } } // now we have an initialized KisPSDLayerStyle object { KisAslLayerStyleSerializer s; s.setStyles(styles); QFile dstFile("test_written.asl"); dstFile.open(QIODevice::WriteOnly); s.saveToDevice(&dstFile); dstFile.close(); } QByteArray resultXMLDoc; { QFile resultFile("test_written.asl"); resultFile.open(QIODevice::ReadOnly); KisAslReader reader; QDomDocument doc = reader.readFile(&resultFile); resultXMLDoc = doc.toByteArray(); } QFile refXMLFile("save_round_trip_src.xml"); refXMLFile.open(QIODevice::WriteOnly); refXMLFile.write(refXMLDoc); refXMLFile.close(); QFile resultXMLFile("save_round_trip_dst.xml"); resultXMLFile.open(QIODevice::WriteOnly); resultXMLFile.write(resultXMLDoc); resultXMLFile.close(); QEXPECT_FAIL("", "Tried to compare two xml files, which are not the same. The order of attributes when serializing is undefined", Continue); QCOMPARE(resultXMLDoc, refXMLDoc); } #include void KisAslLayerStyleSerializerTest::testWritingGlobalPatterns() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); KoResourceServer *server = KoResourceServerProvider::instance()->patternServer(); QList sortedResources = server->sortedResources(); KoPattern *pattern = sortedResources.first(); Q_ASSERT(pattern); dbgKrita << ppVar(pattern->name()); dbgKrita << ppVar(pattern->filename()); style->patternOverlay()->setEffectEnabled(true); style->patternOverlay()->setPattern(pattern); { KisAslLayerStyleSerializer s; s.setStyles(QVector() << style); QFile dstFile("test_written_pattern_only.asl"); dstFile.open(QIODevice::WriteOnly); s.saveToDevice(&dstFile); dstFile.close(); } /* QByteArray resultXMLDoc; { QFile resultFile("test_written.asl"); resultFile.open(QIODevice::ReadOnly); KisAslReader reader; QDomDocument doc = reader.readFile(&resultFile); resultXMLDoc = doc.toByteArray(); } */ } void KisAslLayerStyleSerializerTest::testReadMultipleStyles() { KisPSDLayerStyleSP style(new KisPSDLayerStyle()); QVector styles; { KisAslLayerStyleSerializer s; QString srcFileName(TestUtil::fetchDataFileLazy("asl/multiple_styles.asl")); QFile aslFile(srcFileName); aslFile.open(QIODevice::ReadOnly); s.readFromDevice(&aslFile); styles = s.styles(); } { KisAslLayerStyleSerializer s; QString dstFileName("multiple_styles_out.asl"); QFile aslFile(dstFileName); aslFile.open(QIODevice::WriteOnly); s.setStyles(styles); s.saveToDevice(&aslFile); } { KisAslLayerStyleSerializer s; QString srcFileName("multiple_styles_out.asl"); QFile aslFile(srcFileName); aslFile.open(QIODevice::ReadOnly); s.readFromDevice(&aslFile); styles = s.styles(); dbgKrita << ppVar(styles.size()); } } void KisAslLayerStyleSerializerTest::testWritingGradients() { KoStopGradient stopGradient(""); { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); QList stops; - stops << KoGradientStop(0.0, KoColor(Qt::black, cs)); - stops << KoGradientStop(0.3, KoColor(Qt::red, cs)); - stops << KoGradientStop(0.6, KoColor(Qt::green, cs)); - stops << KoGradientStop(1.0, KoColor(Qt::white, cs)); + stops << KoGradientStop(0.0, KoColor(Qt::black, cs), COLORSTOP); + stops << KoGradientStop(0.3, KoColor(Qt::red, cs), COLORSTOP); + stops << KoGradientStop(0.6, KoColor(Qt::green, cs), COLORSTOP); + stops << KoGradientStop(1.0, KoColor(Qt::white, cs), COLORSTOP); stopGradient.setStops(stops); } KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->outerGlow()->setEffectEnabled(true); style->outerGlow()->setFillType(psd_fill_gradient); style->outerGlow()->setGradient(toQShared(new KoStopGradient(stopGradient))); style->innerGlow()->setEffectEnabled(true); style->innerGlow()->setFillType(psd_fill_gradient); style->innerGlow()->setGradient(toQShared(new KoStopGradient(stopGradient))); style->gradientOverlay()->setEffectEnabled(true); style->gradientOverlay()->setGradient(toQShared(new KoStopGradient(stopGradient))); style->stroke()->setEffectEnabled(true); style->stroke()->setFillType(psd_fill_gradient); style->stroke()->setGradient(toQShared(new KoStopGradient(stopGradient))); { KisAslLayerStyleSerializer s; s.setStyles(QVector() << style); QFile dstFile("test_written_stop_gradient.asl"); dstFile.open(QIODevice::WriteOnly); s.saveToDevice(&dstFile); dstFile.close(); } QString xmlDoc; { QFile resultFile("test_written_stop_gradient.asl"); resultFile.open(QIODevice::ReadOnly); KisAslReader reader; QDomDocument doc = reader.readFile(&resultFile); xmlDoc = doc.toString(); } { // the reference document has stripped "Idnt" field which is random QRegExp rx(""); rx.setMinimal(true); int pos = 0; while ((pos = rx.indexIn(xmlDoc, pos)) != -1) { xmlDoc.remove(pos, rx.matchedLength()); } { //QFile xmlFile("reference_gradients.asl.xml"); //xmlFile.open(QIODevice::WriteOnly); //xmlFile.write(xmlDoc.toLatin1()); //xmlFile.close(); } QString refFileName(TestUtil::fetchDataFileLazy("reference_gradients.asl.xml")); QFile refFile(refFileName); refFile.open(QIODevice::ReadOnly); QString refDoc = QString(refFile.readAll()); QEXPECT_FAIL("", "Tried to compare two gradient files, which are not the same. The order of attributes when serializing is undefined.", Continue); QCOMPARE(xmlDoc, refDoc); } } QTEST_MAIN(KisAslLayerStyleSerializerTest) diff --git a/libs/ui/widgets/kis_gradient_chooser.cc b/libs/ui/widgets/kis_gradient_chooser.cc index ebfa522d34..edc0228e51 100644 --- a/libs/ui/widgets/kis_gradient_chooser.cc +++ b/libs/ui/widgets/kis_gradient_chooser.cc @@ -1,210 +1,220 @@ /* * Copyright (c) 2004 Adrian Page * Copyright (C) 2011 Srikanth Tiyyagura * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "widgets/kis_gradient_chooser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisViewManager.h" #include "kis_global.h" #include "kis_autogradient.h" #include "kis_canvas_resource_provider.h" #include "kis_stopgradient_editor.h" -KisCustomGradientDialog::KisCustomGradientDialog(KoAbstractGradient* gradient, QWidget *parent, const char *name) +KisCustomGradientDialog::KisCustomGradientDialog(KoAbstractGradient* gradient, QWidget *parent, const char *name, + const KoColor &fgColor, const KoColor &bgColor) : KoDialog(parent, Qt::Dialog) { setButtons(Close); setDefaultButton(Close); setObjectName(name); setModal(false); KoStopGradient* stopGradient = dynamic_cast(gradient); if (stopGradient) { - m_page = new KisStopGradientEditor(stopGradient, this, "autogradient", i18n("Custom Stop Gradient")); + m_page = new KisStopGradientEditor(stopGradient, this, "autogradient", i18n("Custom Stop Gradient"), fgColor, bgColor); } else { KoSegmentGradient* segmentedGradient = dynamic_cast(gradient); if (segmentedGradient) { - m_page = new KisAutogradientEditor(segmentedGradient, this, "autogradient", i18n("Custom Segmented Gradient")); + m_page = new KisAutogradientEditor(segmentedGradient, this, "autogradient", i18n("Custom Segmented Gradient"), fgColor, bgColor); } } setCaption(m_page->windowTitle()); setMainWidget(m_page); } KisGradientChooser::KisGradientChooser(QWidget *parent, const char *name) : QFrame(parent) { setObjectName(name); m_lbName = new QLabel(); KoResourceServer * rserver = KoResourceServerProvider::instance()->gradientServer(); QSharedPointer adapter (new KoResourceServerAdapter(rserver)); m_itemChooser = new KoResourceItemChooser(adapter, this); m_itemChooser->showTaggingBar(true); m_itemChooser->setFixedSize(250, 250); m_itemChooser->setColumnCount(1); m_itemChooser->itemView()->keepAspectRatio(false); connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)), this, SLOT(update(KoResource*))); connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)), this, SIGNAL(resourceSelected(KoResource*))); QWidget* buttonWidget = new QWidget(this); QHBoxLayout* buttonLayout = new QHBoxLayout(buttonWidget); m_addGradient = new QToolButton(this); m_addGradient->setText(i18n("Add...")); m_addGradient->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); connect(m_addGradient, SIGNAL(clicked()), this, SLOT(addStopGradient())); buttonLayout->addWidget(m_addGradient); QMenu *menuAddGradient = new QMenu(m_addGradient); QAction* addStopGradient = new QAction(i18n("Stop gradient"), this); connect(addStopGradient, SIGNAL(triggered(bool)), this, SLOT(addStopGradient())); menuAddGradient->addAction(addStopGradient); QAction* addSegmentedGradient = new QAction(i18n("Segmented gradient"), this); connect(addSegmentedGradient, SIGNAL(triggered(bool)), this, SLOT(addSegmentedGradient())); menuAddGradient->addAction(addSegmentedGradient); m_addGradient->setMenu(menuAddGradient); m_addGradient->setPopupMode(QToolButton::MenuButtonPopup); m_editGradient = new QPushButton(); m_editGradient->setText(i18n("Edit...")); m_editGradient->setEnabled(false); connect(m_editGradient, SIGNAL(clicked()), this, SLOT(editGradient())); buttonLayout->addWidget(m_editGradient); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setObjectName("main layout"); mainLayout->setMargin(2); mainLayout->addWidget(m_lbName); mainLayout->addWidget(m_itemChooser, 10); mainLayout->addWidget(buttonWidget); slotUpdateIcons(); setLayout(mainLayout); } KisGradientChooser::~KisGradientChooser() { } KoResource *KisGradientChooser::currentResource() { return m_itemChooser->currentResource(); } void KisGradientChooser::setCurrentResource(KoResource *resource) { m_itemChooser->setCurrentResource(resource); } void KisGradientChooser::setCurrentItem(int row, int column) { m_itemChooser->setCurrentItem(row, column); if (currentResource()) update(currentResource()); } void KisGradientChooser::slotUpdateIcons() { if (m_addGradient && m_editGradient) { m_addGradient->setIcon(KisIconUtils::loadIcon("list-add")); m_editGradient->setIcon(KisIconUtils::loadIcon("configure")); } } +void KisGradientChooser::setForegroundColor(KoColor color) { + m_foregroundColor = color; +} + +void KisGradientChooser::setBackgroundColor(KoColor color) { + m_backgroundColor = color; +} + void KisGradientChooser::update(KoResource * resource) { KoAbstractGradient *gradient = static_cast(resource); m_lbName->setText(gradient ? i18n(gradient->name().toUtf8().data()) : ""); m_editGradient->setEnabled(gradient && gradient->removable()); } void KisGradientChooser::addStopGradient() { KoStopGradient* gradient = new KoStopGradient(""); QList stops; - stops << KoGradientStop(0.0, KoColor(QColor(250, 250, 0), KoColorSpaceRegistry::instance()->rgb8())) << KoGradientStop(1.0, KoColor(QColor(255, 0, 0, 255), KoColorSpaceRegistry::instance()->rgb8())); + stops << KoGradientStop(0.0, KoColor(QColor(250, 250, 0), KoColorSpaceRegistry::instance()->rgb8()), COLORSTOP) + << KoGradientStop(1.0, KoColor(QColor(255, 0, 0, 255), KoColorSpaceRegistry::instance()->rgb8()), COLORSTOP); gradient->setType(QGradient::LinearGradient); gradient->setStops(stops); addGradient(gradient); } void KisGradientChooser::addSegmentedGradient() { KoSegmentGradient* gradient = new KoSegmentGradient(""); gradient->createSegment(INTERP_LINEAR, COLOR_INTERP_RGB, 0.0, 1.0, 0.5, Qt::black, Qt::white); gradient->setName(i18n("unnamed")); addGradient(gradient); } void KisGradientChooser::addGradient(KoAbstractGradient* gradient) { KoResourceServer * rserver = KoResourceServerProvider::instance()->gradientServer(); QString saveLocation = rserver->saveLocation(); - KisCustomGradientDialog dialog(gradient, this, "KisCustomGradientDialog"); + KisCustomGradientDialog dialog(gradient, this, "KisCustomGradientDialog", m_foregroundColor, m_backgroundColor); dialog.exec(); gradient->setFilename(saveLocation + gradient->name() + gradient->defaultFileExtension()); gradient->setValid(true); rserver->addResource(gradient); m_itemChooser->setCurrentResource(gradient); } void KisGradientChooser::editGradient() { - KisCustomGradientDialog dialog(static_cast(currentResource()), this, "KisCustomGradientDialog"); + KisCustomGradientDialog dialog(static_cast(currentResource()), this, "KisCustomGradientDialog", m_foregroundColor, m_backgroundColor); dialog.exec(); } diff --git a/libs/ui/widgets/kis_gradient_chooser.h b/libs/ui/widgets/kis_gradient_chooser.h index abfbdb16fc..92ccc19af3 100644 --- a/libs/ui/widgets/kis_gradient_chooser.h +++ b/libs/ui/widgets/kis_gradient_chooser.h @@ -1,91 +1,96 @@ /* * Copyright (c) 2004 Adrian Page * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_GRADIENT_CHOOSER_H_ #define KIS_GRADIENT_CHOOSER_H_ #include +#include #include #include #include class KoAbstractGradient; class KoStopGradient; class KisViewManager; class QLabel; class QPushButton; class KisAutogradientEditor; class KoResource; class KoResourceItemChooser; class KisCustomGradientDialog : public KoDialog { Q_OBJECT public: - KisCustomGradientDialog(KoAbstractGradient* gradient, QWidget *parent, const char *name); + KisCustomGradientDialog(KoAbstractGradient* gradient, QWidget *parent, const char *name, const KoColor &fgColor, const KoColor &bgColor); private: QWidget * m_page; }; class KRITAUI_EXPORT KisGradientChooser : public QFrame { Q_OBJECT public: KisGradientChooser(QWidget *parent = 0, const char *name = 0); ~KisGradientChooser() override; /// Gets the currently selected resource /// @returns the selected resource, 0 is no resource is selected KoResource *currentResource(); void setCurrentResource(KoResource *resource); void setCurrentItem(int row, int column); Q_SIGNALS: /// Emitted when a resource was selected void resourceSelected(KoResource * resource); public Q_SLOTS: void slotUpdateIcons(); + void setForegroundColor(KoColor); + void setBackgroundColor(KoColor); private Q_SLOTS: virtual void update(KoResource * resource); void addStopGradient(); void addSegmentedGradient(); void editGradient(); private: void addGradient(KoAbstractGradient* gradient); private: QLabel *m_lbName; KoResourceItemChooser * m_itemChooser; + KoColor m_foregroundColor, m_backgroundColor; + QToolButton* m_addGradient; QPushButton* m_editGradient; }; #endif // KIS_GRADIENT_CHOOSER_H_ diff --git a/libs/ui/widgets/kis_stopgradient_slider_widget.cpp b/libs/ui/widgets/kis_stopgradient_slider_widget.cpp index 210486ab72..8df32f3962 100644 --- a/libs/ui/widgets/kis_stopgradient_slider_widget.cpp +++ b/libs/ui/widgets/kis_stopgradient_slider_widget.cpp @@ -1,367 +1,369 @@ /* * Copyright (c) 2004 Cyrille Berger * 2016 Sven Langkamp * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "widgets/kis_stopgradient_slider_widget.h" #include #include #include #include #include #include #include #include #include #include #include "kis_global.h" #include "kis_debug.h" #include "krita_utils.h" KisStopGradientSliderWidget::KisStopGradientSliderWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) , m_selectedStop(0) , m_drag(0) { QLinearGradient defaultGradient; m_defaultGradient.reset(KoStopGradient::fromQGradient(&defaultGradient)); setGradientResource(0); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); setMouseTracking(true); QWindow *window = this->window()->windowHandle(); if (window) { connect(window, SIGNAL(screenChanged(QScreen*)), SLOT(updateHandleSize())); } updateHandleSize(); } void KisStopGradientSliderWidget::updateHandleSize() { QFontMetrics fm(font()); const int h = fm.height(); m_handleSize = QSize(0.34 * h, h); } int KisStopGradientSliderWidget::handleClickTolerance() const { // the size of the default text! return m_handleSize.width(); } void KisStopGradientSliderWidget::setGradientResource(KoStopGradient* gradient) { m_gradient = gradient ? gradient : m_defaultGradient.data(); if (m_gradient && m_selectedStop >= 0) { m_selectedStop = qBound(0, m_selectedStop, m_gradient->stops().size() - 1); emit sigSelectedStop(m_selectedStop); } else { m_selectedStop = -1; } } -void KisStopGradientSliderWidget::paintHandle(qreal position, const QColor &color, bool isSelected, QPainter *painter) +void KisStopGradientSliderWidget::paintHandle(qreal position, const QColor &color, bool isSelected, QString text, QPainter *painter) { - const QRect handlesRect = this->handlesStipeRect(); + const QRect handlesRect = this->handlesStripeRect(); const int handleCenter = handlesRect.left() + position * handlesRect.width(); const int handlesHalfWidth = handlesRect.height() * 0.26; // = 1.0 / 0.66 * 0.34 / 2.0 <-- golden ratio QPolygon triangle(3); triangle[0] = QPoint(handleCenter, handlesRect.top()); triangle[1] = QPoint(handleCenter - handlesHalfWidth, handlesRect.bottom()); triangle[2] = QPoint(handleCenter + handlesHalfWidth, handlesRect.bottom()); + QPoint textPos(handleCenter - handlesHalfWidth, handlesRect.top() - handlesRect.height() / 2); + const qreal lineWidth = 1.0; if (!isSelected) { painter->setPen(QPen(palette().text(), lineWidth)); painter->setBrush(QBrush(color)); painter->setRenderHint(QPainter::Antialiasing); painter->drawPolygon(triangle); } else { painter->setPen(QPen(palette().highlight(), 1.5 * lineWidth)); painter->setBrush(QBrush(color)); painter->setRenderHint(QPainter::Antialiasing); painter->drawPolygon(triangle); } + painter->drawText(textPos, text); } void KisStopGradientSliderWidget::paintEvent(QPaintEvent* pe) { QWidget::paintEvent(pe); QPainter painter(this); painter.setPen(Qt::black); const QRect previewRect = gradientStripeRect(); KritaUtils::renderExactRect(&painter, kisGrowRect(previewRect, 1)); painter.drawRect(previewRect); if (m_gradient) { QImage image = m_gradient->generatePreview(previewRect.width(), previewRect.height()); if (!image.isNull()) { painter.drawImage(previewRect.topLeft(), image); } QList handlePositions = m_gradient->stops(); for (int i = 0; i < handlePositions.count(); i++) { - if (i == m_selectedStop) continue; - paintHandle(handlePositions[i].first, - handlePositions[i].second.toQColor(), - false, &painter); - } - - if (m_selectedStop >= 0) { - paintHandle(handlePositions[m_selectedStop].first, - handlePositions[m_selectedStop].second.toQColor(), - true, &painter); + QString text = ""; + if (handlePositions[i].type == FOREGROUNDSTOP) { + text = "FG"; + } else if (handlePositions[i].type == BACKGROUNDSTOP) { + text = "BG"; + } + paintHandle(handlePositions[i].position, + handlePositions[i].color.toQColor(), + i == m_selectedStop, text, &painter); } } } int findNearestHandle(qreal t, const qreal tolerance, const QList &stops) { int result = -1; qreal minDistance = tolerance; for (int i = 0; i < stops.size(); i++) { const KoGradientStop &stop = stops[i]; - const qreal distance = qAbs(t - stop.first); + const qreal distance = qAbs(t - stop.position); if (distance < minDistance) { minDistance = distance; result = i; } } return result; } void KisStopGradientSliderWidget::mousePressEvent(QMouseEvent * e) { if (!allowedClickRegion(handleClickTolerance()).contains(e->pos())) { QWidget::mousePressEvent(e); return; } if (e->buttons() != Qt::LeftButton ) { QWidget::mousePressEvent(e); return; } - const QRect handlesRect = this->handlesStipeRect(); + const QRect handlesRect = this->handlesStripeRect(); const qreal t = (qreal(e->x()) - handlesRect.x()) / handlesRect.width(); const QList stops = m_gradient->stops(); const int clickedStop = findNearestHandle(t, qreal(handleClickTolerance()) / handlesRect.width(), stops); if (clickedStop >= 0) { if (m_selectedStop != clickedStop) { m_selectedStop = clickedStop; emit sigSelectedStop(m_selectedStop); } m_drag = true; } else { insertStop(qBound(0.0, t, 1.0)); m_drag = true; } update(); updateCursor(e->pos()); } void KisStopGradientSliderWidget::mouseReleaseEvent(QMouseEvent * e) { Q_UNUSED(e); m_drag = false; updateCursor(e->pos()); } int getNewInsertPosition(const KoGradientStop &stop, const QList &stops) { int result = 0; for (int i = 0; i < stops.size(); i++) { - if (stop.first <= stops[i].first) break; + if (stop.position <= stops[i].position) break; result = i + 1; } return result; } void KisStopGradientSliderWidget::mouseMoveEvent(QMouseEvent * e) { updateCursor(e->pos()); if (m_drag) { - const QRect handlesRect = this->handlesStipeRect(); + const QRect handlesRect = this->handlesStripeRect(); double t = (qreal(e->x()) - handlesRect.x()) / handlesRect.width(); QList stops = m_gradient->stops(); if (t < -0.1 || t > 1.1) { if (stops.size() > 2 && m_selectedStop >= 0) { m_removedStop = stops[m_selectedStop]; stops.removeAt(m_selectedStop); m_selectedStop = -1; } } else { if (m_selectedStop < 0) { - m_removedStop.first = qBound(0.0, t, 1.0); + m_removedStop.position = qBound(0.0, t, 1.0); const int newPos = getNewInsertPosition(m_removedStop, stops); stops.insert(newPos, m_removedStop); m_selectedStop = newPos; } else { KoGradientStop draggedStop = stops[m_selectedStop]; - draggedStop.first = qBound(0.0, t, 1.0); + draggedStop.position = qBound(0.0, t, 1.0); stops.removeAt(m_selectedStop); const int newPos = getNewInsertPosition(draggedStop, stops); stops.insert(newPos, draggedStop); m_selectedStop = newPos; } } m_gradient->setStops(stops); emit sigSelectedStop(m_selectedStop); update(); } else { QWidget::mouseMoveEvent(e); } } void KisStopGradientSliderWidget::updateCursor(const QPoint &pos) { const bool isInAllowedRegion = allowedClickRegion(handleClickTolerance()).contains(pos); QCursor currentCursor; if (isInAllowedRegion) { - const QRect handlesRect = this->handlesStipeRect(); + const QRect handlesRect = this->handlesStripeRect(); const qreal t = (qreal(pos.x()) - handlesRect.x()) / handlesRect.width(); const QList stops = m_gradient->stops(); const int clickedStop = findNearestHandle(t, qreal(handleClickTolerance()) / handlesRect.width(), stops); if (clickedStop >= 0) { currentCursor = m_drag ? Qt::ClosedHandCursor : Qt::OpenHandCursor; } } if (currentCursor.shape() != Qt::ArrowCursor) { setCursor(currentCursor); } else { unsetCursor(); } } void KisStopGradientSliderWidget::insertStop(double t) { KIS_ASSERT_RECOVER(t >= 0 && t <= 1.0 ) { t = qBound(0.0, t, 1.0); } QList stops = m_gradient->stops(); KoColor color; m_gradient->colorAt(color, t); - const KoGradientStop stop(t, color); + const KoGradientStop stop(t, color, COLORSTOP); const int newPos = getNewInsertPosition(stop, stops); stops.insert(newPos, stop); m_gradient->setStops(stops); m_selectedStop = newPos; emit sigSelectedStop(m_selectedStop); } QRect KisStopGradientSliderWidget::sliderRect() const { return QRect(QPoint(), size()).adjusted(m_handleSize.width(), 1, -m_handleSize.width(), -1); } QRect KisStopGradientSliderWidget::gradientStripeRect() const { const QRect rc = sliderRect(); return rc.adjusted(0, 0, 0, -m_handleSize.height()); } -QRect KisStopGradientSliderWidget::handlesStipeRect() const +QRect KisStopGradientSliderWidget::handlesStripeRect() const { const QRect rc = sliderRect(); return rc.adjusted(0, rc.height() - m_handleSize.height(), 0, 0); } QRegion KisStopGradientSliderWidget::allowedClickRegion(int tolerance) const { QRegion result; result += sliderRect(); - result += handlesStipeRect().adjusted(-tolerance, 0, tolerance, 0); + result += handlesStripeRect().adjusted(-tolerance, 0, tolerance, 0); return result; } int KisStopGradientSliderWidget::selectedStop() { return m_selectedStop; } void KisStopGradientSliderWidget::setSelectedStop(int selected) { m_selectedStop = selected; emit sigSelectedStop(m_selectedStop); update(); } int KisStopGradientSliderWidget::minimalHeight() const { QFontMetrics fm(font()); const int h = fm.height(); QStyleOptionToolButton opt; QSize sz = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(h, h), this); return sz.height() + m_handleSize.height(); } QSize KisStopGradientSliderWidget::sizeHint() const { const int h = minimalHeight(); return QSize(2 * h, h); } QSize KisStopGradientSliderWidget::minimumSizeHint() const { const int h = minimalHeight(); return QSize(h, h); } diff --git a/libs/ui/widgets/kis_stopgradient_slider_widget.h b/libs/ui/widgets/kis_stopgradient_slider_widget.h index f00d7f83c4..82837bd1ab 100644 --- a/libs/ui/widgets/kis_stopgradient_slider_widget.h +++ b/libs/ui/widgets/kis_stopgradient_slider_widget.h @@ -1,82 +1,82 @@ /* * Copyright (c) 2004 Cyrille Berger * 2016 Sven Langkamp * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_STOP_GRADIENT_SLIDER_WIDGET_H_ #define _KIS_STOP_GRADIENT_SLIDER_WIDGET_H_ #include #include #include #include #include class KisStopGradientSliderWidget : public QWidget { Q_OBJECT public: KisStopGradientSliderWidget(QWidget *parent = 0, Qt::WindowFlags f = 0); public: void paintEvent(QPaintEvent *) override; void setGradientResource(KoStopGradient* gradient); int selectedStop(); void setSelectedStop(int selected); QSize sizeHint() const override; QSize minimumSizeHint() const override; Q_SIGNALS: void sigSelectedStop(int stop); protected: void mousePressEvent(QMouseEvent * e) override; void mouseReleaseEvent(QMouseEvent * e) override; void mouseMoveEvent(QMouseEvent * e) override; private Q_SLOTS: void updateHandleSize(); private: void insertStop(double t); QRect sliderRect() const; QRect gradientStripeRect() const; - QRect handlesStipeRect() const; + QRect handlesStripeRect() const; QRegion allowedClickRegion(int tolerance) const; void updateCursor(const QPoint &pos); - void paintHandle(qreal position, const QColor &color, bool isSelected, QPainter *painter); + void paintHandle(qreal position, const QColor &color, bool isSelected, QString text, QPainter *painter); int handleClickTolerance() const; int minimalHeight() const; private: QScopedPointer m_defaultGradient; KoStopGradient* m_gradient; int m_selectedStop; KoGradientStop m_removedStop; bool m_drag; QSize m_handleSize; }; #endif diff --git a/libs/widgets/KisGradientSliderWidget.cpp b/libs/widgets/KisGradientSliderWidget.cpp index 924cb57533..2255eb5a9e 100644 --- a/libs/widgets/KisGradientSliderWidget.cpp +++ b/libs/widgets/KisGradientSliderWidget.cpp @@ -1,225 +1,239 @@ /* * Copyright (c) 2004 Cyrille Berger * 2004 Sven Langkamp * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisGradientSliderWidget.h" #include #include #include #include #include #include #include #include #include #include #include #define MARGIN 5 #define HANDLE_SIZE 10 +#define MIN_HEIGHT 60 KisGradientSliderWidget::KisGradientSliderWidget(QWidget *parent, const char* name, Qt::WindowFlags f) : QWidget(parent, f), m_currentSegment(0), m_selectedSegment(0), m_drag(0) { setObjectName(name); - setMinimumHeight(30); + setMinimumHeight(MIN_HEIGHT); m_segmentMenu = new QMenu(); m_segmentMenu->addAction(i18n("Split Segment"), this, SLOT(slotSplitSegment())); m_segmentMenu->addAction(i18n("Duplicate Segment"), this, SLOT(slotDuplicateSegment())); m_segmentMenu->addAction(i18n("Mirror Segment"), this, SLOT(slotMirrorSegment())); m_removeSegmentAction = new QAction(i18n("Remove Segment"), this); connect(m_removeSegmentAction, SIGNAL(triggered()), this, SLOT(slotRemoveSegment())); m_segmentMenu->addAction(m_removeSegmentAction); } void KisGradientSliderWidget::setGradientResource(KoSegmentGradient* agr) { m_autogradientResource = agr; m_selectedSegment = m_autogradientResource->segmentAt(0.0); emit sigSelectedSegment(m_selectedSegment); } + + +void KisGradientSliderWidget::paintSegmentHandle(int position, const QString text, const QPoint& textPos, QPainter& painter) +{ + QPolygon triangle(3); + triangle[0] = QPoint(position, height() - HANDLE_SIZE - MARGIN); + triangle[1] = QPoint(position + (HANDLE_SIZE / 2 - 1), height() - MARGIN); + triangle[2] = QPoint(position - (HANDLE_SIZE / 2 - 1), height() - MARGIN); + painter.drawPolygon(triangle); + painter.drawText(textPos, text); +} + void KisGradientSliderWidget::paintEvent(QPaintEvent* pe) { QWidget::paintEvent(pe); QPainter painter(this); painter.fillRect(rect(), palette().window()); painter.setPen(Qt::black); painter.drawRect(MARGIN, MARGIN, width() - 2 * MARGIN, height() - 2 * MARGIN - HANDLE_SIZE); if (m_autogradientResource) { QImage image = m_autogradientResource->generatePreview(width() - 2 * MARGIN - 2, height() - 2 * MARGIN - HANDLE_SIZE - 2); QPixmap pixmap(image.width(), image.height()); if (!image.isNull()) { painter.drawImage(MARGIN + 1, MARGIN + 1, image); } painter.fillRect(MARGIN + 1, height() - MARGIN - HANDLE_SIZE, width() - 2 * MARGIN, HANDLE_SIZE, QBrush(Qt::white)); if (m_selectedSegment) { QRect selection(qRound(m_selectedSegment->startOffset()*(double)(width() - 2 * MARGIN - 2)) + 6, height() - HANDLE_SIZE - MARGIN, qRound((m_selectedSegment->endOffset() - m_selectedSegment->startOffset())*(double)(width() - 12)), HANDLE_SIZE); painter.fillRect(selection, QBrush(palette().highlight())); } - QPolygon triangle(3); - QList handlePositions = m_autogradientResource->getHandlePositions(); - int position; - painter.setBrush(QBrush(Qt::black)); - for (int i = 0; i < handlePositions.count(); i++) { - position = qRound(handlePositions[i] * (double)(width() - 12)) + 6; - triangle[0] = QPoint(position, height() - HANDLE_SIZE - MARGIN); - triangle[1] = QPoint(position + (HANDLE_SIZE / 2 - 1), height() - MARGIN); - triangle[2] = QPoint(position - (HANDLE_SIZE / 2 - 1), height() - MARGIN); - painter.drawPolygon(triangle); - } - painter.setBrush(QBrush(Qt::white)); - QList middleHandlePositions = m_autogradientResource->getMiddleHandlePositions(); - for (int i = 0; i < middleHandlePositions.count(); i++) { - position = qRound(middleHandlePositions[i] * (double)(width() - 12)) + 6; - triangle[0] = QPoint(position, height() - HANDLE_SIZE - MARGIN); - triangle[1] = QPoint(position + (HANDLE_SIZE / 2 - 2), height() - MARGIN); - triangle[2] = QPoint(position - (HANDLE_SIZE / 2 - 2), height() - MARGIN); - painter.drawPolygon(triangle); + QList segments = m_autogradientResource->segments(); + for (int i = 0; i < segments.count(); i++) { + KoGradientSegment* segment = segments[i]; + + //paint segment start + int position = qRound(segment->startOffset() * (double)(width() - 12)) + 6; + QPoint textPos(position, height() - 2 * (HANDLE_SIZE + MARGIN)); + QString text = segment->startType() == FOREGROUND_ENDPOINT ? "FG" : (segment->startType() == BACKGROUND_ENDPOINT ? "BG" : ""); + paintSegmentHandle(position, text, textPos, painter); + + //paint segment end + position = qRound(segment->endOffset() * (double)(width() - 12)) + 6; + textPos.setX(position - HANDLE_SIZE); + text = segment->endType() == FOREGROUND_ENDPOINT ? "FG" : (segment->endType() == BACKGROUND_ENDPOINT ? "BG" : ""); + paintSegmentHandle(position, text, textPos, painter); + + //paint midpoint + position = qRound(segment->middleOffset() * (double)(width() - 12)) + 6; + painter.setBrush(QBrush(Qt::white)); + paintSegmentHandle(position, "", textPos, painter); } } } void KisGradientSliderWidget::mousePressEvent(QMouseEvent * e) { if ((e->y() < MARGIN || e->y() > height() - MARGIN) || (e->x() < MARGIN || e->x() > width() - MARGIN) || e-> button() != Qt::LeftButton) { QWidget::mousePressEvent(e); return; } double t = (double)(e->x() - MARGIN) / (double)(width() - 2 * MARGIN); KoGradientSegment* segment = 0; segment = m_autogradientResource->segmentAt(t); if (segment != 0) { m_currentSegment = segment; QRect leftHandle(qRound(m_currentSegment->startOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 1)), height() - HANDLE_SIZE, HANDLE_SIZE - 1, HANDLE_SIZE); QRect middleHandle(qRound(m_currentSegment->middleOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 2)), height() - HANDLE_SIZE - MARGIN, HANDLE_SIZE - 1, HANDLE_SIZE); QRect rightHandle(qRound(m_currentSegment->endOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 1)), height() - HANDLE_SIZE, HANDLE_SIZE - 1, HANDLE_SIZE); // Change the activation order of the handles to avoid deadlocks if (t > 0.5) { if (leftHandle.contains(e->pos())) m_drag = LEFT_DRAG; else if (middleHandle.contains(e->pos())) m_drag = MIDDLE_DRAG; else if (rightHandle.contains(e->pos())) m_drag = RIGHT_DRAG; } else { if (rightHandle.contains(e->pos())) m_drag = RIGHT_DRAG; else if (middleHandle.contains(e->pos())) m_drag = MIDDLE_DRAG; else if (leftHandle.contains(e->pos())) m_drag = LEFT_DRAG; } if (m_drag == NO_DRAG) { m_selectedSegment = m_currentSegment; emit sigSelectedSegment(m_selectedSegment); } } repaint(); } void KisGradientSliderWidget::mouseReleaseEvent(QMouseEvent * e) { Q_UNUSED(e); m_drag = NO_DRAG; } void KisGradientSliderWidget::mouseMoveEvent(QMouseEvent * e) { if ((e->y() < MARGIN || e->y() > height() - MARGIN) || (e->x() < MARGIN || e->x() > width() - MARGIN)) { QWidget::mouseMoveEvent(e); return; } double t = (double)(e->x() - MARGIN) / (double)(width() - 2 * MARGIN); switch (m_drag) { case RIGHT_DRAG: m_autogradientResource->moveSegmentEndOffset(m_currentSegment, t); break; case LEFT_DRAG: m_autogradientResource->moveSegmentStartOffset(m_currentSegment, t); break; case MIDDLE_DRAG: m_autogradientResource->moveSegmentMiddleOffset(m_currentSegment, t); break; } if (m_drag != NO_DRAG) emit sigChangedSegment(m_currentSegment); repaint(); } void KisGradientSliderWidget::contextMenuEvent(QContextMenuEvent * e) { m_removeSegmentAction->setEnabled(m_autogradientResource->removeSegmentPossible()); m_segmentMenu->popup(e->globalPos()); } void KisGradientSliderWidget::slotSplitSegment() { m_autogradientResource->splitSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } void KisGradientSliderWidget::slotDuplicateSegment() { m_autogradientResource->duplicateSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } void KisGradientSliderWidget::slotMirrorSegment() { m_autogradientResource->mirrorSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } void KisGradientSliderWidget::slotRemoveSegment() { m_selectedSegment = m_autogradientResource->removeSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } diff --git a/libs/widgets/KisGradientSliderWidget.h b/libs/widgets/KisGradientSliderWidget.h index d48866ead0..3c40b67915 100644 --- a/libs/widgets/KisGradientSliderWidget.h +++ b/libs/widgets/KisGradientSliderWidget.h @@ -1,91 +1,93 @@ /* * Copyright (c) 2004 Cyrille Berger * 2004 Sven Langkamp * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_GRADIENT_SLIDER_WIDGET_H_ #define _KIS_GRADIENT_SLIDER_WIDGET_H_ #include #include #include class QAction; class QMenu; class KoGradientSegment; class KoSegmentGradient; #include "kritawidgets_export.h" /** * @brief The KisGradientSliderWidget class makes it possible to edit gradients. */ class KRITAWIDGETS_EXPORT KisGradientSliderWidget : public QWidget { Q_OBJECT public: KisGradientSliderWidget(QWidget *parent = 0, const char* name = 0, Qt::WindowFlags f = 0); public: void paintEvent(QPaintEvent *) override; void setGradientResource(KoSegmentGradient* agr); KoGradientSegment* selectedSegment() { return m_selectedSegment; } Q_SIGNALS: void sigSelectedSegment(KoGradientSegment*); void sigChangedSegment(KoGradientSegment*); protected: void mousePressEvent(QMouseEvent * e) override; void mouseReleaseEvent(QMouseEvent * e) override; void mouseMoveEvent(QMouseEvent * e) override; void contextMenuEvent(QContextMenuEvent * e) override; + void paintSegmentHandle(int position, const QString text, const QPoint& textPos, QPainter& painter); + private Q_SLOTS: void slotSplitSegment(); void slotDuplicateSegment(); void slotMirrorSegment(); void slotRemoveSegment(); private: enum { NO_DRAG, LEFT_DRAG, RIGHT_DRAG, MIDDLE_DRAG }; enum { SPLIT_SEGMENT, DUPLICATE_SEGMENT, MIRROR_SEGMENT, REMOVE_SEGMENT }; KoSegmentGradient* m_autogradientResource; KoGradientSegment* m_currentSegment; KoGradientSegment* m_selectedSegment; QMenu* m_segmentMenu; int m_drag; QAction *m_removeSegmentAction; }; #endif diff --git a/libs/widgets/KoResourceServerProvider.cpp b/libs/widgets/KoResourceServerProvider.cpp index 8bb755f7a4..cf8ac143f7 100644 --- a/libs/widgets/KoResourceServerProvider.cpp +++ b/libs/widgets/KoResourceServerProvider.cpp @@ -1,215 +1,217 @@ /* This file is part of the KDE project Copyright (c) 1999 Matthias Elter Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Sven Langkamp Copyright (C) 2011 Srikanth Tiyyagura This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "KoResourceServerProvider.h" #include #include #include #include #include #include #include #include #include "KoColorSpaceRegistry.h" #include "KoResourcePaths.h" #include "klocalizedstring.h" #include using namespace std; class GradientResourceServer : public KoResourceServer { public: GradientResourceServer(const QString& type, const QString& extensions) : KoResourceServer(type, extensions) , m_foregroundToTransparent(0) , m_foregroundToBackground(0) { insertSpecialGradients(); } void insertSpecialGradients() { const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8(); QList stops; KoStopGradient* gradient = new KoStopGradient(); gradient->setType(QGradient::LinearGradient); gradient->setName(i18n("Foreground to Transparent")); - stops << KoGradientStop(0.0, KoColor(Qt::black, cs)) << KoGradientStop(1.0, KoColor(QColor(0, 0, 0, 0), cs)); + stops << KoGradientStop(0.0, KoColor(Qt::black, cs), FOREGROUNDSTOP); + stops << KoGradientStop(1.0, KoColor(QColor(0, 0, 0, 0), cs), COLORSTOP); gradient->setStops(stops); gradient->setValid(true); gradient->setPermanent(true); addResource(gradient, false, true); m_foregroundToTransparent = gradient; gradient = new KoStopGradient(); gradient->setType(QGradient::LinearGradient); gradient->setName(i18n("Foreground to Background")); stops.clear(); - stops << KoGradientStop(0.0, KoColor(Qt::black, cs)) << KoGradientStop(1.0, KoColor(Qt::white, cs)); + stops << KoGradientStop(0.0, KoColor(Qt::black, cs), FOREGROUNDSTOP); + stops << KoGradientStop(1.0, KoColor(Qt::white, cs), BACKGROUNDSTOP); gradient->setStops(stops); gradient->setValid(true); gradient->setPermanent(true); addResource(gradient, false, true); m_foregroundToBackground = gradient; } private: friend class KoResourceBundle; KoAbstractGradient* createResource( const QString & filename ) override { QString fileExtension; int index = filename.lastIndexOf('.'); if (index != -1) fileExtension = filename.mid(index).toLower(); KoAbstractGradient* grad = 0; if(fileExtension == ".svg" || fileExtension == ".kgr") grad = new KoStopGradient(filename); else if(fileExtension == ".ggr" ) grad = new KoSegmentGradient(filename); return grad; } QList< KoAbstractGradient* > sortedResources() override { QList< KoAbstractGradient* > resources = KoResourceServer::sortedResources(); QList< KoAbstractGradient* > sorted; if (m_foregroundToTransparent && resources.contains(m_foregroundToTransparent)) { sorted.append(resources.takeAt(resources.indexOf(m_foregroundToTransparent))); } if (m_foregroundToBackground && resources.contains(m_foregroundToBackground)) { sorted.append(resources.takeAt(resources.indexOf(m_foregroundToBackground))); } return sorted + resources; } KoAbstractGradient* m_foregroundToTransparent; KoAbstractGradient* m_foregroundToBackground; }; struct Q_DECL_HIDDEN KoResourceServerProvider::Private { KoResourceServer* patternServer; KoResourceServer* gradientServer; KoResourceServer* paletteServer; KoResourceServer *svgSymbolCollectionServer; KoResourceServer* gamutMaskServer; #if defined HAVE_SEEXPR KoResourceServer* seExprScriptServer; #endif }; KoResourceServerProvider::KoResourceServerProvider() : d(new Private) { d->patternServer = new KoResourceServerSimpleConstruction("ko_patterns", "*.pat:*.jpg:*.gif:*.png:*.tif:*.xpm:*.bmp" ); d->patternServer->loadResources(blacklistFileNames(d->patternServer->fileNames(), d->patternServer->blackListedFiles())); d->gradientServer = new GradientResourceServer("ko_gradients", "*.svg:*.ggr"); d->gradientServer->loadResources(blacklistFileNames(d->gradientServer->fileNames(), d->gradientServer->blackListedFiles())); d->paletteServer = new KoResourceServerSimpleConstruction("ko_palettes", "*.kpl:*.gpl:*.pal:*.act:*.aco:*.css:*.colors:*.xml:*.sbz"); d->paletteServer->loadResources(blacklistFileNames(d->paletteServer->fileNames(), d->paletteServer->blackListedFiles())); d->svgSymbolCollectionServer = new KoResourceServerSimpleConstruction("symbols", "*.svg"); d->svgSymbolCollectionServer->loadResources(blacklistFileNames(d->svgSymbolCollectionServer->fileNames(), d->svgSymbolCollectionServer->blackListedFiles())); d->gamutMaskServer = new KoResourceServerSimpleConstruction("ko_gamutmasks", "*.kgm"); d->gamutMaskServer->loadResources(blacklistFileNames(d->gamutMaskServer->fileNames(), d->gamutMaskServer->blackListedFiles())); #if defined HAVE_SEEXPR d->seExprScriptServer = new KoResourceServerSimpleConstruction("kis_seexpr_scripts", "*.kse"); d->seExprScriptServer->loadResources(blacklistFileNames(d->seExprScriptServer->fileNames(), d->seExprScriptServer->blackListedFiles())); #endif } KoResourceServerProvider::~KoResourceServerProvider() { delete d->patternServer; delete d->gradientServer; delete d->paletteServer; delete d->svgSymbolCollectionServer; delete d->gamutMaskServer; #if defined HAVE_SEEXPR delete d->seExprScriptServer; #endif delete d; } Q_GLOBAL_STATIC(KoResourceServerProvider, s_instance) KoResourceServerProvider* KoResourceServerProvider::instance() { return s_instance; } QStringList KoResourceServerProvider::blacklistFileNames(QStringList fileNames, const QStringList &blacklistedFileNames) { if (!blacklistedFileNames.isEmpty()) { foreach (const QString &s, blacklistedFileNames) { fileNames.removeAll(s); } } return fileNames; } KoResourceServer* KoResourceServerProvider::patternServer() { return d->patternServer; } KoResourceServer* KoResourceServerProvider::gradientServer() { return d->gradientServer; } KoResourceServer* KoResourceServerProvider::paletteServer() { return d->paletteServer; } KoResourceServer *KoResourceServerProvider::svgSymbolCollectionServer() { return d->svgSymbolCollectionServer; } KoResourceServer* KoResourceServerProvider::gamutMaskServer() { return d->gamutMaskServer; } #if defined HAVE_SEEXPR KoResourceServer *KoResourceServerProvider::seExprScriptServer() { return d->seExprScriptServer; } #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp index 2918507f00..07ae34a9a9 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp @@ -1,117 +1,122 @@ /* * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "RgbF16ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOpIn.h" #include "compositeops/RgbCompositeOpOut.h" #include "compositeops/RgbCompositeOpBumpmap.h" #include #include RgbF16ColorSpace::RgbF16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_RGBA_HALF_FLT, cmsSigRgbData, p) { addChannel(new KoChannelInfo(i18n("Red"), 0 * sizeof(half), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Green"), 1 * sizeof(half), 1, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Blue"), 2 * sizeof(half), 2, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(half), 3, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT16, 2)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } bool RgbF16ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *RgbF16ColorSpace::clone() const { return new RgbF16ColorSpace(name(), profile()->clone()); } void RgbF16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoRgbF16Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->red))); labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->green))); labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbF16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoRgbF16Traits::Pixel *p = reinterpret_cast(pixel); p->red = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); p->green = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p->blue = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = 1.0; } void RgbF16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbF16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbF16ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); +} + +void RgbF16ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); } diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h index 249a5f6c12..966b79bbcb 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h @@ -1,123 +1,124 @@ /* * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KORGBF16COLORSPACE_H_ #define KORGBF16COLORSPACE_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" struct KoRgbF16Traits; class RgbF16ColorSpace : public LcmsColorSpace { public: RgbF16ColorSpace(const QString &name, KoColorProfile *p); bool willDegrade(ColorSpaceIndependence independence) const override; KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Float16BitsColorDepthID; } virtual KoColorSpace *clone() const; void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const override; void colorFromXML(quint8* pixel, const QDomElement& elt) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; static QString colorSpaceId() { return QString("RGBAF16"); } bool hasHighDynamicRange() const override { return true; } void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class RgbF16ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbF16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_RGBA_HALF_FLT, cmsSigRgbData) { } QString id() const override { return RgbF16ColorSpace::colorSpaceId(); } QString name() const override { return QString("%1 (%2)").arg(RGBAColorModelID.name()).arg(Float16BitsColorDepthID.name()); } bool userVisible() const override { return true; } KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Float16BitsColorDepthID; } int referenceDepth() const override { return 16; } KoColorSpace *createColorSpace(const KoColorProfile *p) const override { return new RgbF16ColorSpace(name(), p->clone()); } QString defaultProfile() const override { return "sRGB-elle-V2-g10.icc"; } bool isHdr() const override { return true; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp index d42aee9260..0cf2e8cadc 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp @@ -1,122 +1,127 @@ /* * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "RgbF32ColorSpace.h" #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOps.h" #include #include #include #include RgbF32ColorSpace::RgbF32ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_RGBA_FLT, cmsSigRgbData, p) { const IccColorProfile *icc_p = dynamic_cast(p); Q_ASSERT(icc_p); QVector uiRanges(icc_p->getFloatUIMinMax()); Q_ASSERT(uiRanges.size() == 3); addChannel(new KoChannelInfo(i18n("Red"), 0 * sizeof(float), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, 4, QColor(255, 0, 0), uiRanges[0])); addChannel(new KoChannelInfo(i18n("Green"), 1 * sizeof(float), 1, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, 4, QColor(0, 255, 0), uiRanges[1])); addChannel(new KoChannelInfo(i18n("Blue"), 2 * sizeof(float), 2, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, 4, QColor(0, 0, 255), uiRanges[2])); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(float), 3, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT32, 4)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } bool RgbF32ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *RgbF32ColorSpace::clone() const { return new RgbF32ColorSpace(name(), profile()->clone()); } void RgbF32ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoRgbF32Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->red))); labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->green))); labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbF32ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoRgbF32Traits::Pixel *p = reinterpret_cast(pixel); p->red = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); p->green = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p->blue = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = 1.0; } void RgbF32ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF32ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbF32ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF32ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbF32ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); +} + +void RgbF32ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); } diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h index 89580dc3be..ce3e713a03 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h @@ -1,125 +1,126 @@ /* * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KORGBF32COLORSPACE_H_ #define KORGBF32COLORSPACE_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" struct KoRgbF32Traits; class RgbF32ColorSpace : public LcmsColorSpace { public: RgbF32ColorSpace(const QString &name, KoColorProfile *p); bool willDegrade(ColorSpaceIndependence independence) const override; KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Float32BitsColorDepthID; } virtual KoColorSpace *clone() const; void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const override; void colorFromXML(quint8* pixel, const QDomElement& elt) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; static QString colorSpaceId() { return QString("RGBAF32"); } bool hasHighDynamicRange() const override { return true; } void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class RgbF32ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbF32ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_RGBA_FLT, cmsSigRgbData) { } QString id() const override { return RgbF32ColorSpace::colorSpaceId(); } QString name() const override { return QString("%1 (%2)").arg(RGBAColorModelID.name()).arg(Float32BitsColorDepthID.name()); } bool userVisible() const override { return true; } KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Float32BitsColorDepthID; } int referenceDepth() const override { return 32; } KoColorSpace *createColorSpace(const KoColorProfile *p) const override { return new RgbF32ColorSpace(name(), p->clone()); } QString defaultProfile() const override { return "sRGB-elle-V2-g10.icc"; } bool isHdr() const override { return true; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp index 1656b77d00..40f1ed1dc3 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp @@ -1,114 +1,119 @@ /* * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "RgbU16ColorSpace.h" #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOps.h" #include "kis_dom_utils.h" #include #include RgbU16ColorSpace::RgbU16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_BGRA_16, cmsSigRgbData, p) { addChannel(new KoChannelInfo(i18n("Blue"), 0 * sizeof(quint16), 2, KoChannelInfo::COLOR, KoChannelInfo::UINT16, 2, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Green"), 1 * sizeof(quint16), 1, KoChannelInfo::COLOR, KoChannelInfo::UINT16, 2, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Red"), 2 * sizeof(quint16), 0, KoChannelInfo::COLOR, KoChannelInfo::UINT16, 2, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(quint16), 3, KoChannelInfo::ALPHA, KoChannelInfo::UINT16, 2)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } bool RgbU16ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA8) { return true; } else { return false; } } KoColorSpace *RgbU16ColorSpace::clone() const { return new RgbU16ColorSpace(name(), profile()->clone()); } void RgbU16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoBgrU16Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->red))); labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->green))); labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbU16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoBgrU16Traits::Pixel *p = reinterpret_cast(pixel); p->red = KoColorSpaceMaths< qreal, KoBgrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); p->green = KoColorSpaceMaths< qreal, KoBgrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p->blue = KoColorSpaceMaths< qreal, KoBgrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = KoColorSpaceMathsTraits::max; } void RgbU16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbU16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } -void RgbU16ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const +void RgbU16ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8* dst, const QRgb* brush, quint8* brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); +} + +void RgbU16ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); } diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h index c94559c4a2..7c37565c96 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h @@ -1,106 +1,107 @@ /* * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KORGBU16COLORSPACE_H_ #define KORGBU16COLORSPACE_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" struct KoBgrU16Traits; class RgbU16ColorSpace : public LcmsColorSpace { public: RgbU16ColorSpace(const QString &name, KoColorProfile *p); bool willDegrade(ColorSpaceIndependence independence) const override; KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Integer16BitsColorDepthID; } virtual KoColorSpace *clone() const; void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const override; void colorFromXML(quint8* pixel, const QDomElement& elt) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; static QString colorSpaceId() { return QString("RGBA16"); } void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class RgbU16ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbU16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_BGRA_16, cmsSigRgbData) { } QString id() const override { return RgbU16ColorSpace::colorSpaceId(); } QString name() const override { return QString("%1 (%2)").arg(RGBAColorModelID.name()).arg(Integer16BitsColorDepthID.name()); } bool userVisible() const override { return true; } KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Integer16BitsColorDepthID; } int referenceDepth() const override { return 16; } KoColorSpace *createColorSpace(const KoColorProfile *p) const override { return new RgbU16ColorSpace(name(), p->clone()); } QString defaultProfile() const override { return "sRGB-elle-V2-g10.icc";//this is a linear space, because 16bit is enough to only enjoy advantages of linear space } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp index ed1834fc11..cb6797e4f3 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp @@ -1,118 +1,123 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "RgbU8ColorSpace.h" #include #include #include #include #include #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOps.h" #include #include #define downscale(quantum) (quantum) //((unsigned char) ((quantum)/257UL)) #define upscale(value) (value) // ((quint8) (257UL*(value))) RgbU8ColorSpace::RgbU8ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_BGRA_8, cmsSigRgbData, p) { addChannel(new KoChannelInfo(i18n("Blue"), 0, 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Green"), 1, 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Red"), 2, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Alpha"), 3, 3, KoChannelInfo::ALPHA, KoChannelInfo::UINT8)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } KoColorSpace *RgbU8ColorSpace::clone() const { return new RgbU8ColorSpace(name(), profile()->clone()); } void RgbU8ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoBgrU8Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->red))); labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->green))); labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbU8ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoBgrU8Traits::Pixel *p = reinterpret_cast(pixel); p->red = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); p->green = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p->blue = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = KoColorSpaceMathsTraits::max; } quint8 RgbU8ColorSpace::intensity8(const quint8 *src) const { const KoBgrU8Traits::Pixel *p = reinterpret_cast(src); return (quint8)(p->red * 0.30 + p->green * 0.59 + p->blue * 0.11); } void RgbU8ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU8ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbU8ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU8ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } -void RgbU8ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const +void RgbU8ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8* dst, const QRgb* brush, quint8* brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); +} + +void RgbU8ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); } diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h index 91c1208991..2fc124bcdc 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h @@ -1,118 +1,119 @@ /* * Copyright (c) 2002 Patrick Julien * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KO_STRATEGY_COLORSPACE_RGB_H_ #define KO_STRATEGY_COLORSPACE_RGB_H_ #include #include #include "KoColorModelStandardIds.h" struct KoBgrU8Traits; class RgbU8ColorSpace : public LcmsColorSpace { public: RgbU8ColorSpace(const QString &name, KoColorProfile *p); bool willDegrade(ColorSpaceIndependence) const override { return false; } KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Integer8BitsColorDepthID; } virtual KoColorSpace *clone() const; void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const override; void colorFromXML(quint8 *pixel, const QDomElement &elt) const override; quint8 intensity8(const quint8 * src) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; static QString colorSpaceId() { return QString("RGBA"); } void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class RgbU8ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbU8ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_BGRA_8, cmsSigRgbData) {} bool userVisible() const override { return true; } QString id() const override { return RgbU8ColorSpace::colorSpaceId(); } QString name() const override { return QString("%1 (%2)").arg(RGBAColorModelID.name()).arg(Integer8BitsColorDepthID.name()); } KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Integer8BitsColorDepthID; } int referenceDepth() const override { return 8; } KoColorSpace *createColorSpace(const KoColorProfile *p) const override { return new RgbU8ColorSpace(name(), p->clone()); } QString defaultProfile() const override { return "sRGB-elle-V2-srgbtrc.icc"; } }; #endif // KO_STRATEGY_COLORSPACE_RGB_H_ diff --git a/plugins/dockers/palettedocker/palettedocker_dock.cpp b/plugins/dockers/palettedocker/palettedocker_dock.cpp index dc9f32b4e9..a580209519 100644 --- a/plugins/dockers/palettedocker/palettedocker_dock.cpp +++ b/plugins/dockers/palettedocker/palettedocker_dock.cpp @@ -1,437 +1,440 @@ /* * Copyright (c) 2013 Sven Langkamp * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "palettedocker_dock.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_wdgpalettedock.h" PaletteDockerDock::PaletteDockerDock( ) : QDockWidget(i18n("Palette")) , m_ui(new Ui_WdgPaletteDock()) , m_model(new KisPaletteModel(this)) , m_paletteChooser(new KisPaletteListWidget(this)) , m_view(0) , m_resourceProvider(0) , m_rServer(KoResourceServerProvider::instance()->paletteServer()) , m_activeDocument(0) , m_paletteEditor(new KisPaletteEditor) , m_actAdd(new QAction(KisIconUtils::loadIcon("list-add"), i18n("Add a color"))) , m_actRemove(new QAction(KisIconUtils::loadIcon("edit-delete"), i18n("Delete color"))) , m_actModify(new QAction(KisIconUtils::loadIcon("edit-rename"), i18n("Modify this spot"))) , m_actEditPalette(new QAction(KisIconUtils::loadIcon("groupLayer"), i18n("Edit this palette"))) , m_colorSelfUpdate(false) { QWidget *mainWidget = new QWidget(this); setWidget(mainWidget); m_ui->setupUi(mainWidget); m_ui->bnAdd->setDefaultAction(m_actAdd.data()); m_ui->bnRemove->setDefaultAction(m_actRemove.data()); m_ui->bnRename->setDefaultAction(m_actModify.data()); m_ui->bnEditPalette->setDefaultAction(m_actEditPalette.data()); // to make sure their icons have the same size m_ui->bnRemove->setIconSize(QSize(16, 16)); m_ui->bnRename->setIconSize(QSize(16, 16)); m_ui->bnAdd->setIconSize(QSize(16, 16)); m_ui->bnEditPalette->setIconSize(QSize(16, 16)); m_ui->paletteView->setPaletteModel(m_model); m_ui->paletteView->setAllowModification(true); m_ui->cmbNameList->setCompanionView(m_ui->paletteView); m_paletteEditor->setPaletteModel(m_model); connect(m_actAdd.data(), SIGNAL(triggered()), SLOT(slotAddColor())); connect(m_actRemove.data(), SIGNAL(triggered()), SLOT(slotRemoveColor())); connect(m_actModify.data(), SIGNAL(triggered()), SLOT(slotEditEntry())); connect(m_actEditPalette.data(), SIGNAL(triggered()), SLOT(slotEditPalette())); connect(m_ui->paletteView, SIGNAL(sigIndexSelected(QModelIndex)), SLOT(slotPaletteIndexSelected(QModelIndex))); connect(m_ui->paletteView, SIGNAL(clicked(QModelIndex)), SLOT(slotPaletteIndexClicked(QModelIndex))); connect(m_ui->paletteView, SIGNAL(doubleClicked(QModelIndex)), SLOT(slotPaletteIndexDoubleClicked(QModelIndex))); connect(m_ui->cmbNameList, SIGNAL(sigColorSelected(const KoColor&)), SLOT(slotNameListSelection(const KoColor&))); m_viewContextMenu.addAction(m_actModify.data()); m_viewContextMenu.addAction(m_actRemove.data()); connect(m_ui->paletteView, SIGNAL(pressed(QModelIndex)), SLOT(slotContextMenu(QModelIndex))); m_paletteChooser->setAllowModification(true); connect(m_paletteChooser, SIGNAL(sigPaletteSelected(KoColorSet*)), SLOT(slotSetColorSet(KoColorSet*))); connect(m_paletteChooser, SIGNAL(sigAddPalette()), SLOT(slotAddPalette())); connect(m_paletteChooser, SIGNAL(sigImportPalette()), SLOT(slotImportPalette())); connect(m_paletteChooser, SIGNAL(sigRemovePalette(KoColorSet*)), SLOT(slotRemovePalette(KoColorSet*))); connect(m_paletteChooser, SIGNAL(sigExportPalette(KoColorSet*)), SLOT(slotExportPalette(KoColorSet*))); m_ui->bnColorSets->setIcon(KisIconUtils::loadIcon("hi16-palette_library")); m_ui->bnColorSets->setToolTip(i18n("Choose palette")); m_ui->bnColorSets->setPopupWidget(m_paletteChooser); KisConfig cfg(true); QString defaultPaletteName = cfg.defaultPalette(); KoColorSet* defaultPalette = m_rServer->resourceByName(defaultPaletteName); if (defaultPalette) { slotSetColorSet(defaultPalette); } else { m_ui->bnAdd->setEnabled(false); m_ui->bnRename->setEnabled(false); m_ui->bnRemove->setEnabled(false); m_ui->bnEditPalette->setEnabled(false); m_ui->paletteView->setAllowModification(false); } KoResourceServer *srv = KoResourceServerProvider::instance()->paletteServer(); srv->addObserver(this); } PaletteDockerDock::~PaletteDockerDock() -{ } +{ + KoResourceServer *srv = KoResourceServerProvider::instance()->paletteServer(); + srv->removeObserver(this); +} void PaletteDockerDock::setViewManager(KisViewManager* kisview) { m_view = kisview; m_resourceProvider = kisview->canvasResourceProvider(); connect(m_resourceProvider, SIGNAL(sigSavingWorkspace(KisWorkspaceResource*)), SLOT(saveToWorkspace(KisWorkspaceResource*))); connect(m_resourceProvider, SIGNAL(sigLoadingWorkspace(KisWorkspaceResource*)), SLOT(loadFromWorkspace(KisWorkspaceResource*))); connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)), this, SLOT(slotFGColorResourceChanged(KoColor))); kisview->nodeManager()->disconnect(m_model); } void PaletteDockerDock::unsetResourceServer() { KoResourceServer *srv = KoResourceServerProvider::instance()->paletteServer(); srv->removeObserver(this); } void PaletteDockerDock::resourceAdded(KoColorSet *resource) { Q_UNUSED(resource); } void PaletteDockerDock::removingResource(KoColorSet *resource) { Q_UNUSED(resource); } void PaletteDockerDock::resourceChanged(KoColorSet *resource) { Q_UNUSED(resource); m_model->sigPaletteModified(); } void PaletteDockerDock::syncTaggedResourceView() {} void PaletteDockerDock::syncTagAddition(const QString& tag) { Q_UNUSED(tag); } void PaletteDockerDock::syncTagRemoval(const QString& tag) { Q_UNUSED(tag); } void PaletteDockerDock::slotContextMenu(const QModelIndex &) { if (QApplication::mouseButtons() == Qt::RightButton) { m_viewContextMenu.exec(QCursor::pos()); } } void PaletteDockerDock::slotAddPalette() { m_paletteEditor->addPalette(); } void PaletteDockerDock::slotRemovePalette(KoColorSet *cs) { m_paletteEditor->removePalette(cs); } void PaletteDockerDock::slotImportPalette() { m_paletteEditor->importPalette(); } void PaletteDockerDock::slotExportPalette(KoColorSet *palette) { KoFileDialog dialog(this, KoFileDialog::SaveFile, "Save Palette"); dialog.setDefaultDir(palette->filename()); dialog.setMimeTypeFilters(QStringList() << "krita/x-colorset"); QString newPath; bool isStandAlone = palette->isGlobal(); QString oriPath = palette->filename(); if ((newPath = dialog.filename()).isEmpty()) { return; } palette->setFilename(newPath); palette->setIsGlobal(true); palette->save(); palette->setFilename(oriPath); palette->setIsGlobal(isStandAlone); } void PaletteDockerDock::setCanvas(KoCanvasBase *canvas) { setEnabled(canvas != 0); if (canvas) { KisCanvas2 *cv = qobject_cast(canvas); m_ui->paletteView->setDisplayRenderer(cv->displayColorConverter()->displayRendererInterface()); } if (m_activeDocument) { m_connections.clear(); for (KoColorSet * &cs : m_activeDocument->paletteList()) { KoColorSet *tmpAddr = cs; cs = new KoColorSet(*cs); m_rServer->removeResourceFromServer(tmpAddr); } } if (m_view && m_view->document()) { m_activeDocument = m_view->document(); m_paletteEditor->setView(m_view); for (KoColorSet *cs : m_activeDocument->paletteList()) { m_rServer->addResource(cs); } m_connections.addConnection(m_activeDocument, &KisDocument::sigPaletteListChanged, this, &PaletteDockerDock::slotUpdatePaletteList); } if (!m_currentColorSet) { slotSetColorSet(0); } } void PaletteDockerDock::unsetCanvas() { setEnabled(false); m_ui->paletteView->setDisplayRenderer(0); m_paletteEditor->setView(0); for (KoResource *r : m_rServer->resources()) { KoColorSet *c = static_cast(r); if (!c->isGlobal()) { m_rServer->removeResourceFromServer(c); } } if (!m_currentColorSet) { slotSetColorSet(0); } } void PaletteDockerDock::slotSetColorSet(KoColorSet* colorSet) { if (colorSet && colorSet->isEditable()) { m_ui->bnAdd->setEnabled(true); m_ui->bnRename->setEnabled(true); m_ui->bnRemove->setEnabled(true); m_ui->bnEditPalette->setEnabled(true); m_ui->paletteView->setAllowModification(true); } else { m_ui->bnAdd->setEnabled(false); m_ui->bnRename->setEnabled(false); m_ui->bnRemove->setEnabled(false); m_ui->bnEditPalette->setEnabled(false); m_ui->paletteView->setAllowModification(false); } m_currentColorSet = colorSet; m_model->setPalette(colorSet); if (colorSet) { KisConfig cfg(true); cfg.setDefaultPalette(colorSet->name()); m_ui->lblPaletteName->setTextElideMode(Qt::ElideLeft); m_ui->lblPaletteName->setText(colorSet->name()); } else { m_ui->lblPaletteName->setText(""); } } void PaletteDockerDock::slotEditPalette() { KisDlgPaletteEditor dlg; if (!m_currentColorSet) { return; } dlg.setPaletteModel(m_model); dlg.setView(m_view); if (dlg.exec() != QDialog::Accepted){ return; } slotSetColorSet(m_currentColorSet); // update GUI } void PaletteDockerDock::slotAddColor() { if (m_resourceProvider) { m_paletteEditor->addEntry(m_resourceProvider->fgColor()); } } void PaletteDockerDock::slotRemoveColor() { QModelIndex index = m_ui->paletteView->currentIndex(); if (!index.isValid()) { return; } m_paletteEditor->removeEntry(index); m_ui->bnRemove->setEnabled(false); } void PaletteDockerDock::setFGColorByPalette(const KisSwatch &entry) { if (m_resourceProvider) { m_colorSelfUpdate = true; m_resourceProvider->setFGColor(entry.color()); m_colorSelfUpdate = false; } } void PaletteDockerDock::saveToWorkspace(KisWorkspaceResource* workspace) { if (!m_currentColorSet.isNull()) { workspace->setProperty("palette", m_currentColorSet->name()); } } void PaletteDockerDock::loadFromWorkspace(KisWorkspaceResource* workspace) { if (workspace->hasProperty("palette")) { KoResourceServer* rServer = KoResourceServerProvider::instance()->paletteServer(); KoColorSet* colorSet = rServer->resourceByName(workspace->getString("palette")); if (colorSet) { slotSetColorSet(colorSet); } } } void PaletteDockerDock::slotFGColorResourceChanged(const KoColor &color) { if (!m_colorSelfUpdate) { m_ui->paletteView->slotFGColorChanged(color); } } void PaletteDockerDock::slotPaletteIndexSelected(const QModelIndex &index) { bool occupied = qvariant_cast(index.data(KisPaletteModel::CheckSlotRole)); if (occupied) { if (!qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { m_ui->bnRemove->setEnabled(true); KisSwatch entry = m_model->getEntry(index); setFGColorByPalette(entry); } } if (!m_currentColorSet->isEditable()) { return; } m_ui->bnRemove->setEnabled(occupied); } void PaletteDockerDock::slotPaletteIndexClicked(const QModelIndex &index) { if (!(qvariant_cast(index.data(KisPaletteModel::CheckSlotRole)))) { setEntryByForeground(index); } } void PaletteDockerDock::slotPaletteIndexDoubleClicked(const QModelIndex &index) { m_paletteEditor->modifyEntry(index); } void PaletteDockerDock::setEntryByForeground(const QModelIndex &index) { m_paletteEditor->setEntry(m_resourceProvider->fgColor(), index); if (m_currentColorSet->isEditable()) { m_ui->bnRemove->setEnabled(true); } } void PaletteDockerDock::slotEditEntry() { QModelIndex index = m_ui->paletteView->currentIndex(); if (!index.isValid()) { return; } m_paletteEditor->modifyEntry(index); } void PaletteDockerDock::slotNameListSelection(const KoColor &color) { m_colorSelfUpdate = true; m_ui->paletteView->selectClosestColor(color); m_resourceProvider->setFGColor(color); m_colorSelfUpdate = false; } void PaletteDockerDock::slotUpdatePaletteList(const QList &oldPaletteList, const QList &newPaletteList) { for (KoColorSet *cs : oldPaletteList) { m_rServer->removeResourceFromServer(cs); } for (KoColorSet *cs : newPaletteList) { m_rServer->addResource(cs); } if (!m_currentColorSet) { slotSetColorSet(0); } } diff --git a/plugins/extensions/animationrenderer/video_saver.cpp b/plugins/extensions/animationrenderer/video_saver.cpp index b83accce6f..444c428cb4 100644 --- a/plugins/extensions/animationrenderer/video_saver.cpp +++ b/plugins/extensions/animationrenderer/video_saver.cpp @@ -1,329 +1,333 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "video_saver.h" #include #include #include #include #include #include #include #include #include #include #include "kis_config.h" #include "KisAnimationRenderingOptions.h" #include #include #include #include #include #include #include #include "KisPart.h" class KisFFMpegProgressWatcher : public QObject { Q_OBJECT public: KisFFMpegProgressWatcher(QFile &progressFile, int totalFrames) : m_progressFile(progressFile), m_totalFrames(totalFrames) { connect(&m_progressWatcher, SIGNAL(fileChanged(QString)), SLOT(slotFileChanged())); m_progressWatcher.addPath(m_progressFile.fileName()); } private Q_SLOTS: void slotFileChanged() { int currentFrame = -1; bool isEnded = false; while(!m_progressFile.atEnd()) { QString line = QString(m_progressFile.readLine()).remove(QChar('\n')); QStringList var = line.split("="); if (var[0] == "frame") { currentFrame = var[1].toInt(); } else if (var[0] == "progress") { isEnded = var[1] == "end"; } } if (isEnded) { emit sigProgressChanged(100); emit sigProcessingFinished(); } else { emit sigProgressChanged(100 * currentFrame / m_totalFrames); } } Q_SIGNALS: void sigProgressChanged(int percent); void sigProcessingFinished(); private: QFileSystemWatcher m_progressWatcher; QFile &m_progressFile; int m_totalFrames; }; class KisFFMpegRunner { public: KisFFMpegRunner(const QString &ffmpegPath) : m_cancelled(false), m_ffmpegPath(ffmpegPath) {} public: KisImportExportErrorCode runFFMpeg(const QStringList &specialArgs, const QString &actionName, const QString &logPath, int totalFrames) { dbgFile << "runFFMpeg: specialArgs" << specialArgs << "actionName" << actionName << "logPath" << logPath << "totalFrames" << totalFrames; QTemporaryFile progressFile(QDir::tempPath() + QDir::separator() + "KritaFFmpegProgress.XXXXXX"); progressFile.open(); m_process.setStandardOutputFile(logPath); m_process.setProcessChannelMode(QProcess::MergedChannels); QStringList args; args << "-v" << "debug" << "-nostdin" << "-progress" << progressFile.fileName() << specialArgs; qDebug() << "\t" << m_ffmpegPath << args.join(" "); m_cancelled = false; m_process.start(m_ffmpegPath, args); return waitForFFMpegProcess(actionName, progressFile, m_process, totalFrames); } void cancel() { m_cancelled = true; m_process.kill(); } private: KisImportExportErrorCode waitForFFMpegProcess(const QString &message, QFile &progressFile, QProcess &ffmpegProcess, int totalFrames) { KisFFMpegProgressWatcher watcher(progressFile, totalFrames); QProgressDialog progress(message, "", 0, 0, KisPart::instance()->currentMainwindow()); progress.setWindowModality(Qt::ApplicationModal); progress.setCancelButton(0); progress.setMinimumDuration(0); progress.setValue(0); progress.setRange(0, 100); QEventLoop loop; loop.connect(&watcher, SIGNAL(sigProcessingFinished()), SLOT(quit())); loop.connect(&ffmpegProcess, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(quit())); loop.connect(&ffmpegProcess, SIGNAL(error(QProcess::ProcessError)), SLOT(quit())); loop.connect(&watcher, SIGNAL(sigProgressChanged(int)), &progress, SLOT(setValue(int))); if (ffmpegProcess.state() != QProcess::NotRunning) { loop.exec(); // wait for some erroneous case ffmpegProcess.waitForFinished(5000); } KisImportExportErrorCode retval = ImportExportCodes::OK; if (ffmpegProcess.state() != QProcess::NotRunning) { // sorry... ffmpegProcess.kill(); retval = ImportExportCodes::Failure; } else if (m_cancelled) { retval = ImportExportCodes::Cancelled; } else if (ffmpegProcess.exitCode()) { retval = ImportExportCodes::Failure; } return retval; } private: QProcess m_process; bool m_cancelled; QString m_ffmpegPath; }; VideoSaver::VideoSaver(KisDocument *doc, bool batchMode) : m_image(doc->image()) , m_doc(doc) , m_batchMode(batchMode) { } VideoSaver::~VideoSaver() { } KisImageSP VideoSaver::image() { return m_image; } KisImportExportErrorCode VideoSaver::encode(const QString &savedFilesMask, const KisAnimationRenderingOptions &options) { if (!QFileInfo(options.ffmpegPath).exists()) { m_doc->setErrorMessage(i18n("ffmpeg could not be found at %1", options.ffmpegPath)); return ImportExportCodes::Failure; } KisImportExportErrorCode resultOuter = ImportExportCodes::OK; KisImageAnimationInterface *animation = m_image->animationInterface(); const int sequenceNumberingOffset = options.sequenceStart; const KisTimeRange clipRange(sequenceNumberingOffset + options.firstFrame, sequenceNumberingOffset + options.lastFrame); // export dimensions could be off a little bit, so the last force option tweaks the pixels for the export to work const QString exportDimensions = QString("scale=w=") .append(QString::number(options.width)) .append(":h=") .append(QString::number(options.height)); //.append(":force_original_aspect_ratio=decrease"); HOTFIX for even:odd dimension images. const QString resultFile = options.resolveAbsoluteVideoFilePath(); const QDir videoDir(QFileInfo(resultFile).absolutePath()); const QFileInfo info(resultFile); const QString suffix = info.suffix().toLower(); const QString palettePath = videoDir.filePath("palette.png"); const QStringList additionalOptionsList = options.customFFMpegOptions.split(' ', QString::SkipEmptyParts); QScopedPointer runner(new KisFFMpegRunner(options.ffmpegPath)); if (suffix == "gif") { { QStringList args; args << "-r" << QString::number(options.frameRate) << "-start_number" << QString::number(clipRange.start()) << "-i" << savedFilesMask << "-vf" << "palettegen" << "-y" << palettePath; KisImportExportErrorCode result = runner->runFFMpeg(args, i18n("Fetching palette..."), videoDir.filePath("log_generate_palette_gif.log"), clipRange.duration()); if (!result.isOk()) { return result; } } { QStringList args; args << "-r" << QString::number(options.frameRate) << "-start_number" << QString::number(clipRange.start()) << "-i" << savedFilesMask << "-i" << palettePath - << "-lavfi" << "[0:v][1:v] paletteuse" - << "-y" << resultFile; + << "-lavfi"; + + QString filterArgs; // if we are exporting out at a different image size, we apply scaling filter if (m_image->width() != options.width || m_image->height() != options.height) { - args << "-vf" << exportDimensions; + filterArgs.append(exportDimensions + "[0:v];"); } + args << filterArgs.append("[0:v][1:v] paletteuse") + << "-y" << resultFile; + dbgFile << "savedFilesMask" << savedFilesMask << "start" << QString::number(clipRange.start()) << "duration" << clipRange.duration(); KisImportExportErrorCode result = runner->runFFMpeg(args, i18n("Encoding frames..."), videoDir.filePath("log_encode_gif.log"), clipRange.duration()); if (!result.isOk()) { return result; } } } else { QStringList args; args << "-r" << QString::number(options.frameRate) << "-start_number" << QString::number(clipRange.start()) << "-i" << savedFilesMask; QFileInfo audioFileInfo = animation->audioChannelFileName(); if (options.includeAudio && audioFileInfo.exists()) { const int msecStart = clipRange.start() * 1000 / animation->framerate(); const int msecDuration = clipRange.duration() * 1000 / animation->framerate(); const QTime startTime = QTime::fromMSecsSinceStartOfDay(msecStart); const QTime durationTime = QTime::fromMSecsSinceStartOfDay(msecDuration); const QString ffmpegTimeFormat("H:m:s.zzz"); args << "-ss" << startTime.toString(ffmpegTimeFormat); args << "-t" << durationTime.toString(ffmpegTimeFormat); args << "-i" << audioFileInfo.absoluteFilePath(); } // if we are exporting out at a different image size, we apply scaling filter // export options HAVE to go after input options, so make sure this is after the audio import if (m_image->width() != options.width || m_image->height() != options.height) { args << "-vf" << exportDimensions; } args << additionalOptionsList << "-y" << resultFile; resultOuter = runner->runFFMpeg(args, i18n("Encoding frames..."), videoDir.filePath("log_encode.log"), clipRange.duration()); } return resultOuter; } KisImportExportErrorCode VideoSaver::convert(KisDocument *document, const QString &savedFilesMask, const KisAnimationRenderingOptions &options, bool batchMode) { VideoSaver videoSaver(document, batchMode); KisImportExportErrorCode res = videoSaver.encode(savedFilesMask, options); return res; } #include "video_saver.moc" diff --git a/plugins/filters/gradientmap/krita_filter_gradient_map.cpp b/plugins/filters/gradientmap/krita_filter_gradient_map.cpp index 17e647b826..35729de3d9 100644 --- a/plugins/filters/gradientmap/krita_filter_gradient_map.cpp +++ b/plugins/filters/gradientmap/krita_filter_gradient_map.cpp @@ -1,143 +1,143 @@ /* * This file is part of the KDE project * * Copyright (c) 2016 Spencer Brown * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "krita_filter_gradient_map.h" #include #include #include #include #include #include #include "kis_config_widget.h" #include #include #include #include #include #include "gradientmap.h" #include #include KritaFilterGradientMap::KritaFilterGradientMap() : KisFilter(id(), FiltersCategoryMapId, i18n("&Gradient Map...")) { setColorSpaceIndependence(FULLY_INDEPENDENT); setShowConfigurationWidget(true); setSupportsLevelOfDetail(true); setSupportsPainting(true); setSupportsAdjustmentLayers(true); setSupportsThreading(true); } void KritaFilterGradientMap::processImpl(KisPaintDeviceSP device, const QRect& applyRect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const { Q_ASSERT(!device.isNull()); QDomDocument doc; if (config->version()==1) { QDomElement elt = doc.createElement("gradient"); KoAbstractGradient *gradientAb = KoResourceServerProvider::instance()->gradientServer()->resourceByName(config->getString("gradientName")); if (!gradientAb) { qWarning() << "Could not find gradient" << config->getString("gradientName"); } gradientAb = KoResourceServerProvider::instance()->gradientServer()->resources().first(); QScopedPointer qGradient(gradientAb->toQGradient()); KoStopGradient::fromQGradient(qGradient.data())->toXML(doc, elt); doc.appendChild(elt); } else { doc.setContent(config->getString("gradientXML", "")); } KoStopGradient gradient = KoStopGradient::fromXML(doc.firstChildElement()); const ColorMode colorMode = ColorMode(config->getInt("colorMode")); KisDitherUtil ditherUtil; if (colorMode == ColorMode::Dither) ditherUtil.setConfiguration(*config, "dither/"); KoColor outColor(Qt::white, device->colorSpace()); KisSequentialIteratorProgress it(device, applyRect, progressUpdater); qreal grey; const int pixelSize = device->colorSpace()->pixelSize(); while (it.nextPixel()) { grey = qreal(device->colorSpace()->intensity8(it.oldRawData())) / 255; if (colorMode == ColorMode::Nearest) { KoGradientStop leftStop, rightStop; if (!gradient.stopsAt(leftStop, rightStop, grey)) continue; - if (std::abs(grey - leftStop.first) < std::abs(grey - rightStop.first)) { - outColor = leftStop.second; + if (std::abs(grey - leftStop.position) < std::abs(grey - rightStop.position)) { + outColor = leftStop.color; } else { - outColor = rightStop.second; + outColor = rightStop.color; } } else if (colorMode == ColorMode::Dither) { KoGradientStop leftStop, rightStop; if (!gradient.stopsAt(leftStop, rightStop, grey)) continue; - qreal localT = (grey - leftStop.first) / (rightStop.first - leftStop.first); + qreal localT = (grey - leftStop.position) / (rightStop.position - leftStop.position); if (localT < ditherUtil.threshold(QPoint(it.x(), it.y()))) { - outColor = leftStop.second; + outColor = leftStop.color; } else { - outColor = rightStop.second; + outColor = rightStop.color; } } else { gradient.colorAt(outColor, grey); } outColor.setOpacity(qMin(KoColor(it.oldRawData(), device->colorSpace()).opacityF(), outColor.opacityF())); outColor.convertTo(device->colorSpace()); memcpy(it.rawData(), outColor.data(), pixelSize); } } KisFilterConfigurationSP KritaFilterGradientMap::factoryConfiguration() const { return new KisFilterConfiguration(id().id(), 2); } KisFilterConfigurationSP KritaFilterGradientMap::defaultConfiguration() const { KisFilterConfigurationSP config = factoryConfiguration(); KoAbstractGradient *gradient = KoResourceServerProvider::instance()->gradientServer()->resources().first(); KoStopGradient stopGradient; QScopedPointer qGradient(gradient->toQGradient()); stopGradient.fromQGradient(qGradient.data()); QDomDocument doc; QDomElement elt = doc.createElement("gradient"); stopGradient.toXML(doc, elt); doc.appendChild(elt); config->setProperty("gradientXML", doc.toString()); config->setProperty("colorMode", false); KisDitherWidget::factoryConfiguration(*config, "dither/"); return config; } KisConfigWidget * KritaFilterGradientMap::createConfigurationWidget(QWidget * parent, const KisPaintDeviceSP dev, bool) const { return new KritaGradientMapConfigWidget(parent, dev); } diff --git a/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp b/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp index 5da1c8ea48..b86d81250f 100644 --- a/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp +++ b/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp @@ -1,95 +1,96 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisBrushOpResources.h" #include #include #include "kis_color_source.h" #include "kis_pressure_mix_option.h" #include "kis_pressure_darken_option.h" #include "kis_pressure_hsv_option.h" #include "kis_color_source_option.h" #include "kis_pressure_sharpness_option.h" #include "kis_texture_option.h" #include "kis_painter.h" #include "kis_paintop_settings.h" struct KisBrushOpResources::Private { QList hsvOptions; KoColorTransformation *hsvTransformation = 0; KisPressureMixOption mixOption; KisPressureDarkenOption darkenOption; }; KisBrushOpResources::KisBrushOpResources(const KisPaintOpSettingsSP settings, KisPainter *painter) : m_d(new Private) { KisColorSourceOption colorSourceOption; colorSourceOption.readOptionSetting(settings); colorSource.reset(colorSourceOption.createColorSource(painter)); sharpnessOption.reset(new KisPressureSharpnessOption()); sharpnessOption->readOptionSetting(settings); sharpnessOption->resetAllSensors(); textureOption.reset(new KisTextureProperties(painter->device()->defaultBounds()->currentLevelOfDetail())); textureOption->fillProperties(settings); + textureOption->setTextureGradient(painter->gradient()); m_d->hsvOptions.append(KisPressureHSVOption::createHueOption()); m_d->hsvOptions.append(KisPressureHSVOption::createSaturationOption()); m_d->hsvOptions.append(KisPressureHSVOption::createValueOption()); Q_FOREACH (KisPressureHSVOption * option, m_d->hsvOptions) { option->readOptionSetting(settings); option->resetAllSensors(); if (option->isChecked() && !m_d->hsvTransformation) { m_d->hsvTransformation = painter->backgroundColor().colorSpace()->createColorTransformation("hsv_adjustment", QHash()); } } m_d->darkenOption.readOptionSetting(settings); m_d->mixOption.readOptionSetting(settings); m_d->darkenOption.resetAllSensors(); m_d->mixOption.resetAllSensors(); } KisBrushOpResources::~KisBrushOpResources() { qDeleteAll(m_d->hsvOptions); delete m_d->hsvTransformation; } void KisBrushOpResources::syncResourcesToSeqNo(int seqNo, const KisPaintInformation &info) { colorSource->selectColor(m_d->mixOption.apply(info), info); m_d->darkenOption.apply(colorSource.data(), info); if (m_d->hsvTransformation) { Q_FOREACH (KisPressureHSVOption * option, m_d->hsvOptions) { option->apply(m_d->hsvTransformation, info); } colorSource->applyColorTransformation(m_d->hsvTransformation); } KisDabCacheUtils::DabRenderingResources::syncResourcesToSeqNo(seqNo, info); } diff --git a/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp b/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp index 74ac1d9b1f..8b8e5e9629 100644 --- a/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp +++ b/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp @@ -1,229 +1,258 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisTextureMaskInfo.h" #include #include #include "kis_embedded_pattern_manager.h" #include #include #include #include #include #include /**********************************************************************/ /* KisTextureMaskInfo */ /**********************************************************************/ KisTextureMaskInfo::KisTextureMaskInfo(int levelOfDetail) : m_levelOfDetail(levelOfDetail) { } KisTextureMaskInfo::KisTextureMaskInfo(const KisTextureMaskInfo &rhs) : m_levelOfDetail(rhs.m_levelOfDetail), m_pattern(rhs.m_pattern), m_scale(rhs.m_scale), m_brightness(rhs.m_brightness), m_contrast(rhs.m_contrast), + m_neutralPoint(rhs.m_neutralPoint), m_invert(rhs.m_invert), m_cutoffLeft(rhs.m_cutoffLeft), m_cutoffRight(rhs.m_cutoffRight), m_cutoffPolicy(rhs.m_cutoffPolicy) { } KisTextureMaskInfo::~KisTextureMaskInfo() { } bool operator==(const KisTextureMaskInfo &lhs, const KisTextureMaskInfo &rhs) { return lhs.m_levelOfDetail == rhs.m_levelOfDetail && (lhs.m_pattern == rhs.m_pattern || (lhs.m_pattern && rhs.m_pattern && lhs.m_pattern->md5() == rhs.m_pattern->md5())) && qFuzzyCompare(lhs.m_scale, rhs.m_scale) && qFuzzyCompare(lhs.m_brightness, rhs.m_brightness) && qFuzzyCompare(lhs.m_contrast, rhs.m_contrast) && + qFuzzyCompare(lhs.m_neutralPoint, rhs.m_neutralPoint) && lhs.m_invert == rhs.m_invert && lhs.m_cutoffLeft == rhs.m_cutoffLeft && lhs.m_cutoffRight == rhs.m_cutoffRight && lhs.m_cutoffPolicy == rhs.m_cutoffPolicy; } KisTextureMaskInfo &KisTextureMaskInfo::operator=(const KisTextureMaskInfo &rhs) { m_levelOfDetail = rhs.m_levelOfDetail; m_pattern = rhs.m_pattern; m_scale = rhs.m_scale; m_brightness = rhs.m_brightness; m_contrast = rhs.m_contrast; + m_neutralPoint = rhs.m_neutralPoint; m_invert = rhs.m_invert; m_cutoffLeft = rhs.m_cutoffLeft; m_cutoffRight = rhs.m_cutoffRight; m_cutoffPolicy = rhs.m_cutoffPolicy; return *this; } int KisTextureMaskInfo::levelOfDetail() const { return m_levelOfDetail; } bool KisTextureMaskInfo::hasMask() const { return m_mask; } KisPaintDeviceSP KisTextureMaskInfo::mask() { return m_mask; } QRect KisTextureMaskInfo::maskBounds() const { return m_maskBounds; } bool KisTextureMaskInfo::fillProperties(const KisPropertiesConfigurationSP setting) { if (!setting->hasProperty("Texture/Pattern/PatternMD5")) { return false; } m_pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); if (!m_pattern) { warnKrita << "WARNING: Couldn't load the pattern for a stroke"; return false; } m_scale = setting->getDouble("Texture/Pattern/Scale", 1.0); m_brightness = setting->getDouble("Texture/Pattern/Brightness"); m_contrast = setting->getDouble("Texture/Pattern/Contrast", 1.0); + m_neutralPoint = setting->getDouble("Texture/Pattern/NeutralPoint", 0.5); m_invert = setting->getBool("Texture/Pattern/Invert"); m_cutoffLeft = setting->getInt("Texture/Pattern/CutoffLeft", 0); m_cutoffRight = setting->getInt("Texture/Pattern/CutoffRight", 255); m_cutoffPolicy = setting->getInt("Texture/Pattern/CutoffPolicy", 0); return true; } void KisTextureMaskInfo::recalculateMask() { if (!m_pattern) return; - const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); + const KoColorSpace* cs; + bool hasAlpha = m_pattern->hasAlpha(); + if (hasAlpha) { + cs = KoColorSpaceRegistry::instance()->rgb8(); + } else { + cs = KoColorSpaceRegistry::instance()->alpha8(); + } if (!m_mask) { m_mask = new KisPaintDevice(cs); } QImage mask = m_pattern->pattern(); if ((mask.format() != QImage::Format_RGB32) | (mask.format() != QImage::Format_ARGB32)) { mask = mask.convertToFormat(QImage::Format_ARGB32); } qreal scale = m_scale * KisLodTransform::lodToScale(m_levelOfDetail); - if (!qFuzzyCompare(scale, 0.0)) { + if (!qFuzzyCompare(scale, 0.0) && !qFuzzyCompare(scale, 1.0)) { QTransform tf; tf.scale(scale, scale); QRect rc = KisAlgebra2D::ensureRectNotSmaller(tf.mapRect(mask.rect()), QSize(2,2)); mask = mask.scaled(rc.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + } else { + // detach the mask from the file loaded from the storage + mask = QImage(mask); } - const QRgb* pixel = reinterpret_cast(mask.constBits()); + QRgb* pixel = reinterpret_cast(mask.bits()); const int width = mask.width(); const int height = mask.height(); - KisHLineIteratorSP iter = m_mask->createHLineIteratorNG(0, 0, width); - for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { const QRgb currentPixel = pixel[row * width + col]; const int red = qRed(currentPixel); const int green = qGreen(currentPixel); const int blue = qBlue(currentPixel); float alpha = qAlpha(currentPixel) / 255.0; const int grayValue = (red * 11 + green * 16 + blue * 5) / 32; float maskValue = (grayValue / 255.0) * alpha + (1 - alpha); maskValue = maskValue - m_brightness; maskValue = ((maskValue - 0.5)*m_contrast)+0.5; if (maskValue > 1.0) {maskValue = 1;} else if (maskValue < 0) {maskValue = 0;} if (m_invert) { maskValue = 1 - maskValue; } if (m_cutoffPolicy == 1 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) { // mask out the dab if it's outside the pattern's cuttoff points - maskValue = OPACITY_TRANSPARENT_F; + alpha = OPACITY_TRANSPARENT_F; } else if (m_cutoffPolicy == 2 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) { - maskValue = OPACITY_OPAQUE_F; + alpha = OPACITY_OPAQUE_F; } - cs->setOpacity(iter->rawData(), maskValue, 1); - iter->nextPixel(); + maskValue = qBound(0.0f, maskValue, 1.0f); + + float neutralAdjustedValue; + + //Adjust neutral point in linear fashion. Uses separate linear equations from 0 to neutralPoint, and neutralPoint to 1, + //to prevent loss of detail (clipping). + if (m_neutralPoint == 1 || (m_neutralPoint != 0 && maskValue <= m_neutralPoint)) { + neutralAdjustedValue = maskValue / (2 * m_neutralPoint); + } + else { + neutralAdjustedValue = 0.5 + (maskValue - m_neutralPoint) / (2 - 2 * m_neutralPoint); + } + + int finalValue = neutralAdjustedValue * 255; + pixel[row * width + col] = QColor(finalValue, finalValue, finalValue, alpha * 255).rgba(); + } - iter->nextRow(); } + m_mask->convertFromQImage(mask, 0); m_maskBounds = QRect(0, 0, width, height); } +bool KisTextureMaskInfo::hasAlpha() { + return m_pattern->hasAlpha(); +} + /**********************************************************************/ /* KisTextureMaskInfoCache */ /**********************************************************************/ Q_GLOBAL_STATIC(KisTextureMaskInfoCache, s_instance) KisTextureMaskInfoCache *KisTextureMaskInfoCache::instance() { return s_instance; } KisTextureMaskInfoSP KisTextureMaskInfoCache::fetchCachedTextureInfo(KisTextureMaskInfoSP info) { QMutexLocker locker(&m_mutex); KisTextureMaskInfoSP &cachedInfo = info->levelOfDetail() > 0 ? m_lodInfo : m_mainInfo; if (!cachedInfo || *cachedInfo != *info) { cachedInfo = info; cachedInfo->recalculateMask(); } return cachedInfo; } diff --git a/plugins/paintops/libpaintop/KisTextureMaskInfo.h b/plugins/paintops/libpaintop/KisTextureMaskInfo.h index 9ac1834388..1c79aca6a1 100644 --- a/plugins/paintops/libpaintop/KisTextureMaskInfo.h +++ b/plugins/paintops/libpaintop/KisTextureMaskInfo.h @@ -1,89 +1,92 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISTEXTUREMASKINFO_H #define KISTEXTUREMASKINFO_H #include #include #include #include class KoPattern; class KisTextureMaskInfo; class KisTextureMaskInfo : public boost::equality_comparable { public: KisTextureMaskInfo(int levelOfDetail); KisTextureMaskInfo(const KisTextureMaskInfo &rhs); ~KisTextureMaskInfo(); friend bool operator==(const KisTextureMaskInfo &lhs, const KisTextureMaskInfo &rhs); KisTextureMaskInfo& operator=(const KisTextureMaskInfo &rhs); int levelOfDetail() const; bool hasMask() const; KisPaintDeviceSP mask(); QRect maskBounds() const; bool fillProperties(const KisPropertiesConfigurationSP setting); void recalculateMask(); + bool hasAlpha(); + private: int m_levelOfDetail = 0; KoPattern *m_pattern = 0; qreal m_scale = 1.0; qreal m_brightness = 0.0; qreal m_contrast = 1.0; + qreal m_neutralPoint = 0.5; bool m_invert = false; int m_cutoffLeft = 0; int m_cutoffRight = 255; int m_cutoffPolicy = 0; KisPaintDeviceSP m_mask; QRect m_maskBounds; }; typedef QSharedPointer KisTextureMaskInfoSP; struct KisTextureMaskInfoCache { static KisTextureMaskInfoCache *instance(); KisTextureMaskInfoSP fetchCachedTextureInfo(KisTextureMaskInfoSP info); private: QMutex m_mutex; QSharedPointer m_lodInfo; QSharedPointer m_mainInfo; }; #endif // KISTEXTUREMASKINFO_H diff --git a/plugins/paintops/libpaintop/forms/wdgtexturechooser.ui b/plugins/paintops/libpaintop/forms/wdgtexturechooser.ui index 51f417e805..2e617760e2 100644 --- a/plugins/paintops/libpaintop/forms/wdgtexturechooser.ui +++ b/plugins/paintops/libpaintop/forms/wdgtexturechooser.ui @@ -1,341 +1,361 @@ KisWdgTextureChooser 0 0 548 391 0 0 0 0 - 0 + 1 Texture 0 0 QFrame::StyledPanel QFrame::Plain 0 Options - - + + + + + 0 0 - Contrast: + Scale: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 200 - 0 - + + + + + 0 + 0 + - - + + 0 0 - Cutoff + Contrast: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + 0 + 0 + + + + 0 0 Brightness: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 200 - 0 - - - - - + 0 0 Cutoff Policy: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 Texturing Mode: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 0 - 0 - - - - - - - - - 0 - 0 - + + + + + 200 + 0 + - - + + 0 0 - Scale: + Cutoff Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + PushButton + + + + + 200 + 0 + + + + + + + + Neutral Point + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 200 + 0 + + + + 0 0 Horizontal Offset: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter The border of the brush will be smoothed to avoid aliasing Invert Pattern Random Offset 0 0 Vertical Offset: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 Random Offset Qt::Vertical 20 40 KisDoubleSliderSpinBox QWidget -
kis_slider_spin_box.h
+
kis_slider_spin_box.h
1
KisSliderSpinBox QWidget
kis_slider_spin_box.h
1
KisPatternChooser QFrame
kis_pattern_chooser.h
1
KisGradientSlider QWidget
KisGradientSlider.h
1
KisMultipliersDoubleSliderSpinBox QWidget
kis_multipliers_double_slider_spinbox.h
1
diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp index eca8aff271..9062a95669 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp @@ -1,185 +1,186 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_based_paintop.h" #include "kis_properties_configuration.h" #include #include "kis_brush_option.h" #include #include #include "kis_painter.h" #include #include "kis_paintop_utils.h" #include "kis_paintop_plugin_utils.h" #include #include #include #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND Q_GLOBAL_STATIC(TextBrushInitializationWorkaround, s_instance) TextBrushInitializationWorkaround *TextBrushInitializationWorkaround::instance() { return s_instance; } void TextBrushInitializationWorkaround::preinitialize(KisPropertiesConfigurationSP settings) { if (KisBrushOptionProperties::isTextBrush(settings.data())) { KisBrushOptionProperties brushOption; brushOption.readOptionSetting(settings); m_brush = brushOption.brush(); m_settings = settings; } else { m_brush = 0; m_settings = 0; } } KisBrushSP TextBrushInitializationWorkaround::tryGetBrush(const KisPropertiesConfigurationSP settings) { return (settings && settings == m_settings ? m_brush : 0); } TextBrushInitializationWorkaround::TextBrushInitializationWorkaround() : m_settings(0) {} TextBrushInitializationWorkaround::~TextBrushInitializationWorkaround() {} void KisBrushBasedPaintOp::preinitializeOpStatically(KisPaintOpSettingsSP settings) { TextBrushInitializationWorkaround::instance()->preinitialize(settings); } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ KisBrushBasedPaintOp::KisBrushBasedPaintOp(const KisPropertiesConfigurationSP settings, KisPainter* painter) : KisPaintOp(painter), m_textureProperties(painter->device()->defaultBounds()->currentLevelOfDetail()) { Q_ASSERT(settings); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND m_brush = TextBrushInitializationWorkaround::instance()->tryGetBrush(settings); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ if (!m_brush) { KisBrushOptionProperties brushOption; brushOption.readOptionSetting(settings); m_brush = brushOption.brush(); } m_brush->notifyStrokeStarted(); m_precisionOption.readOptionSetting(settings); m_dabCache = new KisDabCache(m_brush); m_dabCache->setPrecisionOption(&m_precisionOption); m_mirrorOption.readOptionSetting(settings); m_dabCache->setMirrorPostprocessing(&m_mirrorOption); m_textureProperties.fillProperties(settings); m_dabCache->setTexturePostprocessing(&m_textureProperties); + m_textureProperties.setTextureGradient(painter->gradient()); m_precisionOption.setHasImprecisePositionOptions( m_precisionOption.hasImprecisePositionOptions() | m_mirrorOption.isChecked() | m_textureProperties.m_enabled); } KisBrushBasedPaintOp::~KisBrushBasedPaintOp() { delete m_dabCache; } bool KisBrushBasedPaintOp::checkSizeTooSmall(qreal scale) { scale *= m_brush->scale(); return KisPaintOpUtils::checkSizeTooSmall(scale, m_brush->width(), m_brush->height()); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale) const { // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0)); return effectiveSpacing(metric.width(), metric.height(), 1.0, false, 0.0, false); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPaintInformation &pi) const { return effectiveSpacing(scale, rotation, nullptr, nullptr, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const { return effectiveSpacing(scale, rotation, nullptr, &spacingOption, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisAirbrushOptionProperties *airbrushOption, const KisPressureSpacingOption *spacingOption, const KisPaintInformation &pi) const { bool isotropicSpacing = spacingOption && spacingOption->isotropicSpacing(); MirrorProperties prop = m_mirrorOption.apply(pi); const bool implicitFlipped = prop.horizontalMirror != prop.verticalMirror; // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0)); return KisPaintOpPluginUtils::effectiveSpacing(metric.width(), metric.height(), isotropicSpacing, rotation, implicitFlipped, m_brush->spacing(), m_brush->autoSpacingActive(), m_brush->autoSpacingCoeff(), KisLodTransform::lodToScale(painter()->device()), airbrushOption, spacingOption, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal dabWidth, qreal dabHeight, qreal extraScale, bool isotropicSpacing, qreal rotation, bool axesFlipped) const { return KisPaintOpUtils::effectiveSpacing(dabWidth, dabHeight, extraScale, true, isotropicSpacing, rotation, axesFlipped, m_brush->spacing(), m_brush->autoSpacingActive(), m_brush->autoSpacingCoeff(), KisLodTransform::lodToScale(painter()->device())); } bool KisBrushBasedPaintOp::canPaint() const { return m_brush != 0; } diff --git a/plugins/paintops/libpaintop/kis_texture_chooser.cpp b/plugins/paintops/libpaintop/kis_texture_chooser.cpp index e3dbc1562e..652f489bad 100644 --- a/plugins/paintops/libpaintop/kis_texture_chooser.cpp +++ b/plugins/paintops/libpaintop/kis_texture_chooser.cpp @@ -1,69 +1,76 @@ /* * Copyright (c) 2017 Scott Petrovic * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_texture_chooser.h" #include "kis_texture_option.h" +#include "widgets/kis_gradient_chooser.h" + KisTextureChooser::KisTextureChooser(QWidget *parent) : QWidget(parent) { setupUi(this); textureSelectorWidget->setGrayscalePreview(true); textureSelectorWidget->setCurrentItem(0, 0); scaleSlider->setRange(0.0, 2.0, 2); scaleSlider->setValue(1.0); scaleSlider->addMultiplier(0.1); scaleSlider->addMultiplier(2); scaleSlider->addMultiplier(10); brightnessSlider->setRange(-1.0, 1.0, 2); brightnessSlider->setValue(0.0); brightnessSlider->setToolTip(i18n("Makes texture lighter or darker")); contrastSlider->setRange(0.0, 2.0, 2); contrastSlider->setValue(1.0); + neutralPointSlider->setRange(0.0, 1.0, 2); + neutralPointSlider->setValue(0.5); + neutralPointSlider->setToolTip(i18n("Set gray value to be considered neutral for lightness mode")); + offsetSliderX->setSuffix(i18n(" px")); offsetSliderY->setSuffix(i18n(" px")); QStringList texturingModes; - texturingModes << i18n("Multiply") << i18n("Subtract"); + texturingModes << i18n("Multiply Alpha") << i18n("Subtract Alpha") << i18n("Lightness Map") << i18n("Gradient Map"); cmbTexturingMode->addItems(texturingModes); cmbTexturingMode->setCurrentIndex(KisTextureProperties::SUBTRACT); QStringList cutOffPolicies; cutOffPolicies << i18n("Cut Off Disabled") << i18n("Cut Off Brush") << i18n("Cut Off Pattern"); cmbCutoffPolicy->addItems(cutOffPolicies); cutoffSlider->setMinimumSize(256, 30); cutoffSlider->enableGamma(false); cutoffSlider->setToolTip(i18n("When pattern texture values are outside the range specified" " by the slider, the cut-off policy will be applied.")); chkInvert->setChecked(false); + } KisTextureChooser::~KisTextureChooser() { } diff --git a/plugins/paintops/libpaintop/kis_texture_option.cpp b/plugins/paintops/libpaintop/kis_texture_option.cpp index 4a851c23a8..dd2c8d87b4 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.cpp +++ b/plugins/paintops/libpaintop/kis_texture_option.cpp @@ -1,273 +1,381 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2012 * Copyright (C) Mohit Goyal , (C) 2014 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_texture_option.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include +#include #include #include #include #include #include #include #include "kis_embedded_pattern_manager.h" #include #include "kis_texture_chooser.h" +#include "KoMixColorsOp.h" #include KisTextureOption::KisTextureOption() : KisPaintOpOption(KisPaintOpOption::TEXTURE, true) , m_textureOptions(new KisTextureChooser()) { setObjectName("KisTextureOption"); setConfigurationPage(m_textureOptions); connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResource*)), SLOT(resetGUI(KoResource*))); connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResource*)), SLOT(emitSettingChanged())); connect(m_textureOptions->scaleSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->brightnessSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->contrastSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); + connect(m_textureOptions->neutralPointSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->offsetSliderX, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->randomOffsetX, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_textureOptions->randomOffsetY, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_textureOptions->offsetSliderY, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cmbTexturingMode, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cmbCutoffPolicy, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedBlack(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedWhite(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->chkInvert, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); resetGUI(m_textureOptions->textureSelectorWidget->currentResource()); } KisTextureOption::~KisTextureOption() { delete m_textureOptions; } void KisTextureOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { m_textureOptions->textureSelectorWidget->blockSignals(true); // Checking if (!m_textureOptions->textureSelectorWidget->currentResource()) return; KoPattern *pattern = static_cast(m_textureOptions->textureSelectorWidget->currentResource()); m_textureOptions->textureSelectorWidget->blockSignals(false); // Checking if (!pattern) return; setting->setProperty("Texture/Pattern/Enabled", isChecked()); if (!isChecked()) { return; } qreal scale = m_textureOptions->scaleSlider->value(); qreal brightness = m_textureOptions->brightnessSlider->value(); qreal contrast = m_textureOptions->contrastSlider->value(); + qreal neutralPoint = m_textureOptions->neutralPointSlider->value(); + int offsetX = m_textureOptions->offsetSliderX->value(); if (m_textureOptions ->randomOffsetX->isChecked()) { m_textureOptions->offsetSliderX ->setEnabled(false); m_textureOptions->offsetSliderX ->blockSignals(true); m_textureOptions->offsetSliderX ->setValue(offsetX); m_textureOptions->offsetSliderX ->blockSignals(false); } else { m_textureOptions->offsetSliderX ->setEnabled(true); } int offsetY = m_textureOptions->offsetSliderY->value(); if (m_textureOptions ->randomOffsetY->isChecked()) { m_textureOptions->offsetSliderY ->setEnabled(false); m_textureOptions->offsetSliderY ->blockSignals(true); m_textureOptions->offsetSliderY ->setValue(offsetY); m_textureOptions->offsetSliderY ->blockSignals(false); } else { m_textureOptions->offsetSliderY ->setEnabled(true); } int texturingMode = m_textureOptions->cmbTexturingMode->currentIndex(); bool invert = (m_textureOptions->chkInvert->checkState() == Qt::Checked); setting->setProperty("Texture/Pattern/Scale", scale); setting->setProperty("Texture/Pattern/Brightness", brightness); setting->setProperty("Texture/Pattern/Contrast", contrast); + setting->setProperty("Texture/Pattern/NeutralPoint", neutralPoint); setting->setProperty("Texture/Pattern/OffsetX", offsetX); setting->setProperty("Texture/Pattern/OffsetY", offsetY); setting->setProperty("Texture/Pattern/TexturingMode", texturingMode); setting->setProperty("Texture/Pattern/CutoffLeft", m_textureOptions->cutoffSlider->black()); setting->setProperty("Texture/Pattern/CutoffRight", m_textureOptions->cutoffSlider->white()); setting->setProperty("Texture/Pattern/CutoffPolicy", m_textureOptions->cmbCutoffPolicy->currentIndex()); setting->setProperty("Texture/Pattern/Invert", invert); setting->setProperty("Texture/Pattern/MaximumOffsetX",m_textureOptions->offsetSliderX ->maximum()); setting->setProperty("Texture/Pattern/MaximumOffsetY",m_textureOptions->offsetSliderY ->maximum()); setting->setProperty("Texture/Pattern/isRandomOffsetX",m_textureOptions ->randomOffsetX ->isChecked()); setting->setProperty("Texture/Pattern/isRandomOffsetY",m_textureOptions ->randomOffsetY ->isChecked()); KisEmbeddedPatternManager::saveEmbeddedPattern(setting, pattern); } void KisTextureOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { setChecked(setting->getBool("Texture/Pattern/Enabled")); if (!isChecked()) { return; } KoPattern *pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); if (!pattern) { pattern = static_cast(m_textureOptions->textureSelectorWidget->currentResource()); } m_textureOptions->textureSelectorWidget->setCurrentPattern(pattern); m_textureOptions->scaleSlider->setValue(setting->getDouble("Texture/Pattern/Scale", 1.0)); m_textureOptions->brightnessSlider->setValue(setting->getDouble("Texture/Pattern/Brightness")); m_textureOptions->contrastSlider->setValue(setting->getDouble("Texture/Pattern/Contrast", 1.0)); + m_textureOptions->neutralPointSlider->setValue(setting->getDouble("Texture/Pattern/NeutralPoint", 0.5)); m_textureOptions->offsetSliderX->setValue(setting->getInt("Texture/Pattern/OffsetX")); m_textureOptions->offsetSliderY->setValue(setting->getInt("Texture/Pattern/OffsetY")); m_textureOptions->randomOffsetX->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetX")); m_textureOptions->randomOffsetY->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetY")); m_textureOptions->cmbTexturingMode->setCurrentIndex(setting->getInt("Texture/Pattern/TexturingMode", KisTextureProperties::MULTIPLY)); m_textureOptions->cmbCutoffPolicy->setCurrentIndex(setting->getInt("Texture/Pattern/CutoffPolicy")); m_textureOptions->cutoffSlider->slotModifyBlack(setting->getInt("Texture/Pattern/CutoffLeft", 0)); m_textureOptions->cutoffSlider->slotModifyWhite(setting->getInt("Texture/Pattern/CutoffRight", 255)); m_textureOptions->chkInvert->setChecked(setting->getBool("Texture/Pattern/Invert")); } void KisTextureOption::lodLimitations(KisPaintopLodLimitations *l) const { l->limitations << KoID("texture-pattern", i18nc("PaintOp instant preview limitation", "Texture->Pattern (low quality preview)")); } void KisTextureOption::resetGUI(KoResource* res) { KoPattern *pattern = static_cast(res); if (!pattern) return; m_textureOptions->offsetSliderX->setRange(0, pattern->pattern().width() / 2); m_textureOptions->offsetSliderY->setRange(0, pattern->pattern().height() / 2); } /**********************************************************************/ /* KisTextureProperties */ /**********************************************************************/ KisTextureProperties::KisTextureProperties(int levelOfDetail) : m_levelOfDetail(levelOfDetail) { + KoResourceServer* rserver = KoResourceServerProvider::instance()->gradientServer(); + m_gradient = dynamic_cast(rserver->resources().first()); } void KisTextureProperties::fillProperties(const KisPropertiesConfigurationSP setting) { if (!setting->hasProperty("Texture/Pattern/PatternMD5")) { m_enabled = false; return; } m_maskInfo = toQShared(new KisTextureMaskInfo(m_levelOfDetail)); if (!m_maskInfo->fillProperties(setting)) { warnKrita << "WARNING: Couldn't load the pattern for a stroke"; m_enabled = false; return; } m_maskInfo = KisTextureMaskInfoCache::instance()->fetchCachedTextureInfo(m_maskInfo); m_enabled = setting->getBool("Texture/Pattern/Enabled", false); m_offsetX = setting->getInt("Texture/Pattern/OffsetX"); m_offsetY = setting->getInt("Texture/Pattern/OffsetY"); m_texturingMode = (TexturingMode) setting->getInt("Texture/Pattern/TexturingMode", MULTIPLY); m_strengthOption.readOptionSetting(setting); m_strengthOption.resetAllSensors(); } +void KisTextureProperties::setTextureGradient(const KoAbstractGradient* gradient) { + if (gradient) { + m_gradient = gradient; + m_cachedGradient.setGradient(gradient, 256); + } +} + +void KisTextureProperties::applyLightness(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info) { + if (!m_enabled) return; + + KisPaintDeviceSP mask = m_maskInfo->mask(); + const QRect maskBounds = m_maskInfo->maskBounds(); + + KisPaintDeviceSP fillMaskDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); + const QRect rect = dab->bounds(); + + KIS_SAFE_ASSERT_RECOVER_RETURN(mask); + + int x = offset.x() % maskBounds.width() - m_offsetX; + int y = offset.y() % maskBounds.height() - m_offsetY; + + KisFillPainter fillMaskPainter(fillMaskDevice); + fillMaskPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); + fillMaskPainter.end(); + + qreal pressure = m_strengthOption.apply(info); + quint8* dabData = dab->data(); + + KisSequentialConstIterator it(fillMaskDevice, QRect(x, y, rect.width(), rect.height())); + while (it.nextPixel()) { + const QRgb *maskQRgb = reinterpret_cast(it.oldRawData()); + dab->colorSpace()->fillGrayBrushWithColorAndLightnessWithStrength(dabData, maskQRgb, dabData, pressure, 1); + dabData += dab->pixelSize(); + } +} + +void KisTextureProperties::applyGradient(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info) { + if (!m_enabled) return; + + KIS_SAFE_ASSERT_RECOVER_RETURN(m_gradient && m_gradient->valid()); + + KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); + QRect rect = dab->bounds(); + + KisPaintDeviceSP mask = m_maskInfo->mask(); + const QRect maskBounds = m_maskInfo->maskBounds(); + + KIS_SAFE_ASSERT_RECOVER_RETURN(mask); + + int x = offset.x() % maskBounds.width() - m_offsetX; + int y = offset.y() % maskBounds.height() - m_offsetY; + + + KisFillPainter fillPainter(fillDevice); + fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); + fillPainter.end(); + + qreal pressure = m_strengthOption.apply(info); + quint8* dabData = dab->data(); + + //for gradient textures... + KoMixColorsOp* colorMix = dab->colorSpace()->mixColorsOp(); + qint16 colorWeights[2]; + colorWeights[0] = qRound(pressure * 255); + colorWeights[1] = 255 - colorWeights[0]; + quint8* colors[2]; + + KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width()); + for (int row = 0; row < rect.height(); ++row) { + for (int col = 0; col < rect.width(); ++col) { + + qreal gradientvalue = qreal(*iter->oldRawData()) / 255.0; + KoColor paintcolor; + paintcolor.setColor(m_cachedGradient.cachedAt(gradientvalue), m_gradient->colorSpace()); + paintcolor.setOpacity(qMin(paintcolor.opacityF(), dab->colorSpace()->opacityF(dabData))); + paintcolor.convertTo(dab->colorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); + colors[0] = paintcolor.data(); + KoColor dabColor(dabData, dab->colorSpace()); + colors[1] = dabColor.data(); + colorMix->mixColors(colors, colorWeights, 2, dabData); + + iter->nextPixel(); + dabData += dab->pixelSize(); + } + iter->nextRow(); + } +} + void KisTextureProperties::apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation & info) { if (!m_enabled) return; + if (m_texturingMode == LIGHTNESS) { + applyLightness(dab, offset, info); + return; + } + else if (m_texturingMode == GRADIENT) { + applyGradient(dab, offset, info); + return; + } + KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); QRect rect = dab->bounds(); KisPaintDeviceSP mask = m_maskInfo->mask(); const QRect maskBounds = m_maskInfo->maskBounds(); KIS_SAFE_ASSERT_RECOVER_RETURN(mask); int x = offset.x() % maskBounds.width() - m_offsetX; int y = offset.y() % maskBounds.height() - m_offsetY; KisFillPainter fillPainter(fillDevice); fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); fillPainter.end(); qreal pressure = m_strengthOption.apply(info); - quint8 *dabData = dab->data(); + quint8* dabData = dab->data(); KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width()); for (int row = 0; row < rect.height(); ++row) { for (int col = 0; col < rect.width(); ++col) { if (m_texturingMode == MULTIPLY) { dab->colorSpace()->multiplyAlpha(dabData, quint8(*iter->oldRawData() * pressure), 1); } else { int pressureOffset = (1.0 - pressure) * 255; qint16 maskA = *iter->oldRawData() + pressureOffset; quint8 dabA = dab->colorSpace()->opacityU8(dabData); dabA = qMax(0, (qint16)dabA - maskA); dab->colorSpace()->setOpacity(dabData, dabA, 1); } iter->nextPixel(); dabData += dab->pixelSize(); } iter->nextRow(); } } diff --git a/plugins/paintops/libpaintop/kis_texture_option.h b/plugins/paintops/libpaintop/kis_texture_option.h index 90a6194708..8e284ff974 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.h +++ b/plugins/paintops/libpaintop/kis_texture_option.h @@ -1,98 +1,111 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2012 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_TEXTURE_OPTION_H #define KIS_TEXTURE_OPTION_H #include #include #include #include "kis_paintop_option.h" #include "kis_pressure_texture_strength_option.h" +#include +#include #include "KisTextureMaskInfo.h" #include class KisTextureChooser; class KoPattern; class KoResource; class KisPropertiesConfiguration; class KisPaintopLodLimitations; class PAINTOP_EXPORT KisTextureOption : public KisPaintOpOption { Q_OBJECT public: explicit KisTextureOption(); ~KisTextureOption() override; public Q_SLOTS: void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; void lodLimitations(KisPaintopLodLimitations *l) const override; private Q_SLOTS: void resetGUI(KoResource*); /// called when a new pattern is selected + private: /// UI Widget that stores all the texture options KisTextureChooser* m_textureOptions; }; class PAINTOP_EXPORT KisTextureProperties { public: KisTextureProperties(int levelOfDetail); enum TexturingMode { MULTIPLY, - SUBTRACT + SUBTRACT, + LIGHTNESS, + GRADIENT }; bool m_enabled; /** * @brief apply combine the texture map with the dab * @param dab the colored, final representation of the dab, after mirroring and everything. * @param offset the position of the dab on the image. used to calculate the position of the mask pattern * @param info the paint information */ void apply(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation & info); void fillProperties(const KisPropertiesConfigurationSP setting); + void setTextureGradient(const KoAbstractGradient* gradient); + +private: + + void applyLightness(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info); + void applyGradient(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation& info); private: int m_offsetX; int m_offsetY; TexturingMode m_texturingMode; + const KoAbstractGradient* m_gradient; + KoCachedGradient m_cachedGradient; int m_levelOfDetail; private: KisPressureTextureStrengthOption m_strengthOption; KisTextureMaskInfoSP m_maskInfo; }; #endif // KIS_TEXTURE_OPTION_H