diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-06-26 00:41:16 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-06-26 00:41:16 +0000 |
commit | 698569f8428ca088f764d704034a1330517b98c0 (patch) | |
tree | bf45be6946ebbbee9cce5a5bcf838f4c952d87e6 /chalk/chalkcolor | |
parent | 2785103a6bd4de55bd26d79e34d0fdd4b329a73a (diff) | |
download | koffice-698569f8428ca088f764d704034a1330517b98c0.tar.gz koffice-698569f8428ca088f764d704034a1330517b98c0.zip |
Finish rebranding of Krita as Chalk
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1238363 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'chalk/chalkcolor')
43 files changed, 7395 insertions, 0 deletions
diff --git a/chalk/chalkcolor/Makefile.am b/chalk/chalkcolor/Makefile.am new file mode 100644 index 00000000..9ec16a40 --- /dev/null +++ b/chalk/chalkcolor/Makefile.am @@ -0,0 +1,45 @@ +# all_includes must remain last! +INCLUDES = $(KOFFICE_INCLUDES) \ + -I$(srcdir) \ + -I$(srcdir)/../sdk \ + -I$(srcdir)/colorspaces \ + $(OPENEXR_CFLAGS) \ + $(all_includes) + +lib_LTLIBRARIES = libchalkcolor.la + +if have_openexr +OPENEXR_SOURCES=kis_f16half_base_colorspace.cc +endif + +libchalkcolor_la_SOURCES = kis_color.cc kis_colorspace.cc \ + kis_colorspace_iface.cc kis_colorspace_iface.skel kis_composite_op.cc kis_profile.cc \ + kis_histogram_producer.cc kis_basic_histogram_producers.cc kis_abstract_colorspace.cc \ + kis_colorspace_factory_registry.cc kis_color_conversions.cc kis_u8_base_colorspace.cc \ + kis_u16_base_colorspace.cc kis_f32_base_colorspace.cc $(OPENEXR_SOURCES) + +libchalkcolor_la_LDFLAGS = -version-info 1:0:0 -no-undefined $(all_libraries) +libchalkcolor_la_LIBADD = colorspaces/libchalkcolorspaces.la $(LCMS_LIBS) $(LIB_KPARTS) $(LIB_KDECORE) $(LIB_QT) $(OPENEXR_LIBS) + +include_HEADERS = \ + kis_channelinfo.h \ + kis_color.h \ + kis_colorspace.h \ + kis_composite_op.h \ + kis_profile.h \ + kis_histogram_producer.h \ + kis_basic_histogram_producers.h kis_u8_base_colorspace.h kis_u16_base_colorspace.h kis_f16half_base_colorspace.h kis_f32_base_colorspace.h \ + kis_colorspace_factory_registry.h kis_abstract_colorspace.h + + +if include_kunittest_tests +TESTSDIR = tests +endif + +SUBDIRS = colorspaces . $(TESTSDIR) + +kde_servicetypes_DATA = chalk_colorspace.desktop + +METASOURCES = AUTO + + diff --git a/chalk/chalkcolor/README b/chalk/chalkcolor/README new file mode 100644 index 00000000..f80e0520 --- /dev/null +++ b/chalk/chalkcolor/README @@ -0,0 +1,4 @@ +The color library is a wrapper around lcms and provides colorspaces +that can do things to arrays of bytes that represent pixels. The +number of colorspaces is extensible with plugins. The colorspace +registry is responsible for loading the colorspace plugins. diff --git a/chalk/chalkcolor/TODO b/chalk/chalkcolor/TODO new file mode 100644 index 00000000..81bc56c1 --- /dev/null +++ b/chalk/chalkcolor/TODO @@ -0,0 +1,11 @@ +This library is still dependent upon chalk/sdk for some headers. This +should be changed. The headers concerned are: + +kis_id.h +kis_global.h +kis_annotation.h +kis_integer_maths.h + +Additionally, there is a problem with the histogram producers: those are +tied to the individual base colorspaces, but also need iterators, so they +are in core for the moment. diff --git a/chalk/chalkcolor/chalk_colorspace.desktop b/chalk/chalkcolor/chalk_colorspace.desktop new file mode 100644 index 00000000..7379a273 --- /dev/null +++ b/chalk/chalkcolor/chalk_colorspace.desktop @@ -0,0 +1,38 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=Chalk/ColorSpace +Comment=A module implementing a complete colorspace for use with libchalkcolor +Comment[bg]=Модул, реализиращ пълна цветова гама за употреба с libchalkcolor +Comment[ca]=Un mòdul que implementa un complet espai de colors per a usar-lo amb libchalkcolor +Comment[cy]=Modiwl sy'n gweithredoli gofod lliw cyflawn i'w ddefnyddio efo libchalkcolor +Comment[da]=Et modul som implementerer et fuldstændigt farverum til brug med libchalkcolor +Comment[de]=Ein Modul, das einen kompletten Farbraum zur Benutzung mit libchalkcolor implementiert +Comment[el]=Ένα άρθρωμα που υλοποιεί έναν πλήρη χρωματικό χώρο για χρήση με το libchalkcolor +Comment[en_GB]=A module implementing a complete colourspace for use with libchalkcolor +Comment[es]=Un módulo que implementa un espacio de color completo para usar con libchalkcolor +Comment[et]=Täielikku värviruumi teostav moodul (teegile libchalkcolor) +Comment[fa]=پیمانهای که فضای رنگ کاملی برای استفاده با libchalkcolor پیاده میکند +Comment[fr]=Un module implantant un espace de couleurs complet à utiliser avec libchalkcolor +Comment[fy]=In module dy in folslein kleurgebiet ymplementearret dat brûkt wurde kin mei libchalkcolor +Comment[gl]=Un módulo que implemente un espazo de cor completo para usar con libchalkcolor +Comment[hu]=Teljes színteret megvalósító modul a libchalkcolor programkönyvtárhoz +Comment[is]=Eining með fullu litasvæði til notkunar með libchalkcolor +Comment[it]=Un modulo che implementa uno spazio dei colori completo per usarlo con libchalkcolor +Comment[km]=ម៉ូឌុលដែលអនុវត្តប្រភេទពណ៌ពេញលេញ ដើម្បីប្រើជាមួយ libchalkcolor +Comment[nb]=En modul som implementerer et komplett fargerom til bruk med libchalkcolor +Comment[nds]=En Moduul, dat en helen Klörenruum för den Bruuk mit libchalkcolor inbuut +Comment[ne]=लिबक्रितारङसँग प्रयोग गर्नका लागि सम्पूर्ण रङ खालीस्थानलाई मोड्युललाई औजार बनाइदै +Comment[nl]=Een module die een volledig kleurgebied implementeert dat gebruikt kan worden met libchalkcolor +Comment[pl]=Moduł implementujący kompletną przestrzeń barw do użytku z libchalkcolor +Comment[pt]=Um módulo que implementa um espaço de cores completo para usar com a 'libchalkcolor' +Comment[pt_BR]=Um módulo que implementa um espaço de cores completo para usar com a 'libchalkcolor' +Comment[ru]=Полная поддержка цветовых пространств в libchalkcolor +Comment[sk]=Modul ktorý poskytuje úplný farebný priestor pre použitie s libchalkcolor +Comment[sl]=Modul, v katerem je izveden celoten barvni prostor za uporabo z libchalkcolor +Comment[sr]=Модул који имплементира потпун простор боја за употребу са libchalkcolor +Comment[sr@Latn]=Modul koji implementira potpun prostor boja za upotrebu sa libchalkcolor +Comment[sv]=En modul som implementerar en fullständig färgrymd för användning med libchalkcolor +Comment[uk]=Модуль впровадження повного простору кольорів для вжитку з libchalkcolor +Comment[zh_TW]=實作完整色彩空間以使用 libchalkcolor 的模組 +[PropertyDef::X-Chalk-Version] +Type=int diff --git a/chalk/chalkcolor/colorspaces/Makefile.am b/chalk/chalkcolor/colorspaces/Makefile.am new file mode 100644 index 00000000..90f75d29 --- /dev/null +++ b/chalk/chalkcolor/colorspaces/Makefile.am @@ -0,0 +1,20 @@ +INCLUDES = -I$(srcdir)/.. \ + -I$(srcdir)/../../sdk \ + $(KOFFICE_INCLUDES) \ + $(all_includes) + +noinst_LTLIBRARIES = libchalkcolorspaces.la + +libchalkcolorspaces_la_SOURCES = \ + kis_alpha_colorspace.cc \ + kis_lab_colorspace.cc + +noinst_HEADERS = \ + kis_alpha_colorspace.h \ + kis_lab_colorspace.h + +libchalkcolorspaces_la_LIBADD = $(OPENEXR_LIBS) + +libchalkcolorspaces_la_METASOURCES = AUTO + + diff --git a/chalk/chalkcolor/colorspaces/kis_alpha_colorspace.cc b/chalk/chalkcolor/colorspaces/kis_alpha_colorspace.cc new file mode 100644 index 00000000..4b54556f --- /dev/null +++ b/chalk/chalkcolor/colorspaces/kis_alpha_colorspace.cc @@ -0,0 +1,296 @@ +/* + * 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 <tqimage.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <config.h> + +#include LCMS_HEADER + +#include "kis_alpha_colorspace.h" +#include "kis_u8_base_colorspace.h" +#include "kis_channelinfo.h" +#include "kis_id.h" +#include "kis_integer_maths.h" + +namespace { + const TQ_UINT8 PIXEL_MASK = 0; +} + +KisAlphaColorSpace::KisAlphaColorSpace(KisColorSpaceFactoryRegistry * tqparent, + KisProfile *p) : + KisU8BaseColorSpace(KisID("ALPHA", i18n("Alpha tqmask")), TYPE_GRAY_8, icSigGrayData, tqparent, p) +{ + m_channels.push_back(new KisChannelInfo(i18n("Alpha"), i18n("A"), 0, KisChannelInfo::ALPHA, KisChannelInfo::UINT8)); + m_alphaPos = 0; +} + +KisAlphaColorSpace::~KisAlphaColorSpace() +{ +} + +void KisAlphaColorSpace::fromTQColor(const TQColor& /*c*/, TQ_UINT8 *dst, KisProfile * /*profile*/) +{ + dst[PIXEL_MASK] = OPACITY_OPAQUE; +} + +void KisAlphaColorSpace::fromTQColor(const TQColor& /*c*/, TQ_UINT8 opacity, TQ_UINT8 *dst, KisProfile * /*profile*/) +{ + dst[PIXEL_MASK] = opacity; +} + +void KisAlphaColorSpace::getAlpha(const TQ_UINT8 *pixel, TQ_UINT8 *alpha) const +{ + *alpha = *pixel; +} + +void KisAlphaColorSpace::toTQColor(const TQ_UINT8 */*src*/, TQColor *c, KisProfile * /*profile*/) +{ + c->setRgb(255, 255, 255); +} + +void KisAlphaColorSpace::toTQColor(const TQ_UINT8 *src, TQColor *c, TQ_UINT8 *opacity, KisProfile * /*profile*/) +{ + c->setRgb(255, 255, 255); + *opacity = src[PIXEL_MASK]; +} + +TQ_UINT8 KisAlphaColorSpace::difference(const TQ_UINT8 *src1, const TQ_UINT8 *src2) +{ + // Arithmetic operands smaller than int are converted to int automatically + return TQABS(src2[PIXEL_MASK] - src1[PIXEL_MASK]); +} + +void KisAlphaColorSpace::mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const +{ + if (nColors > 0) { + TQ_UINT32 total = 0; + + while(nColors) + { + nColors--; + total += *colors[nColors] * weights[nColors]; + } + *dst = total / 255; + } +} + +TQValueVector<KisChannelInfo *> KisAlphaColorSpace::channels() const +{ + return m_channels; +} + +bool KisAlphaColorSpace::convertPixelsTo(const TQ_UINT8 *src, + TQ_UINT8 *dst, KisAbstractColorSpace * dstColorSpace, + TQ_UINT32 numPixels, + TQ_INT32 /*renderingIntent*/) +{ + // No lcms trickery here, we are only a opacity channel + TQ_INT32 size = dstColorSpace->pixelSize(); + + TQ_UINT32 j = 0; + TQ_UINT32 i = 0; + + while ( i < numPixels ) { + + dstColorSpace->fromTQColor(TQt::red, OPACITY_OPAQUE - *(src + i), (dst + j)); + + i += 1; + j += size; + + } + return true; + +} + + +//XXX bitblt of ColorSpaceAlpha does not take tqmask into consideration as this is probably not +// used ever +void KisAlphaColorSpace::bitBlt(TQ_UINT8 *dst, + TQ_INT32 dststride, + const TQ_UINT8 *src, + TQ_INT32 srcRowStride, + const TQ_UINT8 *srcAlphaMask, + TQ_INT32 tqmaskRowStride, + TQ_UINT8 opacity, + TQ_INT32 rows, + TQ_INT32 cols, + const KisCompositeOp& op) +{ + + TQ_UINT8 *d; + const TQ_UINT8 *s; + TQ_INT32 i; + TQ_INT32 linesize; + + if (rows <= 0 || cols <= 0) + return; + switch (op.op()) { + case COMPOSITE_COPY: + compositeCopy(dst, dststride, src, srcRowStride, srcAlphaMask, tqmaskRowStride, rows, cols, opacity); + return; + case COMPOSITE_CLEAR: + linesize = sizeof(TQ_UINT8) * cols; + d = dst; + while (rows-- > 0) { + memset(d, OPACITY_TRANSPARENT, linesize); + d += dststride; + } + return; + case COMPOSITE_ERASE: + while (rows-- > 0) { + d = dst; + s = src; + + for (i = cols; i > 0; i--, d ++, s ++) { + if (d[PIXEL_MASK] < s[PIXEL_MASK]) { + continue; + } + else { + d[PIXEL_MASK] = s[PIXEL_MASK]; + } + + } + + dst += dststride; + src += srcRowStride; + } + return; + case COMPOSITE_SUBTRACT: + while (rows-- > 0) { + d = dst; + s = src; + + for (i = cols; i > 0; i--, d++, s++) { + if (d[PIXEL_MASK] <= s[PIXEL_MASK]) { + d[PIXEL_MASK] = MIN_SELECTED; + } else { + d[PIXEL_MASK] -= s[PIXEL_MASK]; + } + } + + dst += dststride; + src += srcRowStride; + } + return; + case COMPOSITE_ALPHA_DARKEN: + while (rows-- > 0) { + d = dst; + s = src; + for (i = cols; i > 0; i--, d++, s++) { + if (s[PIXEL_MASK] == OPACITY_TRANSPARENT) + continue; + int srcAlpha = (s[PIXEL_MASK] * opacity + UINT8_MAX / 2) / UINT8_MAX; + if (srcAlpha > d[PIXEL_MASK]) + d[PIXEL_MASK] = srcAlpha; + } + dst += dststride; + src += srcRowStride; + } + return; + case COMPOSITE_OVER: + default: + if (opacity == OPACITY_TRANSPARENT) + return; + if (opacity != OPACITY_OPAQUE) { + while (rows-- > 0) { + d = dst; + s = src; + for (i = cols; i > 0; i--, d++, s++) { + if (s[PIXEL_MASK] == OPACITY_TRANSPARENT) + continue; + int srcAlpha = (s[PIXEL_MASK] * opacity + UINT8_MAX / 2) / UINT8_MAX; + d[PIXEL_MASK] = (d[PIXEL_MASK] * (UINT8_MAX - srcAlpha) + srcAlpha * UINT8_MAX + UINT8_MAX / 2) / UINT8_MAX; + } + dst += dststride; + src += srcRowStride; + } + } + else { + while (rows-- > 0) { + d = dst; + s = src; + for (i = cols; i > 0; i--, d++, s++) { + if (s[PIXEL_MASK] == OPACITY_TRANSPARENT) + continue; + if (d[PIXEL_MASK] == OPACITY_TRANSPARENT || s[PIXEL_MASK] == OPACITY_OPAQUE) { + memcpy(d, s, 1); + continue; + } + int srcAlpha = s[PIXEL_MASK]; + d[PIXEL_MASK] = (d[PIXEL_MASK] * (UINT8_MAX - srcAlpha) + srcAlpha * UINT8_MAX + UINT8_MAX / 2) / UINT8_MAX; + } + dst += dststride; + src += srcRowStride; + } + } + + } +} + +KisCompositeOpList KisAlphaColorSpace::userVisiblecompositeOps() const +{ + KisCompositeOpList list; + + list.append(KisCompositeOp(COMPOSITE_OVER)); + + return list; +} + +TQString KisAlphaColorSpace::channelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < nChannels()); + TQ_UINT32 channelPosition = m_channels[channelIndex]->pos(); + + return TQString().setNum(pixel[channelPosition]); +} + +TQString KisAlphaColorSpace::normalisedChannelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < nChannels()); + TQ_UINT32 channelPosition = m_channels[channelIndex]->pos(); + + return TQString().setNum(static_cast<float>(pixel[channelPosition]) / UINT8_MAX); +} + + +void KisAlphaColorSpace::convolveColors(TQ_UINT8** colors, TQ_INT32 * kernelValues, KisChannelInfo::enumChannelFlags channelFlags, TQ_UINT8 *dst, TQ_INT32 factor, TQ_INT32 offset, TQ_INT32 nColors) const +{ + TQ_INT32 totalAlpha = 0; + + while (nColors--) + { + TQ_INT32 weight = *kernelValues; + + if (weight != 0) { + totalAlpha += (*colors)[PIXEL_MASK] * weight; + } + colors++; + kernelValues++; + } + + if (channelFlags & KisChannelInfo::FLAG_ALPHA) { + dst[PIXEL_MASK] = CLAMP((totalAlpha/ factor) + offset, 0, TQ_UINT8_MAX); + } +} diff --git a/chalk/chalkcolor/colorspaces/kis_alpha_colorspace.h b/chalk/chalkcolor/colorspaces/kis_alpha_colorspace.h new file mode 100644 index 00000000..dd4f3916 --- /dev/null +++ b/chalk/chalkcolor/colorspaces/kis_alpha_colorspace.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KIS_COLORSPACE_ALPHA_H_ +#define KIS_COLORSPACE_ALPHA_H_ + +#include <tqcolor.h> + +#include "kis_global.h" +#include "kis_u8_base_colorspace.h" + +/** + * The alpha tqmask is a special color strategy that treats all pixels as + * alpha value with a colour common to the tqmask. The default color is white. + */ +class KisAlphaColorSpace : public KisU8BaseColorSpace { +public: + KisAlphaColorSpace(KisColorSpaceFactoryRegistry * tqparent, + KisProfile *p); + virtual ~KisAlphaColorSpace(); + +public: + virtual bool willDegrade(ColorSpaceIndependence) + { + return false; + }; + + virtual void fromTQColor(const TQColor& c, TQ_UINT8 *dst, KisProfile * profile = 0); + virtual void fromTQColor(const TQColor& c, TQ_UINT8 opacity, TQ_UINT8 *dst, KisProfile * profile = 0); + + virtual void getAlpha(const TQ_UINT8 *pixel, TQ_UINT8 *alpha) const; + + virtual void toTQColor(const TQ_UINT8 *src, TQColor *c, KisProfile * profile = 0); + virtual void toTQColor(const TQ_UINT8 *src, TQColor *c, TQ_UINT8 *opacity, KisProfile * profile = 0); + + virtual TQ_UINT8 difference(const TQ_UINT8 *src1, const TQ_UINT8 *src2); + virtual void mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const; + + virtual TQValueVector<KisChannelInfo *> channels() const; + virtual TQ_UINT32 nChannels() const { return 1; }; + virtual TQ_UINT32 nColorChannels() const { return 0; }; + virtual TQ_UINT32 pixelSize() const { return 1; }; + + virtual TQString channelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + virtual TQString normalisedChannelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + + virtual void convolveColors(TQ_UINT8** colors, TQ_INT32* kernelValues, KisChannelInfo::enumChannelFlags channelFlags, TQ_UINT8 *dst, TQ_INT32 factor, TQ_INT32 offset, TQ_INT32 nColors) const; + +protected: + + /** + * Convert a byte array of srcLen pixels *src to the specified color space + * and put the converted bytes into the prepared byte array *dst. + * + * Returns false if the conversion failed, true if it succeeded + */ + virtual bool convertPixelsTo(const TQ_UINT8 *src, + TQ_UINT8 *dst, KisAbstractColorSpace * dstColorSpace, + TQ_UINT32 numPixels, + TQ_INT32 renderingIntent = INTENT_PERCEPTUAL); + + + + virtual void bitBlt(TQ_UINT8 *dst, + TQ_INT32 dststride, + const TQ_UINT8 *src, + TQ_INT32 srcRowStride, + const TQ_UINT8 *srcAlphaMask, + TQ_INT32 tqmaskRowStride, + TQ_UINT8 opacity, + TQ_INT32 rows, + TQ_INT32 cols, + const KisCompositeOp& op); + + KisCompositeOpList userVisiblecompositeOps() const; + +}; + +#endif // KIS_COLORSPACE_ALPHA_H_ diff --git a/chalk/chalkcolor/colorspaces/kis_lab_colorspace.cc b/chalk/chalkcolor/colorspaces/kis_lab_colorspace.cc new file mode 100644 index 00000000..829b4f27 --- /dev/null +++ b/chalk/chalkcolor/colorspaces/kis_lab_colorspace.cc @@ -0,0 +1,571 @@ + /* + * Copyright (c) 2002 Patrick Julien <freak@codepimps.org> + * Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org> + * Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com> + * Copyright (c) 2005 Casper Boemann <cbr@boemann.dk> + * + * 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 <config.h> +#include <limits.h> +#include <stdlib.h> +#include LCMS_HEADER + +#include <tqimage.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "kis_lab_colorspace.h" +#include "kis_color_conversions.h" +#include "kis_integer_maths.h" + +KisLabColorSpace::KisLabColorSpace(KisColorSpaceFactoryRegistry * tqparent, KisProfile *p) + : KisU16BaseColorSpace(KisID("LABA", i18n("L*a*b* (16-bit integer/channel)")), + COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1), + icSigLabData, tqparent, p) + +{ + m_channels.push_back(new KisChannelInfo(i18n("Lightness"), i18n("L"), CHANNEL_L * sizeof(TQ_UINT16), KisChannelInfo::COLOR, KisChannelInfo::UINT16, sizeof(TQ_UINT16), TQColor(100,100,100))); + m_channels.push_back(new KisChannelInfo(i18n("a*"), i18n("a"), CHANNEL_A * sizeof(TQ_UINT16), KisChannelInfo::COLOR, KisChannelInfo::UINT16, sizeof(TQ_UINT16), TQColor(150,150,150))); + m_channels.push_back(new KisChannelInfo(i18n("b*"), i18n("b"), CHANNEL_B * sizeof(TQ_UINT16), KisChannelInfo::COLOR, KisChannelInfo::UINT16, sizeof(TQ_UINT16), TQColor(200,200,200))); + m_channels.push_back(new KisChannelInfo(i18n("Alpha"), i18n("A"), CHANNEL_ALPHA * sizeof(TQ_UINT16), KisChannelInfo::ALPHA, KisChannelInfo::UINT16, sizeof(TQ_UINT16))); + + m_alphaPos = CHANNEL_ALPHA * sizeof(TQ_UINT16); + + init(); +} + +KisLabColorSpace::~KisLabColorSpace() +{ +} + +TQ_UINT8 * KisLabColorSpace::toLabA16(const TQ_UINT8 * data, const TQ_UINT32 nPixels) const +{ + TQ_UINT8 * pixels = new TQ_UINT8[nPixels * pixelSize()]; + memcpy( pixels, data, nPixels * pixelSize() ); + return pixels; +} + +TQ_UINT8 * KisLabColorSpace::fromLabA16(const TQ_UINT8 * labData, const TQ_UINT32 nPixels) const +{ + TQ_UINT8 * pixels = new TQ_UINT8[nPixels * pixelSize()]; + memcpy( pixels, labData, nPixels * pixelSize() ); + return pixels; +} + +TQ_UINT8 KisLabColorSpace::difference(const TQ_UINT8 *src1, const TQ_UINT8 *src2) +{ + cmsCIELab labF1, labF2; + + if (getAlpha(src1) == OPACITY_TRANSPARENT || getAlpha(src2) == OPACITY_TRANSPARENT) + return (getAlpha(src1) == getAlpha(src2) ? 0 : 255); + + cmsLabEncoded2Float(&labF1, (WORD *)src1); + cmsLabEncoded2Float(&labF2, (WORD *)src2); + double diff = cmsDeltaE(&labF1, &labF2); + if(diff>255) + return 255; + else + return TQ_INT8(diff); +} + +void KisLabColorSpace::mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const +{ + TQ_UINT32 totalLightness = 0, totalAlpha = 0; + TQ_UINT32 totala = 0, totalb = 0; + + while (nColors--) + { + const Pixel *color = reinterpret_cast<const Pixel *>( *colors ); + TQ_UINT32 alphaTimesWeight = UINT8_MULT(color->alpha, *weights); + + totalLightness += color->lightness * alphaTimesWeight; + totala += color->a * alphaTimesWeight; + totalb += color->b * alphaTimesWeight; + totalAlpha += alphaTimesWeight; + + weights++; + colors++; + } + + if (totalAlpha > UINT16_MAX) { + totalAlpha = UINT16_MAX; + } + + ((Pixel *)dst)->alpha = totalAlpha; + + if (totalAlpha > 0) { + totalLightness /= totalAlpha; + totala /= totalAlpha; + totalb /= totalAlpha; + } // else the values are already 0 too + + if (totalLightness > MAX_CHANNEL_L) { + totalLightness = MAX_CHANNEL_L; + } + + ((Pixel *)dst)->lightness = totalLightness; + + if (totala > MAX_CHANNEL_AB) { + totala = MAX_CHANNEL_AB; + } + + ((Pixel *)dst)->a = totala; + + if (totalb > MAX_CHANNEL_AB) { + totalb = MAX_CHANNEL_AB; + } + + ((Pixel *)dst)->b = totalb; +} + +void KisLabColorSpace::invertColor(TQ_UINT8 * src, TQ_INT32 nPixels) +{ + TQ_UINT32 psize = pixelSize(); + + while (nPixels--) + { + Pixel * s = reinterpret_cast<Pixel *>( src ); + + s->lightness = MAX_CHANNEL_L - s->lightness; + s->a = MAX_CHANNEL_AB - s->a; + s->b = MAX_CHANNEL_AB - s->b; + + src += psize; + } +} + +void KisLabColorSpace::convolveColors(TQ_UINT8** colors, TQ_INT32 * kernelValues, KisChannelInfo::enumChannelFlags channelFlags, + TQ_UINT8 *dst, TQ_INT32 factor, TQ_INT32 offset, TQ_INT32 nColors) const +{ + TQ_INT32 totalL = 0, totalA = 0, totalB = 0, totalAlpha = 0; + + while ( nColors -- ) + { + const Pixel * pixel = reinterpret_cast<const Pixel *>( *colors ); + TQ_INT32 weight = *kernelValues; + if ( weight != 0 ) { + totalL += pixel->lightness * weight; + totalA += pixel->a * weight; + totalB += pixel->b * weight; + totalAlpha += pixel->alpha * weight; + } + colors++; + kernelValues++; + } + + + Pixel * p = reinterpret_cast< Pixel *>( dst ); + + if (channelFlags & KisChannelInfo::FLAG_COLOR) { + p->lightness = CLAMP( ( totalL / factor) + offset, 0, TQ_UINT16_MAX); + p->a = CLAMP( ( totalA / factor) + offset, 0, TQ_UINT16_MAX); + p->b = CLAMP( ( totalB / factor) + offset, 0, TQ_UINT16_MAX); + } + if (channelFlags & KisChannelInfo::FLAG_ALPHA) { + p->alpha = CLAMP((totalAlpha/ factor) + offset, 0, TQ_UINT16_MAX); + } + +} + +void KisLabColorSpace::darken(const TQ_UINT8 * src, TQ_UINT8 * dst, TQ_INT32 shade, bool compensate, double compensation, TQ_INT32 nPixels) const +{ + // XXX: Is the 255 right for u16 colorspaces? + TQ_UINT32 pSize = pixelSize(); + while ( nPixels-- ) { + const Pixel * s = reinterpret_cast<const Pixel*>( src ); + Pixel * d = reinterpret_cast<Pixel*>( dst ); + + if ( compensate ) { + d->lightness = static_cast<TQ_UINT16>( ( s->lightness * shade ) / ( compensation * 255 ) ); + } + else { + d->lightness = static_cast<TQ_UINT16>( s->lightness * shade / 255 ); + } + d->a = s->a; + d->b = s->b; + d->alpha = s->alpha; + + src += pSize; + dst += pSize; + } +} + + +TQValueVector<KisChannelInfo *> KisLabColorSpace::channels() const +{ + return m_channels; +} + +TQ_UINT32 KisLabColorSpace::nChannels() const +{ + return NUM_CHANNELS; +} + +TQ_UINT32 KisLabColorSpace::nColorChannels() const +{ + return NUM_COLOR_CHANNELS; +} + +TQ_UINT32 KisLabColorSpace::pixelSize() const +{ + return sizeof(Pixel); +} + +void KisLabColorSpace::getSingleChannelPixel(TQ_UINT8 *dst, const TQ_UINT8 *src, TQ_UINT32 channelIndex) +{ + if (channelIndex < NUM_CHANNELS) { + + const Pixel *srcPixel = reinterpret_cast<const Pixel *>(src); + Pixel *dstPixel = reinterpret_cast<Pixel *>(dst); + + switch (channelIndex) { + case CHANNEL_L: + dstPixel->lightness = srcPixel->lightness; + dstPixel->a = CHANNEL_AB_ZERO_OFFSET; + dstPixel->b = CHANNEL_AB_ZERO_OFFSET; + dstPixel->alpha = U16_OPACITY_TRANSPARENT; + break; + case CHANNEL_A: + dstPixel->lightness = MAX_CHANNEL_L / 2; + dstPixel->a = srcPixel->a; + dstPixel->b = CHANNEL_AB_ZERO_OFFSET; + dstPixel->alpha = U16_OPACITY_TRANSPARENT; + break; + case CHANNEL_B: + dstPixel->lightness = MAX_CHANNEL_L / 2; + dstPixel->a = CHANNEL_AB_ZERO_OFFSET; + dstPixel->b = srcPixel->b; + dstPixel->alpha = U16_OPACITY_TRANSPARENT; + break; + case CHANNEL_ALPHA: + dstPixel->lightness = MAX_CHANNEL_L / 2; + dstPixel->a = CHANNEL_AB_ZERO_OFFSET; + dstPixel->b = CHANNEL_AB_ZERO_OFFSET; + dstPixel->alpha = srcPixel->alpha; + break; + } + } +} + +void KisLabColorSpace::compositeOver(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT16 opacity) +{ + while (rows > 0) { + const Pixel *src = reinterpret_cast<const Pixel *>(srcRowStart); + Pixel *dst = reinterpret_cast<Pixel *>(dstRowStart); + const TQ_UINT8 *tqmask = tqmaskRowStart; + TQ_INT32 columns = numColumns; + + while (columns > 0) { + + TQ_UINT16 srcAlpha = src->alpha; + + // apply the alphatqmask + if (tqmask != 0) { + if (*tqmask != OPACITY_OPAQUE) { + srcAlpha = UINT16_MULT(srcAlpha, *tqmask); + } + tqmask++; + } + + if (srcAlpha != U16_OPACITY_TRANSPARENT) { + + if (opacity != U16_OPACITY_OPAQUE) { + srcAlpha = UINT16_MULT(srcAlpha, opacity); + } + + if (srcAlpha == U16_OPACITY_OPAQUE) { + memcpy(dst, src, sizeof(Pixel)); + } else { + TQ_UINT16 dstAlpha = dst->alpha; + + TQ_UINT16 srcBlend; + + if (dstAlpha == U16_OPACITY_OPAQUE) { + srcBlend = srcAlpha; + } else { + TQ_UINT16 newAlpha = dstAlpha + UINT16_MULT(U16_OPACITY_OPAQUE - dstAlpha, srcAlpha); + dst->alpha = newAlpha; + + if (newAlpha != 0) { + srcBlend = UINT16_DIVIDE(srcAlpha, newAlpha); + } else { + srcBlend = srcAlpha; + } + } + + if (srcBlend == U16_OPACITY_OPAQUE) { + memcpy(dst, src, sizeof(Pixel)); + } else { +/*printf("blend is %d\n", srcBlend); +printf("%d %d %d\n", src->lightness, src->a, src->b); +printf("%d %d %d\n", dst->lightness, dst->a, dst->b); +*/ + dst->lightness = UINT16_BLEND(src->lightness, dst->lightness, srcBlend); + dst->a = UINT16_BLEND(src->a, dst->a, srcBlend); + dst->b = UINT16_BLEND(src->b, dst->b, srcBlend); +//printf("%d %d %d\n", dst->lightness, dst->a, dst->b); + } + } + } + + columns--; + src++; + dst++; + } + + rows--; + srcRowStart += srcRowStride; + dstRowStart += dstRowStride; + if(tqmaskRowStart) { + tqmaskRowStart += tqmaskRowStride; + } + } +} + +void KisLabColorSpace::compositeErase(TQ_UINT8 *dst, + TQ_INT32 dstRowSize, + const TQ_UINT8 *src, + TQ_INT32 srcRowSize, + const TQ_UINT8 *srcAlphaMask, + TQ_INT32 tqmaskRowStride, + TQ_INT32 rows, + TQ_INT32 cols, + TQ_UINT16 /*opacity*/) +{ + while (rows-- > 0) + { + const Pixel *s = reinterpret_cast<const Pixel *>(src); + Pixel *d = reinterpret_cast<Pixel *>(dst); + const TQ_UINT8 *tqmask = srcAlphaMask; + + for (TQ_INT32 i = cols; i > 0; i--, s++, d++) + { + TQ_UINT16 srcAlpha = s->alpha; + + // apply the alphatqmask + if (tqmask != 0) { + TQ_UINT8 U8_tqmask = *tqmask; + + if (U8_tqmask != OPACITY_OPAQUE) { + srcAlpha = UINT16_BLEND(srcAlpha, U16_OPACITY_OPAQUE, UINT8_TO_UINT16(U8_tqmask)); + } + tqmask++; + } + d->alpha = UINT16_MULT(srcAlpha, d->alpha); + } + + dst += dstRowSize; + src += srcRowSize; + if(srcAlphaMask) { + srcAlphaMask += tqmaskRowStride; + } + } +} + +void KisLabColorSpace::bitBlt(TQ_UINT8 *dst, + TQ_INT32 dstRowStride, + const TQ_UINT8 *src, + TQ_INT32 srcRowStride, + const TQ_UINT8 *tqmask, + TQ_INT32 tqmaskRowStride, + TQ_UINT8 U8_opacity, + TQ_INT32 rows, + TQ_INT32 cols, + const KisCompositeOp& op) +{ + TQ_UINT16 opacity = UINT8_TO_UINT16(U8_opacity); + + switch (op.op()) { + case COMPOSITE_UNDEF: + // Undefined == no composition + break; + case COMPOSITE_OVER: + compositeOver(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_IN: + //compositeIn(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_OUT: + //compositeOut(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_ATOP: + //compositeAtop(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_XOR: + //compositeXor(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_PLUS: + //compositePlus(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_MINUS: + //compositeMinus(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_ADD: + //compositeAdd(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_SUBTRACT: + //compositeSubtract(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_DIFF: + //compositeDiff(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_MULT: + //compositeMultiply(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_DIVIDE: + //compositeDivide(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_BUMPMAP: + //compositeBumpmap(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_COPY: + compositeCopy(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, U8_opacity); + break; + case COMPOSITE_COPY_RED: + //compositeCopyRed(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_COPY_GREEN: + //compositeCopyGreen(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_COPY_BLUE: + //compositeCopyBlue(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_COPY_OPACITY: + //compositeCopyOpacity(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_CLEAR: + //compositeClear(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_DISSOLVE: + //compositeDissolve(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_DISPLACE: + //compositeDisplace(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; +#if 0 + case COMPOSITE_MODULATE: + compositeModulate(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_THRESHOLD: + compositeThreshold(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; +#endif + case COMPOSITE_NO: + // No composition. + break; + case COMPOSITE_DARKEN: + //compositeDarken(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_LIGHTEN: + //compositeLighten(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_HUE: + //compositeHue(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_SATURATION: + //compositeSaturation(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_VALUE: + //compositeValue(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_COLOR: + //compositeColor(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_COLORIZE: + //compositeColorize(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_LUMINIZE: + //compositeLuminize(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_SCREEN: + //compositeScreen(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_OVERLAY: + //compositeOverlay(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_ERASE: + compositeErase(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_DODGE: + //compositeDodge(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_BURN: + //compositeBurn(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_ALPHA_DARKEN: + abstractCompositeAlphaDarken<TQ_UINT16, U16Mult, Uint8ToU16, U16OpacityTest, + CHANNEL_ALPHA, NUM_COLOR_CHANNELS, NUM_CHANNELS>( + dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, + rows, cols, opacity, U16Mult(), Uint8ToU16(), U16OpacityTest()); + break; + default: + break; + } +} + +KisCompositeOpList KisLabColorSpace::userVisiblecompositeOps() const +{ + KisCompositeOpList list; + + list.append(KisCompositeOp(COMPOSITE_OVER)); + list.append(KisCompositeOp(COMPOSITE_ALPHA_DARKEN)); + + return list; +} + +TQString KisLabColorSpace::channelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const +{ + const Pixel *pix = reinterpret_cast<const Pixel *>(U8_pixel); + Q_ASSERT(channelIndex < nChannels()); + switch(channelIndex) + { + case CHANNEL_L: + return TQString().setNum(pix->lightness); + case CHANNEL_A: + return TQString().setNum(pix->a); + case CHANNEL_B: + return TQString().setNum(pix->b); + case CHANNEL_ALPHA: + return TQString().setNum(pix->alpha); + default: + return TQString("Error"); + } +} + +TQString KisLabColorSpace::normalisedChannelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const +{ + const Pixel *pix = reinterpret_cast<const Pixel *>(U8_pixel); + Q_ASSERT(channelIndex < nChannels()); + + // These convert from lcms encoded format to standard ranges. + + switch(channelIndex) + { + case CHANNEL_L: + return TQString().setNum(100.0 * static_cast<float>(pix->lightness) / MAX_CHANNEL_L); + case CHANNEL_A: + return TQString().setNum(100.0 * ((static_cast<float>(pix->a) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB)); + case CHANNEL_B: + return TQString().setNum(100.0 * ((static_cast<float>(pix->b) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB)); + case CHANNEL_ALPHA: + return TQString().setNum(100.0 * static_cast<float>(pix->alpha) / UINT16_MAX); + default: + return TQString("Error"); + } +} + diff --git a/chalk/chalkcolor/colorspaces/kis_lab_colorspace.h b/chalk/chalkcolor/colorspaces/kis_lab_colorspace.h new file mode 100644 index 00000000..eeab55cf --- /dev/null +++ b/chalk/chalkcolor/colorspaces/kis_lab_colorspace.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2002 Patrick Julien <freak@codepimps.org> + * Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com> + * + * 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_LAB_H_ +#define KIS_STRATEGY_COLORSPACE_LAB_H_ + +#include <tqcolor.h> + +#include <klocale.h> + +#include "kis_global.h" +#include "kis_integer_maths.h" +#include "kis_u16_base_colorspace.h" + +class KisLabColorSpace : public KisU16BaseColorSpace { +public: + KisLabColorSpace(KisColorSpaceFactoryRegistry * tqparent, KisProfile *p); + virtual ~KisLabColorSpace(); + +public: + + /** + * Return a COPY of the provided data. This method is provided to provide consistency, + * but you really don't want to be calling it. + */ + virtual TQ_UINT8 * toLabA16(const TQ_UINT8 * data, const TQ_UINT32 nPixels) const; + + /** + * Return a COPY of the provided data. This method is provided for consistency, + * but you really don't want to call it. + */ + virtual TQ_UINT8 * fromLabA16(const TQ_UINT8 * labData, const TQ_UINT32 nPixels) const; + + + + virtual bool willDegrade(ColorSpaceIndependence independence) + { + if (independence == TO_RGBA8) + return true; + else + return false; + }; + + virtual TQValueVector<KisChannelInfo *> channels() const; + virtual TQ_UINT32 nChannels() const; + virtual TQ_UINT32 nColorChannels() const; + virtual TQ_UINT32 pixelSize() const; + + virtual TQString channelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + virtual TQString normalisedChannelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + virtual void getSingleChannelPixel(TQ_UINT8 *dstPixel, const TQ_UINT8 *srcPixel, TQ_UINT32 channelIndex); + + virtual TQ_UINT8 difference(const TQ_UINT8 *src1, const TQ_UINT8 *src2); + virtual void mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const; + virtual void invertColor(TQ_UINT8 * src, TQ_INT32 nPixels); + virtual void convolveColors(TQ_UINT8** colors, TQ_INT32 * kernelValues, KisChannelInfo::enumChannelFlags channelFlags, TQ_UINT8 *dst, TQ_INT32 factor, TQ_INT32 offset, TQ_INT32 nColors) const; + + virtual void darken(const TQ_UINT8 * src, TQ_UINT8 * dst, TQ_INT32 shade, bool compensate, double compensation, TQ_INT32 nPixels) const; + + virtual KisCompositeOpList userVisiblecompositeOps() const; + +protected: + + virtual void bitBlt(TQ_UINT8 *dst, + TQ_INT32 dstRowStride, + const TQ_UINT8 *src, + TQ_INT32 srcRowStride, + const TQ_UINT8 *srcAlphaMask, + TQ_INT32 tqmaskRowStride, + TQ_UINT8 opacity, + TQ_INT32 rows, + TQ_INT32 cols, + const KisCompositeOp& op); + + void compositeOver(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); +/* + void compositeMultiply(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeDivide(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeScreen(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeOverlay(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeDodge(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeBurn(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeDarken(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeLighten(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeHue(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeSaturation(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeValue(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeColor(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); +*/ + void compositeErase(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + +private: + struct Pixel { + TQ_UINT16 lightness; + TQ_UINT16 a; + TQ_UINT16 b; + TQ_UINT16 alpha; + }; + static const TQ_UINT16 U16_OPACITY_OPAQUE = UINT16_MAX; + static const TQ_UINT16 U16_OPACITY_TRANSPARENT = UINT16_MIN; + + static const TQ_UINT32 NUM_CHANNELS = 4; + static const TQ_UINT32 NUM_COLOR_CHANNELS = 3; + + static const TQ_UINT32 CHANNEL_L = 0; + static const TQ_UINT32 CHANNEL_A = 1; + static const TQ_UINT32 CHANNEL_B = 2; + static const TQ_UINT32 CHANNEL_ALPHA = 3; + + static const TQ_UINT32 MAX_CHANNEL_L = 0xff00; + static const TQ_UINT32 MAX_CHANNEL_AB = 0xffff; + static const TQ_UINT32 CHANNEL_AB_ZERO_OFFSET = 0x8000; + + friend class KisLabColorSpaceTester; +}; + +class KisLabColorSpaceFactory : public KisColorSpaceFactory +{ +public: + /** + * Chalk definition for use in .kra files and internally: unchanging name + + * i18n'able description. + */ + virtual KisID id() const { return KisID("LABA", i18n("L*a*b* (16-bit integer/channel)")); }; + + /** + * lcms colorspace type definition. + */ + virtual TQ_UINT32 colorSpaceType() { return (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)); }; + + virtual icColorSpaceSignature colorSpaceSignature() { return icSigLabData; }; + + virtual KisColorSpace *createColorSpace(KisColorSpaceFactoryRegistry * tqparent, KisProfile *p) { return new KisLabColorSpace(tqparent, p); }; + + virtual TQString defaultProfile() { return "Lab built-in - (lcms internal)"; }; +}; + +#endif // KIS_STRATEGY_COLORSPACE_LAB_H_ diff --git a/chalk/chalkcolor/colorspaces/kis_xyz_colorspace.cc b/chalk/chalkcolor/colorspaces/kis_xyz_colorspace.cc new file mode 100644 index 00000000..98ccead0 --- /dev/null +++ b/chalk/chalkcolor/colorspaces/kis_xyz_colorspace.cc @@ -0,0 +1,624 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <limits.h> +#include <stdlib.h> + +#include <config.h> +#include LCMS_HEADER + +#include <tqimage.h> + +#include <klocale.h> +#include <kdebug.h> + +#include "kis_abstract_colorspace.h" +#include "kis_u16_base_colorspace.h" +#include "kis_xyz_colorspace.h" +#include "kis_integer_maths.h" + +#define downscale(quantum) (quantum) //((unsigned char) ((quantum)/257UL)) +#define upscale(value) (value) // ((TQ_UINT8) (257UL*(value))) + +// XXX: Maybe use TYPE_XYZ_DBL for an extra stimulating performance hit? People shouldn't depend +// on this fallback... + +KisXyzColorSpace::KisXyzColorSpace(KisColorSpaceFactoryRegistry * tqparent, + KisProfile *p) : + KisU16BaseColorSpace(KisID("XYZA", i18n("XYZ/Alpha")), (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)), icSigCmykData, tqparent, p) +{ + m_channels.push_back(new KisChannelInfo(i18n("X"), i18n("X"), 0, KisChannelInfo::COLOR, KisChannelInfo::UINT8)); + m_channels.push_back(new KisChannelInfo(i18n("Y"), i18n("Y"), 1, KisChannelInfo::COLOR, KisChannelInfo::UINT8)); + m_channels.push_back(new KisChannelInfo(i18n("Z"), i18n("Z"), 2, KisChannelInfo::COLOR, KisChannelInfo::UINT8)); + m_channels.push_back(new KisChannelInfo(i18n("Alpha"), i18n("A"), 4, KisChannelInfo::ALPHA, KisChannelInfo::UINT8)); + + m_alphaPos = PIXEL_ALPHA * sizeof(TQ_UINT16); + + init(); +} + + +KisXyzColorSpace::~KisXyzColorSpace() +{ +} + + +TQValueVector<KisChannelInfo *> KisXyzColorSpace::channels() const +{ + return m_channels; +} + +TQ_UINT32 KisXyzColorSpace::nChannels() const +{ + return xyz::MAX_CHANNEL_XYZA; +} + +TQ_UINT32 KisXyzColorSpace::nColorChannels() const +{ + return xyz::MAX_CHANNEL_XYZ; +} + +TQ_UINT32 KisXyzColorSpace::pixelSize() const +{ + return xyz::MAX_CHANNEL_XYZA * sizeof(TQ_UINT16); +} + +KisColorAdjustment * KisXyzColorSpace::createBrightnessContrastAdjustment(TQ_UINT16 *transferValues) +{ + return 0; +} + +void KisXyzColorSpace::applyAdjustment(const TQ_UINT8 *src, TQ_UINT8 *dst, KisColorAdjustment *, TQ_INT32 nPixels) +{ +} + +void KisXyzColorSpace::invertColor(TQ_UINT8 * src, TQ_INT32 nPixels) +{ + TQ_INT32 pSize = pixelSize(); + + while (nPixels--) + { + TQ_UINT16 * p = reinterpret_cast<TQ_UINT16 *>(src); + p[PIXEL_X] = UINT16_MAX - p[PIXEL_X]; + p[PIXEL_Y] = UINT16_MAX - p[PIXEL_Y]; + p[PIXEL_Z] = UINT16_MAX - p[PIXEL_Z]; + src += pSize; + } +} + +void KisXyzColorSpace::mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const +{ +} + +void KisXyzColorSpace::convolveColors(TQ_UINT8** colors, TQ_INT32* kernelValues, KisChannelInfo::enumChannelFlags channelFlags, TQ_UINT8 *dst, TQ_INT32 factor, TQ_INT32 offset, TQ_INT32 nPixels) const +{ +} + +void KisXyzColorSpace::darken(const TQ_UINT8 * src, TQ_UINT8 * dst, TQ_INT32 shade, bool compensate, double compensation, TQ_INT32 nPixels) const +{ +} + +TQ_UINT8 KisXyzColorSpace::intensity8(const TQ_UINT8 * src) const +{ + return 0; +} + +void KisXyzColorSpace::compositeOver(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT16 opacity) +{ + while (rows > 0) { + + const TQ_UINT16 *src = reinterpret_cast<const TQ_UINT16 *>(srcRowStart); + TQ_UINT16 *dst = reinterpret_cast<TQ_UINT16 *>(dstRowStart); + const TQ_UINT8 *tqmask = tqmaskRowStart; + TQ_INT32 columns = numColumns; + + while (columns > 0) { + + TQ_UINT16 srcAlpha = src[PIXEL_ALPHA]; + + // apply the alphatqmask + if (tqmask != 0) { + TQ_UINT8 U8_tqmask = *tqmask; + + if (U8_tqmask != OPACITY_OPAQUE) { + srcAlpha = UINT16_MULT(srcAlpha, UINT8_TO_UINT16(U8_tqmask)); + } + tqmask++; + } + + if (srcAlpha != U16_OPACITY_TRANSPARENT) { + + if (opacity != U16_OPACITY_OPAQUE) { + srcAlpha = UINT16_MULT(srcAlpha, opacity); + } + + if (srcAlpha == U16_OPACITY_OPAQUE) { + memcpy(dst, src, xyz::MAX_CHANNEL_XYZA * sizeof(TQ_UINT16)); + } else { + TQ_UINT16 dstAlpha = dst[PIXEL_ALPHA]; + + TQ_UINT16 srcBlend; + + if (dstAlpha == U16_OPACITY_OPAQUE) { + srcBlend = srcAlpha; + } else { + TQ_UINT16 newAlpha = dstAlpha + UINT16_MULT(U16_OPACITY_OPAQUE - dstAlpha, srcAlpha); + dst[PIXEL_ALPHA] = newAlpha; + + if (newAlpha != 0) { + srcBlend = UINT16_DIVIDE(srcAlpha, newAlpha); + } else { + srcBlend = srcAlpha; + } + } + + if (srcBlend == U16_OPACITY_OPAQUE) { + memcpy(dst, src, xyz::MAX_CHANNEL_XYZ * sizeof(TQ_UINT16)); + } else { + dst[PIXEL_X] = UINT16_BLEND(src[PIXEL_X], dst[PIXEL_X], srcBlend); + dst[PIXEL_Y] = UINT16_BLEND(src[PIXEL_Y], dst[PIXEL_Y], srcBlend); + dst[PIXEL_Z] = UINT16_BLEND(src[PIXEL_Z], dst[PIXEL_Z], srcBlend); + } + } + } + + columns--; + src += xyz::MAX_CHANNEL_XYZA; + dst += xyz::MAX_CHANNEL_XYZA; + } + + rows--; + srcRowStart += srcRowStride; + dstRowStart += dstRowStride; + if(tqmaskRowStart) { + tqmaskRowStart += tqmaskRowStride; + } + } +} + +#define COMMON_COMPOSITE_OP_PROLOG() \ + while (rows > 0) { \ + \ + const TQ_UINT16 *src = reinterpret_cast<const TQ_UINT16 *>(srcRowStart); \ + TQ_UINT16 *dst = reinterpret_cast<TQ_UINT16 *>(dstRowStart); \ + TQ_INT32 columns = numColumns; \ + const TQ_UINT8 *tqmask = tqmaskRowStart; \ + \ + while (columns > 0) { \ + \ + TQ_UINT16 srcAlpha = src[PIXEL_ALPHA]; \ + TQ_UINT16 dstAlpha = dst[PIXEL_ALPHA]; \ + \ + srcAlpha = TQMIN(srcAlpha, dstAlpha); \ + \ + if (tqmask != 0) { \ + TQ_UINT8 U8_tqmask = *tqmask; \ + \ + if (U8_tqmask != OPACITY_OPAQUE) { \ + srcAlpha = UINT16_MULT(srcAlpha, UINT8_TO_UINT16(U8_tqmask)); \ + } \ + tqmask++; \ + } \ + \ + if (srcAlpha != U16_OPACITY_TRANSPARENT) { \ + \ + if (opacity != U16_OPACITY_OPAQUE) { \ + srcAlpha = UINT16_MULT(srcAlpha, opacity); \ + } \ + \ + TQ_UINT16 srcBlend; \ + \ + if (dstAlpha == U16_OPACITY_OPAQUE) { \ + srcBlend = srcAlpha; \ + } else { \ + TQ_UINT16 newAlpha = dstAlpha + UINT16_MULT(U16_OPACITY_OPAQUE - dstAlpha, srcAlpha); \ + dst[PIXEL_ALPHA] = newAlpha; \ + \ + if (newAlpha != 0) { \ + srcBlend = UINT16_DIVIDE(srcAlpha, newAlpha); \ + } else { \ + srcBlend = srcAlpha; \ + } \ + } + +#define COMMON_COMPOSITE_OP_EPILOG() \ + } \ + \ + columns--; \ + src += xyz::MAX_CHANNEL_XYZA; \ + dst += xyz::MAX_CHANNEL_XYZA; \ + } \ + \ + rows--; \ + srcRowStart += srcRowStride; \ + dstRowStart += dstRowStride; \ + if(tqmaskRowStart) { \ + tqmaskRowStart += tqmaskRowStride; \ + } \ + } + +void KisXyzColorSpace::compositeMultiply(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT16 opacity) +{ + COMMON_COMPOSITE_OP_PROLOG(); + + { + + for (int channel = 0; channel < xyz::MAX_CHANNEL_XYZ; channel++) { + TQ_UINT16 srcColor = src[channel]; + TQ_UINT16 dstColor = dst[channel]; + + srcColor = UINT16_MULT(srcColor, dstColor); + + dst[channel] = UINT16_BLEND(srcColor, dstColor, srcBlend); + + } + } + + COMMON_COMPOSITE_OP_EPILOG(); +} + +void KisXyzColorSpace::compositeDivide(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT16 opacity) +{ + COMMON_COMPOSITE_OP_PROLOG(); + + { + for (int channel = 0; channel < xyz::MAX_CHANNEL_XYZ; channel++) { + + TQ_UINT16 srcColor = src[channel]; + TQ_UINT16 dstColor = dst[channel]; + + srcColor = TQMIN((dstColor * (UINT16_MAX + 1u) + (srcColor / 2u)) / (1u + srcColor), UINT16_MAX); + + TQ_UINT16 newColor = UINT16_BLEND(srcColor, dstColor, srcBlend); + + dst[channel] = newColor; + } + } + + COMMON_COMPOSITE_OP_EPILOG(); +} + +void KisXyzColorSpace::compositeScreen(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT16 opacity) +{ + COMMON_COMPOSITE_OP_PROLOG(); + + { + for (int channel = 0; channel < xyz::MAX_CHANNEL_XYZ; channel++) { + + TQ_UINT16 srcColor = src[channel]; + TQ_UINT16 dstColor = dst[channel]; + + srcColor = UINT16_MAX - UINT16_MULT(UINT16_MAX - dstColor, UINT16_MAX - srcColor); + + TQ_UINT16 newColor = UINT16_BLEND(srcColor, dstColor, srcBlend); + + dst[channel] = newColor; + } + } + + COMMON_COMPOSITE_OP_EPILOG(); +} + +void KisXyzColorSpace::compositeOverlay(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT16 opacity) +{ + COMMON_COMPOSITE_OP_PROLOG(); + + { + for (int channel = 0; channel < xyz::MAX_CHANNEL_XYZ; channel++) { + + TQ_UINT16 srcColor = src[channel]; + TQ_UINT16 dstColor = dst[channel]; + + srcColor = UINT16_MULT(dstColor, dstColor + 2u * UINT16_MULT(srcColor, UINT16_MAX - dstColor)); + + TQ_UINT16 newColor = UINT16_BLEND(srcColor, dstColor, srcBlend); + + dst[channel] = newColor; + } + } + + COMMON_COMPOSITE_OP_EPILOG(); +} + +void KisXyzColorSpace::compositeDodge(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT16 opacity) +{ + COMMON_COMPOSITE_OP_PROLOG(); + + { + for (int channel = 0; channel < xyz::MAX_CHANNEL_XYZ; channel++) { + + TQ_UINT16 srcColor = src[channel]; + TQ_UINT16 dstColor = dst[channel]; + + srcColor = TQMIN((dstColor * (UINT16_MAX + 1u)) / (UINT16_MAX + 1u - srcColor), UINT16_MAX); + + TQ_UINT16 newColor = UINT16_BLEND(srcColor, dstColor, srcBlend); + + dst[channel] = newColor; + } + } + + COMMON_COMPOSITE_OP_EPILOG(); +} + +void KisXyzColorSpace::compositeBurn(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT16 opacity) +{ + COMMON_COMPOSITE_OP_PROLOG(); + + { + for (int channel = 0; channel < xyz::MAX_CHANNEL_XYZ; channel++) { + + TQ_UINT16 srcColor = src[channel]; + TQ_UINT16 dstColor = dst[channel]; + + srcColor = TQMIN(((UINT16_MAX - dstColor) * (UINT16_MAX + 1u)) / (srcColor + 1u), UINT16_MAX); + srcColor = CLAMP(UINT16_MAX - srcColor, 0u, UINT16_MAX); + + TQ_UINT16 newColor = UINT16_BLEND(srcColor, dstColor, srcBlend); + + dst[channel] = newColor; + } + } + + COMMON_COMPOSITE_OP_EPILOG(); +} + +void KisXyzColorSpace::compositeDarken(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT16 opacity) +{ + COMMON_COMPOSITE_OP_PROLOG(); + + { + for (int channel = 0; channel < xyz::MAX_CHANNEL_XYZ; channel++) { + + TQ_UINT16 srcColor = src[channel]; + TQ_UINT16 dstColor = dst[channel]; + + srcColor = TQMIN(srcColor, dstColor); + + TQ_UINT16 newColor = UINT16_BLEND(srcColor, dstColor, srcBlend); + + dst[channel] = newColor; + } + } + + COMMON_COMPOSITE_OP_EPILOG(); +} + +void KisXyzColorSpace::compositeLighten(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT16 opacity) +{ + COMMON_COMPOSITE_OP_PROLOG(); + + { + for (int channel = 0; channel < xyz::MAX_CHANNEL_XYZ; channel++) { + + TQ_UINT16 srcColor = src[channel]; + TQ_UINT16 dstColor = dst[channel]; + + srcColor = TQMAX(srcColor, dstColor); + + TQ_UINT16 newColor = UINT16_BLEND(srcColor, dstColor, srcBlend); + + dst[channel] = newColor; + } + } + + COMMON_COMPOSITE_OP_EPILOG(); +} + + + +void KisXyzColorSpace::compositeErase(TQ_UINT8 *dst, + TQ_INT32 dstRowSize, + const TQ_UINT8 *src, + TQ_INT32 srcRowSize, + const TQ_UINT8 *srcAlphaMask, + TQ_INT32 tqmaskRowStride, + TQ_INT32 rows, + TQ_INT32 cols, + TQ_UINT16 /*opacity*/) +{ + while (rows-- > 0) + { + const Pixel *s = reinterpret_cast<const Pixel *>(src); + Pixel *d = reinterpret_cast<Pixel *>(dst); + const TQ_UINT8 *tqmask = srcAlphaMask; + + for (TQ_INT32 i = cols; i > 0; i--, s++, d++) + { + TQ_UINT16 srcAlpha = s -> alpha; + + // apply the alphatqmask + if (tqmask != 0) { + TQ_UINT8 U8_tqmask = *tqmask; + + if (U8_tqmask != OPACITY_OPAQUE) { + srcAlpha = UINT16_BLEND(srcAlpha, U16_OPACITY_OPAQUE, UINT8_TO_UINT16(U8_tqmask)); + } + tqmask++; + } + d -> alpha = UINT16_MULT(srcAlpha, d -> alpha); + } + + dst += dstRowSize; + src += srcRowSize; + if(srcAlphaMask) { + srcAlphaMask += tqmaskRowStride; + } + } +} + +void KisXyzColorSpace::bitBlt(TQ_UINT8 *dst, + TQ_INT32 dstRowStride, + const TQ_UINT8 *src, + TQ_INT32 srcRowStride, + const TQ_UINT8 *tqmask, + TQ_INT32 tqmaskRowStride, + TQ_UINT8 U8_opacity, + TQ_INT32 rows, + TQ_INT32 cols, + const KisCompositeOp& op) +{ + TQ_UINT16 opacity = UINT8_TO_UINT16(U8_opacity); + + switch (op.op()) { + case COMPOSITE_UNDEF: + // Undefined == no composition + break; + case COMPOSITE_OVER: + compositeOver(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_IN: + //compositeIn(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + case COMPOSITE_OUT: + //compositeOut(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_ATOP: + //compositeAtop(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_XOR: + //compositeXor(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_PLUS: + //compositePlus(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_MINUS: + //compositeMinus(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_ADD: + //compositeAdd(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_SUBTRACT: + //compositeSubtract(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_DIFF: + //compositeDiff(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_MULT: + compositeMultiply(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_DIVIDE: + compositeDivide(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_BUMPMAP: + //compositeBumpmap(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_COPY: + compositeCopy(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, U8_opacity); + break; + case COMPOSITE_COPY_RED: + //compositeCopyRed(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_COPY_GREEN: + //compositeCopyGreen(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_COPY_BLUE: + //compositeCopyBlue(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_COPY_OPACITY: + //compositeCopyOpacity(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_CLEAR: + //compositeClear(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_DISSOLVE: + //compositeDissolve(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_DISPLACE: + //compositeDisplace(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; +#if 0 + case COMPOSITE_MODULATE: + compositeModulate(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_THRESHOLD: + compositeThreshold(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; +#endif + case COMPOSITE_NO: + // No composition. + break; + case COMPOSITE_DARKEN: + compositeDarken(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_LIGHTEN: + compositeLighten(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_HUE: + //compositeHue(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_SATURATION: + //compositeSaturation(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_VALUE: + //compositeValue(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_COLOR: + //compositeColor(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_COLORIZE: + //compositeColorize(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_LUMINIZE: + //compositeLuminize(pixelSize(), dst, dstRowStride, src, srcRowStride, rows, cols, opacity); + break; + case COMPOSITE_SCREEN: + compositeScreen(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_OVERLAY: + compositeOverlay(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_ERASE: + compositeErase(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_DODGE: + compositeDodge(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_BURN: + compositeBurn(dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, rows, cols, opacity); + break; + case COMPOSITE_ALPHA_DARKEN: + abstractCompositeAlphaDarken<TQ_UINT16, U16Mult, Uint8ToU16, U16OpacityTest, + PIXEL_ALPHA, xyz::MAX_CHANNEL_XYZ, xyz::MAX_CHANNEL_XYZA>( + dst, dstRowStride, src, srcRowStride, tqmask, tqmaskRowStride, + rows, cols, opacity, U16Mult(), Uint8ToU16(), U16OpacityTest()); + break; + default: + break; + } +} + +KisCompositeOpList KisXyzColorSpace::userVisiblecompositeOps() const +{ + KisCompositeOpList list; + + list.append(KisCompositeOp(COMPOSITE_OVER)); + list.append(KisCompositeOp(COMPOSITE_ALPHA_DARKEN)); + list.append(KisCompositeOp(COMPOSITE_MULT)); + list.append(KisCompositeOp(COMPOSITE_BURN)); + list.append(KisCompositeOp(COMPOSITE_DODGE)); + list.append(KisCompositeOp(COMPOSITE_DIVIDE)); + list.append(KisCompositeOp(COMPOSITE_SCREEN)); + list.append(KisCompositeOp(COMPOSITE_OVERLAY)); + list.append(KisCompositeOp(COMPOSITE_DARKEN)); + list.append(KisCompositeOp(COMPOSITE_LIGHTEN)); + + return list; +} + + diff --git a/chalk/chalkcolor/colorspaces/kis_xyz_colorspace.h b/chalk/chalkcolor/colorspaces/kis_xyz_colorspace.h new file mode 100644 index 00000000..2f7c3efd --- /dev/null +++ b/chalk/chalkcolor/colorspaces/kis_xyz_colorspace.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt (boud@valdyas.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KIS__COLORSPACE_XYZ_H_ +#define KIS__COLORSPACE_XYZ_H_ + +#include <tqcolor.h> + +#include "kis_global.h" +#include "kis_integer_maths.h" +#include "kis_u16_base_colorspace.h" + +namespace xyz { + const TQ_INT32 MAX_CHANNEL_XYZ = 3; + const TQ_INT32 MAX_CHANNEL_XYZA = 4; +} + + + +class KisXyzColorSpace : public KisU16BaseColorSpace { + +public: + + struct Pixel { + TQ_UINT16 X; + TQ_UINT16 Y; + TQ_UINT16 Z; + TQ_UINT16 alpha; + }; + +public: + KisXyzColorSpace(KisColorSpaceFactoryRegistry * tqparent, + KisProfile *p); + virtual ~KisXyzColorSpace(); + + virtual bool willDegrade(ColorSpaceIndependence independence) + { + if (independence == TO_RGBA8) + return true; + else + return false; + }; +public: + // Pixel manipulation + virtual KisColorAdjustment *createBrightnessContrastAdjustment(TQ_UINT16 *transferValues); + virtual void applyAdjustment(const TQ_UINT8 *src, TQ_UINT8 *dst, KisColorAdjustment *, TQ_INT32 nPixels); + virtual void invertColor(TQ_UINT8 * src, TQ_INT32 nPixels); + virtual void mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const; + virtual void convolveColors(TQ_UINT8** colors, TQ_INT32* kernelValues, KisChannelInfo::enumChannelFlags channelFlags, TQ_UINT8 *dst, TQ_INT32 factor, TQ_INT32 offset, TQ_INT32 nPixels) const; + virtual void darken(const TQ_UINT8 * src, TQ_UINT8 * dst, TQ_INT32 shade, bool compensate, double compensation, TQ_INT32 nPixels) const; + virtual TQ_UINT8 intensity8(const TQ_UINT8 * src) const; + + // Information about the colorstrategy + virtual TQValueVector<KisChannelInfo *> channels() const; + virtual TQ_UINT32 nChannels() const; + virtual TQ_UINT32 nColorChannels() const; + virtual TQ_UINT32 pixelSize() const; + + + // Composition + + virtual void bitBlt(TQ_UINT8 *dst, + TQ_INT32 dststride, + const TQ_UINT8 *src, + TQ_INT32 srcRowStride, + const TQ_UINT8 *srcAlphaMask, + TQ_INT32 tqmaskRowStride, + TQ_UINT8 opacity, + TQ_INT32 rows, + TQ_INT32 cols, + const KisCompositeOp& op); + + KisCompositeOpList userVisiblecompositeOps() const; + +protected: + void compositeOver(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeMultiply(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeDivide(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeScreen(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeOverlay(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeDodge(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeBurn(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeDarken(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeLighten(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + void compositeErase(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmask, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 columns, TQ_UINT16 opacity); + +private: + + static const TQ_UINT8 PIXEL_X = 0; + static const TQ_UINT8 PIXEL_Y = 1; + static const TQ_UINT8 PIXEL_Z = 2; + static const TQ_UINT8 PIXEL_ALPHA = 3; + + TQ_UINT8 * m_qcolordata; // A small buffer for conversion from and to qcolor. + +}; + +#endif // KIS__COLORSPACE_XYZ_H_ diff --git a/chalk/chalkcolor/kis_abstract_colorspace.cc b/chalk/chalkcolor/kis_abstract_colorspace.cc new file mode 100644 index 00000000..a19b9d69 --- /dev/null +++ b/chalk/chalkcolor/kis_abstract_colorspace.cc @@ -0,0 +1,762 @@ +/* + * Copyright (c) 2002 Patrick Julien <freak@codepimps.org> + * Copyright (c) 2004 Cyrille Berger <cberger@cberger.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include <tqimage.h> + +#include <kdebug.h> +#include <kconfig.h> + +#include "kis_abstract_colorspace.h" +#include "kis_global.h" +#include "kis_profile.h" +#include "kis_id.h" +#include "kis_integer_maths.h" +#include "kis_color_conversions.h" +#include "kis_colorspace_factory_registry.h" +#include "kis_channelinfo.h" + +class KisColorAdjustmentImpl : public KisColorAdjustment +{ + public: + + KisColorAdjustmentImpl() : KisColorAdjustment() + { + csProfile = 0; + transform = 0; + profiles[0] = 0; + profiles[1] = 0; + profiles[2] = 0; + }; + + ~KisColorAdjustmentImpl() { + + if (transform) + cmsDeleteTransform(transform); + if (profiles[0] && profiles[0] != csProfile) + cmsCloseProfile(profiles[0]); + if(profiles[1] && profiles[1] != csProfile) + cmsCloseProfile(profiles[1]); + if(profiles[2] && profiles[2] != csProfile) + cmsCloseProfile(profiles[2]); + } + + cmsHPROFILE csProfile; + cmsHPROFILE profiles[3]; + cmsHTRANSFORM transform; +}; + +KisAbstractColorSpace::KisAbstractColorSpace(const KisID& id, + DWORD cmType, + icColorSpaceSignature colorSpaceSignature, + KisColorSpaceFactoryRegistry * tqparent, + KisProfile *p) + : m_parent( tqparent ) + , m_profile( p ) + , m_id( id ) + , m_cmType( cmType ) + , m_colorSpaceSignature( colorSpaceSignature ) +{ + m_alphaPos = -1; + m_alphaSize = -1; + m_qcolordata = 0; + m_lastUsedDstColorSpace = 0; + m_lastUsedTransform = 0; + m_lastRGBProfile = 0; + m_lastToRGB = 0; + m_lastFromRGB = 0; + m_defaultFromRGB = 0; + m_defaultToRGB = 0; + m_defaultFromLab = 0; + m_defaultToLab = 0; +} + +void KisAbstractColorSpace::init() +{ + // Default pixel buffer for TQColor conversion + m_qcolordata = new TQ_UINT8[3]; + Q_CHECK_PTR(m_qcolordata); + + if (m_profile == 0) return; + + // For conversions from default rgb + m_lastFromRGB = cmsCreate_sRGBProfile(); + + m_defaultFromRGB = cmsCreateTransform(m_lastFromRGB, TYPE_BGR_8, + m_profile->profile(), m_cmType, + INTENT_PERCEPTUAL, 0); + + m_defaultToRGB = cmsCreateTransform(m_profile->profile(), m_cmType, + m_lastFromRGB, TYPE_BGR_8, + INTENT_PERCEPTUAL, 0); + + cmsHPROFILE hLab = cmsCreateLabProfile(NULL); + + m_defaultFromLab = cmsCreateTransform(hLab, TYPE_Lab_16, m_profile->profile(), m_cmType, + INTENT_PERCEPTUAL, 0); + + m_defaultToLab = cmsCreateTransform(m_profile->profile(), m_cmType, hLab, TYPE_Lab_16, + INTENT_PERCEPTUAL, 0); +} + +KisAbstractColorSpace::~KisAbstractColorSpace() +{ +} + + + +void KisAbstractColorSpace::fromTQColor(const TQColor& color, TQ_UINT8 *dst, KisProfile * profile) +{ + m_qcolordata[2] = color.red(); + m_qcolordata[1] = color.green(); + m_qcolordata[0] = color.blue(); + + + if (profile == 0) { + // Default sRGB + if (!m_defaultFromRGB) return; + + cmsDoTransform(m_defaultFromRGB, m_qcolordata, dst, 1); + } + else { + if (m_lastFromRGB == 0 || (m_lastFromRGB != 0 && m_lastRGBProfile != profile->profile())) { + m_lastFromRGB = cmsCreateTransform(profile->profile(), TYPE_BGR_8, + m_profile->profile(), m_cmType, + INTENT_PERCEPTUAL, 0); + m_lastRGBProfile = profile->profile(); + + } + cmsDoTransform(m_lastFromRGB, m_qcolordata, dst, 1); + } + + setAlpha(dst, OPACITY_OPAQUE, 1); +} + +void KisAbstractColorSpace::fromTQColor(const TQColor& color, TQ_UINT8 opacity, TQ_UINT8 *dst, KisProfile * profile) +{ + fromTQColor(color, dst, profile); + setAlpha(dst, opacity, 1); +} + +void KisAbstractColorSpace::toTQColor(const TQ_UINT8 *src, TQColor *c, KisProfile * profile) +{ + if (profile == 0) { + // Default sRGB transform + if (!m_defaultToRGB) return; + cmsDoTransform(m_defaultToRGB, const_cast <TQ_UINT8 *>(src), m_qcolordata, 1); + } + else { + if (m_lastToRGB == 0 || (m_lastToRGB != 0 && m_lastRGBProfile != profile->profile())) { + m_lastToRGB = cmsCreateTransform(m_profile->profile(), m_cmType, + profile->profile(), TYPE_BGR_8, + INTENT_PERCEPTUAL, 0); + m_lastRGBProfile = profile->profile(); + } + cmsDoTransform(m_lastToRGB, const_cast <TQ_UINT8 *>(src), m_qcolordata, 1); + } + c->setRgb(m_qcolordata[2], m_qcolordata[1], m_qcolordata[0]); +} + +void KisAbstractColorSpace::toTQColor(const TQ_UINT8 *src, TQColor *c, TQ_UINT8 *opacity, KisProfile * profile) +{ + toTQColor(src, c, profile); + *opacity = getAlpha(src); +} + +void KisAbstractColorSpace::toLabA16(const TQ_UINT8 * src, TQ_UINT8 * dst, const TQ_UINT32 nPixels) const +{ + if ( m_defaultToLab == 0 ) return; + + cmsDoTransform( m_defaultToLab, const_cast<TQ_UINT8 *>( src ), dst, nPixels ); +} + +void KisAbstractColorSpace::fromLabA16(const TQ_UINT8 * src, TQ_UINT8 * dst, const TQ_UINT32 nPixels) const +{ + if ( m_defaultFromLab == 0 ) return; + + cmsDoTransform( m_defaultFromLab, const_cast<TQ_UINT8 *>( src ), dst, nPixels ); +} + + +void KisAbstractColorSpace::getSingleChannelPixel(TQ_UINT8 *dstPixel, const TQ_UINT8 *srcPixel, TQ_UINT32 channelIndex) +{ + if (channelIndex < m_channels.count()) { + + fromTQColor(TQt::black, OPACITY_TRANSPARENT, dstPixel); + + const KisChannelInfo *channelInfo = m_channels[channelIndex]; + memcpy(dstPixel + channelInfo->pos(), srcPixel + channelInfo->pos(), channelInfo->size()); + } +} + +bool KisAbstractColorSpace::convertPixelsTo(const TQ_UINT8 * src, + TQ_UINT8 * dst, + KisColorSpace * dstColorSpace, + TQ_UINT32 numPixels, + TQ_INT32 renderingIntent) +{ + if (dstColorSpace->colorSpaceType() == colorSpaceType() + && dstColorSpace->getProfile() == getProfile()) + { + if (src!= dst) + memcpy (dst, src, numPixels * pixelSize()); + + return true; + } + + cmsHTRANSFORM tf = 0; + + TQ_INT32 srcPixelSize = pixelSize(); + TQ_INT32 dstPixelSize = dstColorSpace->pixelSize(); + + if (m_lastUsedTransform != 0 && m_lastUsedDstColorSpace != 0) { + if (dstColorSpace->colorSpaceType() == m_lastUsedDstColorSpace->colorSpaceType() && + dstColorSpace->getProfile() == m_lastUsedDstColorSpace->getProfile()) { + tf = m_lastUsedTransform; + } + } + + if (!tf && m_profile && dstColorSpace->getProfile()) { + + if (!m_transforms.tqcontains(dstColorSpace)) { + tf = createTransform(dstColorSpace, + m_profile, + dstColorSpace->getProfile(), + renderingIntent); + if (tf) { + // XXX: Should we clear the transform cache if it gets too big? + m_transforms[dstColorSpace] = tf; + } + } + else { + tf = m_transforms[dstColorSpace]; + } + + if ( tf ) { + m_lastUsedTransform = tf; + m_lastUsedDstColorSpace = dstColorSpace; + } + } + + if (tf) { + + cmsDoTransform(tf, const_cast<TQ_UINT8 *>(src), dst, numPixels); + + // Lcms does nothing to the destination alpha channel so we must convert that manually. + while (numPixels > 0) { + TQ_UINT8 alpha = getAlpha(src); + dstColorSpace->setAlpha(dst, alpha, 1); + + src += srcPixelSize; + dst += dstPixelSize; + numPixels--; + } + + return true; + } + + // Last resort fallback. This will be removed when this class is renamed KisLCMSColorSpace after 1.5. + while (numPixels > 0) { + TQColor color; + TQ_UINT8 opacity; + + toTQColor(src, &color, &opacity); + dstColorSpace->fromTQColor(color, opacity, dst); + + src += srcPixelSize; + dst += dstPixelSize; + numPixels--; + } + + return true; +} + + +KisColorAdjustment *KisAbstractColorSpace::createBrightnessContrastAdjustment(TQ_UINT16 *transferValues) +{ + if (!m_profile) return 0; + + LPGAMMATABLE transferFunctions[3]; + transferFunctions[0] = cmsBuildGamma(256, 1.0); + transferFunctions[1] = cmsBuildGamma(256, 1.0); + transferFunctions[2] = cmsBuildGamma(256, 1.0); + + for(int i =0; i < 256; i++) + transferFunctions[0]->GammaTable[i] = transferValues[i]; + + KisColorAdjustmentImpl *adj = new KisColorAdjustmentImpl; + adj->profiles[1] = cmsCreateLinearizationDeviceLink(icSigLabData, transferFunctions); + cmsSetDeviceClass(adj->profiles[1], icSigAbstractClass); + + adj->profiles[0] = m_profile->profile(); + adj->profiles[2] = m_profile->profile(); + adj->transform = cmsCreateMultiprofileTransform(adj->profiles, 3, m_cmType, m_cmType, INTENT_PERCEPTUAL, 0); + adj->csProfile = m_profile->profile(); + return adj; +} + +typedef struct { + double Saturation; + +} BCHSWADJUSTS, *LPBCHSWADJUSTS; + + +static int desaturateSampler(register WORD In[], register WORD Out[], register LPVOID /*Cargo*/) +{ + cmsCIELab LabIn, LabOut; + cmsCIELCh LChIn, LChOut; + //LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo; + + cmsLabEncoded2Float(&LabIn, In); + + cmsLab2LCh(&LChIn, &LabIn); + + // Do some adjusts on LCh + LChOut.L = LChIn.L; + LChOut.C = 0;//LChIn.C + bchsw->Saturation; + LChOut.h = LChIn.h; + + cmsLCh2Lab(&LabOut, &LChOut); + + // Back to encoded + cmsFloat2LabEncoded(Out, &LabOut); + + return TRUE; +} + +KisColorAdjustment *KisAbstractColorSpace::createDesaturateAdjustment() +{ + if (!m_profile) return 0; + + KisColorAdjustmentImpl *adj = new KisColorAdjustmentImpl; + + adj->profiles[0] = m_profile->profile(); + adj->profiles[2] = m_profile->profile(); + adj->csProfile = m_profile->profile(); + + LPLUT Lut; + BCHSWADJUSTS bchsw; + + bchsw.Saturation = -25; + + adj->profiles[1] = _cmsCreateProfilePlaceholder(); + if (!adj->profiles[1]) // can't allocate + return NULL; + + cmsSetDeviceClass(adj->profiles[1], icSigAbstractClass); + cmsSetColorSpace(adj->profiles[1], icSigLabData); + cmsSetPCS(adj->profiles[1], icSigLabData); + + cmsSetRenderingIntent(adj->profiles[1], INTENT_PERCEPTUAL); + + // Creates a LUT with 3D grid only + Lut = cmsAllocLUT(); + + cmsAlloc3DGrid(Lut, 32, 3, 3); + + if (!cmsSample3DGrid(Lut, desaturateSampler, static_cast<LPVOID>(&bchsw), 0)) { + // Shouldn't reach here + cmsFreeLUT(Lut); + cmsCloseProfile(adj->profiles[1]); + return NULL; + } + + // Create tags + + cmsAddTag(adj->profiles[1], icSigDeviceMfgDescTag, (LPVOID) "(chalk internal)"); + cmsAddTag(adj->profiles[1], icSigProfileDescriptionTag, (LPVOID) "chalk saturation abstract profile"); + cmsAddTag(adj->profiles[1], icSigDeviceModelDescTag, (LPVOID) "saturation built-in"); + + cmsAddTag(adj->profiles[1], icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ()); + + cmsAddTag(adj->profiles[1], icSigAToB0Tag, (LPVOID) Lut); + + // LUT is already on virtual profile + cmsFreeLUT(Lut); + + adj->transform = cmsCreateMultiprofileTransform(adj->profiles, 3, m_cmType, m_cmType, INTENT_PERCEPTUAL, 0); + + return adj; +} + +KisColorAdjustment *KisAbstractColorSpace::createPerChannelAdjustment(TQ_UINT16 **transferValues) +{ + if (!m_profile) return 0; + + LPGAMMATABLE *transferFunctions = new LPGAMMATABLE[nColorChannels()+1]; + + for(uint ch=0; ch < nColorChannels(); ch++) { + transferFunctions[ch] = cmsBuildGamma(256, 1.0); + for(uint i =0; i < 256; i++) { + transferFunctions[ch]->GammaTable[i] = transferValues[ch][i]; + } + } + + KisColorAdjustmentImpl *adj = new KisColorAdjustmentImpl; + adj->profiles[0] = cmsCreateLinearizationDeviceLink(colorSpaceSignature(), transferFunctions); + adj->profiles[1] = NULL; + adj->profiles[2] = NULL; + adj->csProfile = m_profile->profile(); + adj->transform = cmsCreateTransform(adj->profiles[0], m_cmType, NULL, m_cmType, INTENT_PERCEPTUAL, 0); + + delete [] transferFunctions; + + return adj; +} + + +void KisAbstractColorSpace::applyAdjustment(const TQ_UINT8 *src, TQ_UINT8 *dst, KisColorAdjustment *adjustment, TQ_INT32 nPixels) +{ + KisColorAdjustmentImpl * adj = dynamic_cast<KisColorAdjustmentImpl*>(adjustment); + if (adj) + cmsDoTransform(adj->transform, const_cast<TQ_UINT8 *>(src), dst, nPixels); +} + + +void KisAbstractColorSpace::invertColor(TQ_UINT8 * src, TQ_INT32 nPixels) +{ + TQColor c; + TQ_UINT8 opacity; + TQ_UINT32 psize = pixelSize(); + + while (nPixels--) + { + toTQColor(src, &c, &opacity); + c.setRgb(TQ_UINT8_MAX - c.red(), TQ_UINT8_MAX - c.green(), TQ_UINT8_MAX - c.blue()); + fromTQColor( c, opacity, src); + + src += psize; + } +} + +TQ_UINT8 KisAbstractColorSpace::difference(const TQ_UINT8* src1, const TQ_UINT8* src2) +{ + if (m_defaultToLab) { + + TQ_UINT8 lab1[8], lab2[8]; + cmsCIELab labF1, labF2; + + if (getAlpha(src1) == OPACITY_TRANSPARENT || getAlpha(src2) == OPACITY_TRANSPARENT) + return (getAlpha(src1) == getAlpha(src2) ? 0 : 255); + + cmsDoTransform( m_defaultToLab, const_cast<TQ_UINT8*>( src1 ), lab1, 1); + cmsDoTransform( m_defaultToLab, const_cast<TQ_UINT8*>( src2 ), lab2, 1); + cmsLabEncoded2Float(&labF1, (WORD *)lab1); + cmsLabEncoded2Float(&labF2, (WORD *)lab2); + double diff = cmsDeltaE(&labF1, &labF2); + if(diff>255) + return 255; + else + return TQ_INT8(diff); + } + else { + TQColor c1; + TQ_UINT8 opacity1; + toTQColor(src1, &c1, &opacity1); + + TQColor c2; + TQ_UINT8 opacity2; + toTQColor(src2, &c2, &opacity2); + + TQ_UINT8 red = abs(c1.red() - c2.red()); + TQ_UINT8 green = abs(c1.green() - c2.green()); + TQ_UINT8 blue = abs(c1.blue() - c2.blue()); + return TQMAX(red, TQMAX(green, blue)); + } +} + +void KisAbstractColorSpace::mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const +{ + TQ_UINT32 totalRed = 0, totalGreen = 0, totalBlue = 0, newAlpha = 0; + + TQColor c; + TQ_UINT8 opacity; + + while (nColors--) + { + // Ugly hack to get around the current constness mess of the colour strategy... + const_cast<KisAbstractColorSpace *>(this)->toTQColor(*colors, &c, &opacity); + + TQ_UINT32 alphaTimesWeight = UINT8_MULT(opacity, *weights); + + totalRed += c.red() * alphaTimesWeight; + totalGreen += c.green() * alphaTimesWeight; + totalBlue += c.blue() * alphaTimesWeight; + newAlpha += alphaTimesWeight; + + weights++; + colors++; + } + + Q_ASSERT(newAlpha <= 255); + + if (newAlpha > 0) { + totalRed = UINT8_DIVIDE(totalRed, newAlpha); + totalGreen = UINT8_DIVIDE(totalGreen, newAlpha); + totalBlue = UINT8_DIVIDE(totalBlue, newAlpha); + } + + // Divide by 255. + totalRed += 0x80; + + TQ_UINT32 dstRed = ((totalRed >> 8) + totalRed) >> 8; + Q_ASSERT(dstRed <= 255); + + totalGreen += 0x80; + TQ_UINT32 dstGreen = ((totalGreen >> 8) + totalGreen) >> 8; + Q_ASSERT(dstGreen <= 255); + + totalBlue += 0x80; + TQ_UINT32 dstBlue = ((totalBlue >> 8) + totalBlue) >> 8; + Q_ASSERT(dstBlue <= 255); + + const_cast<KisAbstractColorSpace *>(this)->fromTQColor(TQColor(dstRed, dstGreen, dstBlue), newAlpha, dst); +} + +void KisAbstractColorSpace::convolveColors(TQ_UINT8** colors, TQ_INT32 * kernelValues, KisChannelInfo::enumChannelFlags channelFlags, + TQ_UINT8 *dst, TQ_INT32 factor, TQ_INT32 offset, TQ_INT32 nColors) const +{ + TQ_INT32 totalRed = 0, totalGreen = 0, totalBlue = 0, totalAlpha = 0; + + TQColor dstColor; + TQ_UINT8 dstOpacity; + + const_cast<KisAbstractColorSpace *>(this)->toTQColor(dst, &dstColor, &dstOpacity); + + while (nColors--) + { + TQ_INT32 weight = *kernelValues; + + if (weight != 0) { + TQColor c; + TQ_UINT8 opacity; + const_cast<KisAbstractColorSpace *>(this)->toTQColor( *colors, &c, &opacity ); + totalRed += c.red() * weight; + totalGreen += c.green() * weight; + totalBlue += c.blue() * weight; + totalAlpha += opacity * weight; + } + colors++; + kernelValues++; + } + + + if (channelFlags & KisChannelInfo::FLAG_COLOR) { + const_cast<KisAbstractColorSpace *>(this)->fromTQColor(TQColor(CLAMP((totalRed / factor) + offset, 0, TQ_UINT8_MAX), + CLAMP((totalGreen / factor) + offset, 0, TQ_UINT8_MAX), + CLAMP((totalBlue / factor) + offset, 0, TQ_UINT8_MAX)), + dstOpacity, + dst); + } + if (channelFlags & KisChannelInfo::FLAG_ALPHA) { + const_cast<KisAbstractColorSpace *>(this)->fromTQColor(dstColor, CLAMP((totalAlpha/ factor) + offset, 0, TQ_UINT8_MAX), dst); + } + +} + +void KisAbstractColorSpace::darken(const TQ_UINT8 * src, TQ_UINT8 * dst, TQ_INT32 shade, bool compensate, double compensation, TQ_INT32 nPixels) const +{ + if (m_defaultToLab) { + TQ_UINT16 * labcache = new TQ_UINT16[nPixels * 4]; + cmsDoTransform( m_defaultToLab, const_cast<TQ_UINT8*>( src ), reinterpret_cast<TQ_UINT8*>( labcache ), nPixels ); + for ( int i = 0; i < nPixels * 4; ++i ) { + if ( compensate ) { + labcache[i] = static_cast<TQ_UINT16>( ( labcache[i] * shade ) / ( compensation * 255 ) ); + } + else { + labcache[i] = static_cast<TQ_UINT16>( labcache[i] * shade / 255 ); + } + } + cmsDoTransform( m_defaultFromLab, reinterpret_cast<TQ_UINT8*>( labcache ), dst, nPixels ); + + // Copy alpha + for ( int i = 0; i < nPixels; ++i ) { + TQ_UINT8 alpha = getAlpha( src ); + setAlpha( dst, alpha, 1 ); + } + delete [] labcache; + } + else { + + TQColor c; + TQ_INT32 psize = pixelSize(); + + for (int i = 0; i < nPixels; ++i) { + + const_cast<KisAbstractColorSpace *>(this)->toTQColor(src + (i * psize), &c); + TQ_INT32 r, g, b; + + if (compensate) { + r = static_cast<TQ_INT32>( TQMIN(255, (c.red() * shade) / (compensation * 255))); + g = static_cast<TQ_INT32>( TQMIN(255, (c.green() * shade) / (compensation * 255))); + b = static_cast<TQ_INT32>( TQMIN(255, (c.blue() * shade) / (compensation * 255))); + } + else { + r = static_cast<TQ_INT32>( TQMIN(255, (c.red() * shade / 255))); + g = static_cast<TQ_INT32>( TQMIN(255, (c.green() * shade / 255))); + b = static_cast<TQ_INT32>( TQMIN(255, (c.blue() * shade / 255))); + } + c.setRgb(r, g, b); + + const_cast<KisAbstractColorSpace *>(this)->fromTQColor( c, dst + (i * psize)); + } + } +} + +TQ_UINT8 KisAbstractColorSpace::intensity8(const TQ_UINT8 * src) const +{ + TQColor c; + TQ_UINT8 opacity; + const_cast<KisAbstractColorSpace *>(this)->toTQColor(src, &c, &opacity); + return static_cast<TQ_UINT8>((c.red() * 0.30 + c.green() * 0.59 + c.blue() * 0.11) + 0.5); + +} + + +KisID KisAbstractColorSpace::mathToolboxID() const +{ + return KisID("Basic"); +} + +void KisAbstractColorSpace::bitBlt(TQ_UINT8 *dst, + TQ_INT32 dststride, + KisColorSpace * srcSpace, + const TQ_UINT8 *src, + TQ_INT32 srcRowStride, + const TQ_UINT8 *srcAlphaMask, + TQ_INT32 tqmaskRowStride, + TQ_UINT8 opacity, + TQ_INT32 rows, + TQ_INT32 cols, + const KisCompositeOp& op) +{ + if (rows <= 0 || cols <= 0) + return; + + if (this != srcSpace) { + TQ_UINT32 len = pixelSize() * rows * cols; + + // If our conversion cache is too small, extend it. + if (!m_conversionCache.resize( len, TQGArray::SpeedOptim )) { + kdWarning() << "Could not allocate enough memory for the conversion!\n"; + // XXX: We should do a slow, pixel by pixel bitblt here... + abort(); + } + + for (TQ_INT32 row = 0; row < rows; row++) { + srcSpace->convertPixelsTo(src + row * srcRowStride, + m_conversionCache.data() + row * cols * pixelSize(), this, + cols); + } + + // The old srcRowStride is no longer valid because we converted to the current cs + srcRowStride = cols * pixelSize(); + + bitBlt(dst, + dststride, + m_conversionCache.data(), + srcRowStride, + srcAlphaMask, + tqmaskRowStride, + opacity, + rows, + cols, + op); + + } + else { + bitBlt(dst, + dststride, + src, + srcRowStride, + srcAlphaMask, + tqmaskRowStride, + opacity, + rows, + cols, + op); + } +} + +TQImage KisAbstractColorSpace::convertToTQImage(const TQ_UINT8 *data, TQ_INT32 width, TQ_INT32 height, + KisProfile *dstProfile, + TQ_INT32 renderingIntent, float /*exposure*/) + +{ + TQImage img = TQImage(width, height, 32, 0, TQImage::LittleEndian); + img.setAlphaBuffer( true ); + + KisColorSpace * dstCS; + + if (dstProfile) + dstCS = m_parent->getColorSpace(KisID("RGBA",""),dstProfile->productName()); + else + dstCS = m_parent->getRGB8(); + + if (data) + convertPixelsTo(const_cast<TQ_UINT8 *>(data), img.bits(), dstCS, width * height, renderingIntent); + + return img; +} + + +cmsHTRANSFORM KisAbstractColorSpace::createTransform(KisColorSpace * dstColorSpace, + KisProfile * srcProfile, + KisProfile * dstProfile, + TQ_INT32 renderingIntent) +{ + KConfig * cfg = KGlobal::config(); + bool bpCompensation = cfg->readBoolEntry("useBlackPointCompensation", false); + + int flags = 0; + + if (bpCompensation) { + flags = cmsFLAGS_BLACKPOINTCOMPENSATION; + } + + if (dstColorSpace && dstProfile && srcProfile ) { + cmsHTRANSFORM tf = cmsCreateTransform(srcProfile->profile(), + colorSpaceType(), + dstProfile->profile(), + dstColorSpace->colorSpaceType(), + renderingIntent, + flags); + + return tf; + } + return 0; +} + +void KisAbstractColorSpace::compositeCopy(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 * /*tqmaskRowStart*/, TQ_INT32 /*tqmaskRowStride*/, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT8 opacity) +{ + TQ_UINT8 *dst = dstRowStart; + const TQ_UINT8 *src = srcRowStart; + TQ_INT32 bytesPerPixel = pixelSize(); + + while (rows > 0) { + memcpy(dst, src, numColumns * bytesPerPixel); + + if (opacity != OPACITY_OPAQUE) { + multiplyAlpha(dst, opacity, numColumns); + } + + dst += dstRowStride; + src += srcRowStride; + --rows; + } +} + diff --git a/chalk/chalkcolor/kis_abstract_colorspace.h b/chalk/chalkcolor/kis_abstract_colorspace.h new file mode 100644 index 00000000..75dfd1ef --- /dev/null +++ b/chalk/chalkcolor/kis_abstract_colorspace.h @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2002 Patrick Julien <freak@codepimps.org> + * Copyright (c) 2004 Cyrille Berger <cberger@cberger.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KIS_ABSTRACT_COLORSPACE_H_ +#define KIS_ABSTRACT_COLORSPACE_H_ + +#include <strings.h> + +#include <tqmap.h> +#include <tqcolor.h> +#include <tqstringlist.h> +#include <tqpair.h> + +#include "kis_global.h" +#include "kis_channelinfo.h" +#include "kis_profile.h" +#include "kis_id.h" +#include "kis_composite_op.h" +#include "kis_colorspace.h" +#include "koffice_export.h" + + +class TQPainter; +class KisPixelRO; +class KisColorSpaceFactoryRegistry; + + +/** + * A colorspace strategy is the definition of a certain color model + * in Chalk. + */ +class KRITA_EXPORT KisAbstractColorSpace : public KisColorSpace { + + +public: + + /** + * @param id The unique human and machine readable identifiation of this colorspace + * @param cmType the lcms type indentification for this colorspace, may be 0 + * @param colorSpaceSignature the icc identification for this colorspace, may be 0 + * @param tqparent the registry that owns this instance + * @param profile the profile this colorspace uses for transforms + */ + KisAbstractColorSpace(const KisID & id, + DWORD cmType, + icColorSpaceSignature colorSpaceSignature, + KisColorSpaceFactoryRegistry * tqparent, + KisProfile *profile); + + void init(); + + virtual ~KisAbstractColorSpace(); + + virtual bool operator==(const KisAbstractColorSpace& rhs) const { + return (m_id == rhs.m_id && m_profile == rhs.m_profile); + } + + +//================== Information about this color strategy ========================// + +public: + + + //========== Channels =====================================================// + + // Return a vector describing all the channels this color model has. + virtual TQValueVector<KisChannelInfo *> channels() const = 0; + + virtual TQ_UINT32 nChannels() const = 0; + + virtual TQ_UINT32 nColorChannels() const = 0; + + virtual TQ_UINT32 nSubstanceChannels() const { return 0; }; + + virtual TQ_UINT32 pixelSize() const = 0; + + virtual TQString channelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const = 0; + + virtual TQString normalisedChannelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const = 0; + + virtual TQ_UINT8 scaleToU8(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos) = 0; + + virtual TQ_UINT16 scaleToU16(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos) = 0; + + virtual void getSingleChannelPixel(TQ_UINT8 *dstPixel, const TQ_UINT8 *srcPixel, TQ_UINT32 channelIndex); + + //========== Identification ===============================================// + + virtual KisID id() const { return m_id; } + + void setColorSpaceType(TQ_UINT32 type) { m_cmType = type; } + TQ_UINT32 colorSpaceType() { return m_cmType; } + + virtual icColorSpaceSignature colorSpaceSignature() { return m_colorSpaceSignature; } + + //========== Capabilities =================================================// + + virtual KisCompositeOpList userVisiblecompositeOps() const = 0; + + /** + * Returns true if the colorspace supports channel values outside the + * (normalised) range 0 to 1. + */ + virtual bool hasHighDynamicRange() const { return false; } + + //========== Display profiles =============================================// + + virtual KisProfile * getProfile() const { return m_profile; }; + + +//================= Conversion functions ==================================// + + + virtual void fromTQColor(const TQColor& c, TQ_UINT8 *dst, KisProfile * profile = 0); + virtual void fromTQColor(const TQColor& c, TQ_UINT8 opacity, TQ_UINT8 *dst, KisProfile * profile = 0); + + virtual void toTQColor(const TQ_UINT8 *src, TQColor *c, KisProfile * profile = 0); + virtual void toTQColor(const TQ_UINT8 *src, TQColor *c, TQ_UINT8 *opacity, KisProfile * profile = 0); + + + virtual void toLabA16(const TQ_UINT8 * src, TQ_UINT8 * dst, const TQ_UINT32 nPixels) const; + virtual void fromLabA16(const TQ_UINT8 * src, TQ_UINT8 * dst, const TQ_UINT32 nPixels) const; + + virtual TQImage convertToTQImage(const TQ_UINT8 *data, TQ_INT32 width, TQ_INT32 height, + KisProfile * dstProfile, + TQ_INT32 renderingIntent = INTENT_PERCEPTUAL, + float exposure = 0.0f); + + virtual bool convertPixelsTo(const TQ_UINT8 * src, + TQ_UINT8 * dst, KisColorSpace * dstColorSpace, + TQ_UINT32 numPixels, + TQ_INT32 renderingIntent = INTENT_PERCEPTUAL); + +//============================== Manipulation fucntions ==========================// + + +// +// The manipulation functions have default implementations that _convert_ the pixel +// to a TQColor and back. Reimplement these methods in your color strategy! +// + virtual KisColorAdjustment *createBrightnessContrastAdjustment(TQ_UINT16 *transferValues); + + virtual KisColorAdjustment *createDesaturateAdjustment(); + + virtual KisColorAdjustment *createPerChannelAdjustment(TQ_UINT16 **transferValues); + + virtual void applyAdjustment(const TQ_UINT8 *src, TQ_UINT8 *dst, KisColorAdjustment *, TQ_INT32 nPixels); + + virtual void invertColor(TQ_UINT8 * src, TQ_INT32 nPixels); + + virtual TQ_UINT8 difference(const TQ_UINT8* src1, const TQ_UINT8* src2); + + virtual void mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const; + + virtual void convolveColors(TQ_UINT8** colors, TQ_INT32* kernelValues, KisChannelInfo::enumChannelFlags channelFlags, TQ_UINT8 *dst, TQ_INT32 factor, TQ_INT32 offset, TQ_INT32 nPixels) const; + + virtual void darken(const TQ_UINT8 * src, TQ_UINT8 * dst, TQ_INT32 shade, bool compensate, double compensation, TQ_INT32 nPixels) const; + + virtual TQ_UINT8 intensity8(const TQ_UINT8 * src) const; + + virtual KisID mathToolboxID() const; + + virtual void bitBlt(TQ_UINT8 *dst, + TQ_INT32 dststride, + KisColorSpace * srcSpace, + const TQ_UINT8 *src, + TQ_INT32 srcRowStride, + const TQ_UINT8 *srcAlphaMask, + TQ_INT32 tqmaskRowStride, + TQ_UINT8 opacity, + TQ_INT32 rows, + TQ_INT32 cols, + const KisCompositeOp& op); + +//========================== END of Public API ========================================// + +protected: + + + /** + * Compose two byte arrays containing pixels in the same color + * model together. + */ + virtual void bitBlt(TQ_UINT8 *dst, + TQ_INT32 dstRowSize, + const TQ_UINT8 *src, + TQ_INT32 srcRowStride, + const TQ_UINT8 *srcAlphaMask, + TQ_INT32 tqmaskRowStride, + TQ_UINT8 opacity, + TQ_INT32 rows, + TQ_INT32 cols, + const KisCompositeOp& op) = 0; + + virtual cmsHTRANSFORM createTransform(KisColorSpace * dstColorSpace, + KisProfile * srcProfile, + KisProfile * dstProfile, + TQ_INT32 renderingIntent); + + virtual void compositeCopy(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT8 opacity); + + + // So I don't need to re-implement it everywhere. + template <typename ColorType, + typename NativeMult, typename Uint8ToNative, typename NativeOpacityTest, + int AlphaPos, int NonAlphaSize, int TotalSize> + void abstractCompositeAlphaDarken(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, + const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, + const TQ_UINT8 *tqmaskRowStart, TQ_INT32 tqmaskRowStride, + TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT8 opacity, + NativeMult nativeMult, Uint8ToNative uint8ToNative, + NativeOpacityTest nativeOpacityTest) { + while (rows > 0) { + + const ColorType *src = reinterpret_cast<const ColorType*>(srcRowStart); + ColorType *dst = reinterpret_cast<ColorType*>(dstRowStart); + const TQ_UINT8 *tqmask = tqmaskRowStart; + TQ_INT32 columns = numColumns; + + while (columns > 0) { + + ColorType srcAlpha = src[AlphaPos]; + ColorType dstAlpha = dst[AlphaPos]; + + // apply the alphatqmask + if(tqmask != 0) + { + if(*tqmask != OPACITY_OPAQUE) + srcAlpha = nativeMult(srcAlpha, uint8ToNative(*tqmask)); + tqmask++; + } + + if (opacity != OPACITY_OPAQUE) { + srcAlpha = nativeMult(srcAlpha, uint8ToNative(opacity)); + } + + // not transparent + if (nativeOpacityTest(srcAlpha) && srcAlpha >= dstAlpha) { + dst[AlphaPos] = srcAlpha; + memcpy(dst, src, NonAlphaSize * sizeof(ColorType)); + } + + columns--; + src += TotalSize; + dst += TotalSize; + } + + rows--; + srcRowStart += srcRowStride; + dstRowStart += dstRowStride; + if(tqmaskRowStart) + tqmaskRowStart += tqmaskRowStride; + } + } + +protected: + + TQStringList m_profileFilenames; + TQ_UINT8 * m_qcolordata; // A small buffer for conversion from and to qcolor. + TQ_INT32 m_alphaPos; // The position in _bytes_ of the alpha channel + TQ_INT32 m_alphaSize; // The width in _bytes_ of the alpha channel + + TQValueVector<KisChannelInfo *> m_channels; + + KisColorSpaceFactoryRegistry * m_parent; + +private: + + cmsHTRANSFORM m_defaultToRGB; // Default transform to 8 bit sRGB + cmsHTRANSFORM m_defaultFromRGB; // Default transform from 8 bit sRGB + + cmsHPROFILE m_lastRGBProfile; // Last used profile to transform to/from RGB + cmsHTRANSFORM m_lastToRGB; // Last used transform to transform to RGB + cmsHTRANSFORM m_lastFromRGB; // Last used transform to transform from RGB + + cmsHTRANSFORM m_defaultToLab; + cmsHTRANSFORM m_defaultFromLab; + + KisProfile * m_profile; + KisColorSpace *m_lastUsedDstColorSpace; + cmsHTRANSFORM m_lastUsedTransform; + + KisID m_id; + DWORD m_cmType; // The colorspace type as defined by littlecms + icColorSpaceSignature m_colorSpaceSignature; // The colorspace signature as defined in icm/icc files + + // cmsHTRANSFORM is a void *, so this should work. + typedef TQMap<KisColorSpace *, cmsHTRANSFORM> TransformMap; + TransformMap m_transforms; // Cache for existing transforms + + KisAbstractColorSpace(const KisAbstractColorSpace&); + KisAbstractColorSpace& operator=(const KisAbstractColorSpace&); + + TQMemArray<TQ_UINT8> m_conversionCache; // XXX: This will be a bad problem when we have threading. +}; + +#endif // KIS_STRATEGY_COLORSPACE_H_ diff --git a/chalk/chalkcolor/kis_basic_histogram_producers.cc b/chalk/chalkcolor/kis_basic_histogram_producers.cc new file mode 100644 index 00000000..b769a3d3 --- /dev/null +++ b/chalk/chalkcolor/kis_basic_histogram_producers.cc @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <tqstring.h> +#include <klocale.h> + +#include "config.h" + +#ifdef HAVE_OPENEXR +#include <half.h> +#endif + +#include "kis_global.h" +#include "kis_basic_histogram_producers.h" +#include "kis_integer_maths.h" +#include "kis_channelinfo.h" +#include "kis_colorspace.h" +#include "kis_lab_colorspace.h" + +KisLabColorSpace* KisGenericLabHistogramProducer::m_labCs = 0; + + +KisBasicHistogramProducer::KisBasicHistogramProducer(const KisID& id, int channels, int nrOfBins, KisColorSpace *cs) + : m_channels(channels), + m_nrOfBins(nrOfBins), + m_colorSpace(cs), + m_id(id) +{ + m_bins.resize(m_channels); + for (int i = 0; i < m_channels; i++) + m_bins.at(i).resize(m_nrOfBins); + m_outLeft.resize(m_channels); + m_outRight.resize(m_channels); + m_count = 0; + m_from = 0.0; + m_width = 1.0; +} + +void KisBasicHistogramProducer::clear() { + m_count = 0; + for (int i = 0; i < m_channels; i++) { + for (int j = 0; j < m_nrOfBins; j++) { + m_bins.at(i).at(j) = 0; + } + m_outRight.at(i) = 0; + m_outLeft.at(i) = 0; + } +} + +void KisBasicHistogramProducer::makeExternalToInternal() { + // This function assumes that the pixel is has no 'gaps'. That is to say: if we start + // at byte 0, we can get to the end of the pixel by adding consecutive size()s of + // the channels + TQValueVector<KisChannelInfo *> c = channels(); + uint count = c.count(); + int currentPos = 0; + + for (uint i = 0; i < count; i++) { + for (uint j = 0; j < count; j++) { + if (c.at(j)->pos() == currentPos) { + m_external.append(j); + break; + } + } + currentPos += c.at(m_external.at(m_external.count() - 1))->size(); + } +} + +// ------------ U8 --------------------- + +KisBasicU8HistogramProducer::KisBasicU8HistogramProducer(const KisID& id, KisColorSpace *cs) + : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs) +{ +} + +TQString KisBasicU8HistogramProducer::positionToString(double pos) const { + return TQString("%1").tqarg(static_cast<TQ_UINT8>(pos * UINT8_MAX)); +} + +void KisBasicU8HistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) +{ + if (!pixels) return; + if (!cs) return; + if (nPixels == 0) return; + + TQ_INT32 pSize = cs->pixelSize(); + + if ( selectionMask ) { + while (nPixels > 0) { + if ( ! (m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT) ) { + + for (int i = 0; i < m_channels; i++) { + m_bins.at(i).at(pixels[i])++; + } + m_count++; + + } + + pixels += pSize; + selectionMask++; + nPixels--; + } + } + else { + while (nPixels > 0) { + if ( ! (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT) ) { + + for (int i = 0; i < m_channels; i++) { + m_bins.at(i).at(pixels[i])++; + } + m_count++; + + } + + pixels += pSize; + nPixels--; + } + } +} + +// ------------ U16 --------------------- + +KisBasicU16HistogramProducer::KisBasicU16HistogramProducer(const KisID& id, KisColorSpace *cs) + : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs) +{ +} + +TQString KisBasicU16HistogramProducer::positionToString(double pos) const +{ + return TQString("%1").tqarg(static_cast<TQ_UINT8>(pos * UINT8_MAX)); +} + +double KisBasicU16HistogramProducer::maximalZoom() const +{ + return 1.0 / 255.0; +} + +void KisBasicU16HistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) +{ + // The view + TQ_UINT16 from = static_cast<TQ_UINT16>(m_from * UINT16_MAX); + TQ_UINT16 width = static_cast<TQ_UINT16>(m_width * UINT16_MAX + 0.5); // We include the end + TQ_UINT16 to = from + width; + double factor = 255.0 / width; + + TQ_INT32 pSize = cs->pixelSize(); + + if ( selectionMask ) { + TQ_UINT16* pixel = reinterpret_cast<TQ_UINT16*>(pixels); + while (nPixels > 0) { + if ( ! ((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) { + for (int i = 0; i < m_channels; i++) { + TQ_UINT16 value = pixel[i]; + if (value > to) + m_outRight.at(i)++; + else if (value < from) + m_outLeft.at(i)++; + else + m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++; + } + m_count++; + } + pixels += pSize; + selectionMask++; + nPixels--; + } + } + else { + while (nPixels > 0) { + TQ_UINT16* pixel = reinterpret_cast<TQ_UINT16*>(pixels); + + if ( ! (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) { + for (int i = 0; i < m_channels; i++) { + TQ_UINT16 value = pixel[i]; + if (value > to) + m_outRight.at(i)++; + else if (value < from) + m_outLeft.at(i)++; + else + m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++; + } + m_count++; + } + pixels += pSize; + nPixels--; + + } + } +} + +// ------------ Float32 --------------------- +KisBasicF32HistogramProducer::KisBasicF32HistogramProducer(const KisID& id, KisColorSpace *cs) + : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs) +{ +} + +TQString KisBasicF32HistogramProducer::positionToString(double pos) const { + return TQString("%1").tqarg(static_cast<float>(pos)); // XXX I doubt this is correct! +} + +double KisBasicF32HistogramProducer::maximalZoom() const { + // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromis for the moment + return 1.0 / 255.0; +} + +void KisBasicF32HistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) { + // The view + float from = static_cast<float>(m_from); + float width = static_cast<float>(m_width); + float to = from + width; + float factor = 255.0 / width; + + TQ_INT32 pSize = cs->pixelSize(); + + if ( selectionMask ) { + while (nPixels > 0) { + + float* pixel = reinterpret_cast<float*>(pixels); + if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) { + for (int i = 0; i < m_channels; i++) { + float value = pixel[i]; + if (value > to) + m_outRight.at(i)++; + else if (value < from) + m_outLeft.at(i)++; + else + m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++; + } + m_count++; + } + + pixels += pSize; + selectionMask++; + nPixels--; + + } + } + else { + while (nPixels > 0) { + + float* pixel = reinterpret_cast<float*>(pixels); + if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) { + for (int i = 0; i < m_channels; i++) { + float value = pixel[i]; + if (value > to) + m_outRight.at(i)++; + else if (value < from) + m_outLeft.at(i)++; + else + m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++; + } + m_count++; + } + + pixels += pSize; + nPixels--; + + } + } +} + +#ifdef HAVE_OPENEXR +// ------------ Float16 Half --------------------- +KisBasicF16HalfHistogramProducer::KisBasicF16HalfHistogramProducer(const KisID& id, + KisColorSpace *cs) + : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs) { +} + +TQString KisBasicF16HalfHistogramProducer::positionToString(double pos) const { + return TQString("%1").tqarg(static_cast<float>(pos)); // XXX I doubt this is correct! +} + +double KisBasicF16HalfHistogramProducer::maximalZoom() const { + // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromis for the moment + return 1.0 / 255.0; +} + +void KisBasicF16HalfHistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) { + // The view + float from = static_cast<float>(m_from); + float width = static_cast<float>(m_width); + float to = from + width; + float factor = 255.0 / width; + + TQ_INT32 pSize = cs->pixelSize(); + if ( selectionMask ) { + while (nPixels > 0) { + half* pixel = reinterpret_cast<half*>(pixels); + if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) { + for (int i = 0; i < m_channels; i++) { + float value = pixel[i]; + if (value > to) + m_outRight.at(i)++; + else if (value < from) + m_outLeft.at(i)++; + else + m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++; + } + m_count++; + } + pixels += pSize; + selectionMask++; + nPixels--; + } + } + else { + while (nPixels > 0) { + half* pixel = reinterpret_cast<half*>(pixels); + if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) { + for (int i = 0; i < m_channels; i++) { + float value = pixel[i]; + if (value > to) + m_outRight.at(i)++; + else if (value < from) + m_outLeft.at(i)++; + else + m_bins.at(i).at(static_cast<TQ_UINT8>((value - from) * factor))++; + } + m_count++; + } + pixels += pSize; + nPixels--; + } + } +} +#endif + +// ------------ Generic RGB --------------------- +KisGenericRGBHistogramProducer::KisGenericRGBHistogramProducer() + : KisBasicHistogramProducer(KisID("GENRGBHISTO", i18n("Generic RGB Histogram")), + 3, 256, 0) { + /* we set 0 as colorspece, because we are not based on a specific colorspace. This + is no problem for the superclass since we override channels() */ + m_channelsList.append(new KisChannelInfo(i18n("R"), i18n("R"), 0, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, TQColor(255,0,0))); + m_channelsList.append(new KisChannelInfo(i18n("G"), i18n("G"), 1, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, TQColor(0,255,0))); + m_channelsList.append(new KisChannelInfo(i18n("B"), i18n("B"), 2, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, TQColor(0,0,255))); +} + +TQValueVector<KisChannelInfo *> KisGenericRGBHistogramProducer::channels() { + return m_channelsList; +} + +TQString KisGenericRGBHistogramProducer::positionToString(double pos) const { + return TQString("%1").tqarg(static_cast<TQ_UINT8>(pos * UINT8_MAX)); +} + +double KisGenericRGBHistogramProducer::maximalZoom() const { + return 1.0; +} + + +void KisGenericRGBHistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) +{ + for (int i = 0; i < m_channels; i++) { + m_outRight.at(i) = 0; + m_outLeft.at(i) = 0; + } + + TQColor c; + TQ_INT32 pSize = cs->pixelSize(); + if (selectionMask) { + while (nPixels > 0) { + if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) { + cs->toTQColor(pixels, &c); + m_bins.at(0).at(c.red())++; + m_bins.at(1).at(c.green())++; + m_bins.at(2).at(c.blue())++; + + m_count++; + } + pixels += pSize; + selectionMask++; + nPixels--; + } + + } + else { + while (nPixels > 0) { + + if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) { + cs->toTQColor(pixels, &c); + m_bins.at(0).at(c.red())++; + m_bins.at(1).at(c.green())++; + m_bins.at(2).at(c.blue())++; + + m_count++; + } + pixels += pSize; + nPixels--; + } + } +} + +// ------------ Generic L*a*b* --------------------- +KisGenericLabHistogramProducer::KisGenericLabHistogramProducer() + : KisBasicHistogramProducer(KisID("GENLABHISTO", i18n("L*a*b* Histogram")), 3, 256, 0) { + /* we set 0 as colorspace, because we are not based on a specific colorspace. This + is no problem for the superclass since we override channels() */ + m_channelsList.append(new KisChannelInfo(i18n("L*"), i18n("L"), 0, KisChannelInfo::COLOR, KisChannelInfo::UINT8)); + m_channelsList.append(new KisChannelInfo(i18n("a*"), i18n("a"), 1, KisChannelInfo::COLOR, KisChannelInfo::UINT8)); + m_channelsList.append(new KisChannelInfo(i18n("b*"), i18n("b"), 2, KisChannelInfo::COLOR, KisChannelInfo::UINT8)); + + if (!m_labCs) { + KisProfile *labProfile = new KisProfile(cmsCreateLabProfile(NULL)); + m_labCs = new KisLabColorSpace(0, labProfile); + } + m_colorSpace = m_labCs; +} +KisGenericLabHistogramProducer::~KisGenericLabHistogramProducer() +{ + delete m_channelsList[0]; + delete m_channelsList[1]; + delete m_channelsList[2]; +} + +TQValueVector<KisChannelInfo *> KisGenericLabHistogramProducer::channels() { + return m_channelsList; +} + +TQString KisGenericLabHistogramProducer::positionToString(double pos) const { + return TQString("%1").tqarg(static_cast<TQ_UINT16>(pos * UINT16_MAX)); +} + +double KisGenericLabHistogramProducer::maximalZoom() const { + return 1.0; +} + + +void KisGenericLabHistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) +{ + for (int i = 0; i < m_channels; i++) { + m_outRight.at(i) = 0; + m_outLeft.at(i) = 0; + } + + TQ_UINT8 dst[8]; + TQ_INT32 pSize = cs->pixelSize(); + + if (selectionMask) { + while (nPixels > 0) { + if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) { +/* + cs->toTQColor(pixels, &c); + m_bins.at(0).at(c.red())++; +*/ + m_count++; + } + pixels += pSize; + selectionMask++; + nPixels--; + } + } + else { + while (nPixels > 0) { + if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) { + + cs->convertPixelsTo(pixels, dst, m_colorSpace, 1); + m_bins.at(0).at(m_colorSpace->scaleToU8(dst, 0))++; + m_bins.at(1).at(m_colorSpace->scaleToU8(dst, 1))++; + m_bins.at(2).at(m_colorSpace->scaleToU8(dst, 2))++; + + m_count++; + } + pixels += pSize; + nPixels--; + } + } +} + diff --git a/chalk/chalkcolor/kis_basic_histogram_producers.h b/chalk/chalkcolor/kis_basic_histogram_producers.h new file mode 100644 index 00000000..8b913ece --- /dev/null +++ b/chalk/chalkcolor/kis_basic_histogram_producers.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_BASIC_HISTOGRAM_PRODUCERS_ +#define _KIS_BASIC_HISTOGRAM_PRODUCERS_ + +#include <tqvaluevector.h> +#include <klocale.h> + +#include "config.h" + +#include "kis_histogram_producer.h" +#include "kis_colorspace.h" +#include "kis_id.h" + +class KisLabColorSpace; + +class KisBasicHistogramProducer : public KisHistogramProducer { +public: + KisBasicHistogramProducer(const KisID& id, int channels, int nrOfBins, KisColorSpace *colorSpace); + virtual ~KisBasicHistogramProducer() {} + + virtual void clear(); + + virtual void setView(double from, double size) { m_from = from; m_width = size; } + + virtual const KisID& id() const { return m_id; } + virtual TQValueVector<KisChannelInfo *> channels() { return m_colorSpace->channels(); } + virtual TQ_INT32 numberOfBins() { return m_nrOfBins; } + virtual double viewFrom() const { return m_from; } + virtual double viewWidth() const { return m_width; } + + virtual TQ_INT32 count() { return m_count; } + + virtual TQ_INT32 getBinAt(int channel, int position) + { return m_bins.at(externalToInternal(channel)).at(position); } + + virtual TQ_INT32 outOfViewLeft(int channel) + { return m_outLeft.at(externalToInternal(channel)); } + + virtual TQ_INT32 outOfViewRight(int channel) + { return m_outRight.at(externalToInternal(channel)); } + +protected: + /** + * The order in which channels() returns is not the same as the internal representation, + * that of the pixel internally. This method converts external usage to internal usage. + * This method uses some basic assumtpions about the tqlayout of the pixel, so _extremely_ + * exotic spaces might want to override this (see makeExternalToInternal source for + * those assumptions) + **/ + virtual int externalToInternal(int ext) { + if (channels().count() > 0 && m_external.count() == 0) // Set up the translation table + makeExternalToInternal(); + return m_external.at(ext); + } + // not virtual since that is useless: we call it from constructor + void makeExternalToInternal(); + typedef TQValueVector<TQ_UINT32> vBins; + TQValueVector<vBins> m_bins; + vBins m_outLeft, m_outRight; + double m_from, m_width; + TQ_INT32 m_count; + int m_channels, m_nrOfBins; + KisColorSpace *m_colorSpace; + KisID m_id; + TQValueVector<TQ_INT32> m_external; +}; + +class KisBasicU8HistogramProducer : public KisBasicHistogramProducer { +public: + KisBasicU8HistogramProducer(const KisID& id, KisColorSpace *colorSpace); + virtual void addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *colorSpace); + virtual TQString positionToString(double pos) const; + virtual double maximalZoom() const { return 1.0; } +}; + +class KisBasicU16HistogramProducer : public KisBasicHistogramProducer { +public: + KisBasicU16HistogramProducer(const KisID& id, KisColorSpace *colorSpace); + virtual void addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *colorSpace); + virtual TQString positionToString(double pos) const; + virtual double maximalZoom() const; +}; + +class KisBasicF32HistogramProducer : public KisBasicHistogramProducer { +public: + KisBasicF32HistogramProducer(const KisID& id, KisColorSpace *colorSpace); + virtual void addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *colorSpace); + virtual TQString positionToString(double pos) const; + virtual double maximalZoom() const; +}; + +#ifdef HAVE_OPENEXR +class KisBasicF16HalfHistogramProducer : public KisBasicHistogramProducer { +public: + KisBasicF16HalfHistogramProducer(const KisID& id, KisColorSpace *colorSpace); + virtual void addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *colorSpace); + virtual TQString positionToString(double pos) const; + virtual double maximalZoom() const; +}; +#endif + +/** + * Parametrized on a specific KisHistogramProducer. Its generated producers + * will have the same KisID as the factory's. This is acceptable because we can't mix + * Factories with Producers in the code because they are incompatible types, and + * in the GUI we actually only need a producer's name, not a factory's. + */ +template<class T> class KisBasicHistogramProducerFactory : public KisHistogramProducerFactory { +public: + KisBasicHistogramProducerFactory(const KisID& id, KisColorSpace *colorSpace) + : KisHistogramProducerFactory(id), m_cs(colorSpace) {} + virtual ~KisBasicHistogramProducerFactory() {} + virtual KisHistogramProducerSP generate() { return new T(id(), m_cs); } + virtual bool isCompatibleWith(KisColorSpace* colorSpace) const { return colorSpace->id() == m_cs->id(); } + virtual float preferrednessLevelWith(KisColorSpace* /*colorSpace*/) const { return 1.0; } +protected: + KisColorSpace *m_cs; +}; + +/** + * This is a Producer (with associated factory) that converts the pixels of the colorspace + * to RGB8 with toTQColor, and then does its counting on RGB. This is NOT registered with the + * Registry, because it isCompatibleWith all colorspaces, and should only be used in extreme + * cases (like no other producer being available + **/ +class KisGenericRGBHistogramProducer : public KisBasicHistogramProducer { +public: + KisGenericRGBHistogramProducer(); + virtual void addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *colorSpace); + virtual TQString positionToString(double pos) const; + virtual double maximalZoom() const; + virtual TQValueVector<KisChannelInfo *> channels(); +protected: + TQValueVector<KisChannelInfo *> m_channelsList; +}; + +/** KisGenericRGBHistogramProducer his special Factory that isCompatibleWith everything. */ +class KisGenericRGBHistogramProducerFactory : public KisHistogramProducerFactory { +public: + KisGenericRGBHistogramProducerFactory() + : KisHistogramProducerFactory(KisID("GENRGBHISTO", i18n("Generic RGB"))) {} + virtual ~KisGenericRGBHistogramProducerFactory() {} + virtual KisHistogramProducerSP generate() { return new KisGenericRGBHistogramProducer(); } + virtual bool isCompatibleWith(KisColorSpace*) const { return true; } + virtual float preferrednessLevelWith(KisColorSpace*) const { return 0.0; } +}; + + +/** + * This is a Producer (with associated factory) that converts the pixels of the colorspace + * to L*a*b*, and then does its counting. + * It isCompatibleWith all colorspaces + **/ +class KisGenericLabHistogramProducer : public KisBasicHistogramProducer { + public: + KisGenericLabHistogramProducer(); + virtual ~KisGenericLabHistogramProducer(); + virtual void addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *colorSpace); + virtual TQString positionToString(double pos) const; + virtual double maximalZoom() const; + virtual TQValueVector<KisChannelInfo *> channels(); + protected: + TQValueVector<KisChannelInfo *> m_channelsList; + private: + static KisLabColorSpace* m_labCs; +}; + +/** KisGenericLabHistogramProducer his special Factory that isCompatibleWith everything. */ +class KisGenericLabHistogramProducerFactory : public KisHistogramProducerFactory { + public: + KisGenericLabHistogramProducerFactory() + : KisHistogramProducerFactory(KisID("GENLABHISTO", i18n("Generic L*a*b*"))) {} + virtual ~KisGenericLabHistogramProducerFactory() {} + virtual KisHistogramProducerSP generate() { return new KisGenericLabHistogramProducer(); } + virtual bool isCompatibleWith(KisColorSpace*) const { return true; } + virtual float preferrednessLevelWith(KisColorSpace*) const { return 0.0; } +}; + + +#endif // _KIS_BASIC_HISTOGRAM_PRODUCERS_ diff --git a/chalk/chalkcolor/kis_channelinfo.h b/chalk/chalkcolor/kis_channelinfo.h new file mode 100644 index 00000000..f8ec8301 --- /dev/null +++ b/chalk/chalkcolor/kis_channelinfo.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KIS_CHANNELINFO_H_ +#define KIS_CHANNELINFO_H_ + +#include <tqcolor.h> +#include "tqstring.h" +#include "ksharedptr.h" + +/** + * This class gives some basic information about a channel, + * that is, one of the components that makes up a particular + * pixel. + */ +class KisChannelInfo : public KShared { +public: + enum enumChannelType { + COLOR, // The channel represents a color + ALPHA, // The channel represents the opacity of a pixel + SUBSTANCE, // The channel represents a real-world substance like pigments or medium + SUBSTRATE // The channel represents a real-world painting substrate like a canvas + }; + + enum enumChannelValueType { + UINT8, + UINT16, + FLOAT16, + FLOAT32, + INT8, + INT16, + OTHER // Use this if the channel is neither an integer or a float + }; + enum enumChannelFlags { + FLAG_COLOR = 1, + FLAG_ALPHA = (1 << 1), + FLAG_SUBSTANCE = (1 << 2), + FLAG_SUBSTRATE = (1 << 3), + FLAG_COLOR_AND_ALPHA = FLAG_ALPHA | FLAG_COLOR // HACK to be able to use convolution of color and alpha at the same time + }; + +public: + KisChannelInfo() { }; + /** + * @param name The i18n'ed name of this channel ("Red") + * @param abbrev A one or two letter abbreviation of the name of this channel ("R") + * @param npos the position of the first byte of this channel value in the pixel + * @param channelType the type of this channel (color, alpha, etc) + * @param channelValueType the datatype of this channel + * @param size the size in bytes of this channel + * @param color a color to visually represent this channel by in the gui + */ + KisChannelInfo( const TQString & name, const TQString & abbrev, TQ_INT32 npos, enumChannelType channelType, enumChannelValueType channelValueType, TQ_INT32 size = 1, TQColor color = TQColor(0,0,0)) + : m_name (name), m_abbrev(abbrev), m_pos (npos), m_channelType(channelType), m_channelValueType(channelValueType), m_size(size), m_color(color) { }; +public: + /** + * User-friendly name for this channel for presentation purposes in the gui + */ + inline TQString name() const { return m_name; }; + + /** + * Return the single-letter abbreviation for this channel + */ + inline TQString abbrev() const { return m_abbrev; }; + /** + * returns the position of the first byte of the channel in the pixel + */ + inline TQ_INT32 pos() const { return m_pos; }; + + /** + * returns the number of bytes this channel takes + */ + inline TQ_INT32 size() const { return m_size; }; + + /** + * returns the type of the channel + */ + inline enumChannelType channelType() const { return m_channelType; }; + /** + * return the type of the value of the channel (float, uint8 or uint16) + */ + inline enumChannelValueType channelValueType() const { return m_channelValueType; }; + /** + * This is a color that can be used to represent this channel in histograms and so. + * By default this is black, so keep in mind that many channels might look the same + */ + inline TQColor color() const { return m_color; } + +private: + + TQString m_name; + TQString m_abbrev; + TQ_INT32 m_pos; + enumChannelType m_channelType; + enumChannelValueType m_channelValueType; + TQ_INT32 m_size; + TQColor m_color; + +}; + +#endif // KIS_CHANNELINFO_H_ diff --git a/chalk/chalkcolor/kis_color.cc b/chalk/chalkcolor/kis_color.cc new file mode 100644 index 00000000..7d6acb89 --- /dev/null +++ b/chalk/chalkcolor/kis_color.cc @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include <tqcolor.h> + +#include "kdebug.h" +#include "kis_debug_areas.h" +#include "kis_color.h" +#include "kis_profile.h" +#include "kis_colorspace.h" +#include "kis_colorspace_factory_registry.h" + +KisColor::KisColor() +{ + m_data = 0; + m_colorSpace = 0; +} + +KisColor::~KisColor() +{ + delete [] m_data; +} + +KisColor::KisColor(const TQColor & color, KisColorSpace * colorSpace) + : m_colorSpace(colorSpace) +{ + Q_ASSERT(color.isValid()); + Q_ASSERT(colorSpace); + + m_data = new TQ_UINT8[colorSpace->pixelSize()]; + memset(m_data, 0, m_colorSpace->pixelSize()); + + m_colorSpace->fromTQColor(color, OPACITY_OPAQUE, m_data); +} + + +KisColor::KisColor(const TQColor & color, TQ_UINT8 alpha, KisColorSpace * colorSpace) + : m_colorSpace(colorSpace) +{ + Q_ASSERT(color.isValid()); + Q_ASSERT(colorSpace); + m_data = new TQ_UINT8[colorSpace->pixelSize()]; + memset(m_data, 0, m_colorSpace->pixelSize()); + + m_colorSpace->fromTQColor(color, alpha, m_data); +} + +KisColor::KisColor(const TQ_UINT8 * data, KisColorSpace * colorSpace) + : m_colorSpace(colorSpace) +{ + + m_data = new TQ_UINT8[colorSpace->pixelSize()]; + memset(m_data, 0, m_colorSpace->pixelSize()); + memmove(m_data, data, colorSpace->pixelSize()); +} + + +KisColor::KisColor(const KisColor &src, KisColorSpace * colorSpace) + : m_colorSpace(colorSpace) +{ + m_data = new TQ_UINT8[colorSpace->pixelSize()]; + memset(m_data, 0, m_colorSpace->pixelSize()); + + src.colorSpace()->convertPixelsTo(src.data(), m_data, colorSpace, 1); +} + +KisColor::KisColor(const KisColor & rhs) +{ + if (this == &rhs) return; + + m_colorSpace = rhs.colorSpace(); + m_data = new TQ_UINT8[m_colorSpace->pixelSize()]; + memset(m_data, 0, m_colorSpace->pixelSize()); + memcpy(m_data, rhs.data(), m_colorSpace->pixelSize()); +} + +KisColor & KisColor::operator=(const KisColor & rhs) +{ + delete [] m_data; + m_data = 0; + m_colorSpace = rhs.colorSpace(); + + if (rhs.m_colorSpace && rhs.m_data) { + m_data = new TQ_UINT8[m_colorSpace->pixelSize()]; + memcpy(m_data, rhs.m_data, m_colorSpace->pixelSize()); + } + return * this; +} + +void KisColor::convertTo(KisColorSpace * cs) +{ + //kdDebug(DBG_AREA_CMS) << "Our colormodel: " << m_colorSpace->id().name() + // << ", new colormodel: " << cs->id().name() << "\n"; + + if (m_colorSpace == cs) + return; + + TQ_UINT8 * m_data2 = new TQ_UINT8[cs->pixelSize()]; + memset(m_data2, 0, cs->pixelSize()); + + m_colorSpace->convertPixelsTo(m_data, m_data2, cs, 1); + + delete [] m_data; + m_data = m_data2; + m_colorSpace = cs; +} + + +void KisColor::setColor(TQ_UINT8 * data, KisColorSpace * colorSpace) +{ + delete [] m_data; + m_data = new TQ_UINT8[colorSpace->pixelSize()]; + memcpy(m_data, data, colorSpace->pixelSize()); + m_colorSpace = colorSpace; +} + +// To save the user the trouble of doing color->colorSpace()->toTQColor(color->data(), &c, &a, profile +void KisColor::toTQColor(TQColor *c) const +{ + if (m_colorSpace && m_data) { + // XXX (bsar): There must be a better way, but I'm getting hopelessly confused about constness by now + KisColorSpace * cs(const_cast<KisColorSpace*>(m_colorSpace)); + + cs->toTQColor(m_data, c); + } +} + +void KisColor::toTQColor(TQColor *c, TQ_UINT8 *opacity) const +{ + if (m_colorSpace && m_data) { + // XXX (bsar): There must be a better way, but I'm getting hopelessly confused about constness by now + KisColorSpace * cs(const_cast<KisColorSpace*>(m_colorSpace)); + cs->toTQColor(m_data, c, opacity); + } +} + +TQColor KisColor::toTQColor() const +{ + TQColor c; + toTQColor(&c); + return c; +} + +void KisColor::dump() const +{ + + //kdDebug(DBG_AREA_CMS) << "KisColor (" << this << "), " << m_colorSpace->id().name() << "\n"; + TQValueVector<KisChannelInfo *> channels = m_colorSpace->channels(); + + TQValueVector<KisChannelInfo *>::const_iterator begin = channels.begin(); + TQValueVector<KisChannelInfo *>::const_iterator end = channels.end(); + + for (TQValueVector<KisChannelInfo *>::const_iterator it = begin; it != end; ++it) + { + KisChannelInfo * ch = (*it); + // XXX: setNum always takes a byte. + if (ch->size() == sizeof(TQ_UINT8)) { + // Byte + //kdDebug(DBG_AREA_CMS) << "Channel (byte): " << ch->name() << ": " << TQString().setNum(m_data[ch->pos()]) << "\n"; + } + else if (ch->size() == sizeof(TQ_UINT16)) { + // Short (may also by an nvidia half) + //kdDebug(DBG_AREA_CMS) << "Channel (short): " << ch->name() << ": " << TQString().setNum(*((const TQ_UINT16 *)(m_data+ch->pos()))) << "\n"; + } + else if (ch->size() == sizeof(TQ_UINT32)) { + // Integer (may also be float... Find out how to distinguish these!) + //kdDebug(DBG_AREA_CMS) << "Channel (int): " << ch->name() << ": " << TQString().setNum(*((const TQ_UINT32 *)(m_data+ch->pos()))) << "\n"; + } + } + +} diff --git a/chalk/chalkcolor/kis_color.h b/chalk/chalkcolor/kis_color.h new file mode 100644 index 00000000..b7646586 --- /dev/null +++ b/chalk/chalkcolor/kis_color.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _KIS_COLOR_H_ +#define _KIS_COLOR_H_ + +#include <tqcolor.h> +#include "ksharedptr.h" + +#include "kis_global.h" +#include "kis_profile.h" +#include "kis_colorspace.h" + + +/** + * A KisColor describes a color in a certain colorspace. + * + */ +class KisColor { + +public: + /// Create an empty KisColor. It will be valid, but also black and transparent + KisColor(); + + virtual ~KisColor(); + + /// Create a KisColor from a TQColor. The TQColor is immediately converted to native. The TQColor + /// is assumed to have the current monitor profile. + KisColor(const TQColor & color, KisColorSpace * colorSpace); + + /// Create a KisColor from a TQColor. The TQColor is immediately converted to native. The TQColor + /// is assumed to have the current monitor profile. + KisColor(const TQColor & color, TQ_UINT8 alpha, KisColorSpace * colorSpace); + + /// Create a KisColor using a native color strategy. The data is copied. + KisColor(const TQ_UINT8 * data, KisColorSpace * colorSpace); + + /// Create a KisColor by converting src into another colorspace + KisColor(const KisColor &src, KisColorSpace * colorSpace); + + /// Copy constructor -- deep copies the colors. + KisColor(const KisColor & rhs); + + /// Effective C++, item 11 + KisColor &operator=(const KisColor &); + + /// For easy memcpy'ing etc. + TQ_UINT8 * data() const { return m_data; } + + KisColorSpace * colorSpace() const { return m_colorSpace; } + + KisProfile * profile() const { return m_colorSpace->getProfile(); } + + /// Convert this KisColor to the specified colorspace. If the specified colorspace is the + /// same as the original colorspace, do nothing. Returns the converted KisColor. + void convertTo(KisColorSpace * cs); + + /// Replace the existing color data, and colorspace with the specified data. + void setColor(TQ_UINT8 * data, KisColorSpace * colorSpace = 0); + + /// To save the user the trouble of doing color->colorSpace()->toTQColor(color->data(), &c, &a + void toTQColor(TQColor *c) const; + void toTQColor(TQColor *c, TQ_UINT8 *opacity) const; + + TQColor toTQColor() const; + + void dump() const; + +private: + + TQ_UINT8 * m_data; + + KisColorSpace * m_colorSpace; +}; + +#endif diff --git a/chalk/chalkcolor/kis_color_conversions.cc b/chalk/chalkcolor/kis_color_conversions.cc new file mode 100644 index 00000000..e517c9f7 --- /dev/null +++ b/chalk/chalkcolor/kis_color_conversions.cc @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <cmath> + +#include <tqglobal.h> + +#include "kis_color_conversions.h" + +/** + * A number of often-used conversions between color models + */ + +void rgb_to_hsv(int R, int G, int B, int *H, int *S, int *V) +{ + unsigned int max = R; + unsigned int min = R; + unsigned char maxValue = 0; // r = 0, g = 1, b = 2 + + // find maximum and minimum RGB values + if(static_cast<unsigned int>(G) > max) { + max = G; + maxValue = 1; + } + + if (static_cast<unsigned int>(B) > max) + { + max = B; + maxValue = 2; + } + + if(static_cast<unsigned int>(G) < min) + min = G; + + if(static_cast<unsigned int>(B) < min ) + min = B; + + int delta = max - min; + + // To prevent division by zero later on. + if (delta == 0) delta = 1; + + *V = max; // value + *S = max ? (510 * delta + max) / ( 2 * max) : 0; // saturation + + // calc hue + if(*S == 0) + *H = -1; // undefined hue + else + { + switch(maxValue) + { + case 0: // red + if(G >= B) + *H = (120 * (G - B) + delta) / (2 * delta); + else + *H = (120 * (G - B + delta) + delta) / (2 * delta) + 300; + break; + case 1: // green + if(B > R) + *H = 120 + (120 * (B - R) + delta) / (2 * delta); + else + *H = 60 + (120 * (B - R + delta) + delta) / (2 * delta); + break; + case 2: // blue + if(R > G) + *H = 240 + (120 * (R - G) + delta) / (2 * delta); + else + *H = 180 + (120 * (R - G + delta) + delta) / (2 * delta); + break; + } + } +} + +void hsv_to_rgb(int H, int S, int V, int *R, int *G, int *B) +{ + *R = *G = *B = V; + + if (S != 0 && H != -1) { // chromatic + + if (H >= 360) { + // angle > 360 + H %= 360; + } + + unsigned int f = H % 60; + H /= 60; + unsigned int p = static_cast<unsigned int>(2*V*(255-S)+255)/510; + unsigned int q, t; + + if (H & 1) { + q = static_cast<unsigned int>(2 * V * (15300 - S * f) + 15300) / 30600; + switch (H) { + case 1: + *R = static_cast<int>(q); + *G = static_cast<int>(V); + *B = static_cast<int>(p); + break; + case 3: + *R = static_cast<int>(p); + *G = static_cast<int>(q); + *B = static_cast<int>(V); + break; + case 5: + *R = static_cast<int>(V); + *G = static_cast<int>(p); + *B = static_cast<int>(q); + break; + } + } else { + t = static_cast<unsigned int>(2 * V * (15300 - (S * (60 - f))) + 15300) / 30600; + switch (H) { + case 0: + *R = static_cast<int>(V); + *G = static_cast<int>(t); + *B = static_cast<int>(p); + break; + case 2: + *R = static_cast<int>(p); + *G = static_cast<int>(V); + *B = static_cast<int>(t); + break; + case 4: + *R = static_cast<int>(t); + *G = static_cast<int>(p); + *B = static_cast<int>(V); + break; + } + } + } +} + +#define EPSILON 1e-6 +#define UNDEFINED_HUE -1 + +void RGBToHSV(float r, float g, float b, float *h, float *s, float *v) +{ + float max = TQMAX(r, TQMAX(g, b)); + float min = TQMIN(r, TQMIN(g, b)); + + *v = max; + + if (max > EPSILON) { + *s = (max - min) / max; + } else { + *s = 0; + } + + if (*s < EPSILON) { + *h = UNDEFINED_HUE; + } else { + float delta = max - min; + + if (r == max) { + *h = (g - b) / delta; + } else if (g == max) { + *h = 2 + (b - r) / delta; + } else { + *h = 4 + (r - g) / delta; + } + + *h *= 60; + if (*h < 0) { + *h += 360; + } + } +} + +void HSVToRGB(float h, float s, float v, float *r, float *g, float *b) +{ + if (s < EPSILON || h == UNDEFINED_HUE) { + // Achromatic case + + *r = v; + *g = v; + *b = v; + } else { + float f, p, q, t; + int i; + + if (h > 360 - EPSILON) { + h -= 360; + } + + h /= 60; + i = static_cast<int>(floor(h)); + f = h - i; + p = v * (1 - s); + q = v * (1 - (s * f)); + t = v * (1 - (s * (1 - f))); + + switch (i) { + case 0: + *r = v; + *g = t; + *b = p; + break; + case 1: + *r = q; + *g = v; + *b = p; + break; + case 2: + *r = p; + *g = v; + *b = t; + break; + case 3: + *r = p; + *g = q; + *b = v; + break; + case 4: + *r = t; + *g = p; + *b = v; + break; + case 5: + *r = v; + *g = p; + *b = q; + break; + } + } +} + +void rgb_to_hls(TQ_UINT8 red, TQ_UINT8 green, TQ_UINT8 blue, float * hue, float * lightness, float * saturation) +{ + float r = red / 255.0; + float g = green / 255.0; + float b = blue / 255.0; + float h = 0; + float l = 0; + float s = 0; + + float max, min, delta; + + max = TQMAX(r, g); + max = TQMAX(max, b); + + min = TQMIN(r, g); + min = TQMIN(min, b); + + delta = max - min; + + l = (max + min) / 2; + + if (delta == 0) { + // This is a gray, no chroma... + h = 0; + s = 0; + } + else { + if ( l < 0.5) + s = delta / ( max + min ); + else + s = delta / ( 2 - max - min ); + + float delta_r, delta_g, delta_b; + + delta_r = (( max - r ) / 6 ) / delta; + delta_g = (( max - g ) / 6 ) / delta; + delta_b = (( max - b ) / 6 ) / delta; + + if ( r == max ) + h = delta_b - delta_g; + else if ( g == max) + h = ( 1.0 / 3 ) + delta_r - delta_b; + else if ( b == max) + h = ( 2.0 / 3 ) + delta_g - delta_r; + + if (h < 0) h += 1; + if (h > 1) h += 1; + + } + + *hue = h * 360; + *saturation = s; + *lightness = l; +} + +float hue_value(float n1, float n2, float hue) +{ + if (hue > 360 ) + hue = hue -360; + else if (hue < 0 ) + hue = hue +360; + if (hue < 60 ) + return n1 + (((n2 - n1) * hue) / 60); + else if (hue < 180 ) + return n2; + else if (hue < 240 ) + return n1 + (((n2 - n1) * (240 - hue)) / 60); + else return n1; +} + + +void hls_to_rgb(float h, float l, float s, TQ_UINT8 * r, TQ_UINT8 * g, TQ_UINT8 * b) +{ + float m1, m2; + + if (l <= 0.5 ) + m2 = l * ( 1 + s ); + else + m2 = l + s - l * s; + + m1 = 2 * l - m2; + + *r = (TQ_UINT8)(hue_value(m1, m2, h + 120) * 255 + 0.5); + *g = (TQ_UINT8)(hue_value(m1, m2, h) * 255 + 0.5); + *b = (TQ_UINT8)(hue_value(m1, m2, h - 120) * 255 + 0.5); + +} + +void rgb_to_hls(TQ_UINT8 r, TQ_UINT8 g, TQ_UINT8 b, int * h, int * l, int * s) +{ + float hue, saturation, lightness; + + rgb_to_hls(r, g, b, &hue, &lightness, &saturation); + *h = (int)(hue + 0.5); + *l = (int)(lightness * 255 + 0.5); + *s = (int)(saturation * 255 + 0.5); +} + +void hls_to_rgb(int h, int l, int s, TQ_UINT8 * r, TQ_UINT8 * g, TQ_UINT8 * b) +{ + float hue = h; + float lightness = l / 255.0; + float saturation = s / 255.0; + + hls_to_rgb(hue, lightness, saturation, r, g, b); +} + +/* +A Fast HSL-to-RGB Transform +by Ken Fishkin +from "Graphics Gems", Academic Press, 1990 +*/ + +void RGBToHSL(float r, float g, float b, float *h, float *s, float *l) +{ + float v; + float m; + float vm; + float r2, g2, b2; + + v = TQMAX(r,g); + v = TQMAX(v,b); + m = TQMIN(r,g); + m = TQMIN(m,b); + + if ((*l = (m + v) / 2.0) <= 0.0) { + *h = UNDEFINED_HUE; + *s = 0; + return; + } + if ((*s = vm = v - m) > 0.0) { + *s /= (*l <= 0.5) ? (v + m ) : + (2.0 - v - m) ; + } else { + *h = UNDEFINED_HUE; + return; + } + + + r2 = (v - r) / vm; + g2 = (v - g) / vm; + b2 = (v - b) / vm; + + if (r == v) + *h = (g == m ? 5.0 + b2 : 1.0 - g2); + else if (g == v) + *h = (b == m ? 1.0 + r2 : 3.0 - b2); + else + *h = (r == m ? 3.0 + g2 : 5.0 - r2); + + *h *= 60; +} + +void HSLToRGB(float h, float sl, float l, float *r, float *g, float *b) + +{ + float v; + + v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl); + if (v <= 0) { + *r = *g = *b = 0.0; + } else { + float m; + float sv; + int sextant; + float fract, vsf, mid1, mid2; + + m = l + l - v; + sv = (v - m ) / v; + h /= 60.0; + sextant = static_cast<int>(h); + fract = h - sextant; + vsf = v * sv * fract; + mid1 = m + vsf; + mid2 = v - vsf; + switch (sextant) { + case 0: *r = v; *g = mid1; *b = m; break; + case 1: *r = mid2; *g = v; *b = m; break; + case 2: *r = m; *g = v; *b = mid1; break; + case 3: *r = m; *g = mid2; *b = v; break; + case 4: *r = mid1; *g = m; *b = v; break; + case 5: *r = v; *g = m; *b = mid2; break; + } + } +} + diff --git a/chalk/chalkcolor/kis_color_conversions.h b/chalk/chalkcolor/kis_color_conversions.h new file mode 100644 index 00000000..53a476e0 --- /dev/null +++ b/chalk/chalkcolor/kis_color_conversions.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_CONVERSIONS_H_ +#define _KIS_CONVERSIONS_H_ + +#include <tqglobal.h> + +/** + * A number of often-used conversions between color models + */ + +// 8-bit integer versions. RGBSL are 0-255, H is 0-360. + void rgb_to_hsv(int R, int G, int B, int *H, int *S, int *V); + void hsv_to_rgb(int H, int S, int V, int *R, int *G, int *B); + +// Floating point versions. RGBSL are 0-1, H is 0-360. + void RGBToHSV(float r, float g, float b, float *h, float *s, float *v); + void HSVToRGB(float h, float s, float v, float *r, float *g, float *b); + + void RGBToHSL(float r, float g, float b, float *h, float *s, float *l); + void HSLToRGB(float h, float sl, float l, float *r, float *g, float *b); + + void rgb_to_hls(TQ_UINT8 r, TQ_UINT8 g, TQ_UINT8 b, float * h, float * l, float * s); + + float hue_value(float n1, float n2, float hue); + + void hls_to_rgb(float h, float l, float s, TQ_UINT8 * r, TQ_UINT8 * g, TQ_UINT8 * b); + + void rgb_to_hls(TQ_UINT8 r, TQ_UINT8 g, TQ_UINT8 b, int * h, int * l, int * s); + void hls_to_rgb(int h, int l, int s, TQ_UINT8 * r, TQ_UINT8 * g, TQ_UINT8 * b); + +#endif // _KIS_CONVERSIONS_H_ + diff --git a/chalk/chalkcolor/kis_colorspace.cc b/chalk/chalkcolor/kis_colorspace.cc new file mode 100644 index 00000000..de0c44cf --- /dev/null +++ b/chalk/chalkcolor/kis_colorspace.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_colorspace.h" +#include "kis_colorspace_iface.h" + +KisColorSpace::KisColorSpace() +{ + m_dcop = 0; +} + +KisColorSpace::~KisColorSpace() +{ + delete m_dcop; +} + +DCOPObject * KisColorSpace::dcopObject() +{ + if (!m_dcop) { + m_dcop = new KisColorSpaceIface(this); + Q_CHECK_PTR(m_dcop); + } + return m_dcop; +} diff --git a/chalk/chalkcolor/kis_colorspace.h b/chalk/chalkcolor/kis_colorspace.h new file mode 100644 index 00000000..6071909c --- /dev/null +++ b/chalk/chalkcolor/kis_colorspace.h @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KIS_COLORSPACE_H_ +#define KIS_COLORSPACE_H_ + +#include <config.h> +#include LCMS_HEADER + +#include <tqvaluevector.h> +#include <tqvaluelist.h> + +#include "kis_composite_op.h" +#include "kis_channelinfo.h" + +class DCOPObject; + +class KisProfile; +class KisColorSpaceFactoryRegistry; +class KisMathToolbox; +class KisFilter; + +class KisColorAdjustment +{ +public: + + KisColorAdjustment() {}; + virtual ~KisColorAdjustment() {}; +}; + + +enum ColorSpaceIndependence { + FULLY_INDEPENDENT, + TO_LAB16, + TO_RGBA8, + TO_RGBA16 +}; + +/** + * A colorspace is the definition of a certain color model + * in Chalk. This is the definition of the public API for + * colormodels. + */ +class KisColorSpace { + + +public: + + KisColorSpace(); + virtual ~KisColorSpace(); + + virtual DCOPObject * dcopObject(); + + virtual bool operator==(const KisColorSpace& rhs) const { + return id().id() == rhs.id().id(); + } + + +public: + + //========== Channels =====================================================// + + /// Return a vector describing all the channels this color model has. + virtual TQValueVector<KisChannelInfo *> channels() const = 0; + + /** + * The total number of channels for a single pixel in this color model + */ + virtual TQ_UINT32 nChannels() const = 0; + + /** + * The total number of color channels (excludes alpha and substance) for a single + * pixel in this color model. + */ + virtual TQ_UINT32 nColorChannels() const = 0; + + /** + * The total number of substance channels for a single pixel + * in this color model + */ + virtual TQ_UINT32 nSubstanceChannels() const { return 0; }; + + /** + * The size in bytes of a single pixel in this color model + */ + virtual TQ_UINT32 pixelSize() const = 0; + + /** + * Return a string with the channel's value suitable for display in the gui. + */ + virtual TQString channelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const = 0; + + /** + * Return a string with the channel's value with integer + * channels normalised to the floating point range 0 to 1, if appropriate. + */ + virtual TQString normalisedChannelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const = 0; + + /** + * Convert the value of the channel at the specified position into + * an 8-bit value. The position is not the number of bytes, but + * the position of the channel as defined in the channel info list. + */ + virtual TQ_UINT8 scaleToU8(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos) = 0; + + /** + * Convert the value of the channel at the specified position into + * a 16-bit value. This may be upscaling or downscaling, depending + * on the defined value of the channel + */ + virtual TQ_UINT16 scaleToU16(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos) = 0; + + /** + * Set dstPixel to the pixel containing only the given channel of srcPixel. The remaining channels + * should be set to whatever makes sense for 'empty' channels of this colour space, + * with the intent being that the pixel should look like it only has the given channel. + */ + virtual void getSingleChannelPixel(TQ_UINT8 *dstPixel, const TQ_UINT8 *srcPixel, TQ_UINT32 channelIndex) = 0; + + //========== Identification ===============================================// + + /** + * Chalk definition for use in .kra files and internally: unchanging name + + * i18n'able description. + */ + virtual KisID id() const = 0; + + /** + * lcms colorspace type definition. + */ + virtual TQ_UINT32 colorSpaceType() = 0; + + virtual icColorSpaceSignature colorSpaceSignature() = 0; + + /** + * If false, images in this colorspace will degrade considerably by + * functions, tools and filters that have the given measure of colorspace + * independence. + * + * @param independence the measure to which this colorspace will suffer + * from the manipulations of the tool or filter asking + * @return false if no degradation will take place, true if degradation will + * take place + */ + virtual bool willDegrade(ColorSpaceIndependence independence) = 0; + + //========== Capabilities =================================================// + + /** + * Returns the list of user-visible composite ops supported by this colourspace. Internal + * ops such as COPY, CLEAR, and ERASE, are not included as these make no sense + * for layers in the full image model. + */ + virtual KisCompositeOpList userVisiblecompositeOps() const = 0; + + /** + * Returns true if the colorspace supports channel values outside the + * (normalised) range 0 to 1. + */ + virtual bool hasHighDynamicRange() const = 0; + + + //========== Display profiles =============================================// + + /** + * Return the profile of this color space. This may be 0 + */ + virtual KisProfile * getProfile() const = 0; + +//================= Conversion functions ==================================// + + + /** + * The fromTQColor methods take a given color defined as an RGB TQColor + * and fills a byte array with the corresponding color in the + * the colorspace managed by this strategy. + * + * @param c the TQColor that will be used to fill dst + * @param dst a pointer to a pixel + * @param profile the optional profile that describes the color values of TQColor + */ + virtual void fromTQColor(const TQColor& c, TQ_UINT8 *dst, KisProfile * profile = 0) = 0; + + /** + * The fromTQColor methods take a given color defined as an RGB TQColor + * and fills a byte array with the corresponding color in the + * the colorspace managed by this strategy. + * + * @param c the TQColor that will be used to fill dst + * @param opacity the opacity of the color + * @param dst a pointer to a pixel + * @param profile the optional profile that describes the color values of TQColor + */ + virtual void fromTQColor(const TQColor& c, TQ_UINT8 opacity, TQ_UINT8 *dst, KisProfile * profile = 0) = 0; + + + /** + * The toTQColor methods take a byte array that is at least pixelSize() long + * and converts the contents to a TQColor, using the given profile as a source + * profile and the optional profile as a destination profile. + * + * @param src a pointer to the source pixel + * @param c the TQColor that will be filled with the color at src + * @param profile the optional profile that describes the color in c, for instance the monitor profile + */ + virtual void toTQColor(const TQ_UINT8 *src, TQColor *c, KisProfile * profile = 0) = 0; + + /** + * The toTQColor methods take a byte array that is at least pixelSize() long + * and converts the contents to a TQColor, using the given profile as a source + * profile and the option profile as a destination profile. + * + * @param src a pointer to the source pixel + * @param c the TQColor that will be filled with the color at src + * @param opacity a pointer to a byte that will be filled with the opacity a src + * @param profile the optional profile that describes the color in c, for instance the monitor profile + */ + virtual void toTQColor(const TQ_UINT8 *src, TQColor *c, TQ_UINT8 *opacity, KisProfile * profile = 0) = 0; + + /** + * Convert the pixels in data to (8-bit BGRA) TQImage using the specified profiles. + * The pixels are supposed to be encoded in this color model. The default implementation + * will convert the pixels using either the profiles or the default profiles for the + * current colorstrategy and the RGBA colorstrategy. If that is not what you want, + * or if you think you can do better than lcms, reimplement this methods. + * + * @param data A pointer to a contiguous memory region containing width * height pixels + * @param width in pixels + * @param height in pixels + * @param dstProfile destination profile + * @param renderingIntent the rendering intent + * @param exposure The exposure setting for rendering a preview of a high dynamic range image. + */ + virtual TQImage convertToTQImage(const TQ_UINT8 *data, TQ_INT32 width, TQ_INT32 height, + KisProfile * dstProfile, TQ_INT32 renderingIntent = INTENT_PERCEPTUAL, + float exposure = 0.0f) = 0; + + + /** + * Convert the specified data to Lab. All colorspaces are guaranteed to support this + * + * @param src the source data + * @param dst the destination data + * @param nPixels the number of source pixels + */ + virtual void toLabA16(const TQ_UINT8 * src, TQ_UINT8 * dst, const TQ_UINT32 nPixels) const = 0; + + /** + * Convert the specified data from Lab. to this colorspace. All colorspaces are + * guaranteed to support this. + * + * @param src the pixels in 16 bit lab format + * @param dst the destination data + * @param nPixels the number of pixels in the array + */ + virtual void fromLabA16(const TQ_UINT8 * src, TQ_UINT8 * dst, const TQ_UINT32 nPixels) const = 0; + + /** + * Convert a byte array of srcLen pixels *src to the specified color space + * and put the converted bytes into the prepared byte array *dst. + * + * Returns false if the conversion failed, true if it succeeded + */ + virtual bool convertPixelsTo(const TQ_UINT8 * src, + TQ_UINT8 * dst, KisColorSpace * dstColorSpace, + TQ_UINT32 numPixels, + TQ_INT32 renderingIntent = INTENT_PERCEPTUAL) = 0; + +//============================== Manipulation functions ==========================// + + +// +// The manipulation functions have default implementations that _convert_ the pixel +// to a TQColor and back. Reimplement these methods in your color strategy! +// + + /** + * Get the alpha value of the given pixel, downscaled to an 8-bit value. + */ + virtual TQ_UINT8 getAlpha(const TQ_UINT8 * pixel) const = 0; + + /** + * Set the alpha channel of the given run of pixels to the given value. + * + * pixels -- a pointer to the pixels that will have their alpha set to this value + * alpha -- a downscaled 8-bit value for opacity + * nPixels -- the number of pixels + * + */ + virtual void setAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels) const = 0; + + /** + * Multiply the alpha channel of the given run of pixels by the given value. + * + * pixels -- a pointer to the pixels that will have their alpha set to this value + * alpha -- a downscaled 8-bit value for opacity + * nPixels -- the number of pixels + * + */ + virtual void multiplyAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels) = 0; + + /** + * Applies the specified 8-bit alpha tqmask to the pixels. We assume that there are just + * as many alpha values as pixels but we do not check this; the alpha values + * are assumed to be 8-bits. + */ + virtual void applyAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels) = 0; + + /** + * Applies the inverted 8-bit alpha tqmask to the pixels. We assume that there are just + * as many alpha values as pixels but we do not check this; the alpha values + * are assumed to be 8-bits. + */ + virtual void applyInverseAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels) = 0; + + /** + * Create an adjustment object for adjusting the brightness and contrast + * transferValues is a 256 bins array with values from 0 to 0xFFFF + */ + virtual KisColorAdjustment *createBrightnessContrastAdjustment(TQ_UINT16 *transferValues) = 0; + + /** + * Create an adjustment object for desaturating + */ + virtual KisColorAdjustment *createDesaturateAdjustment() = 0; + + /** + * Create an adjustment object for adjusting individual channels + * transferValues is an array of nColorChannels number of 256 bins array with values from 0 to 0xFFFF + */ + virtual KisColorAdjustment *createPerChannelAdjustment(TQ_UINT16 **transferValues) = 0; + + /** + * Apply the adjustment created with onr of the other functions + */ + virtual void applyAdjustment(const TQ_UINT8 *src, TQ_UINT8 *dst, KisColorAdjustment *, TQ_INT32 nPixels) = 0; + + /** + * Invert color channels of the given pixels + */ + virtual void invertColor(TQ_UINT8 * src, TQ_INT32 nPixels) = 0; + + // XXX: What with alpha channels? YYY: Add an overloaded function that takes alpha into account? + /** + * Get the difference between 2 colors, normalized in the range (0,255) + */ + virtual TQ_UINT8 difference(const TQ_UINT8* src1, const TQ_UINT8* src2) = 0; + + + /** + * Mix the colors given their weights and return in dst + * The sum of weights is assumed 255 */ + virtual void mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const = 0; + + /** + * Convolve the given array of pointers to pixels and return the result + * in dst. The kernel values are clamped between -128 and 128 + */ + virtual void convolveColors(TQ_UINT8** colors, TQ_INT32* kernelValues, KisChannelInfo::enumChannelFlags channelFlags, TQ_UINT8 *dst, TQ_INT32 factor, TQ_INT32 offset, TQ_INT32 nPixels) const = 0; + + /** + * Darken all color channels with the given amount. If compensate is true, + * the compensation factor will be used to limit the darkening. + * + * (See the bumpmap filter) + */ + virtual void darken(const TQ_UINT8 * src, TQ_UINT8 * dst, TQ_INT32 shade, bool compensate, double compensation, TQ_INT32 nPixels) const = 0; + + /** + * Calculate the intensity of the given pixel, scaled down to the range 0-255. XXX: Maybe this should be more flexible + */ + virtual TQ_UINT8 intensity8(const TQ_UINT8 * src) const = 0; + + /** + * Create a mathematical toolbox compatible with this colorspace + */ + virtual KisID mathToolboxID() const =0; + + /** + * Compose two arrays of pixels together. If source and target + * are not the same colour model, the source pixels will be + * converted to the target model. + */ + virtual void bitBlt(TQ_UINT8 *dst, + TQ_INT32 dststride, + KisColorSpace * srcSpace, + const TQ_UINT8 *src, + TQ_INT32 srcRowStride, + const TQ_UINT8 *srcAlphaMask, + TQ_INT32 tqmaskRowStride, + TQ_UINT8 opacity, + TQ_INT32 rows, + TQ_INT32 cols, + const KisCompositeOp& op) = 0; + + /** + * The backgroundfilters will be run periodically on the newly + * created paint device. XXX: Currently this uses times and not + * threads. + */ + virtual TQValueList<KisFilter*> createBackgroundFilters() + { return TQValueList<KisFilter*>(); }; + +private: + + DCOPObject * m_dcop; + +}; + +class KisColorSpaceFactory { +public: + /** + * Chalk definition for use in .kra files and internally: unchanging name + + * i18n'able description. + */ + virtual KisID id() const = 0; + + /** + * lcms colorspace type definition. + */ + virtual TQ_UINT32 colorSpaceType() = 0; + + virtual icColorSpaceSignature colorSpaceSignature() = 0; + + virtual KisColorSpace *createColorSpace(KisColorSpaceFactoryRegistry * tqparent, KisProfile *) = 0; + + /** + * Returns the default icc profile for use with this colorspace. This may be "" + * + & @return the default icc profile name + */ + virtual TQString defaultProfile() = 0; + +}; + +#endif // KIS_COLORSPACE_H_ diff --git a/chalk/chalkcolor/kis_colorspace_factory_registry.cc b/chalk/chalkcolor/kis_colorspace_factory_registry.cc new file mode 100644 index 00000000..6758f7d5 --- /dev/null +++ b/chalk/chalkcolor/kis_colorspace_factory_registry.cc @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2003 Patrick Julien <freak@codepimps.org> + * Copyright (c) 2004 Cyrille Berger <cberger@cberger.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kdebug.h" +#include <kparts/plugin.h> +#include <kservice.h> +#include <ktrader.h> +#include <kparts/componentfactory.h> +#include <kmessagebox.h> +#include <klocale.h> +#include "kis_debug_areas.h" +#include "kis_colorspace.h" +#include "kis_profile.h" +#include "kis_colorspace_factory_registry.h" +#include "kis_alpha_colorspace.h" +#include "kis_lab_colorspace.h" + + +KisColorSpaceFactoryRegistry::KisColorSpaceFactoryRegistry(TQStringList profileFilenames) +{ + // Create the built-in colorspaces + + m_alphaCs = new KisAlphaColorSpace(this, 0); + + // Load the profiles + if (!profileFilenames.empty()) { + KisProfile * profile = 0; + for ( TQStringList::Iterator it = profileFilenames.begin(); it != profileFilenames.end(); ++it ) { + profile = new KisProfile(*it); + Q_CHECK_PTR(profile); + + profile->load(); + if (profile->valid()) { + m_profileMap[profile->productName()] = profile; + } + } + } + + KisProfile *labProfile = new KisProfile(cmsCreateLabProfile(NULL)); + addProfile(labProfile); + add(new KisLabColorSpaceFactory()); +/* XXX where to put this + KisHistogramProducerFactoryRegistry::instance()->add( + new KisBasicHistogramProducerFactory<KisBasicU16HistogramProducer> + (KisID("LABAHISTO", i18n("L*a*b* Histogram")), new KisLabColorSpace(this, 0);) ); +*/ + + // Load all colorspace modules + KTrader::OfferList offers = KTrader::self()->query(TQString::tqfromLatin1("Chalk/ColorSpace"), + TQString::tqfromLatin1("(Type == 'Service') and " + "([X-Chalk-Version] == 2)")); + + if (offers.empty()) { + KMessageBox::sorry(0, i18n("Cannot start Chalk: no colorspaces available.")); + abort(); + } + + KTrader::OfferList::ConstIterator iter; + for(iter = offers.begin(); iter != offers.end(); ++iter) + { + KService::Ptr service = *iter; + int errCode = 0; + KParts::Plugin* plugin = + KParts::ComponentFactory::createInstanceFromService<KParts::Plugin> ( service, this, 0, TQStringList(), &errCode); + if ( plugin ) + kdDebug(DBG_AREA_PLUGINS) << "found colorspace " << service->property("Name").toString() << "\n"; + else { + kdDebug(41006) << "found plugin " << service->property("Name").toString() << ", " << errCode << "\n"; + if( errCode == KParts::ComponentFactory::ErrNoLibrary) + { + kdWarning(41006) << " Error loading plugin was : ErrNoLibrary " << KLibLoader::self()->lastErrorMessage() << endl; + } + } + } +} + +KisColorSpaceFactoryRegistry::KisColorSpaceFactoryRegistry() +{ +} + +KisColorSpaceFactoryRegistry::~KisColorSpaceFactoryRegistry() +{ +} + +KisProfile * KisColorSpaceFactoryRegistry::getProfileByName(const TQString & name) +{ + if (m_profileMap.tqfind(name) == m_profileMap.end()) { + return 0; + } + + return m_profileMap[name]; +} + +TQValueVector<KisProfile *> KisColorSpaceFactoryRegistry::profilesFor(KisID id) +{ + return profilesFor(get(id)); +} + +TQValueVector<KisProfile *> KisColorSpaceFactoryRegistry::profilesFor(KisColorSpaceFactory * csf) +{ + + TQValueVector<KisProfile *> profiles; + + TQMap<TQString, KisProfile * >::Iterator it; + for (it = m_profileMap.begin(); it != m_profileMap.end(); ++it) { + KisProfile * profile = it.data(); + if (profile->colorSpaceSignature() == csf->colorSpaceSignature()) { + profiles.push_back(profile); + } + } + return profiles; +} + +void KisColorSpaceFactoryRegistry::addProfile(KisProfile *p) +{ + if (p->valid()) { + m_profileMap[p->productName()] = p; + } +} + +void KisColorSpaceFactoryRegistry::addPaintDeviceAction(KisColorSpace* cs, + KisPaintDeviceAction* action) { + m_paintDevActionMap[cs->id()].append(action); +} + +TQValueVector<KisPaintDeviceAction *> +KisColorSpaceFactoryRegistry::paintDeviceActionsFor(KisColorSpace* cs) { + return m_paintDevActionMap[cs->id()]; +} + +KisColorSpace * KisColorSpaceFactoryRegistry::getColorSpace(const KisID & csID, const TQString & pName) +{ + TQString profileName = pName; + + if(profileName.isEmpty()) + { + KisColorSpaceFactory *csf = get(csID); + + if(!csf) + return 0; + + profileName = csf->defaultProfile(); + } + + TQString name = csID.id() + "<comb>" + profileName; + + if (m_csMap.tqfind(name) == m_csMap.end()) { + KisColorSpaceFactory *csf = get(csID); + if(!csf) + return 0; + + KisProfile *p = getProfileByName(profileName); + if(!p && profileName != "") + return 0; + KisColorSpace *cs = csf->createColorSpace(this, p); + if(!cs) + return 0; + + m_csMap[name] = cs; + } + + if(m_csMap.tqcontains(name)) + return m_csMap[name]; + else + return 0; +} + + +KisColorSpace * KisColorSpaceFactoryRegistry::getColorSpace(const KisID & csID, const KisProfile * profile) +{ + if( profile ) + { + KisColorSpace *cs = getColorSpace( csID, profile->productName()); + + if(!cs) + { + // The profile was not stored and thus not the combination either + KisColorSpaceFactory *csf = get(csID); + if(!csf) + return 0; + + cs = csf->createColorSpace(this, const_cast<KisProfile *>(profile)); + if(!cs ) + return 0; + + TQString name = csID.id() + "<comb>" + profile->productName(); + m_csMap[name] = cs; + } + + return cs; + } else { + return getColorSpace( csID, ""); + } +} + +KisColorSpace * KisColorSpaceFactoryRegistry::getAlpha8() +{ + return m_alphaCs; +} + +KisColorSpace * KisColorSpaceFactoryRegistry::getRGB8() +{ + return getColorSpace(KisID("RGBA", ""), ""); +} + +#include "kis_colorspace_factory_registry.moc" diff --git a/chalk/chalkcolor/kis_colorspace_factory_registry.h b/chalk/chalkcolor/kis_colorspace_factory_registry.h new file mode 100644 index 00000000..104c99aa --- /dev/null +++ b/chalk/chalkcolor/kis_colorspace_factory_registry.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2003 Patrick Julien <freak@codepimps.org> + * Copyright (c) 2004 Cyrille Berger <cberger@cberger.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_COLORSPACE_FACTORY_REGISTRY_H_ +#define KIS_COLORSPACE_FACTORY_REGISTRY_H_ +#include "tqobject.h" +#include "kis_generic_registry.h" +#include "kis_colorspace.h" + +class TQStringList; +class KisPaintDeviceAction; + +/** + * This class tqcontains: + * - a registry of colorspace instantiated with specific profiles. + * - a registry of singleton colorspace factories. + * - a registry of icc profiles + */ +class KisColorSpaceFactoryRegistry : public TQObject, public KisGenericRegistry<KisColorSpaceFactory *> { + + + Q_OBJECT + TQ_OBJECT + +public: + + /** + * Create a new colorspacefactory registry. The registry will + * load all colorspace modules that have the right version and + * all profiles given in the list. It is always possible + * to add more profiles with addProfile() + * + * @param profileFileNames a list of all filenames of all profiles that need to be loaded initially + */ + KisColorSpaceFactoryRegistry(TQStringList profileFileNames); + + virtual ~KisColorSpaceFactoryRegistry(); + + /** + * Add the profile to the list. + */ + void addProfile(KisProfile * p); + + /** + * Return the profile associated with the given product name, + * or 0. + */ + KisProfile * getProfileByName(const TQString & name); + + /** + * Return the vector of profiles for this colorspacefactory + */ + TQValueVector<KisProfile *> profilesFor(KisColorSpaceFactory * cs); + + TQValueVector<KisProfile *> profilesFor(KisID id); + + /** + * Return the colorspace + profile as named, or NULL if impossible combination. + */ + KisColorSpace * getColorSpace(const KisID & csID, const TQString & profileName); + + /** + * Return the colorspace + profile -- where the profile is matched on the name of the specified profile + */ + KisColorSpace * getColorSpace(const KisID & csID, const KisProfile * profile); + + /** + * Convenience method to get the often used alpha colorspace + */ + KisColorSpace * getAlpha8(); + + /** + * Convenience method to get an RGB colorspace with the default lcms profile + */ + KisColorSpace * getRGB8(); + + /** + * add a KisConstructPaintDeviceAction to the registry for a colorspace + * + * These actions are exectued when an image is created on the first layer + * in the image, on the image width and height rect. + */ + void addPaintDeviceAction(KisColorSpace* cs, KisPaintDeviceAction* action); + + /** + * Get a list of KisConstructPaintDeviceAction for a colorspace + */ + TQValueVector<KisPaintDeviceAction *> paintDeviceActionsFor(KisColorSpace* cs); + +private: + KisColorSpaceFactoryRegistry(); + KisColorSpaceFactoryRegistry(const KisColorSpaceFactoryRegistry&); + KisColorSpaceFactoryRegistry operator=(const KisColorSpaceFactoryRegistry&); + +private: + + TQMap<TQString, KisProfile * > m_profileMap; + TQMap<TQString, KisColorSpace * > m_csMap; + typedef TQValueVector<KisPaintDeviceAction *> PaintActionVector; + TQMap<KisID, PaintActionVector> m_paintDevActionMap; + KisColorSpace *m_alphaCs; +}; + +#endif // KIS_COLORSPACE_FACTORY_REGISTRY_H_ + diff --git a/chalk/chalkcolor/kis_colorspace_iface.cc b/chalk/chalkcolor/kis_colorspace_iface.cc new file mode 100644 index 00000000..6e6b3cb8 --- /dev/null +++ b/chalk/chalkcolor/kis_colorspace_iface.cc @@ -0,0 +1,39 @@ +/* + * This file is part of the KDE project + * + * Copyright (C) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <kapplication.h> + +#include "kis_colorspace_iface.h" +#include "kis_colorspace.h" + +#include <dcopclient.h> + +KisColorSpaceIface::KisColorSpaceIface( KisColorSpace * tqparent ) + : DCOPObject(tqparent->id().id().latin1()) +{ + m_parent = tqparent; +} + +TQByteArray KisColorSpaceIface::invertColor(TQByteArray src, TQ_INT32 nPixels) +{ + m_parent->invertColor((TQ_UINT8*)src.data(), nPixels); + return src; + +} + diff --git a/chalk/chalkcolor/kis_colorspace_iface.h b/chalk/chalkcolor/kis_colorspace_iface.h new file mode 100644 index 00000000..ce52e846 --- /dev/null +++ b/chalk/chalkcolor/kis_colorspace_iface.h @@ -0,0 +1,43 @@ +/* This file is part of the KDE project + * Copyright (C) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_COLORSPACE_IFACE_H +#define _KIS_COLORSPACE_IFACE_H + +#include <dcopref.h> +#include <dcopobject.h> + +#include <tqstring.h> + +class KisColorSpace; + +class KisColorSpaceIface : public DCOPObject +{ + K_DCOP +public: + KisColorSpaceIface( KisColorSpace * tqparent ); +k_dcop: + + TQByteArray invertColor(TQByteArray src, TQ_INT32 nPixels); + +private: + + KisColorSpace *m_parent; +}; + +#endif diff --git a/chalk/chalkcolor/kis_composite_op.cc b/chalk/chalkcolor/kis_composite_op.cc new file mode 100644 index 00000000..ddce271b --- /dev/null +++ b/chalk/chalkcolor/kis_composite_op.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com> + * + * 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 <klocale.h> + +#include "kis_composite_op.h" + +//KisIDCompositeOpMap +std::map<KisID, CompositeOp> KisCompositeOp::s_idOpMap; + +KisCompositeOp::KisCompositeOp() +{ + m_valid = false; +} + +KisCompositeOp::KisCompositeOp(const TQString& id) +{ + if (s_idOpMap.empty()) { + fillMap(); + } + + KisIDCompositeOpMap::const_iterator it; + m_valid = false; + + for (it = s_idOpMap.begin(); it != s_idOpMap.end(); ++it) { + + const KisID& kisId = (*it).first; + + if (kisId.id() == id) { + + m_id = (*it).first; + m_op = (*it).second; + m_valid = true; + break; + } + } +} + +KisCompositeOp::KisCompositeOp(CompositeOp compositeOp) +{ + if (s_idOpMap.empty()) { + fillMap(); + } + + KisIDCompositeOpMap::const_iterator it; + m_valid = false; + + for (it = s_idOpMap.begin(); it != s_idOpMap.end(); ++it) { + + CompositeOp compOp = (*it).second; + + if (compOp == compositeOp) { + + m_id = (*it).first; + m_op = compositeOp; + m_valid = true; + break; + } + } +} + +bool KisCompositeOp::operator==(const KisCompositeOp& other) const +{ + if (isValid() && other.isValid()) { + return op() == other.op(); + } + return false; +} + +bool KisCompositeOp::operator!=(const KisCompositeOp& other) const +{ + return !(*this == other); +} + +void KisCompositeOp::fillMap() +{ + s_idOpMap[KisID("normal", i18n("Normal"))] = COMPOSITE_OVER; + s_idOpMap[KisID("alphadarken", i18n("Alpha Darken"))] = COMPOSITE_ALPHA_DARKEN; + s_idOpMap[KisID("in", i18n("In"))] = COMPOSITE_IN; + s_idOpMap[KisID("out", i18n("Out"))] = COMPOSITE_OUT; + s_idOpMap[KisID("atop", i18n("Atop"))] = COMPOSITE_ATOP; + s_idOpMap[KisID("xor", i18n("Xor"))] = COMPOSITE_XOR; + s_idOpMap[KisID("plus", i18n("Plus"))] = COMPOSITE_PLUS; + s_idOpMap[KisID("minus", i18n("Minus"))] = COMPOSITE_MINUS; + s_idOpMap[KisID("add", i18n("Add"))] = COMPOSITE_ADD; + s_idOpMap[KisID("subtract", i18n("Subtract"))] = COMPOSITE_SUBTRACT; + s_idOpMap[KisID("diff", i18n("Diff"))] = COMPOSITE_DIFF; + s_idOpMap[KisID("multiply", i18n("Multiply"))] = COMPOSITE_MULT; + s_idOpMap[KisID("divide", i18n("Divide"))] = COMPOSITE_DIVIDE; + s_idOpMap[KisID("dodge", i18n("Dodge"))] = COMPOSITE_DODGE; + s_idOpMap[KisID("burn", i18n("Burn"))] = COMPOSITE_BURN; + s_idOpMap[KisID("bumpmap", i18n("Bumpmap"))] = COMPOSITE_BUMPMAP; + s_idOpMap[KisID("copy", i18n("Copy"))] = COMPOSITE_COPY; + s_idOpMap[KisID("copyred", i18n("Copy Red"))] = COMPOSITE_COPY_RED; + s_idOpMap[KisID("copygreen", i18n("Copy Green"))] = COMPOSITE_COPY_GREEN; + s_idOpMap[KisID("copyblue", i18n("Copy Blue"))] = COMPOSITE_COPY_BLUE; + s_idOpMap[KisID("copyopacity", i18n("Copy Opacity"))] = COMPOSITE_COPY_OPACITY; + s_idOpMap[KisID("clear", i18n("Clear"))] = COMPOSITE_CLEAR; + s_idOpMap[KisID("dissolve", i18n("Dissolve"))] = COMPOSITE_DISSOLVE; + s_idOpMap[KisID("displace", i18n("Displace"))] = COMPOSITE_DISPLACE; +#if 0 + s_idOpMap[KisID("modulate", i18n("Modulate"))] = COMPOSITE_MODULATE; + s_idOpMap[KisID("threshold", i18n("Threshold"))] = COMPOSITE_THRESHOLD; +#endif + s_idOpMap[KisID("nocomposition",i18n("No Composition"))] = COMPOSITE_NO; + s_idOpMap[KisID("darken", i18n("Darken"))] = COMPOSITE_DARKEN; + s_idOpMap[KisID("lighten", i18n("Lighten"))] = COMPOSITE_LIGHTEN; + s_idOpMap[KisID("hue", i18n("Hue"))] = COMPOSITE_HUE; + s_idOpMap[KisID("saturation", i18n("Saturation"))] = COMPOSITE_SATURATION; + s_idOpMap[KisID("value", i18n("Value"))] = COMPOSITE_VALUE; + s_idOpMap[KisID("color", i18n("Color"))] = COMPOSITE_COLOR; + s_idOpMap[KisID("colorize", i18n("Colorize"))] = COMPOSITE_COLORIZE; + s_idOpMap[KisID("luminize", i18n("Luminize"))] = COMPOSITE_LUMINIZE; + s_idOpMap[KisID("screen", i18n("Screen"))] = COMPOSITE_SCREEN; + s_idOpMap[KisID("overlay", i18n("Overlay"))] = COMPOSITE_OVERLAY; + s_idOpMap[KisID("copycyan", i18n("Copy Cyan"))] = COMPOSITE_COPY_CYAN; + s_idOpMap[KisID("copymagenta", i18n("Copy Magenta"))] = COMPOSITE_COPY_MAGENTA; + s_idOpMap[KisID("copyyellow", i18n("Copy Yellow"))] = COMPOSITE_COPY_YELLOW; + s_idOpMap[KisID("copyblack", i18n("Copy Black"))] = COMPOSITE_COPY_BLACK; + s_idOpMap[KisID("erase", i18n("Erase"))] = COMPOSITE_ERASE; + s_idOpMap[KisID("undefined", i18n("Undefined"))] = COMPOSITE_UNDEF; +} + diff --git a/chalk/chalkcolor/kis_composite_op.h b/chalk/chalkcolor/kis_composite_op.h new file mode 100644 index 00000000..bd4f10a2 --- /dev/null +++ b/chalk/chalkcolor/kis_composite_op.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com> + * + * 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_COMPOSITE_OP_H_ +#define KIS_COMPOSITE_OP_H_ + +#include <map> +#include <tqvaluelist.h> + +//#include "kis_global.h" +#include "kis_id.h" + +enum CompositeOp { + COMPOSITE_OVER, + COMPOSITE_IN, + COMPOSITE_OUT, + COMPOSITE_ATOP, + COMPOSITE_XOR, + COMPOSITE_PLUS, + COMPOSITE_MINUS, + COMPOSITE_ADD, + COMPOSITE_SUBTRACT, + COMPOSITE_DIFF, + COMPOSITE_MULT, + COMPOSITE_DIVIDE, + COMPOSITE_DODGE, + COMPOSITE_BURN, + COMPOSITE_BUMPMAP, + COMPOSITE_COPY, + COMPOSITE_COPY_RED, + COMPOSITE_COPY_GREEN, + COMPOSITE_COPY_BLUE, + COMPOSITE_COPY_OPACITY, + COMPOSITE_CLEAR, + COMPOSITE_DISSOLVE, + COMPOSITE_DISPLACE, +#if 0 + COMPOSITE_MODULATE, + COMPOSITE_THRESHOLD, +#endif + COMPOSITE_NO, + COMPOSITE_DARKEN, + COMPOSITE_LIGHTEN, + COMPOSITE_HUE, + COMPOSITE_SATURATION, + COMPOSITE_VALUE, + COMPOSITE_COLOR, + COMPOSITE_COLORIZE, + COMPOSITE_LUMINIZE, + COMPOSITE_SCREEN, + COMPOSITE_OVERLAY, + COMPOSITE_COPY_CYAN, + COMPOSITE_COPY_MAGENTA, + COMPOSITE_COPY_YELLOW, + COMPOSITE_COPY_BLACK, + COMPOSITE_ERASE, + COMPOSITE_ALPHA_DARKEN, + COMPOSITE_UNDEF +}; + +class KisCompositeOp { +public: + KisCompositeOp(); + KisCompositeOp(const TQString& id); + KisCompositeOp(CompositeOp compositeOp); + + KisID id() const { return m_id; } + CompositeOp op() const { return m_op; } + + bool isValid() const { return m_valid; } + + bool operator==(const KisCompositeOp& other) const; + bool operator!=(const KisCompositeOp& other) const; + +private: + void fillMap(); + +private: + CompositeOp m_op; + KisID m_id; + bool m_valid; + + typedef std::map<KisID, CompositeOp> KisIDCompositeOpMap; + static KisIDCompositeOpMap s_idOpMap; +}; + +typedef TQValueList<KisCompositeOp> KisCompositeOpList; + +#endif // KIS_COMPOSITE_OP_H diff --git a/chalk/chalkcolor/kis_f16half_base_colorspace.cc b/chalk/chalkcolor/kis_f16half_base_colorspace.cc new file mode 100644 index 00000000..d4886c5f --- /dev/null +++ b/chalk/chalkcolor/kis_f16half_base_colorspace.cc @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com> + * + * 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_global.h" +#include "kis_f16half_base_colorspace.h" + +TQ_UINT8 KisF16HalfBaseColorSpace::getAlpha(const TQ_UINT8 * U8_pixel) const +{ + if (m_alphaPos < 0) return OPACITY_OPAQUE; + + U8_pixel += m_alphaPos; + + const half *pixel = reinterpret_cast<const half *>(U8_pixel); + return HALF_TO_UINT8(*pixel); +} + +void KisF16HalfBaseColorSpace::setAlpha(TQ_UINT8 *U8_pixel, TQ_UINT8 alpha, TQ_INT32 nPixels) const +{ + if (m_alphaPos < 0) return; + TQ_INT32 psize = pixelSize(); + + while (nPixels > 0) { + + half *pixel = reinterpret_cast<half *>(U8_pixel + m_alphaPos); + *pixel = UINT8_TO_HALF(alpha); + + --nPixels; + U8_pixel += psize; + } +} + +void KisF16HalfBaseColorSpace::multiplyAlpha(TQ_UINT8 *U8_pixel, TQ_UINT8 U8_alpha, TQ_INT32 nPixels) +{ + if (m_alphaPos < 0) return; + TQ_INT32 psize = pixelSize(); + half alpha = UINT8_TO_HALF(U8_alpha); + + while (nPixels > 0) { + + half *pixelAlpha = reinterpret_cast<half *>(U8_pixel + m_alphaPos); + *pixelAlpha *= alpha; + + --nPixels; + U8_pixel += psize; + } +} + +void KisF16HalfBaseColorSpace::applyAlphaU8Mask(TQ_UINT8 * U8_pixel, TQ_UINT8 * alpha8, TQ_INT32 nPixels) +{ + if (m_alphaPos < 0) return; + + TQ_INT32 psize = pixelSize(); + + while (nPixels--) { + + half *pixelAlpha = reinterpret_cast<half *>(U8_pixel + m_alphaPos); + *pixelAlpha *= UINT8_TO_HALF(*alpha8); + + ++alpha8; + U8_pixel += psize; + } +} + +void KisF16HalfBaseColorSpace::applyInverseAlphaU8Mask(TQ_UINT8 * U8_pixels, TQ_UINT8 * alpha8, TQ_INT32 nPixels) +{ + if (m_alphaPos < 0) return; + + TQ_INT32 psize = pixelSize(); + + while (nPixels--) { + + half *pixelAlpha = reinterpret_cast<half *>(U8_pixels + m_alphaPos); + *pixelAlpha *= UINT8_TO_HALF(MAX_SELECTED - *alpha8); + + U8_pixels += psize; + ++alpha8; + } +} + +TQString KisF16HalfBaseColorSpace::channelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < (TQ_UINT32)nChannels()); + const half *pixel = reinterpret_cast<const half *>(U8_pixel); + TQ_UINT32 channelPosition = channels()[channelIndex] -> pos() / sizeof(half); + + return TQString().setNum(pixel[channelPosition]); +} + +TQString KisF16HalfBaseColorSpace::normalisedChannelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < (TQ_UINT32)nChannels()); + const half *pixel = reinterpret_cast<const half *>(U8_pixel); + TQ_UINT32 channelPosition = channels()[channelIndex] -> pos() / sizeof(half); + + return TQString().setNum(100.0 * pixel[channelPosition]); +} + +TQ_UINT8 KisF16HalfBaseColorSpace::scaleToU8(const TQ_UINT8 * U8_pixel, TQ_INT32 channelPos) +{ + const half *pixelChannel = reinterpret_cast<const half *>(U8_pixel + channelPos); + return HALF_TO_UINT8(*pixelChannel); +} + +TQ_UINT16 KisF16HalfBaseColorSpace::scaleToU16(const TQ_UINT8 * U8_pixel, TQ_INT32 channelPos) +{ + const half *pixelChannel = reinterpret_cast<const half *>(U8_pixel + channelPos); + return HALF_TO_UINT16(*pixelChannel); +} + diff --git a/chalk/chalkcolor/kis_f16half_base_colorspace.h b/chalk/chalkcolor/kis_f16half_base_colorspace.h new file mode 100644 index 00000000..ff1dbf8c --- /dev/null +++ b/chalk/chalkcolor/kis_f16half_base_colorspace.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KIS_F16HALF_BASE_COLORSPACE_H_ +#define KIS_F16HALF_BASE_COLORSPACE_H_ + +#include <tqcolor.h> + +#include <half.h> + +#include "kis_global.h" +#include "kis_abstract_colorspace.h" +#include "kis_integer_maths.h" + +/** + * This class is the base for all 16-bit float colorspaces using the + * OpenEXR half format. This format can be used with the OpenGL + * extensions GL_NV_half_float and GL_ARB_half_float_pixel. + */ + +inline half UINT8_TO_HALF(uint c) +{ + return static_cast<half>(c) / UINT8_MAX; +} + +inline uint HALF_TO_UINT8(half c) +{ + return static_cast<uint>(CLAMP(static_cast<int>(c * static_cast<int>(UINT8_MAX) + 0.5), + static_cast<int>(UINT8_MIN), static_cast<int>(UINT8_MAX))); +} + + +inline uint HALF_TO_UINT16(half c) +{ + return static_cast<uint>(CLAMP(static_cast<int>(c * static_cast<int>(UINT16_MAX) + 0.5), + static_cast<int>(UINT16_MIN), static_cast<int>(UINT16_MAX))); +} + +inline half HALF_BLEND(half a, half b, half alpha) +{ + return (a - b) * alpha + b; +} + +#define F16HALF_OPACITY_OPAQUE ((half)1.0f) +#define F16HALF_OPACITY_TRANSPARENT ((half)0.0f) + +class KisF16HalfBaseColorSpace : public KisAbstractColorSpace { + +public: + + KisF16HalfBaseColorSpace(const KisID & id, DWORD cmType, icColorSpaceSignature colorSpaceSignature, + KisColorSpaceFactoryRegistry * tqparent, + KisProfile *p) + : KisAbstractColorSpace(id, cmType, colorSpaceSignature, tqparent, p) + { + m_alphaSize = sizeof(half); + }; + + virtual TQ_UINT8 getAlpha(const TQ_UINT8 * pixel) const; + virtual void setAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels) const; + virtual void multiplyAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels); + + virtual void applyAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels); + virtual void applyInverseAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels); + + virtual TQString channelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + virtual TQString normalisedChannelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + + virtual TQ_UINT8 scaleToU8(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos); + virtual TQ_UINT16 scaleToU16(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos); + + virtual bool hasHighDynamicRange() const { return true; } + +protected: + // For Alpha Composite + struct F16HalfMult { + inline half operator()(const half& a, const half& b) const { + return a * b; + } + }; + struct Uint8ToF16Half { + inline half operator()(const TQ_UINT8 src) const { + return UINT8_TO_HALF(src); + } + }; + struct F16HalfOpacityTest { + inline bool operator()(const half& opacity) const { + return opacity > F16HALF_OPACITY_TRANSPARENT + HALF_EPSILON; + } + }; +}; + +#endif // KIS_F16HALF_BASE_COLORSPACE_H_ diff --git a/chalk/chalkcolor/kis_f32_base_colorspace.cc b/chalk/chalkcolor/kis_f32_base_colorspace.cc new file mode 100644 index 00000000..27a6a312 --- /dev/null +++ b/chalk/chalkcolor/kis_f32_base_colorspace.cc @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com> + * + * 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_global.h" +#include "kis_f32_base_colorspace.h" + +TQ_UINT8 KisF32BaseColorSpace::getAlpha(const TQ_UINT8 * U8_pixel) const +{ + if (m_alphaPos < 0) return OPACITY_OPAQUE; + + U8_pixel += m_alphaPos; + + const float *pixel = reinterpret_cast<const float *>(U8_pixel); + return FLOAT_TO_UINT8(*pixel); +} + +void KisF32BaseColorSpace::setAlpha(TQ_UINT8 *U8_pixel, TQ_UINT8 alpha, TQ_INT32 nPixels) const +{ + if (m_alphaPos < 0) return; + TQ_INT32 psize = pixelSize(); + + while (nPixels > 0) { + + float *pixel = reinterpret_cast<float *>(U8_pixel + m_alphaPos); + *pixel = UINT8_TO_FLOAT(alpha); + + --nPixels; + U8_pixel += psize; + } +} + +void KisF32BaseColorSpace::multiplyAlpha(TQ_UINT8 *U8_pixel, TQ_UINT8 U8_alpha, TQ_INT32 nPixels) +{ + if (m_alphaPos < 0) return; + TQ_INT32 psize = pixelSize(); + float alpha = UINT8_TO_FLOAT(U8_alpha); + + while (nPixels > 0) { + + float *pixelAlpha = reinterpret_cast<float *>(U8_pixel + m_alphaPos); + *pixelAlpha *= alpha; + + --nPixels; + U8_pixel += psize; + } +} + +void KisF32BaseColorSpace::applyAlphaU8Mask(TQ_UINT8 * U8_pixel, TQ_UINT8 * alpha8, TQ_INT32 nPixels) +{ + if (m_alphaPos < 0) return; + + TQ_INT32 psize = pixelSize(); + + while (nPixels--) { + + float *pixelAlpha = reinterpret_cast<float *>(U8_pixel + m_alphaPos); + *pixelAlpha *= UINT8_TO_FLOAT(*alpha8); + + ++alpha8; + U8_pixel += psize; + } +} + +void KisF32BaseColorSpace::applyInverseAlphaU8Mask(TQ_UINT8 * U8_pixels, TQ_UINT8 * alpha8, TQ_INT32 nPixels) +{ + if (m_alphaPos < 0) return; + + TQ_INT32 psize = pixelSize(); + + while (nPixels--) { + + float *pixelAlpha = reinterpret_cast<float *>(U8_pixels + m_alphaPos); + *pixelAlpha *= UINT8_TO_FLOAT(MAX_SELECTED - *alpha8); + + U8_pixels += psize; + ++alpha8; + } +} + +TQString KisF32BaseColorSpace::channelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < (TQ_UINT32)nChannels()); + const float *pixel = reinterpret_cast<const float *>(U8_pixel); + TQ_UINT32 channelPosition = channels()[channelIndex]->pos() / sizeof(float); + + return TQString().setNum(pixel[channelPosition]); +} + +TQString KisF32BaseColorSpace::normalisedChannelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < (TQ_UINT32)nChannels()); + const float *pixel = reinterpret_cast<const float *>(U8_pixel); + TQ_UINT32 channelPosition = channels()[channelIndex]->pos() / sizeof(float); + + return TQString().setNum(100.0 * pixel[channelPosition]); +} + +TQ_UINT8 KisF32BaseColorSpace::scaleToU8(const TQ_UINT8 * U8_pixel, TQ_INT32 channelPos) +{ + const float *pixelChannel = reinterpret_cast<const float *>(U8_pixel + channelPos); + return FLOAT_TO_UINT8(*pixelChannel); +} + +TQ_UINT16 KisF32BaseColorSpace::scaleToU16(const TQ_UINT8 * U8_pixel, TQ_INT32 channelPos) +{ + const float *pixelChannel = reinterpret_cast<const float *>(U8_pixel + channelPos); + return FLOAT_TO_UINT16(*pixelChannel); +} + diff --git a/chalk/chalkcolor/kis_f32_base_colorspace.h b/chalk/chalkcolor/kis_f32_base_colorspace.h new file mode 100644 index 00000000..60ecb9aa --- /dev/null +++ b/chalk/chalkcolor/kis_f32_base_colorspace.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KIS_F32_BASE_COLORSPACE_H_ +#define KIS_F32_BASE_COLORSPACE_H_ + +#include <tqcolor.h> + +#include "kis_global.h" +#include "kis_abstract_colorspace.h" +#include "kis_integer_maths.h" + +/** + * This class is the base for all 32-bit float colorspaces. + */ + +inline float UINT8_TO_FLOAT(uint c) +{ + return static_cast<float>(c) / UINT8_MAX; +} + +inline uint FLOAT_TO_UINT8(float c) +{ + return static_cast<uint>(CLAMP(static_cast<int>(c * static_cast<int>(UINT8_MAX) + 0.5), + static_cast<int>(UINT8_MIN), static_cast<int>(UINT8_MAX))); +} + + +inline uint FLOAT_TO_UINT16(float c) +{ + return static_cast<uint>(CLAMP(static_cast<int>(c * static_cast<int>(UINT16_MAX) + 0.5), + static_cast<int>(UINT16_MIN), static_cast<int>(UINT16_MAX))); +} + +inline float FLOAT_BLEND(float a, float b, float alpha) +{ + return (a - b) * alpha + b; +} + +#define F32_OPACITY_OPAQUE 1.0f +#define F32_OPACITY_TRANSPARENT 0.0f + +class KisF32BaseColorSpace : public KisAbstractColorSpace { + +public: + + KisF32BaseColorSpace(const KisID & id, DWORD cmType, icColorSpaceSignature colorSpaceSignature, KisColorSpaceFactoryRegistry * tqparent, KisProfile *p) + : KisAbstractColorSpace(id, cmType, colorSpaceSignature, tqparent, p) + { + m_alphaSize = sizeof(float); + }; + + virtual TQ_UINT8 getAlpha(const TQ_UINT8 * pixel) const; + virtual void setAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels) const; + virtual void multiplyAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels); + + virtual void applyAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels); + virtual void applyInverseAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels); + + virtual TQString channelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + virtual TQString normalisedChannelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + + virtual TQ_UINT8 scaleToU8(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos); + virtual TQ_UINT16 scaleToU16(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos); + + virtual bool hasHighDynamicRange() const { return true; } +}; + +#endif // KIS_F32_BASE_COLORSPACE_H_ diff --git a/chalk/chalkcolor/kis_histogram_producer.cc b/chalk/chalkcolor/kis_histogram_producer.cc new file mode 100644 index 00000000..bbea95ba --- /dev/null +++ b/chalk/chalkcolor/kis_histogram_producer.cc @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_histogram_producer.h" +#include "kis_basic_histogram_producers.h" + +KisHistogramProducerFactoryRegistry* KisHistogramProducerFactoryRegistry::m_singleton = 0; + +KisHistogramProducerFactoryRegistry::KisHistogramProducerFactoryRegistry() { + Q_ASSERT(KisHistogramProducerFactoryRegistry::m_singleton == 0); +} + +KisHistogramProducerFactoryRegistry::~KisHistogramProducerFactoryRegistry() { +} + +KisHistogramProducerFactoryRegistry* KisHistogramProducerFactoryRegistry::instance() { + if(KisHistogramProducerFactoryRegistry::m_singleton == 0) { + KisHistogramProducerFactoryRegistry::m_singleton + = new KisHistogramProducerFactoryRegistry(); + m_singleton->add( new KisGenericLabHistogramProducerFactory() ); + } + return KisHistogramProducerFactoryRegistry::m_singleton; +} + +KisIDList KisHistogramProducerFactoryRegistry::listKeysCompatibleWith( + KisColorSpace* colorSpace) const +{ + KisIDList list; + TQValueList<float> preferredList; + storageMap::const_iterator it = m_storage.begin(); + storageMap::const_iterator endit = m_storage.end(); + // O(n^2), can't this be done better? (But preferrably not by looking up the preferredness + // during the sorting... + while( it != endit ) { + if (it->second->isCompatibleWith(colorSpace)) { + float preferred = it->second->preferrednessLevelWith(colorSpace); + TQValueList<float>::iterator pit = preferredList.begin(); + TQValueList<float>::iterator pend = preferredList.end(); + KisIDList::iterator lit = list.begin(); + + while (pit != pend && preferred <= *pit) { + ++pit; + ++lit; + } + + list.insert(lit, it->first); + preferredList.insert(pit, preferred); + } + ++it; + } + return list; +} diff --git a/chalk/chalkcolor/kis_histogram_producer.h b/chalk/chalkcolor/kis_histogram_producer.h new file mode 100644 index 00000000..2d25b354 --- /dev/null +++ b/chalk/chalkcolor/kis_histogram_producer.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _KIS_HISTOGRAM_PRODUCER_ +#define _KIS_HISTOGRAM_PRODUCER_ + +#include <tqglobal.h> +#include <ksharedptr.h> + +#include <kis_colorspace.h> + +#include "kis_generic_registry.h" + +class KisRectIteratorPixel; +class TQString; +class KisChannelInfo; + +/** + * This class is an interface used in the generation of a histogram. It is a container of + * data, all mathematically interesting things will calculated by a KisHistogram. + * + * The default view will be the entire range each color can be in. And don't let the + * numberOfBins return anything else then 256 unless you have a very good reason for it. + * + * About the views: a view is a zoom combined with a start level: the entire + * range of a channel is 0.0 - 1.0: this is the position. Combined with a zoom, we can + * calculate what part of a channel will fall in a bin. This gives us an interface to + * that the views that is not dependent of the actual colorspace of the histogram. + * The 'size' value is the size, again from 0.0 to 1.0 of the displayed range. + * + * For comfort of the GUI, and because it is logical, channels are accessed in the order + * in which they are found in the channels() method. This is potentially different from + * the order in which they are internally ordered! + **/ +class KisHistogramProducer : public KShared { +public: + KisHistogramProducer() : m_skipTransparent(true), m_skipUnselected(true) {} + virtual ~KisHistogramProducer() {} + + // Methods to change the bins + + /** Clears the data in this producer, but keeps its other settings */ + virtual void clear() = 0; + + /** + * Adds the values from the specified array of pixels to the bins -- does not + * reset anything. + * + * @param pixels A pointer an array of pixeldata in the given colorspace + * @param selectionMask a pointer to an array of bytes, where 0 is unselected and 1-255 is degree of selectedness. The array + * must be just as long as the array of pixels. + * @param nPixels The number of pixels + * @param colorSpace the colorspace that can decode the pixel data. + */ + virtual void addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace* colorSpace) = 0; + + // Methods to set what exactly is being added to the bins + virtual void setView(double from, double width) = 0; + virtual void setSkipTransparent(bool set) { m_skipTransparent = set; } + virtual void setSkipUnselected(bool set) { m_skipUnselected = set; } + + // Methods with general information about this specific producer + virtual const KisID& id() const = 0; + virtual TQValueVector<KisChannelInfo *> channels() = 0; + virtual TQ_INT32 numberOfBins() = 0; + virtual TQString positionToString(double pos) const = 0; + virtual double viewFrom() const = 0; + virtual double viewWidth() const = 0; + virtual double maximalZoom() const = 0; + + // Methods to get information on the data we have seen + virtual TQ_INT32 count() = 0; + virtual TQ_INT32 getBinAt(TQ_INT32 channel, TQ_INT32 position) = 0; + virtual TQ_INT32 outOfViewLeft(TQ_INT32 channel) = 0; + virtual TQ_INT32 outOfViewRight(TQ_INT32 channel) = 0; +protected: + bool m_skipTransparent; + bool m_skipUnselected; +}; + +typedef KSharedPtr<KisHistogramProducer> KisHistogramProducerSP; + +class KisHistogramProducerFactory { +public: + KisHistogramProducerFactory(const KisID& id) : m_id(id) {} + virtual ~KisHistogramProducerFactory() {} + /// Factory method, generates a new KisHistogramProducer + virtual KisHistogramProducerSP generate() = 0; + /// Returns if a colorspace can be used with this producer + virtual bool isCompatibleWith(KisColorSpace* colorSpace) const = 0; + /// Returns a float in the [0.0, 1.0] range, 0.0 means this is a very generic method + virtual float preferrednessLevelWith(KisColorSpace* colorSpace) const = 0; + virtual const KisID& id() const { return m_id; } +protected: + KisID m_id; +}; + +class KisHistogramProducerFactoryRegistry + : public KisGenericRegistry<KisHistogramProducerFactory*> { +public: + virtual ~KisHistogramProducerFactoryRegistry(); + static KisHistogramProducerFactoryRegistry* instance(); + /// returns a list, sorted by preferrence: higher preferance comes first + KisIDList listKeysCompatibleWith(KisColorSpace* colorSpace) const; + +private: + KisHistogramProducerFactoryRegistry(); + KisHistogramProducerFactoryRegistry(const KisHistogramProducerFactoryRegistry&); + KisHistogramProducerFactoryRegistry operator=(const KisHistogramProducerFactoryRegistry&); + + static KisHistogramProducerFactoryRegistry* m_singleton; +}; + +#endif // _KIS_HISTOGRAM_PRODUCER diff --git a/chalk/chalkcolor/kis_profile.cc b/chalk/chalkcolor/kis_profile.cc new file mode 100644 index 00000000..2d0ad98f --- /dev/null +++ b/chalk/chalkcolor/kis_profile.cc @@ -0,0 +1,208 @@ +/* + * kis_profile.cc - part of Krayon + * + * Copyright (c) 2000 Matthias Elter <elter@kde.org> + * 2001 John Califf + * 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 <cfloat> +#include <cmath> +#include <config.h> +#include LCMS_HEADER + +#include <tqimage.h> +#include <tqtextstream.h> +#include <tqfile.h> + +#include <kdebug.h> + +#include "kis_profile.h" +#include "kis_global.h" + +#include "ksharedptr.h" + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <fixx11h.h> + +KisProfile::KisProfile(TQByteArray rawData) + : m_rawData(rawData), + m_filename( TQString() ), + m_valid( false ), + m_suitableForOutput(false) +{ + m_profile = cmsOpenProfileFromMem(rawData.data(), (DWORD)rawData.size()); + init(); +} + +KisProfile::KisProfile(const TQString& file) + : m_filename(file), + m_valid( false ), + m_suitableForOutput( false ) +{ +} + +KisProfile::KisProfile(const cmsHPROFILE profile) + : m_profile(profile), + m_filename( TQString() ), + m_valid( true ) +{ + size_t bytesNeeded=0; + + // Make a raw data image ready for saving + _cmsSaveProfileToMem(m_profile, 0, &bytesNeeded); // calc size + if(m_rawData.tqresize(bytesNeeded)) + { + _cmsSaveProfileToMem(m_profile, m_rawData.data(), &bytesNeeded); // fill buffer + cmsHPROFILE newprofile = cmsOpenProfileFromMem(m_rawData.data(), (DWORD) bytesNeeded); + cmsCloseProfile(m_profile); + m_profile = newprofile; + } + else + m_rawData.resize(0); + + init(); +} + +KisProfile::~KisProfile() +{ + cmsCloseProfile(m_profile); +} + + +bool KisProfile::load() +{ + TQFile file(m_filename); + file.open(IO_ReadOnly); + m_rawData = file.readAll(); + m_profile = cmsOpenProfileFromMem(m_rawData.data(), (DWORD)m_rawData.size()); + file.close(); + + if (m_profile == 0) { + kdWarning() << "Failed to load profile from " << m_filename << endl; + } + + return init(); + +} + +bool KisProfile::init() +{ + if (m_profile) { + m_colorSpaceSignature = cmsGetColorSpace(m_profile); + m_deviceClass = cmsGetDeviceClass(m_profile); + m_productName = cmsTakeProductName(m_profile); + m_productDescription = cmsTakeProductDesc(m_profile); + m_productInfo = cmsTakeProductInfo(m_profile); + m_valid = true; + + // Check if the profile can convert (something->this) +// LPMATSHAPER OutMatShaper = cmsBuildOutputMatrixShaper(m_profile); +// if( OutMatShaper ) +// { +// m_suitableForOutput = true; +// } + cmsCIEXYZTRIPLE Primaries; + + if (cmsTakeColorants(&Primaries, m_profile)) + { + m_suitableForOutput = true; + } + +#if 0 + // XXX: It wasn't that easy to save a little memory: thsi gives an lcms error + // Okay, we know enough. Free the memory; we'll load it again if needed. + + cmsCloseProfile(m_profile); + m_profile = 0; + +#endif + return true; + } + return false; +} + +cmsHPROFILE KisProfile::profile() +{ +#if 0 + if (m_profile = 0) { + TQFile file(m_filename); + file.open(IO_ReadOnly); + m_rawData = file.readAll(); + m_profile = cmsOpenProfileFromMem(m_rawData.data(), (DWORD)m_rawData.size()); + file.close(); + } +#endif + return m_profile; +} + +bool KisProfile::save() +{ + return false; +} + +KisAnnotationSP KisProfile::annotation() const +{ + // XXX we hardcode icc, this is correct for lcms? + // XXX productName(), or just "ICC Profile"? + if (!m_rawData.isEmpty()) + return new KisAnnotation("icc", productName(), m_rawData); + else + return 0; +} + +KisProfile * KisProfile::getScreenProfile (int screen) +{ + +#ifdef Q_WS_X11 + + Atom type; + int format; + unsigned long nitems; + unsigned long bytes_after; + TQ_UINT8 * str; + + static Atom icc_atom = XInternAtom( qt_xdisplay(), "_ICC_PROFILE", False ); + + if ( XGetWindowProperty ( qt_xdisplay(), + qt_xrootwin( screen ), + icc_atom, + 0, + INT_MAX, + False, + XA_CARDINAL, + &type, + &format, + &nitems, + &bytes_after, + (unsigned char **) &str) + ) { + + TQByteArray bytes (nitems); + bytes.assign((char*)str, (TQ_UINT32)nitems); + + return new KisProfile(bytes); + } else { + return NULL; + } +#else + return NULL; + +#endif +} + + diff --git a/chalk/chalkcolor/kis_profile.h b/chalk/chalkcolor/kis_profile.h new file mode 100644 index 00000000..5f732188 --- /dev/null +++ b/chalk/chalkcolor/kis_profile.h @@ -0,0 +1,98 @@ +/* + * kis_profile.h - part of Krayon + * + * Copyright (c) 2000 Matthias Elter <elter@kde.org> + * 2004 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_PROFILE_H +#define KIS_PROFILE_H + +#include <config.h> + +#include LCMS_HEADER + +#include <tqvaluevector.h> +#include <tqcstring.h> + +#include <kio/job.h> + +#include <kis_annotation.h> + +//XXX: Profiles should be loaded by the color strategies +// and be available only through the color strategy +// that matches the profile's color model +class KisProfile { + +public: + KisProfile(TQByteArray rawData); + KisProfile(const TQString& file); + KisProfile(const cmsHPROFILE profile); + + virtual ~KisProfile(); + + virtual bool load(); + virtual bool save(); + + inline icColorSpaceSignature colorSpaceSignature() const { return m_colorSpaceSignature; } + inline icProfileClassSignature deviceClass() const { return m_deviceClass; } + inline TQString productName() const { return m_productName; } + inline TQString productDescription() const { return m_productDescription; } + inline TQString productInfo() const { return m_productInfo; } + inline TQString manufacturer() const { return m_manufacturer; } + cmsHPROFILE profile(); + + KisAnnotationSP annotation() const; + + friend inline bool operator==( const KisProfile &, const KisProfile & ); + + inline bool valid() const { return m_valid; }; + + inline bool isSuitableForOutput() { return m_suitableForOutput; }; + + inline TQString filename() const { return m_filename; } + +public: + + static KisProfile * getScreenProfile(int screen = -1); + +private: + bool init(); + + cmsHPROFILE m_profile; + icColorSpaceSignature m_colorSpaceSignature; + icProfileClassSignature m_deviceClass; + TQString m_productName; + TQString m_productDescription; + TQString m_productInfo; + TQString m_manufacturer; + + TQByteArray m_rawData; + + TQString m_filename; + bool m_valid; + bool m_suitableForOutput; + +}; + +inline bool operator==( const KisProfile & p1, const KisProfile & p2 ) +{ + return p1.m_profile == p2.m_profile; +} + +#endif // KIS_PROFILE_H + diff --git a/chalk/chalkcolor/kis_u16_base_colorspace.cc b/chalk/chalkcolor/kis_u16_base_colorspace.cc new file mode 100644 index 00000000..a735e35c --- /dev/null +++ b/chalk/chalkcolor/kis_u16_base_colorspace.cc @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "kdebug.h" + +#include "kis_global.h" +#include "kis_abstract_colorspace.h" +#include "kis_integer_maths.h" +#include "kis_u16_base_colorspace.h" + + +TQ_UINT8 KisU16BaseColorSpace::getAlpha(const TQ_UINT8 * U8_pixel) const +{ + if (m_alphaPos < 0) return OPACITY_OPAQUE; + + U8_pixel+= m_alphaPos; + + const TQ_UINT16 *pixel = reinterpret_cast<const TQ_UINT16 *>(U8_pixel); + return UINT16_TO_UINT8(*pixel); +} + + +void KisU16BaseColorSpace::setAlpha(TQ_UINT8 *U8_pixel, TQ_UINT8 alpha, TQ_INT32 nPixels) const +{ + if (m_alphaPos < 0) return; + TQ_INT32 psize = pixelSize(); + + + while (nPixels > 0) { + + TQ_UINT16 *pixel = reinterpret_cast<TQ_UINT16 *>(U8_pixel + m_alphaPos); + pixel[0] = UINT8_TO_UINT16(alpha); + + --nPixels; + U8_pixel += psize; + } +} + +void KisU16BaseColorSpace::multiplyAlpha(TQ_UINT8 *U8_pixel, TQ_UINT8 U8_alpha, TQ_INT32 nPixels) +{ + if (m_alphaPos < 0) return; + + TQ_INT32 psize = pixelSize(); + TQ_UINT16 alpha = UINT8_TO_UINT16(U8_alpha); + + while (nPixels > 0) { + + TQ_UINT16 *pixelAlpha = reinterpret_cast<TQ_UINT16 *>(U8_pixel + m_alphaPos); + *pixelAlpha = UINT16_MULT(*pixelAlpha, alpha); + + --nPixels; + U8_pixel += psize; + } +} + +void KisU16BaseColorSpace::applyAlphaU8Mask(TQ_UINT8 * U8_pixel, TQ_UINT8 * alpha8, TQ_INT32 nPixels) +{ + if (m_alphaPos < 0) return; + + TQ_INT32 psize = pixelSize(); + + while (nPixels--) { + + // Go to the alpha position (which is given in bytes from the start of the pixel, + // and cast to short. + + TQ_UINT16 *pixelAlpha = reinterpret_cast<TQ_UINT16 *>(U8_pixel + m_alphaPos); + *pixelAlpha = UINT8_MULT(*pixelAlpha, *alpha8); + + ++alpha8; + U8_pixel += psize; + + } +} + +void KisU16BaseColorSpace::applyInverseAlphaU8Mask(TQ_UINT8 * U8_pixels, TQ_UINT8 * alpha8, TQ_INT32 nPixels) +{ + + if (m_alphaPos < 0) return; + + TQ_INT32 psize = pixelSize(); + + + while(nPixels--) { + + TQ_UINT16 s_alpha8; + TQ_UINT32 p_alpha, s_alpha16; + + TQ_UINT16 *alpha = reinterpret_cast<TQ_UINT16 *>(U8_pixels + m_alphaPos); + + p_alpha = *(alpha); + s_alpha8 = MAX_SELECTED - *alpha8; + s_alpha16 = UINT8_TO_UINT16(s_alpha8); + + // Go to the alpha position (which is given in bytes from the start of the pixel, + // and cast to short. + + alpha[0] = UINT16_MULT(p_alpha, s_alpha16); + + U8_pixels += psize; + ++alpha8; + } +} + +TQString KisU16BaseColorSpace::channelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < (TQ_UINT32)nChannels()); + const TQ_UINT16 *pixel = reinterpret_cast<const TQ_UINT16 *>(U8_pixel); + TQ_UINT32 channelPosition = channels()[channelIndex]->pos() / sizeof(TQ_UINT16); + + return TQString().setNum(pixel[channelPosition]); +} + +TQString KisU16BaseColorSpace::normalisedChannelValueText(const TQ_UINT8 *U8_pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < (TQ_UINT32)nChannels()); + const TQ_UINT16 *pixel = reinterpret_cast<const TQ_UINT16 *>(U8_pixel); + TQ_UINT32 channelPosition = m_channels[channelIndex]->pos() / sizeof(TQ_UINT16); + + return TQString().setNum(100.0 * static_cast<float>(pixel[channelPosition]) / UINT16_MAX); +} + +TQ_UINT8 KisU16BaseColorSpace::scaleToU8(const TQ_UINT8 * U8_pixel, TQ_INT32 channelPos) +{ + const TQ_UINT16 *pixel = reinterpret_cast<const TQ_UINT16 *>(U8_pixel); + return UINT16_TO_UINT8(pixel[channelPos]); +} + +TQ_UINT16 KisU16BaseColorSpace::scaleToU16(const TQ_UINT8 * U8_pixel, TQ_INT32 channelPos) +{ + const TQ_UINT16 *pixel = reinterpret_cast<const TQ_UINT16 *>(U8_pixel); + return pixel[channelPos]; +} + diff --git a/chalk/chalkcolor/kis_u16_base_colorspace.h b/chalk/chalkcolor/kis_u16_base_colorspace.h new file mode 100644 index 00000000..756ab9b5 --- /dev/null +++ b/chalk/chalkcolor/kis_u16_base_colorspace.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KIS_U16_BASE_COLORSPACE_H_ +#define KIS_U16_BASE_COLORSPACE_H_ + +#include "kis_global.h" +#include "kis_abstract_colorspace.h" +#include "kis_integer_maths.h" + +/** + * This is the base class for 16-bit/channel colorspaces with 16-bit alpha + * channels. It defines a number of common methods, like handling 16-bit alpha + * and up- and down-scaling of channels. + */ +class KisU16BaseColorSpace : public KisAbstractColorSpace { + +public: + + static const TQ_UINT16 U16_OPACITY_OPAQUE = UINT16_MAX; + static const TQ_UINT16 U16_OPACITY_TRANSPARENT = UINT16_MIN; + +public: + + KisU16BaseColorSpace(const KisID & id, DWORD cmType, icColorSpaceSignature colorSpaceSignature, + KisColorSpaceFactoryRegistry * tqparent, + KisProfile *p) + : KisAbstractColorSpace(id, cmType, colorSpaceSignature, + tqparent, + p) + { + m_alphaSize = sizeof(TQ_UINT16); + }; + + virtual TQ_UINT8 getAlpha(const TQ_UINT8 * pixel) const; + virtual void setAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels) const; + virtual void multiplyAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels); + + virtual void applyAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels); + virtual void applyInverseAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels); + + virtual TQString channelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + virtual TQString normalisedChannelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + + virtual TQ_UINT8 scaleToU8(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos); + virtual TQ_UINT16 scaleToU16(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos); + +protected: + // For Alpha Composite + struct U16Mult { + inline TQ_UINT16 operator()(const TQ_UINT16& a, const TQ_UINT16& b) const { + return UINT16_MULT(a, b); + } + }; + struct Uint8ToU16 { + inline TQ_UINT16 operator()(const TQ_UINT8 src) const { + return UINT8_TO_UINT16(src); + } + }; + struct U16OpacityTest { + inline bool operator()(const TQ_UINT16& opacity) const { + return opacity != U16_OPACITY_TRANSPARENT; + } + }; +}; +#endif // KIS_U16_BASE_COLORSPACE_H_ diff --git a/chalk/chalkcolor/kis_u8_base_colorspace.cc b/chalk/chalkcolor/kis_u8_base_colorspace.cc new file mode 100644 index 00000000..338d95d6 --- /dev/null +++ b/chalk/chalkcolor/kis_u8_base_colorspace.cc @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <tqcolor.h> + +#include <kdebug.h> + +#include "kis_abstract_colorspace.h" +#include "kis_u8_base_colorspace.h" +#include "kis_integer_maths.h" + +TQ_UINT8 KisU8BaseColorSpace::getAlpha(const TQ_UINT8 * pixel) const +{ + return pixel[m_alphaPos]; +} + + + +void KisU8BaseColorSpace::setAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels) const +{ + if (m_alphaPos < 0) return; + TQ_INT32 psize = pixelSize(); + + pixels += m_alphaPos; + while (nPixels > 0) { + *pixels = alpha; + --nPixels; + pixels += psize; + } + +} + +void KisU8BaseColorSpace::multiplyAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels) +{ + if (m_alphaPos < 0) return; + TQ_INT32 psize = pixelSize(); + + while (nPixels > 0) { + pixels[m_alphaPos] = UINT8_MULT(pixels[m_alphaPos], alpha); + --nPixels; + pixels += psize; + } +} + +void KisU8BaseColorSpace::applyAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels) +{ + TQ_INT32 psize = pixelSize(); + + while (nPixels--) { + + pixels[m_alphaPos] = UINT8_MULT(*(pixels + m_alphaPos) , *alpha); + + alpha++; + pixels += psize; + + } +} + +void KisU8BaseColorSpace::applyInverseAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels) +{ + TQ_INT32 psize = pixelSize(); + + while(nPixels--) { + + TQ_UINT16 p_alpha, s_alpha; + + p_alpha = getAlpha(pixels); + s_alpha = MAX_SELECTED - *alpha; + + setAlpha(pixels, UINT8_MULT(p_alpha, s_alpha), 1); + + pixels += psize; + ++alpha; + } +} + +TQString KisU8BaseColorSpace::channelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < (TQ_UINT32)nChannels()); + TQ_UINT32 channelPosition = m_channels[channelIndex]->pos(); + + return TQString().setNum(pixel[channelPosition]); +} + +TQString KisU8BaseColorSpace::normalisedChannelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const +{ + Q_ASSERT(channelIndex < (TQ_UINT32)nChannels()); + TQ_UINT32 channelPosition = m_channels[channelIndex]->pos(); + + return TQString().setNum(100.0 * static_cast<float>(pixel[channelPosition]) / UINT8_MAX); +} + + +TQ_UINT8 KisU8BaseColorSpace::scaleToU8(const TQ_UINT8 * pixel, TQ_INT32 channelPos) +{ + return pixel[channelPos]; +} + +TQ_UINT16 KisU8BaseColorSpace::scaleToU16(const TQ_UINT8 * pixel, TQ_INT32 channelPos) +{ + return UINT8_TO_UINT16(pixel[channelPos]); +} + diff --git a/chalk/chalkcolor/kis_u8_base_colorspace.h b/chalk/chalkcolor/kis_u8_base_colorspace.h new file mode 100644 index 00000000..098a1f59 --- /dev/null +++ b/chalk/chalkcolor/kis_u8_base_colorspace.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KIS_U8_BASE_COLORSPACE_H_ +#define KIS_U8_BASE_COLORSPACE_H_ + +#include <tqcolor.h> + +#include <tqcolor.h> + +#include "kis_global.h" +#include "kis_abstract_colorspace.h" +#include "kis_integer_maths.h" + +/** + * This class is the base for all homogenous 8-bit/channel colorspaces with 8-bit alpha channels + */ +class KisU8BaseColorSpace : public KisAbstractColorSpace { + +public: + + KisU8BaseColorSpace(const KisID & id, DWORD cmType, icColorSpaceSignature colorSpaceSignature, + KisColorSpaceFactoryRegistry * tqparent, + KisProfile *p) + : KisAbstractColorSpace(id, cmType, colorSpaceSignature, tqparent, p) + { + m_alphaSize = sizeof(TQ_UINT8); + }; + + virtual TQ_UINT8 getAlpha(const TQ_UINT8 * pixel) const; + virtual void setAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels) const; + virtual void multiplyAlpha(TQ_UINT8 * pixels, TQ_UINT8 alpha, TQ_INT32 nPixels); + + virtual void applyAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels); + virtual void applyInverseAlphaU8Mask(TQ_UINT8 * pixels, TQ_UINT8 * alpha, TQ_INT32 nPixels); + + virtual TQString channelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + virtual TQString normalisedChannelValueText(const TQ_UINT8 *pixel, TQ_UINT32 channelIndex) const; + + virtual TQ_UINT8 scaleToU8(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos); + virtual TQ_UINT16 scaleToU16(const TQ_UINT8 * srcPixel, TQ_INT32 channelPos); + +protected: + // For Alpha Composite + struct U8Mult { + inline TQ_UINT8 operator()(const TQ_UINT8& a, const TQ_UINT8& b) const { + return UINT8_MULT(a, b); + } + }; + struct Uint8ToU8 { + inline TQ_UINT8 operator()(const TQ_UINT8 src) const { + return src; + } + }; + struct U8OpacityTest { + inline bool operator()(const TQ_UINT8& opacity) const { + return opacity != OPACITY_TRANSPARENT; + } + }; +}; + + +#endif // KIS_U8_BASE_COLORSPACE_H_ diff --git a/chalk/chalkcolor/tests/Makefile.am b/chalk/chalkcolor/tests/Makefile.am new file mode 100644 index 00000000..e78141e7 --- /dev/null +++ b/chalk/chalkcolor/tests/Makefile.am @@ -0,0 +1,16 @@ +AM_CPPFLAGS = \ + -I$(srcdir)/../../sdk \ + -I$(srcdir)/../ \ + $(all_includes) + +# The check_ target makes sure we don't install the modules, +# $(KDE_CHECK_PLUGIN) assures a shared library is created. +check_LTLIBRARIES = kunittest_kis_color_conversions_tester.la + +kunittest_kis_color_conversions_tester_la_SOURCES = kis_color_conversions_tester.cpp +kunittest_kis_color_conversions_tester_la_LIBADD = -lkunittest ../libchalkcolor.la ../../libchalkcommon.la +kunittest_kis_color_conversions_tester_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries) + +check-local: kunittest_kis_color_conversions_tester.la + kunittestmodrunner + diff --git a/chalk/chalkcolor/tests/kis_color_conversions_tester.cpp b/chalk/chalkcolor/tests/kis_color_conversions_tester.cpp new file mode 100644 index 00000000..d244b5dd --- /dev/null +++ b/chalk/chalkcolor/tests/kis_color_conversions_tester.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com> + * + * 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 <kunittest/runner.h> +#include <kunittest/module.h> + +#include "kis_color_conversions_tester.h" +#include "kis_color_conversions.h" + +using namespace KUnitTest; + +KUNITTEST_MODULE(kunittest_kis_color_conversions_tester, "Color Conversions Tester"); +KUNITTEST_MODULE_REGISTER_TESTER(KisColorConversionsTester); + +void KisColorConversionsTester::allTests() +{ + testRGBHSV(); + testRGBHSL(); +} + +#define EPSILON 1e-6 + +void KisColorConversionsTester::testRGBHSV() +{ + float r, g, b, h, s, v; + + RGBToHSV(1, 0, 0, &h, &s, &v); + CHECK(h, 0.0f); + CHECK(s, 1.0f); + CHECK(v, 1.0f); + + RGBToHSV(1, 1, 0, &h, &s, &v); + CHECK(h, 60.0f); + CHECK(s, 1.0f); + CHECK(v, 1.0f); + + RGBToHSV(0, 1, 0, &h, &s, &v); + CHECK(h, 120.0f); + CHECK(s, 1.0f); + CHECK(v, 1.0f); + + RGBToHSV(0, 1, 1, &h, &s, &v); + CHECK(h, 180.0f); + CHECK(s, 1.0f); + CHECK(v, 1.0f); + + RGBToHSV(0, 0, 1, &h, &s, &v); + CHECK(h, 240.0f); + CHECK(s, 1.0f); + CHECK(v, 1.0f); + + RGBToHSV(1, 0, 1, &h, &s, &v); + CHECK(h, 300.0f); + CHECK(s, 1.0f); + CHECK(v, 1.0f); + + RGBToHSV(0, 0, 0, &h, &s, &v); + CHECK(h, -1.0f); + CHECK(s, 0.0f); + CHECK(v, 0.0f); + + RGBToHSV(1, 1, 1, &h, &s, &v); + CHECK(h, -1.0f); + CHECK(s, 0.0f); + CHECK(v, 1.0f); + + RGBToHSV(0.5, 0.25, 0.75, &h, &s, &v); + CHECK_TOLERANCE(h, 270.0f, EPSILON); + CHECK_TOLERANCE(s, 0.666667f, EPSILON); + CHECK_TOLERANCE(v, 0.75f, EPSILON); + + HSVToRGB(0, 1, 1, &r, &g, &b); + CHECK(r, 1.0f); + CHECK(g, 0.0f); + CHECK(b, 0.0f); + + HSVToRGB(60, 1, 1, &r, &g, &b); + CHECK(r, 1.0f); + CHECK(g, 1.0f); + CHECK(b, 0.0f); + + HSVToRGB(120, 1, 1, &r, &g, &b); + CHECK(r, 0.0f); + CHECK(g, 1.0f); + CHECK(b, 0.0f); + + HSVToRGB(180, 1, 1, &r, &g, &b); + CHECK(r, 0.0f); + CHECK(g, 1.0f); + CHECK(b, 1.0f); + + HSVToRGB(240, 1, 1, &r, &g, &b); + CHECK(r, 0.0f); + CHECK(g, 0.0f); + CHECK(b, 1.0f); + + HSVToRGB(300, 1, 1, &r, &g, &b); + CHECK(r, 1.0f); + CHECK(g, 0.0f); + CHECK(b, 1.0f); + + HSVToRGB(-1, 0, 0, &r, &g, &b); + CHECK(r, 0.0f); + CHECK(g, 0.0f); + CHECK(b, 0.0f); + + HSVToRGB(-1, 0, 1, &r, &g, &b); + CHECK(r, 1.0f); + CHECK(g, 1.0f); + CHECK(b, 1.0f); + + HSVToRGB(270, 0.666667, 0.75, &r, &g, &b); + CHECK_TOLERANCE(r, 0.5f, EPSILON); + CHECK_TOLERANCE(g, 0.25f, EPSILON); + CHECK_TOLERANCE(b, 0.75f, EPSILON); +} + +void KisColorConversionsTester::testRGBHSL() +{ + float r, g, b, h, s, l; + + RGBToHSL(1, 0, 0, &h, &s, &l); + CHECK(h, 360.0f); + CHECK(s, 1.0f); + CHECK(l, 0.5f); + + RGBToHSL(1, 1, 0, &h, &s, &l); + CHECK(h, 60.0f); + CHECK(s, 1.0f); + CHECK(l, 0.5f); + + RGBToHSL(0, 1, 0, &h, &s, &l); + CHECK(h, 120.0f); + CHECK(s, 1.0f); + CHECK(l, 0.5f); + + RGBToHSL(0, 1, 1, &h, &s, &l); + CHECK(h, 180.0f); + CHECK(s, 1.0f); + CHECK(l, 0.5f); + + RGBToHSL(0, 0, 1, &h, &s, &l); + CHECK(h, 240.0f); + CHECK(s, 1.0f); + CHECK(l, 0.5f); + + RGBToHSL(1, 0, 1, &h, &s, &l); + CHECK(h, 300.0f); + CHECK(s, 1.0f); + CHECK(l, 0.5f); + + RGBToHSL(0, 0, 0, &h, &s, &l); + CHECK(h, -1.0f); + CHECK(s, 0.0f); + CHECK(l, 0.0f); + + RGBToHSL(1, 1, 1, &h, &s, &l); + CHECK(h, -1.0f); + CHECK(s, 0.0f); + CHECK(l, 1.0f); + + RGBToHSL(0.5, 0.25, 0.75, &h, &s, &l); + CHECK_TOLERANCE(h, 270.0f, EPSILON); + CHECK_TOLERANCE(s, 0.5f, EPSILON); + CHECK_TOLERANCE(l, 0.5f, EPSILON); + + HSLToRGB(0, 1, 0.5, &r, &g, &b); + CHECK(r, 1.0f); + CHECK(g, 0.0f); + CHECK(b, 0.0f); + + HSLToRGB(60, 1, 0.5, &r, &g, &b); + CHECK(r, 1.0f); + CHECK(g, 1.0f); + CHECK(b, 0.0f); + + HSLToRGB(120, 1, 0.5, &r, &g, &b); + CHECK(r, 0.0f); + CHECK(g, 1.0f); + CHECK(b, 0.0f); + + HSLToRGB(180, 1, 0.5, &r, &g, &b); + CHECK(r, 0.0f); + CHECK(g, 1.0f); + CHECK(b, 1.0f); + + HSLToRGB(240, 1, 0.5, &r, &g, &b); + CHECK(r, 0.0f); + CHECK(g, 0.0f); + CHECK(b, 1.0f); + + HSLToRGB(300, 1, 0.5, &r, &g, &b); + CHECK(r, 1.0f); + CHECK(g, 0.0f); + CHECK(b, 1.0f); + + HSLToRGB(-1, 0, 0, &r, &g, &b); + CHECK(r, 0.0f); + CHECK(g, 0.0f); + CHECK(b, 0.0f); + + HSLToRGB(-1, 0, 1, &r, &g, &b); + CHECK(r, 1.0f); + CHECK(g, 1.0f); + CHECK(b, 1.0f); + + HSLToRGB(270, 0.5, 0.5, &r, &g, &b); + CHECK_TOLERANCE(r, 0.5f, EPSILON); + CHECK_TOLERANCE(g, 0.25f, EPSILON); + CHECK_TOLERANCE(b, 0.75f, EPSILON); +} + diff --git a/chalk/chalkcolor/tests/kis_color_conversions_tester.h b/chalk/chalkcolor/tests/kis_color_conversions_tester.h new file mode 100644 index 00000000..1223abbb --- /dev/null +++ b/chalk/chalkcolor/tests/kis_color_conversions_tester.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com> + * + * 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_COLOR_CONVERSIONS_TESTER_H +#define KIS_COLOR_CONVERSIONS_TESTER_H + +#include <kunittest/tester.h> + +#define CHECK_TOLERANCE( x, y, tolerance ) \ +if ((x) <= (y) + (tolerance) && (x) >= (y) - (tolerance)) \ +{ \ + success(TQString(__FILE__) + "[" + TQString::number(__LINE__) + "]: passed " + #x); \ +} \ +else \ +{ \ + failure(TQString(__FILE__) + "[" + TQString::number(__LINE__) + TQString("]: failed ") + #x + "\n Expected " + #y + ", Actual result " + TQString::number(x)); \ +} \ + + +class KisColorConversionsTester : public KUnitTest::Tester +{ +public: + void allTests(); + void testRGBHSV(); + void testRGBHSL(); +}; + +#endif + |