|
|
|
/*
|
|
|
|
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
|
|
|
|
*
|
|
|
|
* 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 <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <cfloat>
|
|
|
|
|
|
|
|
#include "tqbrush.h"
|
|
|
|
#include "tqcolor.h"
|
|
|
|
#include "tqfontinfo.h"
|
|
|
|
#include "tqfontmetrics.h"
|
|
|
|
#include "tqpen.h"
|
|
|
|
#include "tqregion.h"
|
|
|
|
#include "tqwmatrix.h"
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqmap.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
#include <tqpointarray.h>
|
|
|
|
#include <tqrect.h>
|
|
|
|
#include <tqstring.h>
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
|
|
|
|
#include "kis_brush.h"
|
|
|
|
#include "kis_debug_areas.h"
|
|
|
|
#include "kis_gradient.h"
|
|
|
|
#include "kis_image.h"
|
|
|
|
#include "kis_iterators_pixel.h"
|
|
|
|
#include "kis_layer.h"
|
|
|
|
#include "kis_paint_device.h"
|
|
|
|
#include "kis_pattern.h"
|
|
|
|
#include "kis_rect.h"
|
|
|
|
#include "kis_colorspace.h"
|
|
|
|
#include "kis_types.h"
|
|
|
|
#include "kis_vec.h"
|
|
|
|
#include "kis_selection.h"
|
|
|
|
#include "kis_gradient_painter.h"
|
|
|
|
#include "kis_meta_registry.h"
|
|
|
|
#include "kis_colorspace_factory_registry.h"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
class GradientShapeStrategy {
|
|
|
|
public:
|
|
|
|
GradientShapeStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
|
|
|
|
virtual ~GradientShapeStrategy() {}
|
|
|
|
|
|
|
|
virtual double valueAt(double x, double y) const = 0;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
KisPoint m_gradientVectorStart;
|
|
|
|
KisPoint m_gradientVectorEnd;
|
|
|
|
};
|
|
|
|
|
|
|
|
GradientShapeStrategy::GradientShapeStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
|
|
|
|
: m_gradientVectorStart(gradientVectorStart), m_gradientVectorEnd(gradientVectorEnd)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class LinearGradientStrategy : public GradientShapeStrategy {
|
|
|
|
typedef GradientShapeStrategy super;
|
|
|
|
public:
|
|
|
|
LinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
|
|
|
|
|
|
|
|
virtual double valueAt(double x, double y) const;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
double m_normalisedVectorX;
|
|
|
|
double m_normalisedVectorY;
|
|
|
|
double m_vectorLength;
|
|
|
|
};
|
|
|
|
|
|
|
|
LinearGradientStrategy::LinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
|
|
|
|
: super(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 {
|
|
|
|
typedef LinearGradientStrategy super;
|
|
|
|
public:
|
|
|
|
BiLinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
|
|
|
|
|
|
|
|
virtual double valueAt(double x, double y) const;
|
|
|
|
};
|
|
|
|
|
|
|
|
BiLinearGradientStrategy::BiLinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
|
|
|
|
: super(gradientVectorStart, gradientVectorEnd)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
double BiLinearGradientStrategy::valueAt(double x, double y) const
|
|
|
|
{
|
|
|
|
double t = super::valueAt(x, y);
|
|
|
|
|
|
|
|
// Reflect
|
|
|
|
if (t < -DBL_EPSILON) {
|
|
|
|
t = -t;
|
|
|
|
}
|
|
|
|
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class RadialGradientStrategy : public GradientShapeStrategy {
|
|
|
|
typedef GradientShapeStrategy super;
|
|
|
|
public:
|
|
|
|
RadialGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
|
|
|
|
|
|
|
|
virtual double valueAt(double x, double y) const;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
double m_radius;
|
|
|
|
};
|
|
|
|
|
|
|
|
RadialGradientStrategy::RadialGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
|
|
|
|
: super(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 GradientShapeStrategy {
|
|
|
|
typedef GradientShapeStrategy super;
|
|
|
|
public:
|
|
|
|
SquareGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
|
|
|
|
|
|
|
|
virtual double valueAt(double x, double y) const;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
double m_normalisedVectorX;
|
|
|
|
double m_normalisedVectorY;
|
|
|
|
double m_vectorLength;
|
|
|
|
};
|
|
|
|
|
|
|
|
SquareGradientStrategy::SquareGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
|
|
|
|
: super(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 = TQMAX(distance1, distance2) / m_vectorLength;
|
|
|
|
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class ConicalGradientStrategy : public GradientShapeStrategy {
|
|
|
|
typedef GradientShapeStrategy super;
|
|
|
|
public:
|
|
|
|
ConicalGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
|
|
|
|
|
|
|
|
virtual double valueAt(double x, double y) const;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
double m_vectorAngle;
|
|
|
|
};
|
|
|
|
|
|
|
|
ConicalGradientStrategy::ConicalGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
|
|
|
|
: super(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 GradientShapeStrategy {
|
|
|
|
typedef GradientShapeStrategy super;
|
|
|
|
public:
|
|
|
|
ConicalSymetricGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
|
|
|
|
|
|
|
|
virtual double valueAt(double x, double y) const;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
double m_vectorAngle;
|
|
|
|
};
|
|
|
|
|
|
|
|
ConicalSymetricGradientStrategy::ConicalSymetricGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
|
|
|
|
: super(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 GradientRepeatStrategy {
|
|
|
|
public:
|
|
|
|
GradientRepeatStrategy() {}
|
|
|
|
virtual ~GradientRepeatStrategy() {}
|
|
|
|
|
|
|
|
virtual double valueAt(double t) const = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class GradientRepeatNoneStrategy : public GradientRepeatStrategy {
|
|
|
|
public:
|
|
|
|
static GradientRepeatNoneStrategy *instance();
|
|
|
|
|
|
|
|
virtual double valueAt(double t) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
GradientRepeatNoneStrategy() {}
|
|
|
|
|
|
|
|
static GradientRepeatNoneStrategy *m_instance;
|
|
|
|
};
|
|
|
|
|
|
|
|
GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::m_instance = 0;
|
|
|
|
|
|
|
|
GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::instance()
|
|
|
|
{
|
|
|
|
if (m_instance == 0) {
|
|
|
|
m_instance = new GradientRepeatNoneStrategy();
|
|
|
|
TQ_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();
|
|
|
|
|
|
|
|
virtual double valueAt(double t) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
GradientRepeatForwardsStrategy() {}
|
|
|
|
|
|
|
|
static GradientRepeatForwardsStrategy *m_instance;
|
|
|
|
};
|
|
|
|
|
|
|
|
GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::m_instance = 0;
|
|
|
|
|
|
|
|
GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::instance()
|
|
|
|
{
|
|
|
|
if (m_instance == 0) {
|
|
|
|
m_instance = new GradientRepeatForwardsStrategy();
|
|
|
|
TQ_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<int>(t);
|
|
|
|
|
|
|
|
if (t < DBL_EPSILON) {
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
|
|
|
|
double value = t - i;
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class GradientRepeatAlternateStrategy : public GradientRepeatStrategy {
|
|
|
|
public:
|
|
|
|
static GradientRepeatAlternateStrategy *instance();
|
|
|
|
|
|
|
|
virtual double valueAt(double t) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
GradientRepeatAlternateStrategy() {}
|
|
|
|
|
|
|
|
static GradientRepeatAlternateStrategy *m_instance;
|
|
|
|
};
|
|
|
|
|
|
|
|
GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::m_instance = 0;
|
|
|
|
|
|
|
|
GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::instance()
|
|
|
|
{
|
|
|
|
if (m_instance == 0) {
|
|
|
|
m_instance = new GradientRepeatAlternateStrategy();
|
|
|
|
TQ_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<int>(t);
|
|
|
|
|
|
|
|
double value = t - i;
|
|
|
|
|
|
|
|
if (i % 2 == 1) {
|
|
|
|
value = 1 - value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
KisGradientPainter::KisGradientPainter()
|
|
|
|
: super()
|
|
|
|
{
|
|
|
|
m_gradient = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
KisGradientPainter::KisGradientPainter(KisPaintDeviceSP device) : super(device), m_gradient(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KisGradientPainter::paintGradient(const KisPoint& gradientVectorStart,
|
|
|
|
const KisPoint& gradientVectorEnd,
|
|
|
|
enumGradientShape shape,
|
|
|
|
enumGradientRepeat repeat,
|
|
|
|
double antiAliasThreshold,
|
|
|
|
bool reverseGradient,
|
|
|
|
TQ_INT32 startx,
|
|
|
|
TQ_INT32 starty,
|
|
|
|
TQ_INT32 width,
|
|
|
|
TQ_INT32 height)
|
|
|
|
{
|
|
|
|
m_cancelRequested = false;
|
|
|
|
|
|
|
|
if (!m_gradient) return false;
|
|
|
|
|
|
|
|
GradientShapeStrategy *shapeStrategy = 0;
|
|
|
|
|
|
|
|
switch (shape) {
|
|
|
|
case GradientShapeLinear:
|
|
|
|
shapeStrategy = new LinearGradientStrategy(gradientVectorStart, gradientVectorEnd);
|
|
|
|
break;
|
|
|
|
case GradientShapeBiLinear:
|
|
|
|
shapeStrategy = new BiLinearGradientStrategy(gradientVectorStart, gradientVectorEnd);
|
|
|
|
break;
|
|
|
|
case GradientShapeRadial:
|
|
|
|
shapeStrategy = new RadialGradientStrategy(gradientVectorStart, gradientVectorEnd);
|
|
|
|
break;
|
|
|
|
case GradientShapeSquare:
|
|
|
|
shapeStrategy = new SquareGradientStrategy(gradientVectorStart, gradientVectorEnd);
|
|
|
|
break;
|
|
|
|
case GradientShapeConical:
|
|
|
|
shapeStrategy = new ConicalGradientStrategy(gradientVectorStart, gradientVectorEnd);
|
|
|
|
break;
|
|
|
|
case GradientShapeConicalSymetric:
|
|
|
|
shapeStrategy = new ConicalSymetricGradientStrategy(gradientVectorStart, gradientVectorEnd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
TQ_CHECK_PTR(shapeStrategy);
|
|
|
|
|
|
|
|
GradientRepeatStrategy *repeatStrategy = 0;
|
|
|
|
|
|
|
|
switch (repeat) {
|
|
|
|
case GradientRepeatNone:
|
|
|
|
repeatStrategy = GradientRepeatNoneStrategy::instance();
|
|
|
|
break;
|
|
|
|
case GradientRepeatForwards:
|
|
|
|
repeatStrategy = GradientRepeatForwardsStrategy::instance();
|
|
|
|
break;
|
|
|
|
case GradientRepeatAlternate:
|
|
|
|
repeatStrategy = GradientRepeatAlternateStrategy::instance();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Q_ASSERT(repeatStrategy != 0);
|
|
|
|
|
|
|
|
|
|
|
|
//If the device has a selection only iterate over that selection
|
|
|
|
TQRect r;
|
|
|
|
if( m_device->hasSelection() ) {
|
|
|
|
r = m_device->selection()->selectedExactRect();
|
|
|
|
startx = r.x();
|
|
|
|
starty = r.y();
|
|
|
|
width = r.width();
|
|
|
|
height = r.height();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQ_INT32 endx = startx + width - 1;
|
|
|
|
TQ_INT32 endy = starty + height - 1;
|
|
|
|
|
|
|
|
TQImage layer (width, height, 32);
|
|
|
|
layer.setAlphaBuffer(true);
|
|
|
|
|
|
|
|
int pixelsProcessed = 0;
|
|
|
|
int lastProgressPercent = 0;
|
|
|
|
|
|
|
|
emit notifyProgressStage(i18n("Rendering gradient..."), 0);
|
|
|
|
|
|
|
|
int totalPixels = width * height;
|
|
|
|
|
|
|
|
if (antiAliasThreshold < 1 - DBL_EPSILON) {
|
|
|
|
totalPixels *= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int y = starty; y <= endy; y++) {
|
|
|
|
for (int x = startx; x <= endx; x++) {
|
|
|
|
|
|
|
|
double t = shapeStrategy->valueAt( x, y);
|
|
|
|
t = repeatStrategy->valueAt(t);
|
|
|
|
|
|
|
|
if (reverseGradient) {
|
|
|
|
t = 1 - t;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQColor color;
|
|
|
|
TQ_UINT8 opacity;
|
|
|
|
|
|
|
|
m_gradient->colorAt(t, &color, &opacity);
|
|
|
|
|
|
|
|
layer.setPixel(x - startx, y - starty,
|
|
|
|
tqRgba(color.red(), color.green(), color.blue(), opacity));
|
|
|
|
|
|
|
|
pixelsProcessed++;
|
|
|
|
|
|
|
|
int progressPercent = (pixelsProcessed * 100) / totalPixels;
|
|
|
|
|
|
|
|
if (progressPercent > lastProgressPercent) {
|
|
|
|
emit notifyProgress(progressPercent);
|
|
|
|
lastProgressPercent = progressPercent;
|
|
|
|
|
|
|
|
if (m_cancelRequested) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_cancelRequested) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_cancelRequested && antiAliasThreshold < 1 - DBL_EPSILON) {
|
|
|
|
|
|
|
|
TQColor color;
|
|
|
|
emit notifyProgressStage(i18n("Anti-aliasing gradient..."), lastProgressPercent);
|
|
|
|
TQ_UINT8 * layerPointer = layer.bits();
|
|
|
|
for (int y = starty; y <= endy; y++) {
|
|
|
|
for (int x = startx; x <= endx; x++) {
|
|
|
|
|
|
|
|
double maxDistance = 0;
|
|
|
|
|
|
|
|
TQ_UINT8 redThis = layerPointer[2];
|
|
|
|
TQ_UINT8 greenThis = layerPointer[1];
|
|
|
|
TQ_UINT8 blueThis = layerPointer[0];
|
|
|
|
TQ_UINT8 thisPixelOpacity = layerPointer[3];
|
|
|
|
|
|
|
|
for (int yOffset = -1; yOffset < 2; yOffset++) {
|
|
|
|
for (int xOffset = -1; xOffset < 2; xOffset++) {
|
|
|
|
|
|
|
|
if (xOffset != 0 || yOffset != 0) {
|
|
|
|
int sampleX = x + xOffset;
|
|
|
|
int sampleY = y + yOffset;
|
|
|
|
|
|
|
|
if (sampleX >= startx && sampleX <= endx && sampleY >= starty && sampleY <= endy) {
|
|
|
|
uint x = sampleX - startx;
|
|
|
|
uint y = sampleY - starty;
|
|
|
|
TQ_UINT8 * pixelPos = layer.bits() + (y * width * 4) + (x * 4);
|
|
|
|
TQ_UINT8 red = *(pixelPos +2);
|
|
|
|
TQ_UINT8 green = *(pixelPos + 1);
|
|
|
|
TQ_UINT8 blue = *pixelPos;
|
|
|
|
TQ_UINT8 opacity = *(pixelPos + 3);
|
|
|
|
|
|
|
|
double dRed = (red * opacity - redThis * thisPixelOpacity) / 65535.0;
|
|
|
|
double dGreen = (green * opacity - greenThis * thisPixelOpacity) / 65535.0;
|
|
|
|
double dBlue = (blue * opacity - blueThis * thisPixelOpacity) / 65535.0;
|
|
|
|
|
|
|
|
#define SQRT_3 1.7320508
|
|
|
|
|
|
|
|
double distance =/* sqrt(*/dRed * dRed + dGreen * dGreen + dBlue * dBlue/*) / SQRT_3*/;
|
|
|
|
|
|
|
|
if (distance > maxDistance) {
|
|
|
|
maxDistance = distance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (maxDistance > 3.*antiAliasThreshold*antiAliasThreshold) {
|
|
|
|
const int numSamples = 4;
|
|
|
|
|
|
|
|
int totalRed = 0;
|
|
|
|
int totalGreen = 0;
|
|
|
|
int totalBlue = 0;
|
|
|
|
int totalOpacity = 0;
|
|
|
|
|
|
|
|
for (int ySample = 0; ySample < numSamples; ySample++) {
|
|
|
|
for (int xSample = 0; xSample < numSamples; xSample++) {
|
|
|
|
|
|
|
|
double sampleWidth = 1.0 / numSamples;
|
|
|
|
|
|
|
|
double sampleX = x - 0.5 + (sampleWidth / 2) + xSample * sampleWidth;
|
|
|
|
double sampleY = y - 0.5 + (sampleWidth / 2) + ySample * sampleWidth;
|
|
|
|
|
|
|
|
double t = shapeStrategy->valueAt(sampleX, sampleY);
|
|
|
|
t = repeatStrategy->valueAt(t);
|
|
|
|
|
|
|
|
if (reverseGradient) {
|
|
|
|
t = 1 - t;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQ_UINT8 opacity;
|
|
|
|
|
|
|
|
m_gradient->colorAt(t, &color, &opacity);
|
|
|
|
|
|
|
|
totalRed += color.red();
|
|
|
|
totalGreen += color.green();
|
|
|
|
totalBlue += color.blue();
|
|
|
|
totalOpacity += opacity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int red = totalRed / (numSamples * numSamples);
|
|
|
|
int green = totalGreen / (numSamples * numSamples);
|
|
|
|
int blue = totalBlue / (numSamples * numSamples);
|
|
|
|
int opacity = totalOpacity / (numSamples * numSamples);
|
|
|
|
|
|
|
|
layer.setPixel(x - startx, y - starty, tqRgba(red, green, blue, opacity));
|
|
|
|
}
|
|
|
|
|
|
|
|
pixelsProcessed++;
|
|
|
|
|
|
|
|
int progressPercent = (pixelsProcessed * 100) / totalPixels;
|
|
|
|
|
|
|
|
if (progressPercent > lastProgressPercent) {
|
|
|
|
emit notifyProgress(progressPercent);
|
|
|
|
lastProgressPercent = progressPercent;
|
|
|
|
|
|
|
|
if (m_cancelRequested) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
layerPointer += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_cancelRequested) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_cancelRequested) {
|
|
|
|
kdDebug() << "Have we got a selection? " << m_device->hasSelection() << endl;
|
|
|
|
KisPaintDeviceSP dev = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "temporary device for gradient");
|
|
|
|
dev->writeBytes(layer.bits(), startx, starty, width, height);
|
|
|
|
bltSelection(startx, starty, m_compositeOp, dev, m_opacity, startx, starty, width, height);
|
|
|
|
}
|
|
|
|
delete shapeStrategy;
|
|
|
|
|
|
|
|
emit notifyProgressDone();
|
|
|
|
|
|
|
|
return !m_cancelRequested;
|
|
|
|
}
|