File indexing completed on 2024-05-26 04:34:11

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "particle_brush.h"
0008 
0009 #include "kis_paint_device.h"
0010 #include "kis_random_accessor_ng.h"
0011 
0012 #include <KoColorSpace.h>
0013 #include <KoColor.h>
0014 
0015 #include <math.h>
0016 
0017 const qreal TIME = 0.000030;
0018 
0019 ParticleBrush::ParticleBrush()
0020 {
0021     m_properties = 0;
0022 }
0023 
0024 ParticleBrush::~ParticleBrush()
0025 {
0026 }
0027 
0028 
0029 void ParticleBrush::initParticles()
0030 {
0031     m_particlePos.resize(m_properties->particleCount);
0032     m_particleNextPos.resize(m_properties->particleCount);
0033     m_acceleration.resize(m_properties->particleCount);
0034 }
0035 
0036 void ParticleBrush::setInitialPosition(const QPointF &pos)
0037 {
0038     for (int i = 0; i < m_properties->particleCount; i++) {
0039         m_particlePos[i] = pos;
0040         m_particleNextPos[i] = pos;
0041         m_acceleration[i] = (i + m_properties->particleIterations) * 0.5;
0042     }
0043 }
0044 
0045 
0046 void ParticleBrush::paintParticle(KisRandomAccessorSP accWrite, const KoColorSpace * cs, const QPointF &pos, const KoColor& color, qreal weight, bool respectOpacity)
0047 {
0048     // opacity top left, right, bottom left, right
0049     KoColor myColor(color);
0050     quint8 opacity = respectOpacity ? myColor.opacityU8() : OPACITY_OPAQUE_U8;
0051 
0052     int ipx = floor(pos.x());
0053     int ipy = floor(pos.y());
0054     qreal fx = pos.x() - ipx;
0055     qreal fy = pos.y() - ipy;
0056 
0057     quint8 btl = qRound((1.0 - fx) * (1.0 - fy) * opacity * weight);
0058     quint8 btr = qRound((fx)  * (1.0 - fy) * opacity * weight);
0059     quint8 bbl = qRound((1.0 - fx) * (fy)  * opacity * weight);
0060     quint8 bbr = qRound((fx)  * (fy)  * opacity * weight);
0061 
0062     accWrite->moveTo(ipx  , ipy);
0063     myColor.setOpacity(quint8(qBound<quint16>(OPACITY_TRANSPARENT_U8, btl + cs->opacityU8(accWrite->rawData()), OPACITY_OPAQUE_U8)));
0064     memcpy(accWrite->rawData(), myColor.data(), cs->pixelSize());
0065 
0066     accWrite->moveTo(ipx + 1, ipy);
0067     myColor.setOpacity(quint8(qBound<quint16>(OPACITY_TRANSPARENT_U8, btr + cs->opacityU8(accWrite->rawData()), OPACITY_OPAQUE_U8)));
0068     memcpy(accWrite->rawData(), myColor.data(), cs->pixelSize());
0069 
0070     accWrite->moveTo(ipx, ipy + 1);
0071     myColor.setOpacity(quint8(qBound<quint16>(OPACITY_TRANSPARENT_U8, bbl + cs->opacityU8(accWrite->rawData()), OPACITY_OPAQUE_U8)));
0072     memcpy(accWrite->rawData(), myColor.data(), cs->pixelSize());
0073 
0074     accWrite->moveTo(ipx + 1, ipy + 1);
0075     myColor.setOpacity(quint8(qBound<quint16>(OPACITY_TRANSPARENT_U8, bbr + cs->opacityU8(accWrite->rawData()), OPACITY_OPAQUE_U8)));
0076     memcpy(accWrite->rawData(), myColor.data(), cs->pixelSize());
0077 }
0078 
0079 
0080 
0081 
0082 void ParticleBrush::draw(KisPaintDeviceSP dab, const KoColor& color, const QPointF &pos)
0083 {
0084     KisRandomAccessorSP accessor = dab->createRandomAccessorNG();
0085     const KoColorSpace * cs = dab->colorSpace();
0086 
0087     QRect boundingRect;
0088 
0089     if (m_properties->particleScaleX < 0 || m_properties->particleScaleY < 0 || m_properties->particleGravity < 0) {
0090         boundingRect = dab->defaultBounds()->bounds();
0091     }
0092 
0093     for (int i = 0; i < m_properties->particleIterations; i++) {
0094         for (int j = 0; j < m_properties->particleCount; j++) {
0095             /*
0096                 m_time = 0.01;
0097                 QPointF temp = m_position;
0098                 QPointF dist = m_position - m_oldPosition;
0099                 m_position = m_position + (dist + (m_acceleration*m_time*m_time));
0100                 m_oldPosition = temp;
0101             */
0102 
0103             /*
0104                 QPointF dist = info.pos() - m_position;
0105                 dist *= 0.3; // scale
0106                 dist *= 10; // force
0107                 m_oldPosition += dist;
0108                 m_oldPosition *= 0.989;
0109                 m_position = m_position + m_oldPosition * m_time * m_time;
0110             */
0111 
0112 
0113             QPointF dist = pos - m_particlePos[j];
0114             dist.setX(dist.x() * m_properties->particleScaleX);
0115             dist.setY(dist.y() * m_properties->particleScaleY);
0116             dist = dist * m_acceleration[j];
0117             m_particleNextPos[j] = m_particleNextPos[j] + dist;
0118             m_particleNextPos[j] *= m_properties->particleGravity;
0119             m_particlePos[j] = m_particlePos[j] + (m_particleNextPos[j] * TIME);
0120 
0121             /**
0122              * When the scale is negative the equation becomes
0123              * unstable, and the point coordinates grow to infinity,
0124              * so just limit them in that case.
0125              *
0126              * Generally, the effect of instability might be quite
0127              * interesting for the painters.
0128              */
0129 
0130             // If scale is negative, position can easily jump into infinity
0131             //  and then it won't be caught by contains();
0132             //  and then it will be passed to the lockless hashtable
0133             //  and then it will crash.
0134             // Hence better to catch infinity here and just not paint anything.
0135             QPointF pointF = m_particlePos[j];
0136 
0137             const qint32 max = 2147483600;
0138             const qint32 min = -max;
0139             bool nearInfinity = pointF.x() < min || pointF.x () > max || pointF.y() < min || pointF.y() > max;
0140             bool inside = boundingRect.contains(m_particlePos[j].toPoint());
0141 
0142             if (boundingRect.isEmpty() || (inside && !nearInfinity)) {
0143                 paintParticle(accessor, cs, m_particlePos[j], color, m_properties->particleWeight, true);
0144             }
0145 
0146         }//for j
0147     }//for i
0148 }
0149 
0150 
0151