diff --git a/plugins/paintops/CMakeLists.txt b/plugins/paintops/CMakeLists.txt index 8fe2cc7786..efb6b402f2 100644 --- a/plugins/paintops/CMakeLists.txt +++ b/plugins/paintops/CMakeLists.txt @@ -1,20 +1,21 @@ include_directories(libpaintop) add_subdirectory( libpaintop ) add_subdirectory( defaultpresets ) add_subdirectory( defaultpaintops ) add_subdirectory( hairy ) add_subdirectory( chalk ) add_subdirectory( dynadraw ) add_subdirectory( deform ) add_subdirectory( curvebrush ) add_subdirectory( spray ) add_subdirectory( filterop ) add_subdirectory( experiment ) add_subdirectory( particle ) add_subdirectory( gridbrush ) add_subdirectory( hatching) add_subdirectory( sketch ) add_subdirectory( colorsmudge ) add_subdirectory( roundmarker ) add_subdirectory( tangentnormal ) +add_subdirectory( watercolor ) diff --git a/plugins/paintops/watercolor/CMakeLists.txt b/plugins/paintops/watercolor/CMakeLists.txt new file mode 100644 index 0000000000..6620c735ca --- /dev/null +++ b/plugins/paintops/watercolor/CMakeLists.txt @@ -0,0 +1,11 @@ +set(kritawatercolorpaintop_SOURCES + kis_splat.cpp + kis_wetmap.cpp + kis_splat_generator.cpp + ) + +add_library(kritawatercolorpaintop MODULE ${kritawatercolorpaintop_SOURCES}) + +target_link_libraries(kritawatercolorpaintop kritalibpaintop) + +install(TARGETS kritawatercolorpaintop DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/paintops/watercolor/kis_splat.cpp b/plugins/paintops/watercolor/kis_splat.cpp new file mode 100644 index 0000000000..794782f928 --- /dev/null +++ b/plugins/paintops/watercolor/kis_splat.cpp @@ -0,0 +1,152 @@ +#include "kis_splat.h" +#include "kis_random_generator.h" + +#define START_OPACITY 50 +#define STANDART_LIFETIME 30 + +KisSplat::KisSplat(QPointF offset, int width, KoColor splatColor) + : m_life(STANDART_LIFETIME), m_roughness(1.f), m_flow(1.f), + m_motionBias(QPointF(0.f, 0.f)), m_initColor(splatColor) +{ + m_fix = 8*STANDART_LIFETIME; + m_initColor.setOpacity(quint8(START_OPACITY)); + + int r = width / 2; + int n = 128; + + qreal dt = 2.f * M_PI / n; + QPointF p; + + for (int i = 0; i < n; i++) + { + p.setX(cos(i * dt)); + p.setY(sin(i * dt)); + m_vertices.push_back(QPointF(static_cast (r * p.x()) + offset.x(), + static_cast (r * p.y()) + offset.y())); + m_velocities.push_back(QPointF(2.f * p.x(), + 2.f * p.y())); + } + m_initSize = CalcSize(); + m_startTime = QTime::currentTime(); +} + +KisSplat::KisSplat(QPointF offset, QPointF velocityBias, int width, int life, + qreal roughness, qreal flow, qreal radialSpeed, KoColor splatColor) + : m_life(life), m_roughness(roughness), m_flow(flow), + m_motionBias(velocityBias), m_initColor(splatColor) +{ + m_fix = 8*STANDART_LIFETIME; + m_initColor.setOpacity(quint8(START_OPACITY)); + int r = width / 2; + int n = 128; + + qreal dt = 2.f * M_PI / n; + QPointF p; + for (int i = 0; i < n; i++) + { + p.setX(cos(i * dt)); + p.setY(sin(i * dt)); + m_vertices.push_back(QPointF(static_cast (r * p.x()) + offset.x(), + static_cast (r * p.y()) + offset.y())); + m_velocities.push_back(QPointF(radialSpeed * p.x(), + radialSpeed * p.y())); + } + m_initSize = CalcSize(); + m_startTime = QTime::currentTime(); +} + +qreal KisSplat::CalcSize() +{ + if (m_vertices.length() < 3) + return 0.f; + + QPointF v0 = m_vertices[0]; + qreal v0x = v0.x(); + qreal v0y = v0.y(); + QPointF e0 = m_vertices[1] - v0; + qreal e0x = e0.x(); + qreal e0y = e0.y(); + qreal s = 0.f; + int length = m_vertices.length(); + + for (int i = 2; i < length; i++) { + QPointF v2 = m_vertices[i]; + qreal e1x = v2.x() - v0x; + qreal e1y = v2.y() - v0y; + s += e0x * e1y - e0y * e1x; + e0x = e1x; + e0y = e1y; + } + + return s >= 0 ? s : -s; +} + +KoColor KisSplat::getColor() +{ + KoColor current; + current = m_initColor; + qreal multiply = m_initSize / CalcSize(); + if (multiply < 0.f || multiply > 1.f) + multiply = 1; + + current.setOpacity(current.opacityU8() * multiply); + return current; +} + +QPainterPath KisSplat::shape() const +{ + QPainterPath path; + + int len = m_vertices.length(); + path = *(new QPainterPath(m_vertices[0])); + + for (int i = 0; i < len-2; i+=2) { + path.quadTo(m_vertices[i+1], m_vertices[i+2]); + } + + path.quadTo(m_vertices[len-1], m_vertices[0]); + + return path; +} + +QRectF KisSplat::boundingRect() const +{ + return m_vertices.boundingRect(); +} + +int KisSplat::update(KisWetMap *wetMap) +{ + if (m_life <= 0) { + if (m_fix <= 0) { + return KisSplat::Dried; + } else { + m_fix--; + return KisSplat::Fixed; + } + } + + m_life--; + + KisRandomGenerator randG(NULL); + + for (int i = 0; i < m_vertices.length(); i++) { + QPointF x = m_vertices[i]; + QPointF v = m_velocities[i]; + QPointF d = (1.f - alpha) * m_motionBias + alpha / randG.doubleRandomAt(1.f, 1.f + m_roughness) * v; + + QPointF x1 = x + m_flow * d; + QPointF(randG.doubleRandomAt(-m_roughness, m_roughness), + randG.doubleRandomAt(-m_roughness, m_roughness)); + int wet = wetMap->getWater((int)x1.x(), (int)x1.y()); // Считываение количества жидкости + + if (wet > 0) + m_vertices[i] = x1; + } + + if (!m_life) { + for (int i = 0; i < m_vertices.length(); i++) { + m_velocities[i] = QPointF(0, 0); + } + } + + return KisSplat::Flowing; +} diff --git a/plugins/paintops/watercolor/kis_splat.h b/plugins/paintops/watercolor/kis_splat.h new file mode 100644 index 0000000000..cce086f0ec --- /dev/null +++ b/plugins/paintops/watercolor/kis_splat.h @@ -0,0 +1,53 @@ +#ifndef KIS_SPLAT_H +#define KIS_SPLAT_H + +#include +#include +#include +#include +#include + +#include "kis_wetmap.h" +#include "kis_random_generator.h" + + +class KisSplat +{ +public: + enum SplatState { + Flowing, + Fixed, + Dried + }; + + KisSplat(QPointF offset, int width, KoColor splatColor); + KisSplat(QPointF offset, QPointF velocityBias, int width, int life, + qreal roughness, qreal flow, qreal radialSpeed, KoColor splatColor); + + qreal CalcSize(); + KoColor getColor(); + + QPainterPath shape() const; + QRectF boundingRect() const; + + int update(KisWetMap *wetMap); + +private: + const float alpha = 0.33f; + + QPolygonF m_vertices; + QList m_velocities; + int m_life; + qreal m_roughness; + qreal m_flow; + QPointF m_motionBias; + + QTime m_startTime; + + qreal m_initSize; + KoColor m_initColor; + + int m_fix; +}; + +#endif diff --git a/plugins/paintops/watercolor/kis_splat_generator.cpp b/plugins/paintops/watercolor/kis_splat_generator.cpp new file mode 100644 index 0000000000..076d462eab --- /dev/null +++ b/plugins/paintops/watercolor/kis_splat_generator.cpp @@ -0,0 +1,45 @@ +#include "kis_splat_generator.h" +#include "kis_painter.h" + +SplatGenerator::SplatGenerator(int width, KoColor &clr, + KisPaintDeviceSP dev) : m_width(width), + m_color(clr), + m_device(dev) +{ + m_wetMap = new KisWetMap(); + + m_flowing = new QVector(); + m_fixed = new QVector(); + m_dried = new QVector(); +} + +void SplatGenerator::generateFromPoints(QVector &points, int msec) +{ + foreach (QPointF pnt, points) { + m_flowing->push_back(new KisSplat(pnt, m_width, m_color)); + m_wetMap->addWater(pnt.toPoint(), m_width); + } + + KisPainter painter(m_device); + painter.setPaintColor(m_color); + + for (int i = 0; i <= msec; i +=33) { + foreach (KisSplat *splat, *m_flowing) { + painter.fillPainterPath(splat->shape()); + + if (splat->update(m_wetMap) == KisSplat::Fixed) { + m_fixed->push_back(splat); + m_flowing->removeOne(splat); + } + } + + foreach (KisSplat *splat, *m_fixed) { + if (splat->update(m_wetMap) == KisSplat::Dried) { + m_dried->push_back(splat); + m_fixed->removeOne(splat); + } + } + + m_wetMap->update(); + } +} diff --git a/plugins/paintops/watercolor/kis_splat_generator.h b/plugins/paintops/watercolor/kis_splat_generator.h new file mode 100644 index 0000000000..513a6d4f16 --- /dev/null +++ b/plugins/paintops/watercolor/kis_splat_generator.h @@ -0,0 +1,30 @@ +#ifndef KIS_SPLAT_GENERATOR +#define KIS_SPLAT_GENERATOR + +#include +#include "kis_wetmap.h" +#include "kis_splat.h" + +#include "KoColor.h" +#include "kis_types.h" + +class SplatGenerator +{ +public: + SplatGenerator(int width, KoColor &clr, KisPaintDeviceSP dev); + + void generateFromPoints(QVector &points, int msec); + +private: + QVector *m_flowing; + QVector *m_fixed; + QVector *m_dried; + + KisWetMap *m_wetMap; + + int m_width; + KoColor m_color; + KisPaintDeviceSP m_device; +}; + +#endif diff --git a/plugins/paintops/watercolor/kis_wet_paintert.cpp b/plugins/paintops/watercolor/kis_wet_paintert.cpp new file mode 100644 index 0000000000..3b19337d3a --- /dev/null +++ b/plugins/paintops/watercolor/kis_wet_paintert.cpp @@ -0,0 +1,9 @@ +#include "kis_wet_paintert.h" + +struct KisWetPainter::Private +{ + Private(KisPaintDeviceSP _device, const KoColor &_color) : device(_device), color(_color) {} + + KisPaintDeviceSP device; + const KoColor &color; +}; diff --git a/plugins/paintops/watercolor/kis_wet_paintert.h b/plugins/paintops/watercolor/kis_wet_paintert.h new file mode 100644 index 0000000000..adf44fd382 --- /dev/null +++ b/plugins/paintops/watercolor/kis_wet_paintert.h @@ -0,0 +1,21 @@ +#ifndef KIS_WET_PAINTERT_H +#define KIS_WET_PAINTERT_H + +#include + +#include "kis_types.h" + +#include "kritaimage_export.h" + +class KRITAIMAGE_EXPORT KisWetPainter +{ +public: + KisMarkerPaintert(KisPaintDeviceSP device); + +private: + struct Private; + const QScopedPointer m_d; +}; + + +#endif /* KIS_WET_PAINTERT_H */ diff --git a/plugins/paintops/watercolor/kis_wetmap.cpp b/plugins/paintops/watercolor/kis_wetmap.cpp new file mode 100644 index 0000000000..89fd4b6af3 --- /dev/null +++ b/plugins/paintops/watercolor/kis_wetmap.cpp @@ -0,0 +1,55 @@ +#include "kis_wetmap.h" +#include "KoColorSpaceRegistry.h" +#include "kis_sequential_iterator.h" +#include + +KisWetMap::KisWetMap() +{ + m_wetMap = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb16()); +} + +void KisWetMap::addWater(QPoint pos, qreal radius) +{ + QRect rect(pos - QPointF(radius, radius).toPoint(), + pos + QPointF(radius, radius).toPoint()); + KisSequentialIterator it(m_wetMap, rect); + + do { + qint16 *mydata = reinterpret_cast(it.rawData()); + mydata[0] = 255; + + QPoint place(it.x(), it.y()); + QVector2D vec(place - pos); + + vec.normalize(); + + mydata[1] = 255 * vec.length(); + mydata[2] = 255 * vec.length(); + } while (it.nextPixel()); +} + +void KisWetMap::update() +{ + KisSequentialIterator it(m_wetMap, m_wetMap->exactBounds()); + + do { + qint16 *mydata = reinterpret_cast(it.rawData()); + if (mydata[0] > 0) { + mydata[0]--; + } else { + mydata[1] = 0; + mydata[2] = 0; + } + } while (it.nextPixel()); +} + +int KisWetMap::getWater(int x, int y) +{ + KisSequentialIterator it(m_wetMap, m_wetMap->exactBounds()); + while (it.x() != x && it.y() != y) + it.nextPixel(); + + qint16 *mydata = reinterpret_cast(it.rawData()); + + return mydata[0]; +} diff --git a/plugins/paintops/watercolor/kis_wetmap.h b/plugins/paintops/watercolor/kis_wetmap.h new file mode 100644 index 0000000000..5bdc41e5be --- /dev/null +++ b/plugins/paintops/watercolor/kis_wetmap.h @@ -0,0 +1,22 @@ +#ifndef KIS_WETMAP_H +#define KIS_WETMAP_H + +#include "kis_paint_device.h" + +class KisWetMap +{ +public: + KisWetMap(); + + void addWater(QPoint pos, qreal radius); + void update(); + int getWater(int x, int y); +private: + KisPaintDeviceSP m_wetMap; + float* m_waterVelocitiesX; + float* m_waterVelocitiesY; + int m_width; + int m_height; +}; + +#endif