summaryrefslogtreecommitdiffstats
path: root/krita/colorspaces/wet
diff options
context:
space:
mode:
Diffstat (limited to 'krita/colorspaces/wet')
-rw-r--r--krita/colorspaces/wet/Makefile.am27
-rw-r--r--krita/colorspaces/wet/kis_texture_filter.cc43
-rw-r--r--krita/colorspaces/wet/kis_texture_filter.h38
-rw-r--r--krita/colorspaces/wet/kis_texture_painter.cc92
-rw-r--r--krita/colorspaces/wet/kis_texture_painter.h40
-rw-r--r--krita/colorspaces/wet/kis_wet_colorspace.cc514
-rw-r--r--krita/colorspaces/wet/kis_wet_colorspace.h219
-rw-r--r--krita/colorspaces/wet/kis_wet_palette_widget.cc245
-rw-r--r--krita/colorspaces/wet/kis_wet_palette_widget.h67
-rw-r--r--krita/colorspaces/wet/kis_wetness_visualisation_filter.cc77
-rw-r--r--krita/colorspaces/wet/kis_wetness_visualisation_filter.h50
-rw-r--r--krita/colorspaces/wet/kis_wetop.cc230
-rw-r--r--krita/colorspaces/wet/kis_wetop.h73
-rw-r--r--krita/colorspaces/wet/kritawetplugin.desktop86
-rw-r--r--krita/colorspaces/wet/todo24
-rw-r--r--krita/colorspaces/wet/wdgpressure.ui60
-rw-r--r--krita/colorspaces/wet/wet_plugin.cc128
-rw-r--r--krita/colorspaces/wet/wet_plugin.h45
-rw-r--r--krita/colorspaces/wet/wetdreams/Makefile6
-rw-r--r--krita/colorspaces/wet/wetdreams/wetmain.c517
-rw-r--r--krita/colorspaces/wet/wetdreams/wetpaint.c101
-rw-r--r--krita/colorspaces/wet/wetdreams/wetpaint.h4
-rw-r--r--krita/colorspaces/wet/wetdreams/wetphysics.c334
-rw-r--r--krita/colorspaces/wet/wetdreams/wetphysics.h9
-rw-r--r--krita/colorspaces/wet/wetdreams/wetpix.c332
-rw-r--r--krita/colorspaces/wet/wetdreams/wetpix.h87
-rw-r--r--krita/colorspaces/wet/wetdreams/wettexture.c84
-rw-r--r--krita/colorspaces/wet/wetdreams/wettexture.h9
-rw-r--r--krita/colorspaces/wet/wetphysicsfilter.cc424
-rw-r--r--krita/colorspaces/wet/wetphysicsfilter.h87
-rw-r--r--krita/colorspaces/wet/wetplugin.rc8
31 files changed, 4060 insertions, 0 deletions
diff --git a/krita/colorspaces/wet/Makefile.am b/krita/colorspaces/wet/Makefile.am
new file mode 100644
index 00000000..aaf561b0
--- /dev/null
+++ b/krita/colorspaces/wet/Makefile.am
@@ -0,0 +1,27 @@
+kritarcdir = $(kde_datadir)/kritaplugins
+kritarc_DATA = wetplugin.rc
+kde_services_DATA = kritawetplugin.desktop
+
+EXTRA_DIST = $(kritarc_DATA)
+
+INCLUDES = -I$(srcdir)/../../sdk \
+ -I$(srcdir)/../../core \
+ -I$(srcdir)/../../kritacolor/color_strategy/ \
+ -I$(srcdir)/../../kritacolor/ \
+ -I$(srcdir)/../../ui \
+ -I$(srcdir)/../../kopalette \
+ $(KOFFICE_INCLUDES) \
+ -I$(interfacedir) \
+ $(KOPAINTER_INCLUDES) \
+ $(all_includes)
+
+kde_module_LTLIBRARIES = kritawetplugin.la
+
+kritawetplugin_la_SOURCES = kis_wet_colorspace.cc wet_plugin.cc kis_wetop.cc kis_wet_palette_widget.cc kis_wetness_visualisation_filter.cc kis_texture_painter.cc kis_texture_filter.cc wetphysicsfilter.cc wdgpressure.ui
+noinst_HEADERS = kis_wet_colorspace.h wet_plugin.h wetphysicsfilter.h kis_wetop.cc kis_wet_palette_widget.h kis_texture_painter.h kis_wetness_visualisation_filter.h kis_texture_filter.h wetphysicsfilter.h
+
+kritawetplugin_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+kritawetplugin_la_LIBADD = ../../libkritacommon.la $(LIB_KOPAINTER) $(LIB_KOFFICECORE)
+
+kritawetplugin_la_METASOURCES = AUTO
+
diff --git a/krita/colorspaces/wet/kis_texture_filter.cc b/krita/colorspaces/wet/kis_texture_filter.cc
new file mode 100644
index 00000000..1fc2e4d3
--- /dev/null
+++ b/krita/colorspaces/wet/kis_texture_filter.cc
@@ -0,0 +1,43 @@
+/*
+ * kis_texture_filter.cc -- Part of Krita
+ *
+ * Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
+ *
+ * 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 <kdebug.h>
+#include <kis_view.h>
+#include <kis_image.h>
+#include <kis_debug_areas.h>
+#include "kis_texture_painter.h"
+#include "kis_texture_filter.h"
+
+void WetPaintDevAction::act(KisPaintDeviceSP device, Q_INT32 w, Q_INT32 h) const {
+ KisColorSpace * cs = device->colorSpace();
+
+ if (cs->id() != KisID("WET","")) {
+ kdDebug(DBG_AREA_CMS) << "You set this kind of texture on non-wet layers!.\n";
+ return;
+ } else {
+ kdDebug(DBG_AREA_CMS) << "Wet Paint Action activated!\n";
+ }
+
+ // XXX if params of the painter get configurable, make them here configurable as well?
+ KisTexturePainter painter(device);
+ painter.createTexture(0, 0, w, h);
+ painter.end();
+}
+
diff --git a/krita/colorspaces/wet/kis_texture_filter.h b/krita/colorspaces/wet/kis_texture_filter.h
new file mode 100644
index 00000000..fb2ef021
--- /dev/null
+++ b/krita/colorspaces/wet/kis_texture_filter.h
@@ -0,0 +1,38 @@
+/*
+ * kis_texture_filter.h -- Part of Krita
+ *
+ * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
+ *
+ * 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 _TEXTURE_FILTER_H
+#define _TEXTURE_FILTER_H
+
+#include <qstring.h>
+#include <klocale.h>
+#include <kis_paint_device_action.h>
+
+/// Initializes a wet paint device with a texture
+class WetPaintDevAction : public KisPaintDeviceAction {
+public:
+ virtual ~WetPaintDevAction() {}
+
+ virtual void act(KisPaintDeviceSP device, Q_INT32 w = 0, Q_INT32 h = 0) const;
+ virtual QString name() const { return i18n("Wet Texture"); }
+ virtual QString description() const { return i18n("Add a texture to the wet canvas"); }
+};
+
+#endif // _TEXTURE_FILTER_H
diff --git a/krita/colorspaces/wet/kis_texture_painter.cc b/krita/colorspaces/wet/kis_texture_painter.cc
new file mode 100644
index 00000000..a98038e6
--- /dev/null
+++ b/krita/colorspaces/wet/kis_texture_painter.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
+ *
+ * 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 <math.h>
+
+#include <kdebug.h>
+
+#include <kis_global.h>
+#include <kis_image.h>
+#include <kis_iterators_pixel.h>
+#include <kis_layer.h>
+#include <kis_paint_device.h>
+#include <kis_painter.h>
+#include <kis_types.h>
+
+#include "kis_wet_colorspace.h"
+#include "kis_texture_painter.h"
+
+KisTexturePainter::KisTexturePainter()
+ : super()
+{
+ // XXX make at least one of these configurable, probably blurh
+ m_height = 1;
+ m_blurh = 0.7;
+}
+
+KisTexturePainter::KisTexturePainter(KisPaintDeviceSP device) : super(device)
+{
+ m_height = 1;
+ m_blurh = 0.7;
+}
+
+void KisTexturePainter::createTexture( Q_INT32 x, Q_INT32 y, Q_INT32 w, Q_INT32 h)
+{
+ double hscale = 128 * m_height / RAND_MAX;
+
+ int ibh = (int) floor(256 * m_blurh + 0.5);
+
+ // initialize with random data
+ for (int y2 = 0; y2 < h; y2++) {
+ KisHLineIterator i = m_device->createHLineIterator(x, y + y2, w, true);
+ while (!i.isDone()) {
+ WetPack* pack = reinterpret_cast<WetPack*>(i.rawData());
+ WetPix* w = &(pack->adsorb);
+ w->h = ( Q_UINT16)floor(128 + hscale * rand());
+ ++i;
+ }
+ }
+
+ int lh;
+
+ // Blur horizontally
+ for (int y2 = 0; y2 < h; y2++) {
+ KisHLineIterator i = m_device->createHLineIterator(x, y + y2, w, true);
+
+ WetPack* pack = reinterpret_cast<WetPack*>(i.rawData());
+ WetPix* w = &(pack->adsorb);
+ lh = w->h;
+ ++i;
+
+ while (!i.isDone()) {
+ pack = reinterpret_cast<WetPack*>(i.rawData());
+ w = &(pack->adsorb);
+ w->h += ((lh - w->h) * ibh + 128) >> 8;
+ lh = w->h;
+ // XXX to make it easier for us later on, we store the height data in paint
+ // as well!
+ w = &(pack->paint);
+ w->h = lh;
+ ++i;
+ }
+ }
+
+ // Vertical blurring was commented out in wetdreams, the effect seems to be achievable
+ // without this.
+ // I think this is because with blur in one direction, you get more the effect of
+ // having 'fibers' in your paper
+}
diff --git a/krita/colorspaces/wet/kis_texture_painter.h b/krita/colorspaces/wet/kis_texture_painter.h
new file mode 100644
index 00000000..a3323492
--- /dev/null
+++ b/krita/colorspaces/wet/kis_texture_painter.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
+ *
+ * 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_TEXTURE_PAINTER_H_
+#define KIS_TEXTURE_PAINTER_H_
+
+#include "kis_types.h"
+#include "kis_painter.h"
+
+class KisTexturePainter : public KisPainter
+{
+
+ typedef KisPainter super;
+
+public:
+
+ KisTexturePainter();
+ KisTexturePainter(KisPaintDeviceSP device);
+
+ void createTexture( Q_INT32 x, Q_INT32 y, Q_INT32 w, Q_INT32 h);
+
+private:
+ double m_blurh, m_height;
+
+};
+#endif //KIS_TEXTURE_PAINTER_H_
diff --git a/krita/colorspaces/wet/kis_wet_colorspace.cc b/krita/colorspaces/wet/kis_wet_colorspace.cc
new file mode 100644
index 00000000..4ca96eb5
--- /dev/null
+++ b/krita/colorspaces/wet/kis_wet_colorspace.cc
@@ -0,0 +1,514 @@
+/*
+ * Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * 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 <limits.h>
+#include <stdlib.h>
+
+#include <config.h>
+#include LCMS_HEADER
+
+#include <qimage.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kis_debug_areas.h>
+#include "kis_abstract_colorspace.h"
+#include "kis_colorspace_factory_registry.h"
+#include "kis_image.h"
+#include "kis_wet_colorspace.h"
+#include "wetphysicsfilter.h"
+#include "kis_integer_maths.h"
+
+namespace {
+ static const WetPix m_paint = { 707, 0, 707, 0, 707, 0, 240, 0 };
+
+ /* colors from Curtis et al, Siggraph 97 */
+
+ static const WetPix m_paintbox[] = {
+ {496, 0, 16992, 0, 3808, 0, 0, 0},
+ {16992, 9744, 21712, 6400, 25024, 3296, 0, 0},
+ {6512, 6512, 6512, 4880, 11312, 0, 0, 0},
+ {16002, 0, 2848, 0, 16992, 0, 0, 0},
+ {22672, 0, 5328, 2272, 4288, 2640, 0, 0},
+ {8000, 0, 16992, 0, 28352, 0, 0, 0},
+ {5696, 5696, 12416, 2496, 28352, 0, 0, 0},
+ {0, 0, 5136, 0, 28352, 0, 0, 0},
+ {2320, 1760, 7344, 4656, 28352, 0, 0, 0},
+ {8000, 0, 3312, 0, 5504, 0, 0, 0},
+ {13680, 0, 16992, 0, 3312, 0, 0, 0},
+ {5264, 5136, 1056, 544, 6448, 6304, 0, 0},
+ {11440, 11440, 11440, 11440, 11440, 11440, 0, 0},
+ {11312, 0, 11312, 0, 11312, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0} };
+
+ static const int m_nPaints = 15;
+}
+
+void wetPixToDouble(WetPixDbl * dst, WetPix *src)
+{
+ dst->rd = (1.0 / 8192.0) * src->rd;
+ dst->rw = (1.0 / 8192.0) * src->rw;
+ dst->gd = (1.0 / 8192.0) * src->gd;
+ dst->gw = (1.0 / 8192.0) * src->gw;
+ dst->bd = (1.0 / 8192.0) * src->bd;
+ dst->bw = (1.0 / 8192.0) * src->bw;
+ dst->w = (1.0 / 8192.0) * src->w;
+ dst->h = (1.0 / 8192.0) * src->h;
+}
+
+void wetPixFromDouble(WetPix * dst, WetPixDbl *src)
+{
+ int v;
+
+ v = (int)floor (8192.0 * src->rd + 0.5);
+ dst->rd = CLAMP(v, 0, 65535);
+
+ v = (int)floor (8192.0 * src->rw + 0.5);
+ dst->rw = CLAMP(v, 0, 65535);
+
+ v = (int)floor (8192.0 * src->gd + 0.5);
+ dst->gd = CLAMP(v, 0, 65535);
+
+ v = (int)floor (8192.0 * src->gw + 0.5);
+ dst->gw = CLAMP(v, 0, 65535);
+
+ v = (int)floor (8192.0 * src->bd + 0.5);
+ dst->bd = CLAMP(v, 0, 65535);
+
+ v = (int)floor (8192.0 * src->bw + 0.5);
+ dst->bw = CLAMP(v, 0, 65535);
+
+ v = (int)floor (8192.0 * src->w + 0.5);
+ dst->w = CLAMP(v, 0, 511);
+
+ v = (int)floor (8192.0 * src->h + 0.5);
+ dst->h = CLAMP(v, 0, 511);
+
+}
+
+int getH(int r, int g, int b)
+{
+ int h, s, v;
+ QColor c(r,g, b);
+ c.getHsv(&h, &s, &v);
+ return h;
+}
+
+KisWetColorSpace::KisWetColorSpace(KisColorSpaceFactoryRegistry * parent, KisProfile *p) :
+ KisAbstractColorSpace(KisID("WET", i18n("Watercolors")), 0, icMaxEnumData, parent, p)
+{
+ wet_init_render_tab();
+
+ m_paintNames << i18n("Quinacridone Rose")
+ << i18n("Indian Red")
+ << i18n("Cadmium Yellow")
+ << i18n("Hookers Green")
+ << i18n("Cerulean Blue")
+ << i18n("Burnt Umber")
+ << i18n("Cadmium Red")
+ << i18n("Brilliant Orange")
+ << i18n("Hansa Yellow")
+ << i18n("Phthalo Green")
+ << i18n("French Ultramarine")
+ << i18n("Interference Lilac")
+ << i18n("Titanium White")
+ << i18n("Ivory Black")
+ << i18n("Pure Water");
+
+ m_channels.push_back(new KisChannelInfo(i18n("Red Concentration"), "Rc", 0, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Myth Red"), "Rm", 1, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Green Concentration"), "Gc", 2, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Myth Green"), "Gm", 3, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Blue Concentration"), "Bc", 4, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Myth Blue"), "Bm", 5, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Water Volume"), "W", 6, KisChannelInfo::SUBSTANCE, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Paper Height"), "H", 7, KisChannelInfo::SUBSTANCE, KisChannelInfo::UINT16));
+
+ m_channels.push_back(new KisChannelInfo(i18n("Adsorbed Red Concentration"), "Rc", 8, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Adsorbed Myth Red"), "Rm", 9, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Adsorbed Green Concentration"), "Gc", 10, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Adsorbed Myth Green"), "Gm", 11, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Adsorbed Blue Concentration"), "Bc", 12, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Adsorbed Myth Blue"), "Bm", 13, KisChannelInfo::COLOR, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Adsorbed Water Volume"), "W", 14, KisChannelInfo::SUBSTANCE, KisChannelInfo::UINT16));
+ m_channels.push_back(new KisChannelInfo(i18n("Adsorbed Paper Height"), "H", 15, KisChannelInfo::SUBSTANCE, KisChannelInfo::UINT16));
+
+ // Store the hue; we'll pick the paintbox color that closest to the given QColor's hue.
+ m_conversionMap[getH(240, 32, 160)] = m_paintbox[0]; // Quinacridone Rose
+ m_conversionMap[getH(159, 88, 43)] = m_paintbox[1]; // Indian Red
+ m_conversionMap[getH(254, 220, 64)] = m_paintbox[2]; // Cadmium Yellow
+ m_conversionMap[getH(36, 180, 32)] = m_paintbox[3]; // Hookers Green
+ m_conversionMap[getH(16, 185, 215)] = m_paintbox[4]; // Cerulean Blue
+ m_conversionMap[getH(96, 32, 8)] = m_paintbox[5]; // Burnt Umber
+ m_conversionMap[getH(254, 96, 8)] = m_paintbox[6]; // Cadmium Red
+ m_conversionMap[getH(255, 136, 8)] = m_paintbox[7]; // Brilliant Orange
+ m_conversionMap[getH(240, 199, 8)] = m_paintbox[8]; // Hansa Yellow
+ m_conversionMap[getH(96, 170, 130)] = m_paintbox[9]; // Phthalo Green
+ m_conversionMap[getH(48, 32, 170)] = m_paintbox[10]; // French Ultramarine
+ m_conversionMap[getH(118, 16, 135)] = m_paintbox[11]; // Interference Lilac
+ m_conversionMap[getH(254, 254, 254)] = m_paintbox[12]; // Titanium White
+ m_conversionMap[getH(64, 64, 74)] = m_paintbox[13]; // Ivory Black
+
+ m_paintwetness = false;
+ phasebig = 0;
+}
+
+
+KisWetColorSpace::~KisWetColorSpace()
+{
+}
+
+void KisWetColorSpace::fromQColor(const QColor& c, Q_UINT8 *dst, KisProfile * /*profile*/)
+{
+ WetPack* p = reinterpret_cast<WetPack*>(dst);
+
+ int h = getH(c.red(), c.green(), c.blue());
+ int delta = 256;
+ int key = 0;
+ QMap<int, WetPix>::Iterator it;
+ QMap<int, WetPix>::Iterator end = m_conversionMap.end();
+ for (it = m_conversionMap.begin(); it != end; ++it) {
+ if (abs(it.key() - h) < delta) {
+ delta = abs(it.key() - h);
+ key = it.key();
+ }
+ }
+
+ // Translate the special QCOlors from our paintbox to wetpaint paints.
+ if (m_conversionMap.contains(key)) {
+ (*p).paint = m_conversionMap[key];
+ (*p).adsorb = m_conversionMap[key]; // or maybe best add water here?
+ } else {
+ // water
+ (*p).paint = m_paintbox[14];
+ (*p).adsorb = m_paintbox[14];
+ }
+}
+
+void KisWetColorSpace::fromQColor(const QColor& c, Q_UINT8 /*opacity*/, Q_UINT8 *dst, KisProfile * /*profile*/)
+{
+ fromQColor(c, dst);
+}
+
+ Q_UINT8 KisWetColorSpace::getAlpha(const Q_UINT8 */*pixel*/) const
+{
+ return OPACITY_OPAQUE;
+}
+
+void KisWetColorSpace::setAlpha( Q_UINT8 * /*pixels*/, Q_UINT8 /*alpha*/, Q_INT32 /*nPixels*/) const
+{
+}
+
+void KisWetColorSpace::multiplyAlpha( Q_UINT8 * /*pixels*/, Q_UINT8 /*alpha*/, Q_INT32 /*nPixels*/)
+{
+}
+
+void KisWetColorSpace::applyAlphaU8Mask( Q_UINT8 * /*pixels*/, Q_UINT8 * /*alpha*/, Q_INT32 /*nPixels*/)
+{
+}
+
+void KisWetColorSpace::applyInverseAlphaU8Mask( Q_UINT8 * /*pixels*/, Q_UINT8 * /*alpha*/, Q_INT32 /*nPixels*/)
+{
+}
+
+ Q_UINT8 KisWetColorSpace::scaleToU8(const Q_UINT8 * /*srcPixel*/, Q_INT32 /*channelPos*/)
+{
+ return 0;
+}
+
+Q_UINT16 KisWetColorSpace::scaleToU16(const Q_UINT8 * /*srcPixel*/, Q_INT32 /*channelPos*/)
+{
+ return 0;
+}
+
+
+void KisWetColorSpace::toQColor(const Q_UINT8 *src, QColor *c, KisProfile * /*profile*/)
+{
+ Q_UINT8 * rgb = new Q_UINT8[3];
+ Q_CHECK_PTR(rgb);
+
+ memset(rgb, 255, 3);
+
+ // Composite the two layers in each pixelSize
+
+ WetPack * wp = (WetPack*)src;
+
+ // First the adsorption layer
+ wet_composite(RGB, rgb, &wp->adsorb);
+
+ // Then the paint layer (which comes first in our double-packed pixel)
+ wet_composite(RGB, rgb, &wp->paint);
+
+ c->setRgb(rgb[0], rgb[1], rgb[2]);
+
+ delete[]rgb;
+}
+
+void KisWetColorSpace::toQColor(const Q_UINT8 *src, QColor *c, Q_UINT8 */*opacity*/, KisProfile * /*profile*/)
+{
+ toQColor(src, c);
+}
+
+void KisWetColorSpace::mixColors(const Q_UINT8 **/*colors*/, const Q_UINT8 */*weights*/, Q_UINT32 /*nColors*/, Q_UINT8 */*dst*/) const
+{
+}
+
+QValueVector<KisChannelInfo *> KisWetColorSpace::channels() const
+{
+ return m_channels;
+}
+
+ Q_UINT32 KisWetColorSpace::nChannels() const
+{
+ return 16;
+}
+
+ Q_UINT32 KisWetColorSpace::nColorChannels() const
+{
+ return 12;
+}
+
+ Q_UINT32 KisWetColorSpace::nSubstanceChannels() const
+{
+ return 4;
+}
+
+
+ Q_UINT32 KisWetColorSpace::pixelSize() const
+{
+ return 32; // This color strategy wants an unsigned short for each
+ // channel, and every pixel consists of two wetpix structs
+ // -- even though for many purposes we need only one wetpix
+ // struct.
+}
+
+
+
+// XXX: use profiles to display correctly on calibrated displays.
+QImage KisWetColorSpace::convertToQImage(const Q_UINT8 *data, Q_INT32 width, Q_INT32 height,
+ KisProfile * /*dstProfile*/,
+ Q_INT32 /*renderingIntent*/, float /*exposure*/)
+{
+
+ QImage img(width, height, 32);
+
+ Q_UINT8 *rgb = (Q_UINT8*) img.bits();
+ const WetPack* wetData = reinterpret_cast<const WetPack*>(data);
+
+ // Clear to white -- the following code actually composits the contents of the
+ // wet pixels with the contents of the image buffer, so they need to be
+ // prepared
+ memset(rgb, 255, width * height * 4);
+ // Composite the two layers in each pixelSize
+
+ Q_INT32 i = 0;
+ while ( i < width * height) {
+ // First the adsorption layers
+ WetPack* wp = const_cast<WetPack*>(&wetData[i]); // XXX don't do these things!
+ // XXX Probably won't work on MSB archs!
+ wet_composite(BGR, rgb, &(wp->adsorb));
+ // Then the paint layer (which comes first in our double-packed pixel)
+ wet_composite(BGR, rgb, &(wp->paint));
+
+ // XXX pay attention to this comment!!
+ // Display the wet stripes -- this only works if we have at least three scanlines in height,
+ // because otherwise the phase trick won't work.
+
+ // Because we work in a stateless thing, and we can't just draw this wetness
+ // indication AFTER this (e.g. like the selection), we have to do un nice things:
+ // Because we (hopefully atm!) don't use the height of the paint wetpix, we abuse
+ // that to store a state. It's not perfect, but it works for now...
+ if (m_paintwetness) {
+ wet_render_wetness(rgb, wp);
+ }
+
+ i++;
+ rgb += sizeof( Q_UINT32); // Because the QImage is 4 bytes deep.
+
+ }
+
+ return img;
+}
+
+void KisWetColorSpace::bitBlt( Q_UINT8 *dst,
+ Q_INT32 dstRowSize,
+ const Q_UINT8 *src,
+ Q_INT32 srcRowStride,
+ const Q_UINT8 */*srcAlphaMask*/,
+ Q_INT32 /*maskRowStride*/,
+ Q_UINT8 /*opacity*/,
+ Q_INT32 rows,
+ Q_INT32 cols,
+ const KisCompositeOp& op)
+{
+ if (rows <= 0 || cols <= 0)
+ return;
+
+ Q_UINT8 *d;
+ const Q_UINT8 *s;
+
+ Q_INT32 linesize = pixelSize() * cols;
+ d = dst;
+ s = src;
+
+ // Do as if we 'stack' them atop of each other
+ if (op == COMPOSITE_OVER) {
+ while (rows-- > 0) {
+ for (int i = 0; i < cols; i++) {
+ WetPack* dstPack = &(reinterpret_cast<WetPack*>(d))[i];
+ const WetPack* srcPack = &(reinterpret_cast<const WetPack*>(s))[i];
+ combinePixels(&(dstPack->paint), &(dstPack->paint), &(srcPack->paint));
+ combinePixels(&(dstPack->adsorb), &(dstPack->adsorb), &(srcPack->adsorb));
+ }
+ d += dstRowSize; // size??
+ s += srcRowStride;
+ }
+
+ return;
+ }
+
+ // Just copy the src onto the dst, we don't do fancy things here,
+ // we do those in the paint op, because we need pressure to determine
+ // paint deposition.
+
+ while (rows-- > 0) {
+ memcpy(d, s, linesize);
+ d += dstRowSize; // size??
+ s += srcRowStride;
+ }
+}
+
+void KisWetColorSpace::wet_init_render_tab()
+{
+ int i;
+
+ double d;
+ int a, b;
+
+ wet_render_tab = new Q_UINT32[4096];
+ Q_CHECK_PTR(wet_render_tab);
+
+ for (i = 0; i < 4096; i++)
+ {
+ d = i * (1.0 / 512.0);
+
+ if (i == 0)
+ a = 0;
+ else
+ a = (int) floor (0xff00 / i + 0.5);
+
+ b = (int) floor (0x8000 * exp (-d) + 0.5);
+ wet_render_tab[i] = (a << 16) | b;
+ }
+
+}
+
+void KisWetColorSpace::wet_composite(RGBMode m, Q_UINT8 *rgb, WetPix * wet)
+{
+ int r, g, b;
+ int d, w;
+ int ab;
+ int wa;
+
+ if (m == RGB)
+ r = rgb[0];
+ else
+ r = rgb[2];
+ w = wet[0].rw >> 4;
+ d = wet[0].rd >> 4;
+
+ ab = wet_render_tab[d];
+
+ wa = (w * (ab >> 16) + 0x80) >> 8;
+ r = wa + (((r - wa) * (ab & 0xffff) + 0x4000) >> 15);
+ if (m == RGB)
+ rgb[0] = r;
+ else
+ rgb[2] = r;
+
+ // Green is 1 both in RGB as BGR
+ g = rgb[1];
+ w = wet[0].gw >> 4;
+ d = wet[0].gd >> 4;
+ d = d >= 4096 ? 4095 : d;
+ ab = wet_render_tab[d];
+ wa = (w * (ab >> 16) + 0x80) >> 8;
+ g = wa + (((g - wa) * (ab & 0xffff) + 0x4000) >> 15);
+ rgb[1] = g;
+
+ if (m == RGB)
+ b = rgb[2];
+ else
+ b = rgb[0];
+ w = wet[0].bw >> 4;
+ d = wet[0].bd >> 4;
+ d = d >= 4096 ? 4095 : d;
+ ab = wet_render_tab[d];
+ wa = (w * (ab >> 16) + 0x80) >> 8;
+ b = wa + (((b - wa) * (ab & 0xffff) + 0x4000) >> 15);
+ if (m == RGB)
+ rgb[2] = b;
+ else
+ rgb[0] = b;
+}
+
+void KisWetColorSpace::wet_render_wetness( Q_UINT8 * rgb, WetPack * pack)
+{
+ int highlight = 255 - (pack->paint.w >> 1);
+
+ if (highlight < 255 && ((phase++) % 3 == 0)) {
+ for (int i = 0; i < 3; i++)
+ rgb[i] = 255 - (((255 - rgb[i]) * highlight) >> 8);
+ }
+ phase &= 3;
+}
+
+KisCompositeOpList KisWetColorSpace::userVisiblecompositeOps() const
+{
+ KisCompositeOpList list;
+
+ list.append(KisCompositeOp(COMPOSITE_OVER));
+
+ return list;
+}
+
+QString KisWetColorSpace::channelValueText(const Q_UINT8 *U8_pixel, Q_UINT32 channelIndex) const
+{
+ Q_ASSERT(channelIndex < nChannels());
+ const Q_UINT16 *pixel = reinterpret_cast<const Q_UINT16 *>(U8_pixel);
+ Q_UINT32 channelPosition = m_channels[channelIndex]->pos();
+
+ return QString().setNum(pixel[channelPosition]);
+}
+
+QString KisWetColorSpace::normalisedChannelValueText(const Q_UINT8 *U8_pixel, Q_UINT32 channelIndex) const
+{
+ Q_ASSERT(channelIndex < nChannels());
+ const Q_UINT16 *pixel = reinterpret_cast<const Q_UINT16 *>(U8_pixel);
+ Q_UINT32 channelPosition = m_channels[channelIndex]->pos();
+
+ return QString().setNum(static_cast<float>(pixel[channelPosition]) / UINT16_MAX);
+}
+
+QValueList<KisFilter *> KisWetColorSpace::createBackgroundFilters()
+{
+ QValueList<KisFilter *> filterList;
+ KisFilter * f = new WetPhysicsFilter();
+ filterList << f;
+ return filterList;
+}
diff --git a/krita/colorspaces/wet/kis_wet_colorspace.h b/krita/colorspaces/wet/kis_wet_colorspace.h
new file mode 100644
index 00000000..9c66c52e
--- /dev/null
+++ b/krita/colorspaces/wet/kis_wet_colorspace.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
+ *
+ * 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_STRATEGY_COLORSPACE_WET_H_
+#define KIS_STRATEGY_COLORSPACE_WET_H_
+
+#include <qcolor.h>
+#include <qstringlist.h>
+#include <qvaluelist.h>
+#include <qmap.h>
+
+#include "kis_global.h"
+#include "kis_abstract_colorspace.h"
+
+class KisFilter;
+
+/**
+ * The wet colourspace is one of the more complicated colour spaces. Every
+ * pixel actually consists of two pixels: the paint pixel and the adsorbtion
+ * pixel. This corresponds to the two layers of the wetpack structure in the
+ * original wetdreams code by Raph Levien.
+ */
+
+// XXX: This should really be in a namespace.
+
+typedef struct _WetPix WetPix;
+typedef struct _WetPixDbl WetPixDbl;
+typedef struct _WetPack WetPack;
+
+/*
+ * White is made up of myth-red, myth-green, and myth-blue. Myth-red
+ * looks red when viewed reflectively, but cyan when viewed
+ * transmissively (thus, it vaguely resembles a dichroic
+ * filter). Myth-red over black is red, and myth-red over white is
+ * white.
+ *
+ * Total red channel concentration is myth-red concentration plus
+ * cyan concentration.
+ */
+
+struct _WetPix {
+ Q_UINT16 rd; /* Total red channel concentration */
+ Q_UINT16 rw; /* Myth-red concentration */
+
+ Q_UINT16 gd; /* Total green channel concentration */
+ Q_UINT16 gw; /* Myth-green concentration */
+
+ Q_UINT16 bd; /* Total blue channel concentration */
+ Q_UINT16 bw; /* Myth-blue concentration */
+
+ Q_UINT16 w; /* Water volume */
+ Q_UINT16 h; /* Height of paper surface XXX: This might just as well be a single
+ channel in our colour model that has two of
+ these wetpix structs for every paint device pixels*/
+};
+
+struct _WetPack {
+ WetPix paint; /* Paint layer */
+ WetPix adsorb; /* Adsorbtion layer */
+};
+
+struct _WetPixDbl {
+ double rd; /* Total red channel concentration */
+ double rw; /* Myth-red concentration */
+ double gd; /* Total green channel concentration */
+ double gw; /* Myth-green concentration */
+ double bd; /* Total blue channel concentration */
+ double bw; /* Myth-blue concentration */
+ double w; /* Water volume */
+ double h; /* Height of paper surface */
+};
+
+
+
+void wetPixToDouble(WetPixDbl * dst, WetPix *src);
+void wetPixFromDouble(WetPix * dst, WetPixDbl *src);
+
+
+class KisWetColorSpace : public KisAbstractColorSpace {
+public:
+ KisWetColorSpace(KisColorSpaceFactoryRegistry * parent, KisProfile *p);
+ virtual ~KisWetColorSpace();
+
+
+ virtual bool willDegrade(ColorSpaceIndependence independence)
+ {
+ if (independence == TO_RGBA8 || independence == TO_LAB16)
+ return true;
+ else
+ return false;
+ };
+
+
+
+
+public:
+
+ // Semi-clever: we have only fifteen wet paint colors that are mapped to the
+ // qcolors that are put in the painter by the special wet paint palette. Other
+ // QColors are mapped to plain water...
+ virtual void fromQColor(const QColor& c, Q_UINT8 *dst, KisProfile * profile = 0);
+ virtual void fromQColor(const QColor& c, Q_UINT8 opacity, Q_UINT8 *dst, KisProfile * profile = 0);
+
+ virtual void toQColor(const Q_UINT8 *src, QColor *c, KisProfile * profile = 0);
+ virtual void toQColor(const Q_UINT8 *src, QColor *c, Q_UINT8 *opacity, KisProfile * profile = 0);
+
+ virtual Q_UINT8 getAlpha(const Q_UINT8 * pixel) const;
+ virtual void setAlpha( Q_UINT8 * pixels, Q_UINT8 alpha, Q_INT32 nPixels) const;
+ virtual void multiplyAlpha( Q_UINT8 * pixels, Q_UINT8 alpha, Q_INT32 nPixels);
+
+ virtual void applyAlphaU8Mask( Q_UINT8 * pixels, Q_UINT8 * alpha, Q_INT32 nPixels);
+ virtual void applyInverseAlphaU8Mask( Q_UINT8 * pixels, Q_UINT8 * alpha, Q_INT32 nPixels);
+
+ virtual Q_UINT8 scaleToU8(const Q_UINT8 * srcPixel, Q_INT32 channelPos);
+ virtual Q_UINT16 scaleToU16(const Q_UINT8 * srcPixel, Q_INT32 channelPos);
+
+ virtual void mixColors(const Q_UINT8 **colors, const Q_UINT8 *weights, Q_UINT32 nColors, Q_UINT8 *dst) const;
+
+ virtual QValueVector<KisChannelInfo *> channels() const;
+ virtual Q_UINT32 nChannels() const;
+ virtual Q_UINT32 nColorChannels() const;
+ virtual Q_UINT32 nSubstanceChannels() const;
+ virtual Q_UINT32 pixelSize() const;
+
+ virtual QString channelValueText(const Q_UINT8 *pixel, Q_UINT32 channelIndex) const;
+ virtual QString normalisedChannelValueText(const Q_UINT8 *pixel, Q_UINT32 channelIndex) const;
+
+ virtual QImage convertToQImage(const Q_UINT8 *data, Q_INT32 width, Q_INT32 height,
+ KisProfile * dstProfile,
+ Q_INT32 renderingIntent = INTENT_PERCEPTUAL,
+ float exposure = 0.0f);
+
+ virtual QValueList<KisFilter*> createBackgroundFilters();
+
+ virtual KisCompositeOpList userVisiblecompositeOps() const;
+
+ void setPaintWetness(bool b) { m_paintwetness = b; } // XXX this needs better design!
+ bool paintWetness() { return m_paintwetness; }
+ void resetPhase() { phase = phasebig++; phasebig &= 3; }
+
+ void combinePixels(WetPix* dst, WetPix const* src1, WetPix const* src2) const {
+ dst->rd = src1->rd + src2->rd;
+ dst->rw = src1->rw + src2->rw;
+ dst->gd = src1->gd + src2->gd;
+ dst->gw = src1->gw + src2->gw;
+ dst->bd = src1->bd + src2->bd;
+ dst->bw = src1->bw + src2->bw;
+ dst->w = src1->w + src2->w;
+ }
+protected:
+ virtual void bitBlt( Q_UINT8 *dst,
+ Q_INT32 dstRowSize,
+ const Q_UINT8 *src,
+ Q_INT32 srcRowStride,
+ const Q_UINT8 *srcAlphaMask,
+ Q_INT32 maskRowStride,
+ Q_UINT8 opacity,
+ Q_INT32 rows,
+ Q_INT32 cols,
+ const KisCompositeOp& op);
+private:
+
+ // This was static, but since we have only one instance of the color strategy,
+ // it can be just as well a private member variable.
+ void wet_init_render_tab();
+
+ /// Convert a single pixel from its wet representation to rgb: internal rgb: rgb[0] = R, etc
+ typedef enum { RGB, BGR } RGBMode;
+ void wet_composite(RGBMode m, Q_UINT8 *rgb, WetPix * wet);
+
+ void wet_render_wetness( Q_UINT8 * rgb, WetPack * pack);
+
+private:
+ Q_UINT32 * wet_render_tab;
+
+ QStringList m_paintNames;
+ QMap<int, WetPix> m_conversionMap;
+
+ bool m_paintwetness;
+ int phase, phasebig;
+
+};
+
+class KisWetColorSpaceFactory : public KisColorSpaceFactory
+{
+public:
+ /**
+ * Krita definition for use in .kra files and internally: unchanging name +
+ * i18n'able description.
+ */
+ virtual KisID id() const { return KisID("WET", i18n("Watercolors")); };
+
+ /**
+ * lcms colorspace type definition.
+ */
+ virtual Q_UINT32 colorSpaceType() { return 0; };
+
+ virtual icColorSpaceSignature colorSpaceSignature() { return icMaxEnumData; };
+
+ virtual KisColorSpace *createColorSpace(KisColorSpaceFactoryRegistry * parent, KisProfile *p) { return new KisWetColorSpace(parent, p); };
+
+ virtual QString defaultProfile() { return ""; };
+};
+
+#endif // KIS_STRATEGY_COLORSPACE_WET_H_
diff --git a/krita/colorspaces/wet/kis_wet_palette_widget.cc b/krita/colorspaces/wet/kis_wet_palette_widget.cc
new file mode 100644
index 00000000..ae405f94
--- /dev/null
+++ b/krita/colorspaces/wet/kis_wet_palette_widget.cc
@@ -0,0 +1,245 @@
+/*
+ * This file is part of Krita
+ *
+ * Copyright (c) 1999 Matthias Elter (me@kde.org)
+ * Copyright (c) 2001-2002 Igor Jansen (rm@kde.org)
+ *
+ * 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 <qpushbutton.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qcolor.h>
+#include <qdrawutil.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qspinbox.h>
+#include <qstyle.h>
+#include <qtooltip.h>
+
+#include <klocale.h>
+#include <knuminput.h>
+#include <koFrameButton.h>
+
+#include <kis_meta_registry.h>
+#include <kis_factory.h>
+#include <kis_canvas_subject.h>
+#include <kis_colorspace_factory_registry.h>
+#include <kis_color.h>
+#include <kis_color_cup.h>
+
+#include "kis_wet_colorspace.h"
+#include "kis_wet_palette_widget.h"
+
+KisWetPaletteWidget::KisWetPaletteWidget(QWidget *parent, const char *name) : super(parent, name)
+{
+ m_subject = 0;
+
+ QVBoxLayout * vl = new QVBoxLayout(this, 0, -1, "main layout");
+
+ QGridLayout * l = new QGridLayout(vl, 2, 8, 2, "color wells grid");
+
+ KisColorCup * b;
+ int WIDTH = 24;
+ int HEIGHT = 24;
+
+ b = new KisColorCup(this);
+ b->setColor( QColor(240, 32, 160) );
+ l->addWidget(b, 0, 0);
+ QToolTip::add(b, i18n("Quinacridone Rose"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(159, 88, 43));
+ l->addWidget(b, 0, 1);
+ QToolTip::add(b,i18n("Indian Red"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor( QColor(254, 220, 64) );
+ l->addWidget(b, 0, 2);
+ QToolTip::add(b,i18n("Cadmium Yellow"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(36, 180, 32));
+ l->addWidget(b, 0, 3);
+ QToolTip::add(b,i18n("Hookers Green"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(16, 185, 215));
+ l->addWidget(b, 0, 4);
+ QToolTip::add(b,i18n("Cerulean Blue"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(96, 32, 8));
+ l->addWidget(b, 0, 5);
+ QToolTip::add(b,i18n("Burnt Umber"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(254, 96, 8));
+ l->addWidget(b, 0, 6);
+ QToolTip::add(b,i18n("Cadmium Red"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(255, 136, 8));
+ l->addWidget(b, 0, 7);
+ QToolTip::add(b,i18n("Brilliant Orange"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(240, 199, 8));
+ l->addWidget(b, 1, 0);
+ QToolTip::add(b,i18n("Hansa Yellow"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(96, 170, 130));
+ l->addWidget(b, 1, 1);
+ QToolTip::add(b,i18n("Phthalo Green"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(48, 32, 170));
+ l->addWidget(b, 1, 2);
+ QToolTip::add(b,i18n("French Ultramarine"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(118, 16, 135));
+ l->addWidget(b, 1, 3);
+ QToolTip::add(b,i18n("Interference Lilac"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(254, 254, 254));
+ l->addWidget(b, 1, 4);
+ QToolTip::add(b,i18n("Titanium White"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(64, 64, 74));
+ l->addWidget(b, 1, 5);
+ QToolTip::add(b,i18n("Ivory Black"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ b = new KisColorCup(this);
+ b->setColor(QColor(255, 255, 255));
+ l->addWidget(b, 1, 6);
+ QToolTip::add(b,i18n("Pure Water"));
+ b->setFixedSize(WIDTH, HEIGHT);
+ connect(b, SIGNAL(changed(const QColor &)), SLOT(slotFGColorSelected(const QColor &)));
+
+ QGridLayout * g2 = new QGridLayout(vl, 2, 2);
+
+ QLabel * label = new QLabel(i18n("Paint strength:"), this);
+ g2->addWidget(label, 0, 0);
+ m_strength = new KDoubleNumInput(0.0, 2.0, 1.0, 0.1, 1, this);
+ m_strength->setRange(0.0, 2.0, 0.1, true);
+ connect(m_strength, SIGNAL(valueChanged(double)), this, SLOT(slotStrengthChanged(double)));
+ g2->addWidget(m_strength, 0, 1);
+
+ label = new QLabel(i18n("Wetness:"), this);
+ g2->addWidget(label, 1, 0);
+ m_wetness = new KIntNumInput(16, this);
+ connect(m_wetness, SIGNAL(valueChanged(int)), this, SLOT(slotWetnessChanged(int)));
+ m_wetness->setRange(0, 16, true);
+ g2->addWidget(m_wetness, 1, 1);
+
+ g2->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum));
+
+}
+
+void KisWetPaletteWidget::update(KisCanvasSubject *subject)
+{
+ m_subject = subject;
+
+}
+
+void KisWetPaletteWidget::slotFGColorSelected(const QColor& c)
+{
+ KisWetColorSpace* cs = dynamic_cast<KisWetColorSpace*>(KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("WET", ""), ""));
+ Q_ASSERT(cs);
+
+ WetPack pack;
+ Q_UINT8* data = reinterpret_cast< Q_UINT8*>(&pack);
+ cs->fromQColor(c, data);
+ pack.paint.w = 15 * m_wetness->value();
+ // upscale from double to uint16:
+ pack.paint.h = static_cast< Q_UINT16>(m_strength->value() * (double)(0xffff/2));
+ KisColor color(data, cs);
+
+ if(m_subject)
+ m_subject->setFGColor(color);
+}
+
+void KisWetPaletteWidget::slotWetnessChanged(int n)
+{
+ if (!m_subject)
+ return;
+
+ KisWetColorSpace* cs = dynamic_cast<KisWetColorSpace*>(KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("WET", ""), ""));
+ Q_ASSERT(cs);
+
+ KisColor color = m_subject->fgColor();
+ color.convertTo(cs);
+ WetPack pack = *(reinterpret_cast<WetPack*>(color.data()));
+ pack.paint.w = 15 * n;
+
+ color.setColor(reinterpret_cast< Q_UINT8*>(&pack), cs);
+ m_subject->setFGColor(color);
+}
+
+void KisWetPaletteWidget::slotStrengthChanged(double n)
+{
+ if (!m_subject)
+ return;
+
+ KisWetColorSpace* cs = dynamic_cast<KisWetColorSpace*>(
+ KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("WET", ""), ""));
+ Q_ASSERT(cs);
+
+ KisColor color = m_subject->fgColor();
+ color.convertTo(cs);
+ WetPack pack = *(reinterpret_cast<WetPack*>(color.data()));
+ pack.paint.h = static_cast< Q_UINT16>(n * (double)(0xffff/2)); // upscale from double to uint16
+
+ color.setColor(reinterpret_cast< Q_UINT8*>(&pack), cs);
+ m_subject->setFGColor(color);
+}
+
+
+#include "kis_wet_palette_widget.moc"
diff --git a/krita/colorspaces/wet/kis_wet_palette_widget.h b/krita/colorspaces/wet/kis_wet_palette_widget.h
new file mode 100644
index 00000000..fb2800d3
--- /dev/null
+++ b/krita/colorspaces/wet/kis_wet_palette_widget.h
@@ -0,0 +1,67 @@
+/* This file is part of the KDE project
+ *
+ * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * 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_WET_PALETTE_WIDGET_H
+#define KIS_WET_PALETTE_WIDGET_H
+
+#include "qwidget.h"
+#include "qpushbutton.h"
+
+#include "kis_canvas_subject.h"
+#include "kis_canvas_observer.h"
+
+#include <koffice_export.h>
+
+class KoFrameButton;
+class QGridLayout;
+class QColor;
+class QLabel;
+class QSpinBox;
+class QColor;
+class KIntNumInput;
+class KDoubleNumInput;
+
+class KRITAUI_EXPORT KisWetPaletteWidget
+ : public QWidget,
+ public KisCanvasObserver
+{
+ Q_OBJECT
+ typedef QWidget super;
+
+public:
+ KisWetPaletteWidget(QWidget *parent = 0L, const char *name = 0);
+ virtual ~KisWetPaletteWidget() {}
+
+protected slots:
+
+ void slotFGColorSelected(const QColor& c);
+ void slotWetnessChanged(int);
+ void slotStrengthChanged(double);
+
+private:
+ void update(KisCanvasSubject*);
+
+private:
+ KisCanvasSubject *m_subject;
+ KDoubleNumInput* m_strength;
+ KIntNumInput* m_wetness;
+
+
+};
+
+#endif
diff --git a/krita/colorspaces/wet/kis_wetness_visualisation_filter.cc b/krita/colorspaces/wet/kis_wetness_visualisation_filter.cc
new file mode 100644
index 00000000..411a3495
--- /dev/null
+++ b/krita/colorspaces/wet/kis_wetness_visualisation_filter.cc
@@ -0,0 +1,77 @@
+/*
+ * kis_wetness_visualisation_filter.cc -- Part of Krita
+ *
+ * Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
+ *
+ * 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 <kdebug.h>
+
+#include <klocale.h>
+#include "kis_meta_registry.h"
+#include <kis_view.h>
+#include <kis_image.h>
+#include <kis_colorspace_factory_registry.h>
+#include <kis_factory.h>
+#include "kis_wet_colorspace.h"
+#include <kis_debug_areas.h>
+#include "kis_wetness_visualisation_filter.h"
+
+WetnessVisualisationFilter::WetnessVisualisationFilter(KisView* view)
+ : m_view(view), m_action(0) {
+ connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotTimeout()));
+}
+
+// XXX this needs to work on a per-layer basis!
+
+void WetnessVisualisationFilter::setAction(KToggleAction* action) {
+ m_action = action;
+ if (!m_action)
+ return;
+ KisWetColorSpace* cs = dynamic_cast<KisWetColorSpace*>(
+ KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("WET", ""),"") );
+ Q_ASSERT(cs);
+ m_action->setChecked(cs->paintWetness());
+}
+
+void WetnessVisualisationFilter::slotActivated() {
+ kdDebug(DBG_AREA_CMS) << "activated" << endl;
+ if (!m_action) {
+ kdDebug(DBG_AREA_CMS) << "no action" << endl;
+ return;
+ }
+ KisWetColorSpace* cs = dynamic_cast<KisWetColorSpace*>(
+ KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("WET", ""),"") );
+ Q_ASSERT(cs);
+ if (!m_action->isChecked()) {
+ m_timer.stop();
+ cs->setPaintWetness(false);
+ } else {
+ m_timer.start(500);
+ cs->setPaintWetness(true);
+ }
+}
+
+void WetnessVisualisationFilter::slotTimeout() {
+ KisWetColorSpace* cs = dynamic_cast<KisWetColorSpace*>(
+ KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("WET", ""),"") );
+ Q_ASSERT(cs);
+ if (!cs) return;
+ cs->resetPhase();
+
+}
+
+#include "kis_wetness_visualisation_filter.moc"
diff --git a/krita/colorspaces/wet/kis_wetness_visualisation_filter.h b/krita/colorspaces/wet/kis_wetness_visualisation_filter.h
new file mode 100644
index 00000000..ad5da5d3
--- /dev/null
+++ b/krita/colorspaces/wet/kis_wetness_visualisation_filter.h
@@ -0,0 +1,50 @@
+/*
+ * kis_wetness_visualisation_filter.h -- Part of Krita
+ *
+ * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
+ *
+ * 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 _WETNESS_VISUALISATION_FILTER_H
+#define _WETNESS_VISUALISATION_FILTER_H
+
+#include <qobject.h>
+#include <qtimer.h>
+#include <kactionclasses.h>
+
+class KisView;
+
+class WetnessVisualisationFilter : public QObject
+{
+ Q_OBJECT
+public:
+ WetnessVisualisationFilter(KisView* view);
+ virtual ~WetnessVisualisationFilter() {}
+ void setAction(KToggleAction* action);
+ // XXX: Figure out a way to match a filter exactly to a colorspace
+ virtual ColorSpaceIndependence colorSpaceIndependence() { return FULLY_INDEPENDENT; };
+ virtual bool workWith(KisColorSpace* cs) { return (cs->id() == KisID("WET")); };
+private slots:
+ void slotActivated();
+ void slotTimeout();
+
+private:
+ KisView * m_view;
+ KToggleAction * m_action;
+ QTimer m_timer;
+};
+
+#endif // _WETNESS_VISUALISATION_FILTER_H
diff --git a/krita/colorspaces/wet/kis_wetop.cc b/krita/colorspaces/wet/kis_wetop.cc
new file mode 100644
index 00000000..cff3ac1e
--- /dev/null
+++ b/krita/colorspaces/wet/kis_wetop.cc
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * 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 <qrect.h>
+#include <qcheckbox.h>
+
+#include <kdebug.h>
+
+#include <kis_brush.h>
+#include <kis_debug_areas.h>
+#include <kis_paint_device.h>
+#include <kis_painter.h>
+#include <kis_types.h>
+#include <kis_paintop.h>
+#include <kis_iterators_pixel.h>
+#include <kis_layer.h>
+#include <kis_meta_registry.h>
+#include <kis_colorspace_factory_registry.h>
+#include "kis_input_device.h"
+
+#include "kis_wetop.h"
+#include "kis_wet_colorspace.h"
+
+KisWetOpSettings::KisWetOpSettings(QWidget *parent)
+ : super(parent)
+{
+ m_options = new WetPaintOptions(parent, "wet option widget");
+}
+
+bool KisWetOpSettings::varySize() const
+{
+ return m_options->checkSize->isChecked();
+}
+
+bool KisWetOpSettings::varyWetness() const
+{
+ return m_options->checkWetness->isChecked();
+}
+
+bool KisWetOpSettings::varyStrength() const
+{
+ return m_options->checkStrength->isChecked();
+}
+
+KisPaintOp * KisWetOpFactory::createOp(const KisPaintOpSettings *settings, KisPainter * painter)
+{
+ const KisWetOpSettings *wetopSettings = dynamic_cast<const KisWetOpSettings *>(settings);
+ Q_ASSERT(settings == 0 || wetopSettings != 0);
+
+ KisPaintOp * op = new KisWetOp(wetopSettings, painter);
+ Q_CHECK_PTR(op);
+ return op;
+}
+
+KisPaintOpSettings* KisWetOpFactory::settings(QWidget * parent, const KisInputDevice& inputDevice)
+{
+ if (inputDevice == KisInputDevice::mouse()) {
+ // No options for mouse, only tablet devices
+ return 0;
+ } else {
+ return new KisWetOpSettings(parent);
+ }
+}
+
+KisWetOp::KisWetOp(const KisWetOpSettings * settings, KisPainter * painter)
+ : super(painter)
+{
+ if (settings) {
+ m_size = settings->varySize();
+ m_wetness = settings->varyWetness();
+ m_strength = settings->varyStrength();
+ } else {
+ m_size = false;
+ m_wetness = false;
+ m_strength = false;
+ }
+}
+
+KisWetOp::~KisWetOp()
+{
+}
+
+void KisWetOp::paintAt(const KisPoint &pos, const KisPaintInformation& info)
+{
+ if (!m_painter) return;
+
+ if (!m_painter->device()) return;
+ KisPaintDeviceSP device = m_painter->device();
+ if (!m_painter->device()) return;
+
+ KisBrush *brush = m_painter->brush();
+ Q_ASSERT(brush);
+
+ if (! brush->canPaintFor(info) )
+ return;
+
+ KisPaintInformation inf(info);
+
+ if (!m_size)
+ inf.pressure = PRESSURE_DEFAULT;
+
+ KisPaintDeviceSP dab = 0;
+
+ if (brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) {
+ dab = brush->image(KisMetaRegistry::instance()->csRegistry()->getAlpha8(), inf);
+ }
+ else {
+ KisAlphaMaskSP mask = brush->mask(inf);
+ dab = computeDab(mask, KisMetaRegistry::instance()->csRegistry()->getAlpha8());
+ }
+
+ KisColorSpace * cs = device->colorSpace();
+
+ if (cs->id() != KisID("WET","")) {
+ kdDebug(DBG_AREA_CMS) << "You cannot paint wet paint on dry pixels.\n";
+ return;
+ }
+
+ KisColor paintColor = m_painter->paintColor();
+ paintColor.convertTo(cs);
+ // hopefully this does
+ // nothing, conversions are bad ( wet->rgb->wet gives horrible mismatches, due to
+ // the conversion to rgb actually rendering the paint above white
+
+ WetPack* paintPack = reinterpret_cast<WetPack*>(paintColor.data());
+ WetPix paint = paintPack->paint;
+
+ // Get the paint info (we store the strength in the otherwise unused (?) height field of
+ // the paint
+ // double wetness = paint.w; // XXX: Was unused
+ // strength is a double in the 0 - 2 range, but upscaled to Q_UINT16:
+ //kdDebug() << "Original strength as in paint.h: " << paint.h << endl;
+
+ double strength = 2.0 * static_cast<double>(paint.h) / (double)(0xffff);
+
+ //kdDebug() << "Before strength: " << strength << endl;
+
+ if (m_strength)
+ strength = strength * (strength + info.pressure) * 0.5;
+ else
+ strength = strength * (strength + PRESSURE_DEFAULT) * 0.5;
+
+ double pressure = 0.75 + 0.25 * info.pressure;
+
+ //kdDebug() << "info.pressure " << info.pressure << ", local pressure: " << pressure << ", strength: " << strength << endl;
+
+ WetPack currentPack;
+ WetPix currentPix;
+ double eff_height;
+ double press, contact;
+
+ int maskW = brush->maskWidth(inf);
+ int maskH = brush->maskHeight(inf);
+ KoPoint dest = (pos - (brush->hotSpot(inf)));
+ int xStart = (int)dest.x();
+ int yStart = (int)dest.y();
+
+ for (int y = 0; y < maskH; y++) {
+ KisHLineIteratorPixel dabIt = dab->createHLineIterator(0, y, maskW, false);
+ KisHLineIteratorPixel it = device->createHLineIterator(xStart, yStart+y, maskW, true);
+
+ while (!dabIt.isDone()) {
+ // This only does something with .paint, and not with adsorb.
+ currentPack = *(reinterpret_cast<WetPack*>(it.rawData()));
+ WetPix currentData = currentPack.adsorb;
+ currentPix = currentPack.paint;
+
+ // Hardcoded threshold for the dab 'strength': above it, it will get painted
+ if (*dabIt.rawData() > 125)
+ press = pressure * 0.25;
+ else
+ press = -1;
+ //kdDebug() << "After mysterious line, press becomes: " << press << ", this is the same as in the orignal. Good" << endl;
+ // XXX - 192 is probably only useful for paper with a texture...
+ eff_height = (currentData.h + currentData.w - 192.0) * (1.0 / 255.0);
+ contact = (press + eff_height) * 0.2;
+ double old_contact = contact;
+ if (contact > 0.5)
+ contact = 1.0 - 0.5 * exp(-2.0 * contact - 1.0);
+
+ //kdDebug() << "Contact was " << old_contact << " and has become: " << contact << endl;
+ if (contact > 0.0001) {
+ int v;
+ double rnd = rand() * (1.0 / RAND_MAX);
+
+ v = currentPix.rd;
+ currentPix.rd = floor(v + (paint.rd * strength - v) * contact + rnd);
+ //kdDebug() << "Rd was " << v << " and has become " << currentPix.rd << endl;
+ v = currentPix.rw;
+ currentPix.rw = floor(v + (paint.rw * strength - v) * contact + rnd);
+ v = currentPix.gd;
+ currentPix.gd = floor(v + (paint.gd * strength - v) * contact + rnd);
+ v = currentPix.gw;
+ currentPix.gw = floor(v + (paint.gw * strength - v) * contact + rnd);
+ v = currentPix.bd;
+ currentPix.bd = floor(v + (paint.bd * strength - v) * contact + rnd);
+ v = currentPix.bw;
+ currentPix.bw = floor(v + (paint.bw * strength - v) * contact + rnd);
+ v = currentPix.w;
+ if (m_wetness)
+ currentPix.w = (CLAMP(floor(
+ v + (paint.w * (0.5 + pressure) - v) * contact + rnd), 0, 512));
+ else
+ currentPix.w = floor(v + (paint.w - v) * contact + rnd);
+
+ currentPack.paint = currentPix;
+ *(reinterpret_cast<WetPack*>(it.rawData())) = currentPack;
+ }
+ ++dabIt;
+ ++it;
+ }
+ }
+
+ m_painter->addDirtyRect(QRect(xStart, yStart, maskW, maskH));
+}
diff --git a/krita/colorspaces/wet/kis_wetop.h b/krita/colorspaces/wet/kis_wetop.h
new file mode 100644
index 00000000..cc488bf9
--- /dev/null
+++ b/krita/colorspaces/wet/kis_wetop.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * 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_WETOP_H_
+#define KIS_WETOP_H_
+
+#include "kis_paintop.h"
+#include "kis_types.h"
+#include "kis_colorspace.h"
+#include "wdgpressure.h"
+
+class KisPoint;
+class KisPainter;
+class KisInputDevice;
+
+class KisWetOpFactory : public KisPaintOpFactory {
+public:
+ KisWetOpFactory() {}
+ virtual ~KisWetOpFactory() {}
+
+ virtual KisPaintOp * createOp(const KisPaintOpSettings *settings, KisPainter * painter);
+ virtual KisID id() { return KisID("wetbrush", i18n("Watercolor Brush")); }
+ virtual bool userVisible(KisColorSpace* cs) { return cs->id() == KisID("WET", ""); }
+ virtual KisPaintOpSettings *settings(QWidget * parent, const KisInputDevice& inputDevice);
+};
+
+class KisWetOpSettings : public KisPaintOpSettings {
+ typedef KisPaintOpSettings super;
+public:
+ KisWetOpSettings(QWidget *parent);
+
+ bool varySize() const;
+ bool varyWetness() const;
+ bool varyStrength() const;
+
+ virtual QWidget *widget() const { return m_options; }
+
+private:
+ WetPaintOptions *m_options;
+};
+
+class KisWetOp : public KisPaintOp {
+
+ typedef KisPaintOp super;
+ bool m_size;
+ bool m_wetness;
+ bool m_strength;
+
+public:
+
+ KisWetOp(const KisWetOpSettings *settings, KisPainter * painter);
+ virtual ~KisWetOp();
+
+ void paintAt(const KisPoint &pos, const KisPaintInformation& info);
+
+};
+
+#endif // KIS_WETOP_H_
diff --git a/krita/colorspaces/wet/kritawetplugin.desktop b/krita/colorspaces/wet/kritawetplugin.desktop
new file mode 100644
index 00000000..b6b20e84
--- /dev/null
+++ b/krita/colorspaces/wet/kritawetplugin.desktop
@@ -0,0 +1,86 @@
+[Desktop Entry]
+Name=Watercolor Paint Plugin
+Name[bg]=Приставка акварелни бои
+Name[ca]=Connector de pintura aquarel·la
+Name[cy]=Ategyn Paent Dyfrlliw
+Name[da]=Plugin for vandfarvemaling
+Name[de]=Modul für Wasserfarben-Effekte
+Name[el]=Πρόσθετο υδατογραφίας
+Name[eo]=Akvokolorpentrada kromaĵo
+Name[es]=Complemento de pintura de acuarela
+Name[et]=Vesivärvijoonistuse plugin
+Name[eu]=Akuarelazko margoen plugina
+Name[fa]=وصلۀ رنگ‌آمیزی آبرنگ
+Name[fi]=Vesivärikuvaliitännäinen
+Name[fr]=Module de dessin à l'aquarelle
+Name[fy]=Wetter skilderplugin
+Name[gl]=Plugin de Pintura con Cores de Auga
+Name[he]=תוסף צביעה בצבעי מים
+Name[hu]=Vízfesték modul
+Name[is]=Vatnslita íforrit
+Name[it]=Plugin per la pittura ad acquerello
+Name[ja]=水彩プラグイン
+Name[km]=កម្មវិធី​ជំនួយ​សម្រាប់​គូរ​គំនូរ​ពណ៌​ទឹក
+Name[lv]=Zīmēšanas ar ūdenskrāsām spraudnis
+Name[ms]=Plugin Warna Cat Air
+Name[nb]=Paint-programtillegg for vannfarger
+Name[nds]=Waterklöör-Effektmoduul
+Name[ne]=पानी रङ पेन्ट प्लगइन
+Name[nl]=Waterkleur schilderplugin
+Name[nn]=Programtillegg for vassfargar
+Name[pl]=Wtyczka malowania akwarelami
+Name[pt]='Plugin' de Pintura a Água
+Name[pt_BR]=Plug-in de Marca D'água
+Name[ru]=Акварель
+Name[sk]=Modul pre vodové farby
+Name[sl]=Vstavek za slikanje z vodnimi barvami
+Name[sr]=Прикључак за сликање воденим бојама
+Name[sr@Latn]=Priključak za slikanje vodenim bojama
+Name[sv]=Insticksprogram för vattenfärgsmålning
+Name[uk]=Втулок малювання акварельними фарбами
+Name[zh_CN]=水彩绘画插件
+Name[zh_TW]=水色繪畫外掛程式
+Comment=Color model and tools for painting with simulated watercolors
+Comment[bg]=Цветови модел и инструменти за рисуване със симулирани акварелни бои
+Comment[ca]=Model de color i eines per a pintar amb aquarel·les simulades
+Comment[cy]=Model lliw ac offer ar gyfer paentio efo dyfrliwiau wedi'u hefelychu
+Comment[da]=Farvemodel og værktøjer til at male med simulerede vandfarver
+Comment[de]=Farbmodell und Werkzeuge zum Malen mit simulierten Wasserfarben
+Comment[el]=Χρωματικό μοντέλο και εργαλεία για τη ζωγραφική με εξομοίωση χρωμάτων υδατογραφίας
+Comment[en_GB]=Colour model and tools for painting with simulated watercolours
+Comment[es]=Modelo de color y herramientas para pintar con acuarelas simuladas
+Comment[et]=Vesivärvijoonistuse simulatsiooni värvimudel ja tööriistad
+Comment[eu]=Akuarelen itxuraz margotzeko tresnak eta kolore-eredua
+Comment[fa]=مدل رنگ و ابزارها برای رنگ‌آمیزی با آبرنگهای شبیه‌سازی شده
+Comment[fi]=Värimalli ja työkalut simuloiduille vesiväreille
+Comment[fr]=Modèle de couleurs et outils pour dessiner avec des couleurs simulées d'aquarelle
+Comment[fy]=Kleurmodel en -ark foar it skilderjen mei simulearre wetterkleuren
+Comment[gl]=Modelo de cores e ferramentas para pintar con cores de auga simulados
+Comment[he]=מודל צבעים וכלים לצביעה תוך הדמיה של צבעי מים
+Comment[hu]=Színmodell és eszközök szimulált vízfestékes képekhez
+Comment[is]=Litategundir og tól til að teikna með vatnslitum
+Comment[it]=Modello di colore e strumenti per il disegno con acquerelli simulati
+Comment[ja]=水彩画をシミュレートして描画するためのカラーモデルとツール
+Comment[km]=គំរូពណ៌ និង​ឧបករណ៍​សម្រាប់​គូរ​គំនូរ​ដែល​មាន​ពណ៌​ស្រដៀង​ទឹក
+Comment[ms]=Model warna dan alat lukisan dengan cat air tiruan
+Comment[nb]=Fargemodell og verktøy for maling med simulerte vannfarger
+Comment[nds]=Klöörmodell un Warktüüch för't Malen mit Waterklöreneffekten
+Comment[ne]=बनावटी पानीरङहरू सँग पेन्टीङ्गका लागि रङ मोडेल
+Comment[nl]=Kleurmodel en -gereedschappen voor het schilderen met gesimuleerde waterkleuren
+Comment[nn]=Fargemodell og verktøy for måling med simulerte vassfargar
+Comment[pl]=Przestrzeń barw oraz narzędzia do symulacji malowania farbami akwarelowymi
+Comment[pt]=Modelo de cor e ferramentas para pintar com cores aquosas simuladas
+Comment[pt_BR]=Modelo de cor e ferramentas para pintura de cores simuladas de água
+Comment[ru]=Цветовое пространство и инструменты рисования акварелью
+Comment[sk]=Model farieb a nástroje na kreslenie vodovými farbami
+Comment[sl]=Barvni model in orodja za slikanje s similiranimi vodnimi barvami
+Comment[sr]=Модел боја и алати за сликање симулираним воденим бојама
+Comment[sr@Latn]=Model boja i alati za slikanje simuliranim vodenim bojama
+Comment[sv]=Färgmodell och verktyg för att måla med simulerade vattenfärger
+Comment[uk]=Модель кольорів та засобів для малювання з симулюванням акварелі
+Comment[zh_CN]=模拟水彩绘画的色彩模型和工具
+Comment[zh_TW]=以模擬水色繪製的色彩模型與工具
+ServiceTypes=Krita/ColorSpace,Krita/ViewPlugin
+Type=Service
+X-KDE-Library=kritawetplugin
+X-Krita-Version=2
diff --git a/krita/colorspaces/wet/todo b/krita/colorspaces/wet/todo
new file mode 100644
index 00000000..2f6d358b
--- /dev/null
+++ b/krita/colorspaces/wet/todo
@@ -0,0 +1,24 @@
+* Implement wet model
+* Implement wet paintop
+* Implement dry filter
+* Enable/disable all relevant gui bits on switching to/from a wet layer
+* Create something that can periodically call the dry filter
+* A palette with the special colour for wet painting: a docker tab for the colour docker.
+
+Perhaps: a special layer type or paint device, with an extensible option widget.
+
+When to create the height field? Ideally, the height field should be visualized in subtle shades
+of grey before painting.
+
+How to hack into the creation of paint devices? We need initializers, extra options, status
+widgets and thread or timer based continuous running filters.
+
+-> Maybe add an initializePaintDevice(rect) method to the color strategy that is called with the image
+rect on creation. Then, for uninitialized rects (because of autolayers extension) call the method again.
+
+A wet layer is equivalent to the wetpack; two layers, i.e., stacked pixels, per pixel.
+
+The filter will contain the physics model: paint flow, drying and adsorbing onto the lower "layer"
+
+
+
diff --git a/krita/colorspaces/wet/wdgpressure.ui b/krita/colorspaces/wet/wdgpressure.ui
new file mode 100644
index 00000000..92161294
--- /dev/null
+++ b/krita/colorspaces/wet/wdgpressure.ui
@@ -0,0 +1,60 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>WetPaintOptions</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>WetPaintOptions</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>382</width>
+ <height>31</height>
+ </rect>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Pressure effects:</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>checkSize</cstring>
+ </property>
+ <property name="text">
+ <string>Size</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>checkWetness</cstring>
+ </property>
+ <property name="text">
+ <string>Wetness</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>checkStrength</cstring>
+ </property>
+ <property name="text">
+ <string>Strength</string>
+ </property>
+ </widget>
+ </hbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/krita/colorspaces/wet/wet_plugin.cc b/krita/colorspaces/wet/wet_plugin.cc
new file mode 100644
index 00000000..cbb4787b
--- /dev/null
+++ b/krita/colorspaces/wet/wet_plugin.cc
@@ -0,0 +1,128 @@
+/*
+ * wet_plugin.cc -- Part of Krita
+ *
+ * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
+ *
+ * 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 <vector>
+
+#include <qobject.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qdockwindow.h>
+#include <qpoint.h>
+#include <qlabel.h>
+#include <qwidget.h>
+
+#include <kactionclasses.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kinstance.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <ktempfile.h>
+#include <kdebug.h>
+#include <kgenericfactory.h>
+
+#include <kopalettemanager.h>
+#include <KoMainWindow.h>
+
+#include <kis_debug_areas.h>
+#include "kis_meta_registry.h"
+#include <kis_factory.h>
+#include <kis_image.h>
+#include <kis_debug_areas.h>
+#include <kis_types.h>
+#include <kis_view.h>
+#include <kis_colorspace_factory_registry.h>
+#include <kis_tool_registry.h>
+#include <kis_paintop_registry.h>
+#include <kis_canvas_subject.h>
+#include <kis_basic_histogram_producers.h>
+
+#include "wet_plugin.h"
+#include "kis_wet_palette_widget.h"
+#include "kis_wet_colorspace.h"
+#include "kis_wetop.h"
+#include "kis_wetness_visualisation_filter.h"
+#include "kis_texture_filter.h"
+#include "wetphysicsfilter.h"
+
+typedef KGenericFactory<WetPlugin> WetPluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kritawetplugin, WetPluginFactory( "kritacore" ) )
+
+
+WetPlugin::WetPlugin(QObject *parent, const char *name, const QStringList &)
+ : KParts::Plugin(parent, name)
+{
+ setInstance(WetPluginFactory::instance());
+
+ // This is not a gui plugin; only load it when the doc is created.
+ if ( parent->inherits("KisColorSpaceFactoryRegistry") ) {
+ KisColorSpaceFactoryRegistry * f = dynamic_cast<KisColorSpaceFactoryRegistry*>(parent);
+
+ KisColorSpace* colorSpaceWet = new KisWetColorSpace(f, 0);
+
+ KisColorSpaceFactory * csf = new KisWetColorSpaceFactory();
+ Q_CHECK_PTR(colorSpaceWet);
+
+ // colorspace
+ f->add(csf);
+
+ // histogram producer
+ KisHistogramProducerFactoryRegistry::instance()->add(
+ new KisBasicHistogramProducerFactory<KisBasicU16HistogramProducer>
+ (KisID("WETHISTO", i18n("Wet")), colorSpaceWet) );
+
+ // wet brush op
+ KisPaintOpRegistry::instance()->add(new KisWetOpFactory);
+
+ // Dry filter
+ KisFilterRegistry::instance()->add( new WetPhysicsFilter() );
+
+ // Texture Action:
+ f->addPaintDeviceAction(colorSpaceWet, new WetPaintDevAction);
+ }
+ else if (parent->inherits("KisView"))
+ {
+ setInstance(WetPluginFactory::instance());
+ setXMLFile(locate("data","kritaplugins/wetplugin.rc"), true);
+
+ m_view = dynamic_cast<KisView*>(parent);
+ // Wetness visualisation
+ WetnessVisualisationFilter * wf = new WetnessVisualisationFilter(m_view);
+ wf->setAction(new KToggleAction(i18n("Wetness Visualisation"), 0, 0, wf,
+ SLOT(slotActivated()), actionCollection(), "wetnessvisualisation"));
+
+ // Create the wet palette
+ KisWetPaletteWidget * w = new KisWetPaletteWidget(m_view);
+ Q_CHECK_PTR(w);
+
+ w->setCaption(i18n("Watercolors"));
+
+ m_view->canvasSubject()->paletteManager()->addWidget(w, "watercolor docker", krita::COLORBOX, INT_MAX, PALETTE_DOCKER, false);
+ m_view->canvasSubject()->attach(w);
+ }
+
+
+}
+
+WetPlugin::~WetPlugin()
+{
+}
+
+#include "wet_plugin.moc"
diff --git a/krita/colorspaces/wet/wet_plugin.h b/krita/colorspaces/wet/wet_plugin.h
new file mode 100644
index 00000000..117ee253
--- /dev/null
+++ b/krita/colorspaces/wet/wet_plugin.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2005 Boudewijn Rempt (boud@valdyas.org)
+ *
+ * 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 WET_PLUGIN_H_
+#define WET_PLUGIN_H_
+
+#include <kparts/plugin.h>
+
+#include "kis_types.h"
+
+class KisView;
+class KisWetColorSpace;
+
+/**
+ * A plugin wrapper around the WET colour space strategy.
+ */
+class WetPlugin : public KParts::Plugin
+{
+ Q_OBJECT
+public:
+ WetPlugin(QObject *parent, const char *name, const QStringList &);
+ virtual ~WetPlugin();
+
+private:
+
+ KisView* m_view;
+
+};
+
+#endif // WET_PLUGIN_H_
diff --git a/krita/colorspaces/wet/wetdreams/Makefile b/krita/colorspaces/wet/wetdreams/Makefile
new file mode 100644
index 00000000..3daf4d76
--- /dev/null
+++ b/krita/colorspaces/wet/wetdreams/Makefile
@@ -0,0 +1,6 @@
+CFLAGS = -O3 `gtk-config --cflags` -Wall -ansi -pedantic
+LDFLAGS = -O3 `gtk-config --libs` -Wall -ansi -pedantic
+
+all: wetmain
+
+wetmain: wetmain.o wetpix.o wetpaint.o wettexture.o wetphysics.o
diff --git a/krita/colorspaces/wet/wetdreams/wetmain.c b/krita/colorspaces/wet/wetdreams/wetmain.c
new file mode 100644
index 00000000..151385f5
--- /dev/null
+++ b/krita/colorspaces/wet/wetdreams/wetmain.c
@@ -0,0 +1,517 @@
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <math.h>
+#include "wetpix.h"
+#include "wetpaint.h"
+#include "wettexture.h"
+#include "wetphysics.h"
+WetPack *pack; /* The global wet pack */
+
+double lastx, lasty;
+double dist;
+double spacing = 2;
+
+WetPix paint = { 707, 0, 707, 0, 707, 0, 240, 0 };
+
+/* colors from Curtis et al, Siggraph 97 */
+
+WetPix paintbox[] = {
+ {496, 0, 16992, 0, 3808, 0, 0, 0},
+ {16992, 9744, 21712, 6400, 25024, 3296, 0, 0},
+ {6512, 6512, 6512, 4880, 11312, 0, 0, 0},
+ {16002, 0, 2848, 0, 16992, 0, 0, 0},
+ {22672, 0, 5328, 2272, 4288, 2640, 0, 0},
+ {8000, 0, 16992, 0, 28352, 0, 0, 0},
+ {5696, 5696, 12416, 2496, 28352, 0, 0, 0},
+ {0, 0, 5136, 0, 28352, 0, 0, 0},
+ {2320, 1760, 7344, 4656, 28352, 0, 0, 0},
+ {8000, 0, 3312, 0, 5504, 0, 0, 0},
+ {13680, 0, 16992, 0, 3312, 0, 0, 0},
+ {5264, 5136, 1056, 544, 6448, 6304, 0, 0},
+ {11440, 11440, 11440, 11440, 11440, 11440, 0, 0},
+ {11312, 0, 11312, 0, 11312, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0}
+};
+
+int n_paints = 15;
+
+char *paintstr = "select paint";
+
+GtkWidget *paintname;
+
+char *paintnames[] = {
+ "Quinacridone Rose",
+ "Indian Red",
+ "Cadmium Yellow",
+ "Hookers Green",
+ "Cerulean Blue",
+ "Burnt Umber",
+ "Cadmium Red",
+ "Brilliant Orange",
+ "Hansa Yellow",
+ "Phthalo Green",
+ "French Ultramarine",
+ "Interference Lilac",
+ "Titanium White",
+ "Ivory Black",
+ "Pure Water"
+};
+
+GtkWidget *autodryb;
+int timo = 0;
+int adsorb_cnt;
+
+GtkObject *brushsize_adjust;
+
+GtkObject *wetness_adjust;
+
+GtkObject *strength_adjust;
+
+static void stop_drying(void)
+{
+ timo = 0;
+ gtk_label_set_text(GTK_LABEL(paintname), paintstr);
+}
+
+static double strength_func(double strength, double pressure)
+{
+ return strength * (strength + pressure) * 0.5;
+}
+
+static gint wet_button_press(GtkWidget * widget, GdkEventButton * event)
+{
+#define noVERBOSE
+#ifdef VERBOSE
+ g_print("button press %f %f %f\n", event->x, event->y,
+ event->pressure);
+
+#endif
+ wet_dab(pack->layers[1],
+ &paint,
+ event->x,
+ event->y,
+ ((GtkAdjustment *) brushsize_adjust)->value *
+ event->pressure, 0.75 + 0.25 * event->pressure,
+ strength_func(((GtkAdjustment *) strength_adjust)->value,
+ event->pressure));
+
+ lastx = event->x;
+ lasty = event->y;
+ dist = 0;
+
+ stop_drying();
+
+ gtk_widget_queue_draw(widget);
+ return TRUE;
+}
+
+static gint wet_motion(GtkWidget * widget, GdkEventMotion * event)
+{
+ double delta;
+#ifdef VERBOSE
+ g_print("motion %f %f %f %d\n", event->x, event->y,
+ event->pressure, event->state);
+
+#endif
+ stop_drying();
+
+ if (!(event->state & 256))
+ return TRUE;
+
+ delta = sqrt((event->x - lastx) * (event->x - lastx) +
+ (event->y - lasty) * (event->y - lasty));
+
+ dist += delta;
+
+ if (dist >= spacing) {
+ /* todo: interpolate position and pressure of the dab */
+ wet_dab(pack->layers[1],
+ &paint,
+ event->x,
+ event->y,
+ ((GtkAdjustment *) brushsize_adjust)->value *
+ event->pressure, 0.75 + 0.25 * event->pressure,
+ strength_func(((GtkAdjustment *) strength_adjust)->
+ value, event->pressure));
+ gtk_widget_queue_draw(widget);
+ dist -= spacing;
+ }
+
+ lastx = event->x;
+ lasty = event->y;
+
+ return TRUE;
+}
+
+static void dry(GtkWidget * da)
+{
+ g_print("drying...");
+ gtk_label_set_text(GTK_LABEL(paintname), "drying...");
+ gtk_widget_draw(paintname, NULL);
+ gdk_flush();
+ wet_flow(pack->layers[1]);
+ adsorb_cnt++;
+ if (adsorb_cnt == 2) {
+ wet_adsorb(pack->layers[1], pack->layers[0]);
+ wet_dry(pack->layers[1]);
+ adsorb_cnt = 0;
+ }
+
+ gtk_widget_draw(da, NULL);
+#if 0
+ gtk_label_set_text(GTK_LABEL(paintname), paintstr);
+#endif
+ g_print("done\n");
+}
+
+static gint wet_dry_button_press(GtkWidget * widget, GtkWidget * da)
+{
+ dry(da);
+
+ timo = 0;
+
+ return TRUE;
+}
+
+static gint clear_button_press(GtkWidget * widget, GtkWidget * da)
+{
+ wet_layer_clear(pack->layers[0]);
+ wet_layer_clone_texture(pack->layers[0], pack->layers[1]);
+ wet_layer_clear(pack->layers[1]);
+ wet_layer_clone_texture(pack->layers[1], pack->layers[0]);
+
+ gtk_widget_draw(da, NULL);
+
+ stop_drying();
+
+ return TRUE;
+}
+
+static gint dry_timer(gpointer * dummy)
+{
+ GtkWidget *da = (GtkWidget *) dummy;
+
+ timo++;
+ if (timo >= 10) {
+ if (gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(autodryb))) {
+ dry(da);
+ }
+
+ timo -= 2;
+ }
+ return TRUE;
+}
+
+static gint
+wet_expose(GtkWidget * widget, GdkEventExpose * event, WetPack * pack)
+{
+ byte *rgb;
+ int rowstride;
+
+#ifdef VERBOSE
+ g_print("expose: %d layers\n", pack->n_layers);
+#endif
+
+ rowstride = event->area.width * 3;
+ rowstride = (rowstride + 3) & -4; /* align to 4-byte boundary */
+ rgb = g_new(byte, event->area.height * rowstride);
+
+ wet_pack_render(rgb, rowstride,
+ pack,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ gdk_draw_rgb_image(widget->window,
+ widget->style->black_gc,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height,
+ GDK_RGB_DITHER_MAX, rgb, rowstride);
+
+ g_free(rgb);
+ return FALSE;
+}
+
+
+static void init_input(void)
+{
+ GList *tmp_list;
+ GdkDeviceInfo *info;
+
+ tmp_list = gdk_input_list_devices();
+
+ info = NULL;
+ while (tmp_list) {
+ info = (GdkDeviceInfo *) tmp_list->data;
+#ifdef VERBOSE
+ g_print("device: %s\n", info->name);
+#endif
+ if (!g_strcasecmp(info->name, "wacom") ||
+ !g_strcasecmp(info->name, "stylus") ||
+ !g_strcasecmp(info->name, "eraser")) {
+ gdk_input_set_mode(info->deviceid,
+ GDK_MODE_SCREEN);
+ }
+ tmp_list = tmp_list->next;
+ }
+ if (!info)
+ return;
+}
+
+static gint
+pselect_expose(GtkWidget * widget, GdkEventExpose * event, WetPack * pack)
+{
+ byte *rgb;
+ int x;
+ int paint_quad, paint_num;
+ int last_pn;
+ int bg;
+
+#ifdef VERBOSE
+ g_print("expose: %d layers\n", pack->n_layers);
+#endif
+
+ rgb = g_new(byte, pack->layers[0]->width * 3);
+
+ last_pn = 0;
+ for (x = 0; x < pack->layers[0]->width; x++) {
+ paint_quad =
+ floor(4 * x * n_paints / pack->layers[0]->width + 0.5);
+ paint_num = paint_quad >> 2;
+ if (last_pn != paint_num) {
+ rgb[x * 3] = 255;
+ rgb[x * 3 + 1] = 255;
+ rgb[x * 3 + 2] = 255;
+ last_pn = paint_num;
+ } else {
+ if ((paint_quad & 3) > 0 && (paint_quad & 3) < 3)
+ bg = 0;
+ else
+ bg = 255;
+ rgb[x * 3] = bg;
+ rgb[x * 3 + 1] = bg;
+ rgb[x * 3 + 2] = bg;
+ wet_composite(&rgb[x * 3], 0, &paintbox[paint_num],
+ 0, 1, 1);
+ }
+ }
+
+ gdk_draw_rgb_image(widget->window,
+ widget->style->black_gc,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height,
+ GDK_RGB_DITHER_MAX,
+ rgb + (event->area.x) * 3, 0);
+
+ g_free(rgb);
+ return FALSE;
+}
+
+static gint
+pselect_button_press(GtkWidget * widget, GdkEventButton * event)
+{
+ int paint_num;
+ int wet;
+
+#ifdef VERBOSE
+ g_print("pselect button press %f %f %f\n", event->x, event->y,
+ event->pressure);
+
+#endif
+ paint_num = floor((event->x * n_paints) / pack->layers[0]->width);
+
+ /* preserve wetness */
+ wet = paint.w;
+ paint = paintbox[paint_num];
+ paint.w = wet;
+ paintstr = paintnames[paint_num];
+ /*
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (wetness_adjust), paint.w);
+ */
+ gtk_label_set_text(GTK_LABEL(paintname), paintstr);
+
+ stop_drying();
+
+ return TRUE;
+}
+
+static void wetness_update(GtkAdjustment * adj, gpointer data)
+{
+ paint.w = floor(15 * adj->value + 0.5);
+}
+
+int main(int argc, char **argv)
+{
+ GtkWidget *w;
+ GtkWidget *v;
+ GtkWidget *eb;
+ GtkWidget *da;
+ GtkWidget *peb;
+ GtkWidget *pda;
+ GtkWidget *h;
+ GtkWidget *b;
+ GtkWidget *db;
+ GtkWidget *h2;
+ GtkWidget *l;
+ GtkWidget *brushsize;
+ GtkWidget *wetness;
+ GtkWidget *strength;
+ int xs = 512;
+ int ys = 512;
+
+ gtk_init(&argc, &argv);
+
+ if (argc >= 3) {
+ xs = atoi(argv[1]);
+ ys = atoi(argv[2]);
+ if (xs == 0)
+ xs = 512;
+ if (ys == 0)
+ ys = 512;
+ }
+
+
+ init_input();
+
+ gdk_rgb_init();
+
+ gtk_widget_set_default_colormap(gdk_rgb_get_cmap());
+ gtk_widget_set_default_visual(gdk_rgb_get_visual());
+
+ pack = wet_pack_new(xs, ys);
+
+ wet_pack_maketexture(pack, 1, 0.7, 0.5);
+
+ w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect(GTK_OBJECT(w), "destroy",
+ (GtkSignalFunc) gtk_main_quit, NULL);
+
+ v = gtk_vbox_new(FALSE, 2);
+ gtk_container_add(GTK_CONTAINER(w), v);
+ gtk_widget_show(v);
+
+ eb = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(v), eb);
+ gtk_widget_show(eb);
+
+ gtk_widget_set_extension_events(eb, GDK_EXTENSION_EVENTS_ALL);
+
+ gtk_widget_set_events(eb, GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_PROXIMITY_OUT_MASK);
+
+ gtk_signal_connect(GTK_OBJECT(eb), "button_press_event",
+ (GtkSignalFunc) wet_button_press, NULL);
+ gtk_signal_connect(GTK_OBJECT(eb), "motion_notify_event",
+ (GtkSignalFunc) wet_motion, NULL);
+
+ da = gtk_drawing_area_new();
+ gtk_drawing_area_size(GTK_DRAWING_AREA(da), xs, ys);
+ gtk_container_add(GTK_CONTAINER(eb), da);
+ gtk_widget_show(da);
+
+ gtk_signal_connect(GTK_OBJECT(da), "expose_event",
+ (GtkSignalFunc) wet_expose, pack);
+
+ peb = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(v), peb);
+ gtk_widget_show(peb);
+
+ gtk_widget_set_extension_events(peb, GDK_EXTENSION_EVENTS_ALL);
+
+ gtk_widget_set_events(peb, GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_PROXIMITY_OUT_MASK);
+
+ gtk_signal_connect(GTK_OBJECT(peb), "button_press_event",
+ (GtkSignalFunc) pselect_button_press, NULL);
+
+ pda = gtk_drawing_area_new();
+ gtk_drawing_area_size(GTK_DRAWING_AREA(pda), xs, 16);
+ gtk_container_add(GTK_CONTAINER(peb), pda);
+ gtk_widget_show(pda);
+
+ gtk_signal_connect(GTK_OBJECT(pda), "expose_event",
+ (GtkSignalFunc) pselect_expose, pack);
+
+ paintname = gtk_label_new(paintstr);
+ gtk_container_add(GTK_CONTAINER(v), paintname);
+ gtk_widget_show(paintname);
+
+ h = gtk_hbox_new(TRUE, 5);
+ gtk_container_add(GTK_CONTAINER(v), h);
+ gtk_widget_show(h);
+
+ b = gtk_button_new_with_label("Dry");
+ gtk_container_add(GTK_CONTAINER(h), b);
+ gtk_widget_show(b);
+
+ gtk_signal_connect(GTK_OBJECT(b), "clicked",
+ (GtkSignalFunc) wet_dry_button_press, da);
+
+ autodryb = gtk_toggle_button_new_with_label("Auto Dry");
+ gtk_container_add(GTK_CONTAINER(h), autodryb);
+ gtk_widget_show(autodryb);
+
+ db = gtk_button_new_with_label("Clear");
+ gtk_container_add(GTK_CONTAINER(h), db);
+ gtk_widget_show(db);
+
+ gtk_signal_connect(GTK_OBJECT(db), "clicked",
+ (GtkSignalFunc) clear_button_press, da);
+
+ h2 = gtk_hbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(v), h2);
+ gtk_widget_show(h2);
+
+ l = gtk_label_new("Brush size: ");
+ gtk_container_add(GTK_CONTAINER(h2), l);
+ gtk_widget_show(l);
+
+ brushsize_adjust = gtk_adjustment_new(10, 0, 32, 0.1, 0.1, 0);
+ brushsize = gtk_hscale_new(GTK_ADJUSTMENT(brushsize_adjust));
+ gtk_container_add(GTK_CONTAINER(h2), brushsize);
+ gtk_widget_show(brushsize);
+
+ h2 = gtk_hbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(v), h2);
+ gtk_widget_show(h2);
+
+ l = gtk_label_new("Wetness: ");
+ gtk_container_add(GTK_CONTAINER(h2), l);
+ gtk_widget_show(l);
+
+ wetness_adjust = gtk_adjustment_new(16, 0, 16, 1.0, 1.0, 0);
+ wetness = gtk_hscale_new(GTK_ADJUSTMENT(wetness_adjust));
+ gtk_container_add(GTK_CONTAINER(h2), wetness);
+ gtk_widget_show(wetness);
+ gtk_signal_connect(GTK_OBJECT(wetness_adjust), "value_changed",
+ (GtkSignalFunc) wetness_update, NULL);
+
+ h2 = gtk_hbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(v), h2);
+ gtk_widget_show(h2);
+
+ l = gtk_label_new("Strength: ");
+ gtk_container_add(GTK_CONTAINER(h2), l);
+ gtk_widget_show(l);
+
+ strength_adjust = gtk_adjustment_new(1, 0, 2, 0.1, 0.1, 0);
+ strength = gtk_hscale_new(GTK_ADJUSTMENT(strength_adjust));
+ gtk_scale_set_digits(GTK_SCALE(strength), 2);
+ gtk_container_add(GTK_CONTAINER(h2), strength);
+ gtk_widget_show(strength);
+
+ gtk_widget_show(w);
+
+ gtk_timeout_add(50, (GtkFunction) dry_timer, da);
+
+ gtk_main();
+
+ return 0;
+}
diff --git a/krita/colorspaces/wet/wetdreams/wetpaint.c b/krita/colorspaces/wet/wetdreams/wetpaint.c
new file mode 100644
index 00000000..c1ac0d0c
--- /dev/null
+++ b/krita/colorspaces/wet/wetdreams/wetpaint.c
@@ -0,0 +1,101 @@
+
+#include <stdlib.h>
+#include <math.h>
+#include "wetpix.h"
+#include "wetphysics.h"
+#include "wetpaint.h"
+
+/* This function is not entirely satisfactory - the compositing is basically
+ opaque, and it really should do wet compositing. */
+void
+wet_dab(WetLayer * layer,
+ WetPix * paint,
+ double x, double y, double r, double pressure, double strength)
+{
+ double r_fringe;
+ int x0, y0;
+ int x1, y1;
+ WetPix *wet_line;
+ int xp, yp;
+ double xx, yy, rr;
+ double eff_height;
+ double press, contact;
+ WetPixDbl wet_tmp, wet_tmp2;
+
+ r_fringe = r + 1;
+ x0 = floor(x - r_fringe);
+ y0 = floor(y - r_fringe);
+ x1 = ceil(x + r_fringe);
+ y1 = ceil(y + r_fringe);
+ if (x0 < 0)
+ x0 = 0;
+ if (y0 < 0)
+ y0 = 0;
+ if (x1 >= layer->width)
+ x1 = layer->width;
+ if (y1 >= layer->height)
+ y1 = layer->height;
+
+ wet_line = layer->buf + y0 * layer->rowstride;
+ for (yp = y0; yp < y1; yp++) {
+ yy = (yp + 0.5 - y);
+ yy *= yy;
+ for (xp = x0; xp < x1; xp++) {
+ xx = (xp + 0.5 - x);
+ xx *= xx;
+ rr = yy + xx;
+ if (rr < r * r)
+ press = pressure * 0.25;
+ else
+ press = -1;
+ eff_height =
+ (wet_line[xp].h + wet_line[xp].w -
+ 192) * (1.0 / 255);
+ contact = (press + eff_height) * 0.2;
+ if (contact > 0.5)
+ contact =
+ 1 - 0.5 * exp(-2.0 * contact - 1);
+ if (contact > 0.0001) {
+ int v;
+ double rnd = rand() * (1.0 / RAND_MAX);
+
+ v = wet_line[xp].rd;
+ wet_line[xp].rd =
+ floor(v +
+ (paint->rd * strength -
+ v) * contact + rnd);
+ v = wet_line[xp].rw;
+ wet_line[xp].rw =
+ floor(v +
+ (paint->rw * strength -
+ v) * contact + rnd);
+ v = wet_line[xp].gd;
+ wet_line[xp].gd =
+ floor(v +
+ (paint->gd * strength -
+ v) * contact + rnd);
+ v = wet_line[xp].gw;
+ wet_line[xp].gw =
+ floor(v +
+ (paint->gw * strength -
+ v) * contact + rnd);
+ v = wet_line[xp].bd;
+ wet_line[xp].bd =
+ floor(v +
+ (paint->bd * strength -
+ v) * contact + rnd);
+ v = wet_line[xp].bw;
+ wet_line[xp].bw =
+ floor(v +
+ (paint->bw * strength -
+ v) * contact + rnd);
+ v = wet_line[xp].w;
+ wet_line[xp].w =
+ floor(v + (paint->w - v) * contact +
+ rnd);
+
+ }
+ }
+ wet_line += layer->rowstride;
+ }
+}
diff --git a/krita/colorspaces/wet/wetdreams/wetpaint.h b/krita/colorspaces/wet/wetdreams/wetpaint.h
new file mode 100644
index 00000000..5cec2659
--- /dev/null
+++ b/krita/colorspaces/wet/wetdreams/wetpaint.h
@@ -0,0 +1,4 @@
+void wet_dab(WetLayer * layer,
+ WetPix * paint,
+ double x, double y,
+ double r, double pressure, double strength);
diff --git a/krita/colorspaces/wet/wetdreams/wetphysics.c b/krita/colorspaces/wet/wetdreams/wetphysics.c
new file mode 100644
index 00000000..d8b321a8
--- /dev/null
+++ b/krita/colorspaces/wet/wetdreams/wetphysics.c
@@ -0,0 +1,334 @@
+/* Cool physics functions for wet paint */
+
+#include <gtk/gtk.h>
+#include <math.h>
+#include "wetpix.h"
+#include "wetphysics.h"
+
+/* symmetric combine, i.e. wet mixing.
+
+ This does not set the dst h field.
+*/
+void wet_pix_combine(WetPixDbl * dst, WetPixDbl * src1, WetPixDbl * src2)
+{
+ dst->rd = src1->rd + src2->rd;
+ dst->rw = src1->rw + src2->rw;
+#if 0
+ g_print("rd %f rw %f\n", dst->rd, dst->rw);
+#endif
+ dst->gd = src1->gd + src2->gd;
+ dst->gw = src1->gw + src2->gw;
+ dst->bd = src1->bd + src2->bd;
+ dst->bw = src1->bw + src2->bw;
+ dst->w = src1->w + src2->w;
+#if 0
+ g_print("%f + %f -> %f\n", src1->w, src2->w, dst->w);
+#endif
+}
+
+void wet_pix_dilute(WetPixDbl * dst, WetPix * src, double dilution)
+{
+ double scale = dilution * (1.0 / 8192.0);
+
+
+ dst->rd = src->rd * scale;
+#if 0
+ g_print("dilution %f scale %f rd %f\n", dilution, scale, dst->rd);
+#endif
+ dst->rw = src->rw * scale;
+ dst->gd = src->gd * scale;
+ dst->gw = src->gw * scale;
+ dst->bd = src->bd * scale;
+ dst->bw = src->bw * scale;
+ dst->w = src->w * (1.0 / 8192.0);
+ dst->h = src->h * (1.0 / 8192.0);
+}
+
+void wet_pix_reduce(WetPixDbl * dst, WetPix * src, double dilution)
+{
+ wet_pix_dilute(dst, src, dilution);
+ dst->w *= dilution;
+}
+
+/* allows visualization of adsorption by rotating the hue 120 degrees */
+/* layer-merge combining. src1 is the top layer
+
+ This does not set the dst h or w fields.
+*/
+void
+wet_pix_merge(WetPixDbl * dst, WetPixDbl * src1, double dilution1,
+ WetPixDbl * src2)
+{
+ double d1, w1, d2, w2;
+ double ed1, ed2;
+
+ if (src1->rd < 1e-4) {
+ dst->rd = src2->rd;
+ dst->rw = src2->rw;
+ } else if (src2->rd < 1e-4) {
+ dst->rd = src1->rd * dilution1;
+ dst->rw = src1->rw * dilution1;
+ } else {
+ d1 = src1->rd;
+ w1 = src1->rw;
+ d2 = src2->rd;
+ w2 = src2->rw;
+ dst->rd = d1 * dilution1 + d2;
+ ed1 = exp(-d1 * dilution1);
+ ed2 = exp(-d2);
+ dst->rw = dst->rd * ((1 - ed1) * w1 / d1 +
+ ed1 * (1 - ed2) * w2 / d2) /
+ (1 - ed1 * ed2);
+ }
+
+ if (src1->gd < 1e-4) {
+ dst->gd = src2->gd;
+ dst->gw = src2->gw;
+ } else if (src2->gd < 1e-4) {
+ dst->gd = src1->gd * dilution1;
+ dst->gw = src1->gw * dilution1;
+ } else {
+ d1 = src1->gd;
+ w1 = src1->gw;
+ d2 = src2->gd;
+ w2 = src2->gw;
+ dst->gd = d1 * dilution1 + d2;
+ ed1 = exp(-d1 * dilution1);
+ ed2 = exp(-d2);
+ dst->gw = dst->gd * ((1 - ed1) * w1 / d1 +
+ ed1 * (1 - ed2) * w2 / d2) /
+ (1 - ed1 * ed2);
+ }
+
+ if (src1->bd < 1e-4) {
+ dst->bd = src2->bd;
+ dst->bw = src2->bw;
+ } else if (src2->bd < 1e-4) {
+ dst->bd = src1->bd * dilution1;
+ dst->bw = src1->bw * dilution1;
+ } else {
+ d1 = src1->bd;
+ w1 = src1->bw;
+ d2 = src2->bd;
+ w2 = src2->bw;
+ dst->bd = d1 * dilution1 + d2;
+ ed1 = exp(-d1 * dilution1);
+ ed2 = exp(-d2);
+ dst->bw = dst->bd * ((1 - ed1) * w1 / d1 +
+ ed1 * (1 - ed2) * w2 / d2) /
+ (1 - ed1 * ed2);
+ }
+
+}
+
+void wet_flow(WetLayer * layer)
+{
+ /* XXX: Is this like a convolution operation? BSAR */
+ int x, y;
+ int width = layer->width;
+ int height = layer->height;
+ int rs = layer->rowstride;
+ double *flow_t, *flow_b, *flow_l, *flow_r;
+ double *fluid, *outflow;
+ WetPix *wet_line = layer->buf;
+ WetPix *wet_old;
+ int my_height;
+ int ix;
+ double ft, fb, fl, fr; /* top, bottom, left, right */
+ WetPixDbl wet_mix, wet_tmp;
+
+ flow_t = g_new(double, width * height);
+ flow_b = g_new(double, width * height);
+ flow_l = g_new(double, width * height);
+ flow_r = g_new(double, width * height);
+ fluid = g_new(double, width * height);
+ outflow = g_new(double, width * height);
+ wet_old = g_new(WetPix, width * height);
+
+ /* assumes rowstride == width */
+ memcpy(wet_old, layer->buf, sizeof(WetPix) * width * height);
+
+ ix = width + 1;
+ for (y = 1; y < height - 1; y++) {
+ wet_line += rs;
+ for (x = 1; x < width - 1; x++) {
+ if (wet_line[x].w > 0) {
+ my_height = wet_line[x].h + wet_line[x].w;
+ ft = (wet_line[x - rs].h +
+ wet_line[x - rs].w) - my_height;
+ fb = (wet_line[x + rs].h +
+ wet_line[x + rs].w) - my_height;
+ fl = (wet_line[x - 1].h +
+ wet_line[x - 1].w) - my_height;
+ fr = (wet_line[x + 1].h +
+ wet_line[x + 1].w) - my_height;
+
+ fluid[ix] =
+ 0.4 * sqrt(wet_line[x].w * 1.0 /
+ 255.0);
+
+ /* smooth out the flow a bit */
+ flow_t[ix] =
+ 0.1 * (10 + ft * 0.75 - fb * 0.25);
+ if (flow_t[ix] > 1)
+ flow_t[ix] = 1;
+ if (flow_t[ix] < 0)
+ flow_t[ix] = 0;
+ flow_b[ix] =
+ 0.1 * (10 + fb * 0.75 - ft * 0.25);
+ if (flow_b[ix] > 1)
+ flow_b[ix] = 1;
+ if (flow_b[ix] < 0)
+ flow_b[ix] = 0;
+ flow_l[ix] =
+ 0.1 * (10 + fl * 0.75 - fr * 0.25);
+ if (flow_l[ix] > 1)
+ flow_l[ix] = 1;
+ if (flow_l[ix] < 0)
+ flow_l[ix] = 0;
+ flow_r[ix] =
+ 0.1 * (10 + fr * 0.75 - fl * 0.25);
+ if (flow_r[ix] > 1)
+ flow_r[ix] = 1;
+ if (flow_r[ix] < 0)
+ flow_r[ix] = 0;
+
+ outflow[ix] = 0;
+ }
+ ix++;
+ }
+ ix += 2;
+ }
+
+ ix = width + 1;
+ wet_line = layer->buf;
+ for (y = 1; y < height - 1; y++) {
+ wet_line += rs;
+ for (x = 1; x < width - 1; x++) {
+ if (wet_line[x].w > 0) {
+ /* reduce flow in dry areas */
+ flow_t[ix] *= fluid[ix] * fluid[ix - rs];
+ outflow[ix - rs] += flow_t[ix];
+ flow_b[ix] *= fluid[ix] * fluid[ix + rs];
+ outflow[ix + rs] += flow_b[ix];
+ flow_l[ix] *= fluid[ix] * fluid[ix - 1];
+ outflow[ix - 1] += flow_l[ix];
+ flow_r[ix] *= fluid[ix] * fluid[ix + 1];
+ outflow[ix + 1] += flow_r[ix];
+ }
+ ix++;
+ }
+ ix += 2;
+ }
+
+ wet_line = layer->buf;
+ ix = width + 1;
+ for (y = 1; y < height - 1; y++) {
+ wet_line += rs;
+ for (x = 1; x < width - 1; x++) {
+ if (wet_line[x].w > 0) {
+ wet_pix_reduce(&wet_mix, &wet_old[ix],
+ 1 - outflow[ix]);
+
+ wet_pix_reduce(&wet_tmp, &wet_old[ix - rs],
+ flow_t[ix]);
+ wet_pix_combine(&wet_mix, &wet_mix,
+ &wet_tmp);
+ wet_pix_reduce(&wet_tmp, &wet_old[ix + rs],
+ flow_b[ix]);
+ wet_pix_combine(&wet_mix, &wet_mix,
+ &wet_tmp);
+ wet_pix_reduce(&wet_tmp, &wet_old[ix - 1],
+ flow_l[ix]);
+ wet_pix_combine(&wet_mix, &wet_mix,
+ &wet_tmp);
+ wet_pix_reduce(&wet_tmp, &wet_old[ix + 1],
+ flow_r[ix]);
+ wet_pix_combine(&wet_mix, &wet_mix,
+ &wet_tmp);
+
+ wet_pix_from_double(&wet_line[x],
+ &wet_mix);
+
+#if 0
+ if (ix % 3201 == 0)
+ g_print("%f %f %f %f %f %f\n",
+ outflow[ix],
+ flow_t[ix],
+ flow_b[ix],
+ flow_l[ix],
+ flow_r[ix], fluid[ix]);
+#endif
+ }
+ ix++;
+ }
+ ix += 2;
+ }
+
+ g_free(flow_t);
+ g_free(flow_b);
+ g_free(flow_l);
+ g_free(flow_r);
+ g_free(fluid);
+ g_free(outflow);
+ g_free(wet_old);
+}
+
+void wet_dry(WetLayer * layer)
+{
+ int x, y;
+ WetPix *wet_line = layer->buf;
+ int width = layer->width;
+ int height = layer->height;
+ int rs = layer->rowstride;
+ int w;
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ w = wet_line[x].w;
+ w -= 1;
+ if (w > 0)
+ wet_line[x].w = w;
+ else
+ wet_line[x].w = 0;
+
+ }
+ wet_line += rs;
+ }
+}
+
+/* Move stuff from the upperlayer to the lower layer. This is filter-level stuff*/
+void wet_adsorb(WetLayer * layer, WetLayer * adsorb)
+{
+ int x, y;
+ WetPix *wet_line = layer->buf;
+ WetPix *ads_line = adsorb->buf;
+ int width = layer->width;
+ int height = layer->height;
+ int rs = layer->rowstride;
+ double ads;
+ WetPixDbl wet_top;
+ WetPixDbl wet_bot;
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ /* do adsorption */
+ if (wet_line[x].w == 0)
+ continue;
+ ads = 0.5 / MAX(wet_line[x].w, 1);
+
+ wet_pix_to_double(&wet_top, &wet_line[x]);
+ wet_pix_to_double(&wet_bot, &ads_line[x]);
+ wet_pix_merge(&wet_bot, &wet_top, ads, &wet_bot);
+ wet_pix_from_double(&ads_line[x], &wet_bot);
+ wet_line[x].rd = wet_line[x].rd * (1 - ads);
+ wet_line[x].rw = wet_line[x].rw * (1 - ads);
+ wet_line[x].gd = wet_line[x].gd * (1 - ads);
+ wet_line[x].gw = wet_line[x].gw * (1 - ads);
+ wet_line[x].bd = wet_line[x].bd * (1 - ads);
+ wet_line[x].bw = wet_line[x].bw * (1 - ads);
+ }
+ wet_line += rs;
+ ads_line += rs;
+ }
+}
diff --git a/krita/colorspaces/wet/wetdreams/wetphysics.h b/krita/colorspaces/wet/wetdreams/wetphysics.h
new file mode 100644
index 00000000..25140956
--- /dev/null
+++ b/krita/colorspaces/wet/wetdreams/wetphysics.h
@@ -0,0 +1,9 @@
+void wet_pix_combine(WetPixDbl * dst, WetPixDbl * src1, WetPixDbl * src2);
+
+void wet_pix_dilute(WetPixDbl * dst, WetPix * src, double dilution);
+
+void wet_flow(WetLayer * layer);
+
+void wet_dry(WetLayer * layer);
+
+void wet_adsorb(WetLayer * layer, WetLayer * adsorb);
diff --git a/krita/colorspaces/wet/wetdreams/wetpix.c b/krita/colorspaces/wet/wetdreams/wetpix.c
new file mode 100644
index 00000000..812a038d
--- /dev/null
+++ b/krita/colorspaces/wet/wetdreams/wetpix.c
@@ -0,0 +1,332 @@
+/* Routines for manipulating wet pixels.
+
+ Copyright 1999 Raph Levien <raph@gimp.org>
+
+ Released under GPL.
+
+ A wet pixel is a sequence of eight bytes, arranged as follows:
+
+ Red value when composited over black
+ Green value when composited over black
+ Blue value when composited over black
+ Volume of water
+ Red value when composited over white
+ Green value when composited over white
+ Blue value when composited over white
+ Height of paper surface
+
+*/
+
+#include <gtk/gtk.h>
+#include <string.h>
+#include <math.h>
+#include "wetpix.h"
+
+u32 *wet_render_tab = NULL;
+
+static void wet_init_render_tab(void)
+{
+ int i;
+ double d;
+ int a, b;
+
+ wet_render_tab = g_new(u32, 4096);
+ for (i = 0; i < 4096; i++) {
+ d = i * (1.0 / 512.0);
+ if (i == 0)
+ a = 0;
+ else
+ a = floor(0xff00 / i + 0.5);
+ b = floor(0x8000 * exp(-d) + 0.5);
+#if 0
+ g_print("%d: %x %x\n", i, a, b);
+#endif
+ wet_render_tab[i] = (a << 16) | b;
+ }
+}
+
+void
+wet_composite(byte * rgb, int rgb_rowstride,
+ WetPix * wet, int wet_rowstride,
+ int width, int height)
+{
+ int x, y;
+ byte *rgb_line = rgb;
+ WetPix *wet_line = wet;
+
+ if (wet_render_tab == NULL)
+ wet_init_render_tab();
+
+ for (y = 0; y < height; y++) {
+ byte *rgb_ptr = rgb_line;
+ WetPix *wet_ptr = wet_line;
+ for (x = 0; x < width; x++) {
+ int r, g, b;
+ int d, w;
+ int ab;
+ int wa;
+
+ r = rgb_ptr[0];
+ w = wet_ptr[0].rw >> 4;
+ d = wet_ptr[0].rd >> 4;
+ /*
+ d = d >= 4096 ? 4095 : d;
+ */
+ ab = wet_render_tab[d];
+ wa = (w * (ab >> 16) + 0x80) >> 8;
+ r = wa +
+ (((r - wa) * (ab & 0xffff) + 0x4000) >> 15);
+ rgb_ptr[0] = r;
+
+#if 0
+ if (x == 128 && y == 128) {
+ g_print("w %d d %d r %d\n", w, d, r);
+ }
+#endif
+
+ g = rgb_ptr[1];
+ w = wet_ptr[0].gw >> 4;
+ d = wet_ptr[0].gd >> 4;
+ d = d >= 4096 ? 4095 : d;
+ ab = wet_render_tab[d];
+ wa = (w * (ab >> 16) + 0x80) >> 8;
+ g = wa +
+ (((g - wa) * (ab & 0xffff) + 0x4000) >> 15);
+ rgb_ptr[1] = g;
+
+ b = rgb_ptr[2];
+ w = wet_ptr[0].bw >> 4;
+ d = wet_ptr[0].bd >> 4;
+ d = d >= 4096 ? 4095 : d;
+ ab = wet_render_tab[d];
+ wa = (w * (ab >> 16) + 0x80) >> 8;
+ b = wa +
+ (((b - wa) * (ab & 0xffff) + 0x4000) >> 15);
+ rgb_ptr[2] = b;
+
+ rgb_ptr += 3;
+ wet_ptr++;
+ }
+ rgb_line += rgb_rowstride;
+ wet_line += wet_rowstride;
+ }
+}
+
+void
+wet_render_wetness(byte * rgb, int rgb_rowstride,
+ WetLayer * layer, int x0, int y0, int width, int height)
+{
+ static int wet_phase = 0;
+ int x, y;
+ byte *rgb_line = rgb;
+ WetPix *wet_line = layer->buf + (y0 * layer->rowstride) + x0;
+ int highlight;
+
+ for (y = 0; y < height; y++) {
+ byte *rgb_ptr = rgb_line;
+ WetPix *wet_ptr = wet_line;
+ for (x = 0; x < width; x++) {
+ if (((x + y) & 3) == wet_phase) {
+ highlight = 255 - (wet_ptr[0].w >> 1);
+ if (highlight < 255) {
+ rgb_ptr[0] =
+ 255 -
+ (((255 -
+ rgb_ptr[0]) *
+ highlight) >> 8);
+ rgb_ptr[1] =
+ 255 -
+ (((255 -
+ rgb_ptr[1]) *
+ highlight) >> 8);
+ rgb_ptr[2] =
+ 255 -
+ (((255 -
+ rgb_ptr[2]) *
+ highlight) >> 8);
+ }
+ }
+ rgb_ptr += 3;
+ wet_ptr++;
+ }
+ rgb_line += rgb_rowstride;
+ wet_line += layer->rowstride;
+ }
+ wet_phase += 1;
+ wet_phase &= 3;
+}
+
+void
+wet_composite_layer(byte * rgb, int rgb_rowstride,
+ WetLayer * layer,
+ int x0, int y0, int width, int height)
+{
+ /* todo: sanitycheck bounds */
+ wet_composite(rgb, rgb_rowstride,
+ layer->buf + (y0 * layer->rowstride) + x0,
+ layer->rowstride, width, height);
+}
+
+void
+wet_pack_render(byte * rgb, int rgb_rowstride,
+ WetPack * pack, int x0, int y0, int width, int height)
+{
+ int y;
+ byte *rgb_line = rgb;
+ int i;
+
+ /* clear rgb buffer to white */
+ for (y = 0; y < height; y++) {
+ memset(rgb_line, 255, width * 3);
+ rgb_line += rgb_rowstride;
+ }
+
+ /* black stripe */
+/* rgb_line = rgb;
+ for (y = y0; y < 8 && y < y0 + height; y++)
+ {
+ memset (rgb_line, 0, width * 3);
+ rgb_line += rgb_rowstride;
+ }
+*/
+
+ for (i = 0; i < pack->n_layers; i++)
+ wet_composite_layer(rgb, rgb_rowstride,
+ pack->layers[i],
+ x0, y0, width, height);
+
+ wet_render_wetness(rgb, rgb_rowstride,
+ pack->layers[pack->n_layers - 1],
+ x0, y0, width, height);
+}
+
+WetLayer *wet_layer_new(int width, int height)
+{
+ WetLayer *layer;
+
+ layer = g_new(WetLayer, 1);
+
+ layer->buf = g_new(WetPix, width * height);
+ layer->width = width;
+ layer->height = height;
+ layer->rowstride = width;
+
+ return layer;
+}
+
+void wet_layer_clear(WetLayer * layer)
+{
+ int x, y;
+ WetPix *wet_line = layer->buf;
+ int width = layer->width;
+
+ for (y = 0; y < layer->height; y++) {
+ for (x = 0; x < width; x++) {
+ /* transparent, dry, smooth */
+ wet_line[x].rd = 0;
+ wet_line[x].rw = 0;
+ wet_line[x].gd = 0;
+ wet_line[x].gw = 0;
+ wet_line[x].bd = 0;
+ wet_line[x].bw = 0;
+ wet_line[x].w = 0;
+ wet_line[x].h = 128;
+ }
+ wet_line += layer->rowstride;
+ }
+}
+
+WetPack *wet_pack_new(int width, int height)
+{
+ WetPack *pack;
+
+ pack = g_new(WetPack, 1);
+
+ pack->n_layers = 2;
+ pack->layers = g_new(WetLayer *, pack->n_layers);
+ pack->layers[0] = wet_layer_new(width, height);
+ wet_layer_clear(pack->layers[0]);
+ pack->layers[1] = wet_layer_new(width, height);
+ wet_layer_clear(pack->layers[1]);
+
+ return pack;
+}
+
+void wet_pix_to_double(WetPixDbl * dst, WetPix * src)
+{
+ dst->rd = (1.0 / 8192.0) * src->rd;
+ dst->rw = (1.0 / 8192.0) * src->rw;
+ dst->gd = (1.0 / 8192.0) * src->gd;
+ dst->gw = (1.0 / 8192.0) * src->gw;
+ dst->bd = (1.0 / 8192.0) * src->bd;
+ dst->bw = (1.0 / 8192.0) * src->bw;
+ dst->w = (1.0 / 8192.0) * src->w;
+ dst->h = (1.0 / 8192.0) * src->h;
+}
+
+void wet_pix_from_double(WetPix * dst, WetPixDbl * src)
+{
+ int v;
+
+ v = floor(8192.0 * src->rd + 0.5);
+ if (v < 0)
+ v = 0;
+ if (v > 65535)
+ v = 65535;
+ dst->rd = v;
+
+ g_print("src->rd = %f, dst->rd = %d\n", src->rd, dst->rd);
+
+ v = floor(8192.0 * src->rw + 0.5);
+ if (v < 0)
+ v = 0;
+ if (v > 65535)
+ v = 65535;
+ dst->rw = v;
+
+ v = floor(8192.0 * src->gd + 0.5);
+ if (v < 0)
+ v = 0;
+ if (v > 65535)
+ v = 65535;
+ dst->gd = v;
+
+ v = floor(8192.0 * src->gw + 0.5);
+ if (v < 0)
+ v = 0;
+ if (v > 65535)
+ v = 65535;
+ dst->gw = v;
+
+ v = floor(8192.0 * src->bd + 0.5);
+ if (v < 0)
+ v = 0;
+ if (v > 65535)
+ v = 65535;
+ dst->bd = v;
+
+ v = floor(8192.0 * src->bw + 0.5);
+ if (v < 0)
+ v = 0;
+ if (v > 65535)
+ v = 65535;
+ dst->bw = v;
+
+ v = floor(8192.0 * src->w + 0.5);
+ if (v < 0)
+ v = 0;
+ if (v > 511)
+ v = 511;
+ dst->w = v;
+#if 0
+ g_print("src->w = %f, dst->w = %d\n", src->w, dst->w);
+#endif
+
+ v = floor(8192.0 * src->h + 0.5);
+ if (v < 0)
+ v = 0;
+ if (v > 511)
+ v = 511;
+ dst->h = v;
+
+}
diff --git a/krita/colorspaces/wet/wetdreams/wetpix.h b/krita/colorspaces/wet/wetdreams/wetpix.h
new file mode 100644
index 00000000..3dc7913f
--- /dev/null
+++ b/krita/colorspaces/wet/wetdreams/wetpix.h
@@ -0,0 +1,87 @@
+/* Routines for manipulating wet pixels.
+
+ Copyright 1999 Raph Levien <raph@gimp.org>
+
+ Released under GPL.
+
+ A wet pixel is an eight word sequence, representing partially
+ transparent wet paint on a paper surface.
+
+*/
+
+typedef unsigned char byte;
+typedef unsigned short u16;
+typedef unsigned int u32;
+
+typedef struct _WetPix WetPix;
+typedef struct _WetLayer WetLayer;
+typedef struct _WetPack WetPack;
+
+typedef struct _WetPixDbl WetPixDbl;
+
+/* White is made up of myth-red, myth-green, and myth-blue. Myth-red
+ looks red when viewed reflectively, but cyan when viewed
+ transmissively (thus, it vaguely resembles a dichroic
+ filter). Myth-red over black is red, and myth-red over white is
+ white.
+
+ Total red channel concentration is myth-red concentration plus
+ cyan concentration.
+
+*/
+
+struct _WetPix {
+ u16 rd; /* Total red channel concentration */
+ u16 rw; /* Myth-red concentration */
+ u16 gd; /* Total green channel concentration */
+ u16 gw; /* Myth-green concentration */
+ u16 bd; /* Total blue channel concentration */
+ u16 bw; /* Myth-blue concentration */
+ u16 w; /* Water volume */
+ u16 h; /* Height of paper surface */
+};
+
+struct _WetLayer {
+ WetPix *buf;
+ int width;
+ int height;
+ int rowstride;
+};
+
+struct _WetPack {
+ int n_layers;
+ WetLayer **layers;
+};
+
+struct _WetPixDbl {
+ double rd; /* Total red channel concentration */
+ double rw; /* Myth-red concentration */
+ double gd; /* Total green channel concentration */
+ double gw; /* Myth-green concentration */
+ double bd; /* Total blue channel concentration */
+ double bw; /* Myth-blue concentration */
+ double w; /* Water volume */
+ double h; /* Height of paper surface */
+};
+
+void wet_composite(byte * rgb, int rgb_rowstride,
+ WetPix * wet, int wet_rowstride,
+ int width, int height);
+
+void wet_composite_layer(byte * rgb, int rgb_rowstride,
+ WetLayer * layer,
+ int x0, int y0, int width, int height);
+
+void wet_pack_render(byte * rgb, int rgb_rowstride,
+ WetPack * pack,
+ int x0, int y0, int width, int height);
+
+WetLayer *wet_layer_new(int width, int height);
+
+void wet_layer_clear(WetLayer * layer);
+
+WetPack *wet_pack_new(int width, int height);
+
+void wet_pix_to_double(WetPixDbl * dst, WetPix * src);
+
+void wet_pix_from_double(WetPix * dst, WetPixDbl * src);
diff --git a/krita/colorspaces/wet/wetdreams/wettexture.c b/krita/colorspaces/wet/wetdreams/wettexture.c
new file mode 100644
index 00000000..620ad8b5
--- /dev/null
+++ b/krita/colorspaces/wet/wetdreams/wettexture.c
@@ -0,0 +1,84 @@
+/* synthesize a surface texture */
+
+#include <stdlib.h>
+#include <math.h>
+#include "wetpix.h"
+
+void
+wet_layer_maketexture(WetLayer * layer,
+ double height, double blurh, double blurv)
+{
+ int x, y;
+ int width = layer->width;
+ int lheight = layer->height;
+ int rowstride = layer->rowstride;
+ WetPix *wet_line = layer->buf;
+ double hscale = 128 * height / RAND_MAX;
+ int lh;
+ int ibh, ibv;
+
+ ibh = floor(256 * blurh + 0.5);
+#ifdef VERBOSE
+ g_print("ibh = %d\n", ibh);
+#endif
+ ibv = floor(256 * blurv + 0.5);
+
+ for (y = 0; y < lheight; y++) {
+ for (x = 0; x < width; x++) {
+ wet_line[x].h = floor(128 + hscale * rand());
+ }
+ /* g_print ("%d\n", wet_line[0].h); */
+ wet_line += rowstride;
+ }
+
+ wet_line = layer->buf;
+ for (y = 0; y < lheight; y++) {
+ lh = wet_line[0].h;
+ for (x = 1; x < width; x++) {
+ wet_line[x].h +=
+ ((lh - wet_line[x].h) * ibh + 128) >> 8;
+ lh = wet_line[x].h;
+ }
+ wet_line += rowstride;
+ }
+
+#if 0
+ for (x = 0; x < width; x++) {
+ wet_line = layer->buf + x;
+ lh = wet_line[0].h;
+ for (y = 1; y < lheight; y++) {
+ wet_line += rowstride;
+ wet_line[0].h +=
+ ((lh - wet_line[0].h) * ibv + 128) >> 8;
+ lh = wet_line[0].h;
+ }
+ }
+#endif
+}
+
+void wet_layer_clone_texture(WetLayer * dst, WetLayer * src)
+{
+ int x, y;
+ int width = src->width;
+ WetPix *dst_line = dst->buf;
+ WetPix *src_line = src->buf;
+
+ for (y = 0; y < src->height; y++) {
+ for (x = 0; x < width; x++) {
+ dst_line[x].h = src_line[x].h;
+ }
+ dst_line += dst->rowstride;
+ src_line += src->rowstride;
+ }
+}
+
+void
+wet_pack_maketexture(WetPack * pack,
+ double height, double blurh, double blurv)
+{
+ int i;
+
+ wet_layer_maketexture(pack->layers[0], height, blurh, blurv);
+ for (i = 1; i < pack->n_layers; i++)
+ wet_layer_clone_texture(pack->layers[i], pack->layers[0]);
+}
diff --git a/krita/colorspaces/wet/wetdreams/wettexture.h b/krita/colorspaces/wet/wetdreams/wettexture.h
new file mode 100644
index 00000000..c3cbc0d2
--- /dev/null
+++ b/krita/colorspaces/wet/wetdreams/wettexture.h
@@ -0,0 +1,9 @@
+/* synthesize a surface texture */
+
+void wet_layer_maketexture(WetLayer * layer,
+ double height, double blurh, double blurv);
+
+void wet_layer_clone_texture(WetLayer * dst, WetLayer * src);
+
+void wet_pack_maketexture(WetPack * pack,
+ double height, double blurh, double blurv);
diff --git a/krita/colorspaces/wet/wetphysicsfilter.cc b/krita/colorspaces/wet/wetphysicsfilter.cc
new file mode 100644
index 00000000..195a17be
--- /dev/null
+++ b/krita/colorspaces/wet/wetphysicsfilter.cc
@@ -0,0 +1,424 @@
+/*
+ * This file is part of the KDE project
+ *
+ * Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
+ * 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 <vector>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <kis_iterators_pixel.h>
+#include <kis_filter_registry.h>
+#include <kis_debug_areas.h>
+#include <kis_types.h>
+#include <kis_paint_device.h>
+#include <kis_debug_areas.h>
+#include "wetphysicsfilter.h"
+
+/*
+ * [11:14] <boud> CyrilleB: I think I know why watercolor drying creates that funny pattern (you can see it if you have a very wet canvas with lots of paint and leave it drying for a while): our dry filter must have an off-by-one error to the right and bottom, which is also why the buggy drying didn't remove all of previously applied paint but left a fringe.
+ * [11:14] <pippin> does the drying behave kind of like an error diffusion?
+ * [11:14] <pippin> (it sounds like error diffusion artifacts,.)
+ * [11:15] <boud> pippin: not sure what error diffusion is...
+ * [11:15] <pippin> used for digital halftoning
+ * [11:15] <pippin> take a greyscale image,.. you want to end up with binary (could be less, but let's use 1bit result)
+ * [11:15] <CyrilleB> boud: the funny pattern is also in wetdreams when you disable wetness visualisation
+ * [11:15] <boud> CyrilleB: I don't mean the checkerboard pattern
+ * [11:16] <pippin> then for each pixel you calculate the difference between the current value and the desired value (0 or 255)
+ * [11:16] <CyrilleB> boud: which one then ?
+ * [11:16] <pippin> the error is distributed to the neighbour pixels (to the right, down and down to the left in pixels which have not yet been processed
+ * [11:16] <pippin> )
+ * [11:16] <boud> CyrilleB: it's only apparent when you let something dry for some time, it looks like meandering snakes (like the old game "snake")
+ * [11:16] <CyrilleB> pippin: somehow yes
+ * [11:16] <boud> pippin: that is possible
+ * [11:17] <pippin> boud: this leads to "bleeding" of data to the right and down,..
+ * [11:17] <boud> pippin: but on the other hand, when the filter worked on the old tiles (empty ones) it also left a fringe of color.
+ * [11:17] <pippin> having the "error" spread in different directions on each iteration might fix something like this,.
+ * [11:18] <boud> Which leads me to think it's an off-by one.
+ * [11:25] <boud> No, it isn't off by one. Then pippin must be right.
+ * [11:26] <pippin> if I am, this is a fun debug session, not even having the code or the visual results available,. just hanging around on irc :)
+ * [11:27] <boud> Well, I don't have time to investigate right now, but it sounds very plausible.
+ * [11:27] <CyrilleB> pippin: :)
+ * [11:28] <boud> of course, the code _is_ available :-)
+ * [11:28] <pippin> if there is some form of diffusion matrix that is directional around the current pixel,. having that mask rotate depending on the modulus of the current iteration # should cancel such an effect out
+ */
+WetPhysicsFilter::WetPhysicsFilter()
+ : KisFilter(id(), "artistic", i18n("Dry the Paint"))
+{
+ m_adsorbCount = 0;
+}
+
+void WetPhysicsFilter::process(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisFilterConfiguration* /*config*/, const QRect& rect)
+{
+ kdDebug() << "Physics processing " << src->name() << m_adsorbCount << endl;
+ // XXX: It would be nice be able to interleave this, instead of
+ // having the same loop over our pixels three times.
+ flow(src, dst, rect);
+ if (m_adsorbCount++ == 2) {
+// XXX I think we could combine dry and adsorb, yes
+ adsorb(src, dst, rect);
+ dry(src, dst, rect);
+ m_adsorbCount = 0;
+ }
+ setProgressDone(); // Must be called even if you don't really support progression
+}
+
+
+void WetPhysicsFilter::flow(KisPaintDeviceSP src, KisPaintDeviceSP /*dst*/, const QRect & r)
+{
+ /* XXX: Is this like a convolution operation? BSAR */
+ int width = r.width();
+ int height = r.height();
+
+ kdDebug() << "Flowing: " << r << endl;
+
+ /* width of a line in a layer in pixel units, not in bytes -- used to move to the next
+ line in the fluid masks below */
+ int rs = width; // rowstride
+
+ double * flow_t = new double[width * height];
+ Q_CHECK_PTR(flow_t);
+
+ double * flow_b = new double[width * height];
+ Q_CHECK_PTR(flow_b);
+
+ double * flow_l = new double[width * height];
+ Q_CHECK_PTR(flow_l);
+
+ double * flow_r = new double[width * height];
+ Q_CHECK_PTR(flow_r);
+
+ double * fluid = new double[width * height];
+ Q_CHECK_PTR(fluid);
+
+ double * outflow = new double[width * height];
+ Q_CHECK_PTR(outflow);
+
+ // Height of the paper surface. Do we also increase height because of paint deposits?
+ int my_height;
+
+ // Flow to the top, bottom, left, right of the currentpixel
+ double ft, fb, fl, fr;
+
+ // Temporary pixel constructs
+ WetPixDbl wet_mix, wet_tmp;
+
+ // XXX If the flow touches areas that have not been initialized with a height field yet,
+ // create a heigth field.
+
+ // We need three iterators, because we're working on a five-point convolution kernel (no corner pixels are being used)
+
+ // First iteration: compute fluid deposits around the paper.
+ Q_INT32 dx, dy;
+ dx = r.x();
+ dy = r.y();
+
+ int ix = width + 1; // keeps track where we are in the one-dimensional arrays
+
+ for (Q_INT32 y2 = 1; y2 < height - 1; ++y2) {
+ KisHLineIteratorPixel srcIt = src->createHLineIterator(dx, dy + y2, width, false);
+ KisHLineIteratorPixel upIt = src->createHLineIterator(dx + 1, dy + y2 - 1, width - 2, false);
+ KisHLineIteratorPixel downIt = src->createHLineIterator(dx + 1, dy + y2 + 1, width - 2, false);
+
+ // .paint is the first field in our wetpack, so this is ok (even though not nice)
+ WetPix left = *(reinterpret_cast<WetPix*>(srcIt.rawData()));
+ ++srcIt;
+ WetPix current = *(reinterpret_cast<WetPix*>(srcIt.rawData()));
+ ++srcIt;
+ WetPix right = *(reinterpret_cast<WetPix*>(srcIt.rawData()));
+ WetPix up, down;
+
+ while (!srcIt.isDone()) {
+ up = *(reinterpret_cast<WetPix*>(upIt.rawData()));
+ down = *(reinterpret_cast<WetPix*>(downIt.rawData()));
+
+ if (current.w > 0) {
+ my_height = current.h + current.w;
+ ft = (up.h + up.w) - my_height;
+ fb = (down.h + down.w) - my_height;
+ fl = (left.h + left.w) - my_height;
+ fr = (right.h + right.w) - my_height;
+
+ fluid[ix] = 0.4 * sqrt(current.w * 1.0 / 255.0);
+
+ /* smooth out the flow a bit */
+ flow_t[ix] = CLAMP(0.1 * (10 + ft * 0.75 - fb * 0.25), 0, 1);
+
+ flow_b[ix] = CLAMP(0.1 * (10 + fb * 0.75 - ft * 0.25), 0, 1);
+
+ flow_l[ix] = CLAMP(0.1 * (10 + fl * 0.75 - fr * 0.25), 0, 1);
+
+ flow_r[ix] = CLAMP(0.1 * (10 + fr * 0.75 - fl * 0.25), 0, 1);
+
+ outflow[ix] = 0;
+ }
+
+ ++srcIt;
+ ++upIt;
+ ++downIt;
+ ix++;
+ left = current;
+ current = right;
+ right = *(reinterpret_cast<WetPix*>(srcIt.rawData()));
+ }
+ ix+=2; // one for the last pixel on the line, and one for the first of the next line
+ }
+ // Second iteration: Reduce flow in dry areas
+ ix = width + 1;
+
+ for (Q_INT32 y2 = 1; y2 < height - 1; ++y2) {
+ KisHLineIteratorPixel srcIt = src->createHLineIterator(dx + 1, dy + y2, width - 2, false);
+ while (!srcIt.isDone()) {
+ if ((reinterpret_cast<WetPix*>(srcIt.rawData()))->w > 0) {
+ /* reduce flow in dry areas */
+ flow_t[ix] *= fluid[ix] * fluid[ix - rs];
+ outflow[ix - rs] += flow_t[ix];
+ flow_b[ix] *= fluid[ix] * fluid[ix + rs];
+ outflow[ix + rs] += flow_b[ix];
+ flow_l[ix] *= fluid[ix] * fluid[ix - 1];
+ outflow[ix - 1] += flow_l[ix];
+ flow_r[ix] *= fluid[ix] * fluid[ix + 1];
+ outflow[ix + 1] += flow_r[ix];
+ }
+ ++srcIt;
+ ix++;
+ }
+ ix += 2;
+ }
+
+ // Third iteration: Combine the paint from the flow areas.
+ ix = width + 1;
+ for (Q_INT32 y2 = 1; y2 < height - 1; ++y2) {
+ KisHLineIteratorPixel srcIt = src->createHLineIterator(dx, dy + y2, width, false);
+ KisHLineIteratorPixel upIt = src->createHLineIterator(dx + 1, dy + y2 - 1, width - 2, false);
+ KisHLineIteratorPixel downIt = src->createHLineIterator(dx + 1, dy + y2 + 1, width - 2, false);
+
+ KisHLineIteratorPixel dstIt = src->createHLineIterator(dx + 1, dy + y2, width - 2, true);
+
+ WetPix left = *(reinterpret_cast<const WetPix*>(srcIt.oldRawData()));
+ ++srcIt;
+ WetPix current = *(reinterpret_cast<const WetPix*>(srcIt.oldRawData()));
+ ++srcIt;
+ WetPix right = *(reinterpret_cast<const WetPix*>(srcIt.oldRawData()));
+ WetPix up, down;
+
+ while (!srcIt.isDone()) {
+ up = *(reinterpret_cast<const WetPix*>(upIt.oldRawData()));
+ down = *(reinterpret_cast<const WetPix*>(downIt.oldRawData()));
+
+ if ((reinterpret_cast<WetPix*>(srcIt.rawData()))->w > 0) {
+ reducePixel(&wet_mix, &current, 1 - outflow[ix]);
+ reducePixel(&wet_tmp, &up, flow_t[ix]);
+ combinePixels(&wet_mix, &wet_mix, &wet_tmp);
+ reducePixel(&wet_tmp, &down, flow_b[ix]);
+ combinePixels(&wet_mix, &wet_mix, &wet_tmp);
+ reducePixel(&wet_tmp, &left, flow_l[ix]);
+ combinePixels(&wet_mix, &wet_mix, &wet_tmp);
+ reducePixel(&wet_tmp, &right, flow_r[ix]);
+ combinePixels(&wet_mix, &wet_mix, &wet_tmp);
+ WetPix* target = reinterpret_cast<WetPix*>(dstIt.rawData());
+ wetPixFromDouble(target, &wet_mix);
+ }
+ ++srcIt;
+ ++dstIt;
+ ++upIt;
+ ++downIt;
+ ix++;
+
+ left = current;
+ current = right;
+ right = *(reinterpret_cast<const WetPix*>(srcIt.oldRawData()));
+ }
+ ix += 2;
+ }
+
+ delete[] flow_t;
+ delete[] flow_b;
+ delete[] flow_l;
+ delete[] flow_r;
+ delete[] fluid;
+ delete[] outflow;
+}
+
+void WetPhysicsFilter::dry(KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect & r)
+{
+ kdDebug () << "Drying " << r << endl;
+ for (Q_INT32 y = 0; y < r.height(); y++) {
+ KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), r.y() + y, r.width(), false);
+ KisHLineIteratorPixel dstIt = dst->createHLineIterator(r.x(), r.y() + y, r.width(), true);
+
+ Q_UINT16 w;
+ while (!srcIt.isDone()) {
+ // Two wet pixels in one KisWetColorSpace pixels.
+
+ WetPack pack = *(reinterpret_cast<WetPack*>(srcIt.rawData()));
+ WetPix* p = &(pack.paint);
+
+ w = p->w; // no -1 here because we work on unsigned ints!
+
+ if (w > 0)
+ p->w = w - 1;
+ else
+ p->w = 0;
+
+ *(reinterpret_cast<WetPack*>(dstIt.rawData())) = pack;
+
+ ++dstIt;
+ ++srcIt;
+ }
+ }
+}
+
+void WetPhysicsFilter::adsorb(KisPaintDeviceSP src, KisPaintDeviceSP /*dst*/, const QRect & r)
+{
+ kdDebug() << "Adsorbing " << r << endl;
+ for (Q_INT32 y = 0; y < r.height(); y++) {
+ KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), r.y() + y, r.width(), true);
+
+ double ads;
+
+ WetPixDbl wet_top;
+ WetPixDbl wet_bot;
+
+ WetPack * pack;
+ Q_UINT16 w;
+
+ while (!srcIt.isDone()) {
+ // Two wet pixels in one KisWetColorSpace pixels.
+ pack = reinterpret_cast<WetPack*>(srcIt.rawData());
+ WetPix* paint = &pack->paint;
+ WetPix* adsorb = &pack->adsorb;
+
+ /* do adsorption */
+ w = paint->w;
+
+ if (w == 0) {
+ ++srcIt;
+ }
+ else {
+
+ ads = 0.5 / QMAX(w, 1);
+
+ wetPixToDouble(&wet_top, paint);
+ wetPixToDouble(&wet_bot, adsorb);
+
+ mergePixel(&wet_bot, &wet_top, ads, &wet_bot);
+ wetPixFromDouble(adsorb, &wet_bot);
+
+ paint->rd = (Q_UINT16) (paint->rd*(1 - ads));
+ paint->rw = (Q_UINT16) (paint->rw*(1 - ads));
+ paint->gd = (Q_UINT16) (paint->gd*(1 - ads));
+ paint->gw = (Q_UINT16) (paint->gw*(1 - ads));
+ paint->bd = (Q_UINT16) (paint->bd*(1 - ads));
+ paint->bw = (Q_UINT16) (paint->bw*(1 - ads));
+
+ ++srcIt;
+ }
+ }
+ }
+}
+
+void WetPhysicsFilter::combinePixels (WetPixDbl *dst, WetPixDbl *src1, WetPixDbl *src2)
+{
+ dst->rd = src1->rd + src2->rd;
+ dst->rw = src1->rw + src2->rw;
+ dst->gd = src1->gd + src2->gd;
+ dst->gw = src1->gw + src2->gw;
+ dst->bd = src1->bd + src2->bd;
+ dst->bw = src1->bw + src2->bw;
+ dst->w = src1->w + src2->w;
+}
+
+void WetPhysicsFilter::dilutePixel (WetPixDbl *dst, WetPix *src, double dilution)
+{
+ double scale = dilution * (1.0 / 8192.0);
+
+ dst->rd = src->rd * scale;
+ dst->rw = src->rw * scale;
+ dst->gd = src->gd * scale;
+ dst->gw = src->gw * scale;
+ dst->bd = src->bd * scale;
+ dst->bw = src->bw * scale;
+ dst->w = src->w * (1.0 / 8192.0);
+ dst->h = src->h * (1.0 / 8192.0);
+}
+
+
+void WetPhysicsFilter::reducePixel (WetPixDbl *dst, WetPix *src, double dilution)
+{
+ dilutePixel(dst, src, dilution);
+ dst->w *= dilution;
+}
+
+void WetPhysicsFilter::mergePixel (WetPixDbl *dst, WetPixDbl *src1, double dilution1,
+ WetPixDbl *src2)
+{
+ double d1, w1, d2, w2;
+ double ed1, ed2;
+
+ if (src1->rd < 1e-4) {
+ dst->rd = src2->rd;
+ dst->rw = src2->rw;
+ } else if (src2->rd < 1e-4) {
+ dst->rd = src1->rd * dilution1;
+ dst->rw = src1->rw * dilution1;
+ } else {
+ d1 = src1->rd;
+ w1 = src1->rw;
+ d2 = src2->rd;
+ w2 = src2->rw;
+ dst->rd = d1 * dilution1 + d2;
+ ed1 = exp(-d1 * dilution1);
+ ed2 = exp(-d2);
+ dst->rw = dst->rd * ((1 - ed1) * w1 / d1 + ed1 * (1 - ed2) * w2 / d2) / (1 - ed1 * ed2);
+ }
+
+ if (src1->gd < 1e-4) {
+ dst->gd = src2->gd;
+ dst->gw = src2->gw;
+ } else if (src2->gd < 1e-4) {
+ dst->gd = src1->gd * dilution1;
+ dst->gw = src1->gw * dilution1;
+ } else {
+ d1 = src1->gd;
+ w1 = src1->gw;
+ d2 = src2->gd;
+ w2 = src2->gw;
+ dst->gd = d1 * dilution1 + d2;
+ ed1 = exp(-d1 * dilution1);
+ ed2 = exp(-d2);
+ dst->gw = dst->gd * ((1 - ed1) * w1 / d1 + ed1 * (1 - ed2) * w2 / d2) / (1 - ed1 * ed2);
+ }
+
+ if (src1->bd < 1e-4) {
+ dst->bd = src2->bd;
+ dst->bw = src2->bw;
+ } else if (src2->bd < 1e-4) {
+ dst->bd = src1->bd * dilution1;
+ dst->bw = src1->bw * dilution1;
+ } else {
+ d1 = src1->bd;
+ w1 = src1->bw;
+ d2 = src2->bd;
+ w2 = src2->bw;
+ dst->bd = d1 * dilution1 + d2;
+ ed1 = exp(-d1 * dilution1);
+ ed2 = exp(-d2);
+ dst->bw = dst->bd * ((1 - ed1) * w1 / d1 + ed1 * (1 - ed2) * w2 / d2) / (1 - ed1 * ed2);
+ }
+}
diff --git a/krita/colorspaces/wet/wetphysicsfilter.h b/krita/colorspaces/wet/wetphysicsfilter.h
new file mode 100644
index 00000000..a02f8402
--- /dev/null
+++ b/krita/colorspaces/wet/wetphysicsfilter.h
@@ -0,0 +1,87 @@
+/*
+ * This file is part of Krita
+ *
+ * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * 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 WET_PHYSICS_FILTER_H
+#define WET_PHYSICS_FILTER_H
+
+#include <klocale.h>
+
+#include <kis_filter.h>
+#include <kis_types.h>
+
+#include "kis_wet_colorspace.h"
+
+class KisID;
+class QRect;
+
+
+/**
+ * The wet physics filter must be run regularly from a timer
+ * or preferably from a thread. Every time the filter is processed
+ * the paint flows; every third time, the paint is adsorbed unto the
+ * lower pixel and dried.
+ *
+ * Note: this might also be implemented as three separate filters.
+ * That might even be better.
+ */
+class WetPhysicsFilter: public KisFilter
+{
+public:
+ WetPhysicsFilter();
+public:
+ virtual void process(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisFilterConfiguration*, const QRect& r);
+
+ static inline KisID id() { return KisID("wetphysics", i18n("Watercolor Physics Simulation Filter")); };
+
+ virtual bool supportsPainting() { return false; }
+ virtual bool supportsPreview() { return false; }
+ virtual ColorSpaceIndependence colorSpaceIndependence() { return FULLY_INDEPENDENT; };
+ virtual bool workWith(KisColorSpace* cs) { return (cs->id() == KisID("WET")); };
+
+private:
+
+ void flow(KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect & r);
+ void dry(KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect & r);
+
+ // Move stuff from the upperlayer to the lower layer. This is filter-level stuff.
+ void adsorb(KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect & r);
+
+ // NOTE: this does not set the height fields
+ void combinePixels (WetPixDbl *dst, WetPixDbl *src1, WetPixDbl *src2);
+ void dilutePixel (WetPixDbl *dst, WetPix *src, double dilution);
+ void reducePixel (WetPixDbl *dst, WetPix *src, double dilution);
+
+ /*
+ * Allows visualization of adsorption by rotating the hue 120 degrees
+ * layer-merge combining. src1 is the top layer
+ *
+ * This does not set the dst h or w fields.
+ */
+ void mergePixel (WetPixDbl *dst, WetPixDbl *src1, double dilution1, WetPixDbl *src2);
+
+
+private:
+
+ Q_INT32 m_adsorbCount;
+
+
+};
+
+#endif
diff --git a/krita/colorspaces/wet/wetplugin.rc b/krita/colorspaces/wet/wetplugin.rc
new file mode 100644
index 00000000..39cb6f81
--- /dev/null
+++ b/krita/colorspaces/wet/wetplugin.rc
@@ -0,0 +1,8 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui library="kritawetplugin" version="1">
+<MenuBar>
+<Menu name="View"><text>&amp;View</text>
+ <Action name="wetnessvisualisation"/>
+</Menu>
+</MenuBar>
+</kpartgui>