diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /krita/colorspaces/wet | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'krita/colorspaces/wet')
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 <[email protected]> + * + * 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 ([email protected]) + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 ([email protected]) + * Copyright (c) 2001-2002 Igor Jansen ([email protected]) + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 ([email protected]) + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 ([email protected]) + * + * 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 ([email protected]) + * + * 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 <[email protected]> + + 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 <[email protected]> + + 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 <[email protected]> + * 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, ¤t, 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 <[email protected]> + * + * 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>&View</text> + <Action name="wetnessvisualisation"/> +</Menu> +</MenuBar> +</kpartgui> |