/* * Copyright (c) 2005 Boudewijn Rempt * * 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 #include #include #include LCMS_HEADER #include #include #include #include "kis_color_conversions.h" #include "kis_abstract_colorspace.h" #include "kis_colorspace_registry.h" #include "kis_image.h" #include "kis_wet_sticky_colorspace.h" #include "kis_integer_maths.h" #include "kis_types.h" #include "kis_channelinfo.h" #define NOWSDEBUG using namespace WetAndSticky; enum WetStickyChannelIndex { BLUE_CHANNEL_INDEX, GREEN_CHANNEL_INDEX, RED_CHANNEL_INDEX, ALPHA_CHANNEL_INDEX, HUE_CHANNEL_INDEX, SATURATION_CHANNEL_INDEX, LIGHTNESS_CHANNEL_INDEX, LIQUID_CONTENT_CHANNEL_INDEX, DRYING_RATE_CHANNEL_INDEX, MISCIBILITY_CHANNEL_INDEX, GRAVITATIONAL_DIRECTION_INDEX, GRAVITATIONAL_STRENGTH_CHANNEL_INDEX, ABSORBANCY_CHANNEL_INDEX, PAINT_VOLUME_CHANNEL_INDEX }; KisWetStickyColorSpace::KisWetStickyColorSpace() : KisAbstractColorSpace(KisID("W&S", i18n("Wet & Sticky")), 0, icMaxEnumData) { TQ_INT32 pos = 0; // Basic representational definition m_channels.push_back(new KisChannelInfo(i18n("Blue"), "B", pos, COLOR, 1)); m_channels.push_back(new KisChannelInfo(i18n("Green"), "G", ++pos, COLOR, 1)); m_channels.push_back(new KisChannelInfo(i18n("Red"), "R", ++pos, COLOR, 1)); m_channels.push_back(new KisChannelInfo(i18n("Alpha"), "A", ++pos, ALPHA, 1)); // Paint definition m_channels.push_back(new KisChannelInfo(i18n("Hue"), "H", ++pos, COLOR, sizeof(float))); m_channels.push_back(new KisChannelInfo(i18n("Saturation"), "S", pos+=sizeof(float) , COLOR, sizeof(float))); m_channels.push_back(new KisChannelInfo(i18n("Lightness"), "L", pos+=sizeof(float), COLOR, sizeof(float))); m_channels.push_back(new KisChannelInfo(i18n("Liquid Content"), "Q", pos+=sizeof(float), SUBSTANCE, 1)); m_channels.push_back(new KisChannelInfo(i18n("Drying Rate"), "D", ++pos, SUBSTANCE, 1)); m_channels.push_back(new KisChannelInfo(i18n("Miscibility"), "M", ++pos, SUBSTANCE, 1)); // Substrate definition m_channels.push_back(new KisChannelInfo(i18n("Gravitational Direction"), "Gd", ++pos, SUBSTRATE, sizeof(enumDirection))); m_channels.push_back(new KisChannelInfo(i18n("Gravitational Strength"), "Gs", pos+=sizeof(enumDirection), SUBSTRATE, 1)); m_channels.push_back(new KisChannelInfo(i18n("Absorbency"), "Ad", ++pos, SUBSTRATE, 1)); m_channels.push_back(new KisChannelInfo(i18n("Paint Volume"), "V", ++pos, SUBSTANCE, 1)); m_alphaPos = 3; m_alphaSize = 1; setDefaultProfile( 0 ); #ifdef WSDEBUG TQValueVector_it it; int i = 0; for (it = m_channels.begin(); it != m_channels.end(); ++it) { KisChannelInfo * ch = (*it); kdDebug(DBG_AREA_CMS) << "Channel: " << ch->name() << ", " << ch->pos() << ", " << i << "\n"; ++i; } kdDebug(DBG_AREA_CMS) << "Size of cell: " << sizeof(CELL) << "\n"; #endif } KisWetStickyColorSpace::~KisWetStickyColorSpace() { } void KisWetStickyColorSpace::fromTQColor(const TQColor& c, TQ_UINT8 *dst, KisProfile * profile) { CELL_PTR p = (CELL_PTR) dst; TQ_UINT8 r, g, b; r = c.red(); g = c.green(); b = c.blue(); p -> red = r; p -> green = g; p -> blue = b; p -> alpha = OPACITY_OPAQUE; rgb_to_hls(r, g, b, &p->hue, &p->lightness, &p->saturation); p -> liquid_content = 0; p -> drying_rate = 0; p -> miscibility = 0; p -> direction = DOWN; p -> strength = 10; p -> absorbancy = 10; p -> volume = 0; #ifdef WSDEBUG kdDebug(DBG_AREA_CMS) << "qcolor: " << " r: " << c.red() << " b: " << c.blue() << " g: " << c.red() << " native color: (" << TQString().setNum(p->red) << ", " << TQString().setNum(p->green) << ", " << TQString().setNum(p->blue) << ", " << TQString().setNum(p->alpha) << ") " << ", hls: (" << p->hue << ", " << p->lightness << ", " << p->saturation << ")\n"; #endif } void KisWetStickyColorSpace::fromTQColor(const TQColor& c, TQ_UINT8 opacity, TQ_UINT8 *dst, KisProfile * profile) { CELL_PTR p = (CELL_PTR) dst; TQ_UINT8 r, g, b; r = c.red(); g = c.green(); b = c.blue(); p -> red = r; p -> green = g; p -> blue = b; p -> alpha = opacity; rgb_to_hls(r, g, b, &p -> hue, &p -> lightness, &p -> saturation); p ->liquid_content = 0; p ->drying_rate = 0; p ->miscibility = 0; p -> direction = DOWN; p -> strength = 10; p -> absorbancy = 10; p -> volume = 0; #ifdef WSDEBUG kdDebug(DBG_AREA_CMS) << "qcolor: " << " r: " << c.red() << " b: " << c.blue() << " g: " << c.red() << " opacity: " << opacity << " native color: (" << TQString().setNum(p->red) << ", " << TQString().setNum(p->green) << ", " << TQString().setNum(p->blue) << ", " << TQString().setNum(p->alpha) << ") " << ", hls: (" << p->hue << ", " << p->lightness << ", " << p->saturation << ")\n"; #endif } void KisWetStickyColorSpace::toTQColor(const TQ_UINT8 *src, TQColor *c, KisProfile * profile) { CELL_PTR p = (CELL_PTR) src; c -> setRgb(p -> red, p -> green, p -> blue); #ifdef WSDEBUG kdDebug(DBG_AREA_CMS) << "Created qcolor from wet & sticky: " << " r: " << c->red() << " b: " << c->blue() << " g: " << c->red() << "\n"; #endif } void KisWetStickyColorSpace::toTQColor(const TQ_UINT8 *src, TQColor *c, TQ_UINT8 *opacity, KisProfile * profile) { CELL_PTR p = (CELL_PTR) src; c -> setRgb(p -> red, p -> green, p -> blue); *opacity = p -> alpha; #ifdef WSDEBUG kdDebug(DBG_AREA_CMS) << "Created qcolor from wet & sticky: " << " r: " << c->red() << " b: " << c->blue() << " g: " << c->red() << "\n"; #endif } KisPixelRO KisWetStickyColorSpace::toKisPixelRO(const TQ_UINT8 *src, KisProfile * profile) { return KisPixelRO (src, src, this, profile); } KisPixel KisWetStickyColorSpace::toKisPixel(TQ_UINT8 *src, KisProfile * profile) { return KisPixel (src, src, this, profile); } void KisWetStickyColorSpace::mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const { } TQ_UINT8 KisWetStickyColorSpace::getAlpha(const TQ_UINT8 *pixel) const { return ((CELL_PTR)pixel)->alpha; } void KisWetStickyColorSpace::setAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels) const { while (nPixels > 0) { ((CELL_PTR)pixels)->alpha = alpha; --nPixels; pixels+=pixelSize(); } } void KisWetStickyColorSpace::applyAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels) { } void KisWetStickyColorSpace::applyInverseAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels) { } TQ_UINT8 KisWetStickyColorSpace::scaleToU8(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos) { return 0; } TQ_UINT16 KisWetStickyColorSpace::scaleToU16(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos) { return 0; } TQValueVector KisWetStickyColorSpace::channels() const { return m_channels; } bool KisWetStickyColorSpace::hasAlpha() const { return true; } TQ_INT32 KisWetStickyColorSpace::nChannels() const { return 14; } TQ_INT32 KisWetStickyColorSpace::nColorChannels() const { return 3; } TQ_INT32 KisWetStickyColorSpace::nSubstanceChannels() const { return 4; } TQ_INT32 KisWetStickyColorSpace::pixelSize() const { return sizeof(CELL); } TQImage KisWetStickyColorSpace::convertToTQImage(const TQ_UINT8 *data, TQ_INT32 width, TQ_INT32 height, KisProfile * /*srcProfile*/, KisProfile * /*dstProfile*/, TQ_INT32 /*renderingIntent*/, float /*exposure*/) { TQImage img(width, height, 32, 0, TQImage::LittleEndian); TQ_INT32 i = 0; uchar *j = img.bits(); CELL_PTR p = (CELL_PTR) data; while ( i < width * height) { const TQ_UINT8 PIXEL_BLUE = 0; const TQ_UINT8 PIXEL_GREEN = 1; const TQ_UINT8 PIXEL_RED = 2; const TQ_UINT8 PIXEL_ALPHA = 3; *( j + PIXEL_ALPHA ) = p -> alpha; *( j + PIXEL_RED ) = p -> red; *( j + PIXEL_GREEN ) = p -> green; *( j + PIXEL_BLUE ) = p -> blue; p++; i++; j += 4; // Because we're hard-coded 32 bits deep, 4 bytes } return img; } bool KisWetStickyColorSpace::convertPixelsTo(const TQ_UINT8 * src, KisProfile * /*srcProfile*/, TQ_UINT8 * dst, KisAbstractColorSpace * dstColorSpace, KisProfile * dstProfile, TQ_UINT32 numPixels, TQ_INT32 /*renderingIntent*/) { TQ_INT32 dSize = dstColorSpace -> pixelSize(); TQ_INT32 sSize = pixelSize(); TQ_UINT32 j = 0; TQ_UINT32 i = 0; TQColor c; CELL_PTR cp; while ( i < numPixels ) { cp = (CELL_PTR) (src + i); c.setRgb(cp -> red, cp -> green, cp -> blue); dstColorSpace -> fromTQColor(c, cp -> alpha, (dst + j), dstProfile); i += sSize; j += dSize; } return true; } void KisWetStickyColorSpace::bitBlt(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *mask, TQ_INT32 maskRowStride, TQ_UINT8 opacity, TQ_INT32 rows, TQ_INT32 cols, const KisCompositeOp& op) { switch (op.op()) { case COMPOSITE_UNDEF: // Undefined == no composition break; case COMPOSITE_OVER: compositeOver(dst, dstRowStride, src, srcRowStride, mask, maskRowStride, rows, cols, opacity); break; case COMPOSITE_COPY: default: compositeCopy(dst, dstRowStride, src, srcRowStride, mask, maskRowStride, rows, cols, opacity); break; } } void KisWetStickyColorSpace::compositeOver(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *maskRowStart, TQ_INT32 maskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT8 opacity) { // XXX: This is basically the same as with rgb and used to composite layers for Composition for // painting works differently while (rows > 0) { const TQ_UINT8 *src = srcRowStart; TQ_UINT8 *dst = dstRowStart; const TQ_UINT8 *mask = maskRowStart; TQ_INT32 columns = numColumns; while (columns > 0) { CELL_PTR dstCell = (CELL_PTR) dst; CELL_PTR srcCell = (CELL_PTR) src; #ifdef WSDEBUG kdDebug(DBG_AREA_CMS) << "Source: " << rows << ", " << columns << " color: " << srcCell->red << ", " << srcCell->blue << ", " << srcCell->green << ", " << srcCell->alpha << ", " << srcCell->volume << "\n"; kdDebug(DBG_AREA_CMS) << "Destination: " << rows << ", " << columns << " color: " << dstCell->red << ", " << dstCell->blue << ", " << dstCell->green << ", " << dstCell->alpha << ", " << dstCell->volume << "\n"; #endif TQ_UINT8 srcAlpha = srcCell->alpha; // apply the alphamask if(mask != 0) { if(*mask != OPACITY_OPAQUE) srcAlpha = UINT8_MULT(srcAlpha, *mask); mask++; } if (srcAlpha != OPACITY_TRANSPARENT) { if (opacity != OPACITY_OPAQUE) { srcAlpha = UINT8_MULT(srcCell->alpha, opacity); } if (srcAlpha == OPACITY_OPAQUE) { memcpy(dst, src, 3); // XXX: First three bytes for rgb? } else { TQ_UINT8 dstAlpha = dstCell->alpha; TQ_UINT8 srcBlend; if (dstAlpha == OPACITY_OPAQUE) { srcBlend = srcAlpha; } else { TQ_UINT8 newAlpha = dstAlpha + UINT8_MULT(OPACITY_OPAQUE - dstAlpha, srcAlpha); dstCell->alpha = newAlpha; if (newAlpha != 0) { srcBlend = UINT8_DIVIDE(srcAlpha, newAlpha); } else { srcBlend = srcAlpha; } } if (srcBlend == OPACITY_OPAQUE) { memcpy(dst, src, 3); //XXX: First three bytes for rgb? } else { dstCell->red = UINT8_BLEND(srcCell->red, dstCell->red, srcBlend); dstCell->green = UINT8_BLEND(srcCell->green, dstCell->green, srcBlend); dstCell->blue = UINT8_BLEND(srcCell->blue, dstCell->blue, srcBlend); } } } columns--; src += sizeof(CELL); dst += sizeof(CELL); } rows--; srcRowStart += srcRowStride; dstRowStart += dstRowStride; if(maskRowStart) maskRowStart += maskRowStride; } } void KisWetStickyColorSpace::compositeCopy(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *mask, TQ_INT32 maskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT8 opacity) { TQ_INT32 linesize = sizeof(CELL) * columns; TQ_UINT8 *d; const TQ_UINT8 *s; d = dst; s = src; while (rows-- > 0) { memcpy(d, s, linesize); d += dstRowStride; s += srcRowStride; } } KisCompositeOpList KisWetStickyColorSpace::userVisiblecompositeOps() const { KisCompositeOpList list; list.append(KisCompositeOp(COMPOSITE_OVER)); return list; } TQString KisWetStickyColorSpace::channelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const { Q_ASSERT(channelIndex < nChannels()); const CELL *pixel = reinterpret_cast(U8_pixel); switch (channelIndex) { case BLUE_CHANNEL_INDEX: return TQString().setNum(pixel -> blue); case GREEN_CHANNEL_INDEX: return TQString().setNum(pixel -> green); case RED_CHANNEL_INDEX: return TQString().setNum(pixel -> red); case ALPHA_CHANNEL_INDEX: return TQString().setNum(pixel -> alpha); case HUE_CHANNEL_INDEX: return TQString().setNum(pixel -> hue); case SATURATION_CHANNEL_INDEX: return TQString().setNum(pixel -> saturation); case LIGHTNESS_CHANNEL_INDEX: return TQString().setNum(pixel -> lightness); case LIQUID_CONTENT_CHANNEL_INDEX: return TQString().setNum(pixel -> liquid_content); case DRYING_RATE_CHANNEL_INDEX: return TQString().setNum(pixel -> drying_rate); case MISCIBILITY_CHANNEL_INDEX: return TQString().setNum(pixel -> miscibility); case GRAVITATIONAL_DIRECTION_INDEX: { switch (pixel -> direction) { case UP: return i18n("Up"); case DOWN: return i18n("Down"); case LEFT: return i18n("Left"); case RIGHT: return i18n("Right"); default: Q_ASSERT(false); return TQString(); } } case GRAVITATIONAL_STRENGTH_CHANNEL_INDEX: return TQString().setNum(pixel -> strength); case ABSORBANCY_CHANNEL_INDEX: return TQString().setNum(pixel -> absorbancy); case PAINT_VOLUME_CHANNEL_INDEX: return TQString().setNum(pixel -> volume); default: Q_ASSERT(false); return TQString(); } } TQString KisWetStickyColorSpace::normalisedChannelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const { Q_ASSERT(channelIndex < nChannels()); const CELL *pixel = reinterpret_cast(U8_pixel); //XXX: Are these right? switch (channelIndex) { case BLUE_CHANNEL_INDEX: return TQString().setNum(static_cast(pixel -> blue) / UINT8_MAX); case GREEN_CHANNEL_INDEX: return TQString().setNum(static_cast(pixel -> green) / UINT8_MAX); case RED_CHANNEL_INDEX: return TQString().setNum(static_cast(pixel -> red) / UINT8_MAX); case ALPHA_CHANNEL_INDEX: return TQString().setNum(static_cast(pixel -> alpha) / UINT8_MAX); case HUE_CHANNEL_INDEX: return TQString().setNum(pixel -> hue); case SATURATION_CHANNEL_INDEX: return TQString().setNum(pixel -> saturation); case LIGHTNESS_CHANNEL_INDEX: return TQString().setNum(pixel -> lightness); case LIQUID_CONTENT_CHANNEL_INDEX: return TQString().setNum(static_cast(pixel -> liquid_content) / UINT8_MAX); case DRYING_RATE_CHANNEL_INDEX: return TQString().setNum(static_cast(pixel -> drying_rate) / UINT8_MAX); case MISCIBILITY_CHANNEL_INDEX: return TQString().setNum(static_cast(pixel -> miscibility) / UINT8_MAX); case GRAVITATIONAL_DIRECTION_INDEX: { switch (pixel -> direction) { case UP: return i18n("Up"); case DOWN: return i18n("Down"); case LEFT: return i18n("Left"); case RIGHT: return i18n("Right"); default: Q_ASSERT(false); return TQString(); } } case GRAVITATIONAL_STRENGTH_CHANNEL_INDEX: return TQString().setNum(static_cast(pixel -> strength) / UINT8_MAX); case ABSORBANCY_CHANNEL_INDEX: return TQString().setNum(static_cast(pixel -> absorbancy) / UINT8_MAX); case PAINT_VOLUME_CHANNEL_INDEX: return TQString().setNum(static_cast(pixel -> volume) / UINT8_MAX); default: Q_ASSERT(false); return TQString(); } }