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