diff options
Diffstat (limited to 'chalk/colorspaces/wet/kis_wet_colorspace.cpp')
-rw-r--r-- | chalk/colorspaces/wet/kis_wet_colorspace.cpp | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/chalk/colorspaces/wet/kis_wet_colorspace.cpp b/chalk/colorspaces/wet/kis_wet_colorspace.cpp new file mode 100644 index 000000000..b867e01ea --- /dev/null +++ b/chalk/colorspaces/wet/kis_wet_colorspace.cpp @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <limits.h> +#include <stdlib.h> + +#include <config.h> +#include LCMS_HEADER + +#include <tqimage.h> + +#include <tdelocale.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; + TQColor 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 TQColor'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::fromTQColor(const TQColor& c, TQ_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; + TQMap<int, WetPix>::Iterator it; + TQMap<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 TQCOlors 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::fromTQColor(const TQColor& c, TQ_UINT8 /*opacity*/, TQ_UINT8 *dst, KisProfile * /*profile*/) +{ + fromTQColor(c, dst); +} + + TQ_UINT8 KisWetColorSpace::getAlpha(const TQ_UINT8 */*pixel*/) const +{ + return OPACITY_OPAQUE; +} + +void KisWetColorSpace::setAlpha( TQ_UINT8 * /*pixels*/, TQ_UINT8 /*alpha*/, TQ_INT32 /*nPixels*/) const +{ +} + +void KisWetColorSpace::multiplyAlpha( TQ_UINT8 * /*pixels*/, TQ_UINT8 /*alpha*/, TQ_INT32 /*nPixels*/) +{ +} + +void KisWetColorSpace::applyAlphaU8Mask( TQ_UINT8 * /*pixels*/, TQ_UINT8 * /*alpha*/, TQ_INT32 /*nPixels*/) +{ +} + +void KisWetColorSpace::applyInverseAlphaU8Mask( TQ_UINT8 * /*pixels*/, TQ_UINT8 * /*alpha*/, TQ_INT32 /*nPixels*/) +{ +} + + TQ_UINT8 KisWetColorSpace::scaleToU8(const TQ_UINT8 * /*srcPixel*/, TQ_INT32 /*channelPos*/) +{ + return 0; +} + +TQ_UINT16 KisWetColorSpace::scaleToU16(const TQ_UINT8 * /*srcPixel*/, TQ_INT32 /*channelPos*/) +{ + return 0; +} + + +void KisWetColorSpace::toTQColor(const TQ_UINT8 *src, TQColor *c, KisProfile * /*profile*/) +{ + TQ_UINT8 * rgb = new TQ_UINT8[3]; + TQ_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::toTQColor(const TQ_UINT8 *src, TQColor *c, TQ_UINT8 */*opacity*/, KisProfile * /*profile*/) +{ + toTQColor(src, c); +} + +void KisWetColorSpace::mixColors(const TQ_UINT8 **/*colors*/, const TQ_UINT8 */*weights*/, TQ_UINT32 /*nColors*/, TQ_UINT8 */*dst*/) const +{ +} + +TQValueVector<KisChannelInfo *> KisWetColorSpace::channels() const +{ + return m_channels; +} + + TQ_UINT32 KisWetColorSpace::nChannels() const +{ + return 16; +} + + TQ_UINT32 KisWetColorSpace::nColorChannels() const +{ + return 12; +} + + TQ_UINT32 KisWetColorSpace::nSubstanceChannels() const +{ + return 4; +} + + + TQ_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. +TQImage KisWetColorSpace::convertToTQImage(const TQ_UINT8 *data, TQ_INT32 width, TQ_INT32 height, + KisProfile * /*dstProfile*/, + TQ_INT32 /*renderingIntent*/, float /*exposure*/) +{ + + TQImage img(width, height, 32); + + TQ_UINT8 *rgb = (TQ_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 + + TQ_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( TQ_UINT32); // Because the TQImage is 4 bytes deep. + + } + + return img; +} + +void KisWetColorSpace::bitBlt( TQ_UINT8 *dst, + TQ_INT32 dstRowSize, + const TQ_UINT8 *src, + TQ_INT32 srcRowStride, + const TQ_UINT8 */*srcAlphaMask*/, + TQ_INT32 /*maskRowStride*/, + TQ_UINT8 /*opacity*/, + TQ_INT32 rows, + TQ_INT32 cols, + const KisCompositeOp& op) +{ + if (rows <= 0 || cols <= 0) + return; + + TQ_UINT8 *d; + const TQ_UINT8 *s; + + TQ_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 TQ_UINT32[4096]; + TQ_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, TQ_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( TQ_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; +} + +TQString KisWetColorSpace::channelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < nChannels()); + const TQ_UINT16 *pixel = reinterpret_cast<const TQ_UINT16 *>(U8_pixel); + TQ_UINT32 channelPosition = m_channels[channelIndex]->pos(); + + return TQString().setNum(pixel[channelPosition]); +} + +TQString KisWetColorSpace::normalisedChannelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < nChannels()); + const TQ_UINT16 *pixel = reinterpret_cast<const TQ_UINT16 *>(U8_pixel); + TQ_UINT32 channelPosition = m_channels[channelIndex]->pos(); + + return TQString().setNum(static_cast<float>(pixel[channelPosition]) / UINT16_MAX); +} + +TQValueList<KisFilter *> KisWetColorSpace::createBackgroundFilters() +{ + TQValueList<KisFilter *> filterList; + KisFilter * f = new WetPhysicsFilter(); + filterList << f; + return filterList; +} |