You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
386 lines
13 KiB
386 lines
13 KiB
/*
|
|
* 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 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 <config.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include LCMS_HEADER
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <tdelocale.h>
|
|
|
|
#include "kis_lms_f32_colorspace.h"
|
|
#include "kis_color_conversions.h"
|
|
|
|
namespace {
|
|
const TQ_INT32 MAX_CHANNEL_LMS = 3;
|
|
const TQ_INT32 MAX_CHANNEL_LMSA = 4;
|
|
}
|
|
|
|
#include "kis_integer_maths.h"
|
|
|
|
#define FLOAT_MAX 1.0f //temp
|
|
|
|
#define EPSILON 1e-6
|
|
|
|
// FIXME: lcms doesn't support 32-bit float
|
|
#define F32_LCMS_TYPE TYPE_BGRA_16
|
|
|
|
// disable the lcms handling by setting profile=0
|
|
KisLmsF32ColorSpace::KisLmsF32ColorSpace(KisColorSpaceFactoryRegistry * parent, KisProfile */*p*/) :
|
|
KisF32BaseColorSpace(KisID("LMSAF32", i18n("LMS (32-bit float/channel)")), F32_LCMS_TYPE, icSig3colorData, parent, 0)
|
|
{
|
|
m_channels.push_back(new KisChannelInfo(i18n("Long"), i18n("L"), PIXEL_LONGWAVE * sizeof(float), KisChannelInfo::COLOR, KisChannelInfo::FLOAT32, sizeof(float)));
|
|
m_channels.push_back(new KisChannelInfo(i18n("Middle"), i18n("M"), PIXEL_MIDDLEWAVE * sizeof(float), KisChannelInfo::COLOR, KisChannelInfo::FLOAT32, sizeof(float)));
|
|
m_channels.push_back(new KisChannelInfo(i18n("Short"), i18n("S"), PIXEL_SHORTWAVE * sizeof(float), KisChannelInfo::COLOR, KisChannelInfo::FLOAT32, sizeof(float)));
|
|
m_channels.push_back(new KisChannelInfo(i18n("Alpha"), i18n("A"), PIXEL_ALPHA * sizeof(float), KisChannelInfo::ALPHA, KisChannelInfo::FLOAT32, sizeof(float)));
|
|
|
|
m_alphaPos = PIXEL_ALPHA * sizeof(float);
|
|
}
|
|
|
|
KisLmsF32ColorSpace::~KisLmsF32ColorSpace()
|
|
{
|
|
}
|
|
|
|
void KisLmsF32ColorSpace::setPixel(TQ_UINT8 *dst, float longWave, float middleWave, float shortWave, float alpha) const
|
|
{
|
|
Pixel *dstPixel = reinterpret_cast<Pixel *>(dst);
|
|
|
|
dstPixel->longWave = longWave;
|
|
dstPixel->middleWave = middleWave;
|
|
dstPixel->shortWave = shortWave;
|
|
dstPixel->alpha = alpha;
|
|
}
|
|
|
|
void KisLmsF32ColorSpace::getPixel(const TQ_UINT8 *src, float *longWave, float *middleWave, float *shortWave, float *alpha) const
|
|
{
|
|
const Pixel *srcPixel = reinterpret_cast<const Pixel *>(src);
|
|
|
|
*longWave = srcPixel->longWave;
|
|
*middleWave = srcPixel->middleWave;
|
|
*shortWave = srcPixel->shortWave;
|
|
*alpha = srcPixel->alpha;
|
|
}
|
|
|
|
void KisLmsF32ColorSpace::fromTQColor(const TQColor& c, TQ_UINT8 *dstU8, KisProfile * /*profile*/)
|
|
{
|
|
Pixel *dst = reinterpret_cast<Pixel *>(dstU8);
|
|
|
|
dst->longWave = computeLong(c.red(),c.green(),c.blue());
|
|
dst->middleWave = computeMiddle(c.red(),c.green(),c.blue());
|
|
dst->shortWave = computeShort(c.red(),c.green(),c.blue());
|
|
}
|
|
|
|
void KisLmsF32ColorSpace::fromTQColor(const TQColor& c, TQ_UINT8 opacity, TQ_UINT8 *dstU8, KisProfile * /*profile*/)
|
|
{
|
|
Pixel *dst = reinterpret_cast<Pixel *>(dstU8);
|
|
|
|
dst->longWave = computeLong(c.red(),c.green(),c.blue());
|
|
dst->middleWave = computeMiddle(c.red(),c.green(),c.blue());
|
|
dst->shortWave = computeShort(c.red(),c.green(),c.blue());
|
|
dst->alpha = UINT8_TO_FLOAT(opacity);
|
|
}
|
|
|
|
void KisLmsF32ColorSpace::toTQColor(const TQ_UINT8 *srcU8, TQColor *c, KisProfile * /*profile*/)
|
|
{
|
|
const Pixel *src = reinterpret_cast<const Pixel *>(srcU8);
|
|
|
|
c->setRgb(computeRed(src->longWave,src->middleWave,src->shortWave), computeGreen(src->longWave,src->middleWave,src->shortWave), computeBlue(src->longWave,src->middleWave,src->shortWave));
|
|
}
|
|
|
|
void KisLmsF32ColorSpace::toTQColor(const TQ_UINT8 *srcU8, TQColor *c, TQ_UINT8 *opacity, KisProfile * /*profile*/)
|
|
{
|
|
const Pixel *src = reinterpret_cast<const Pixel *>(srcU8);
|
|
|
|
c->setRgb(computeRed(src->longWave,src->middleWave,src->shortWave), computeGreen(src->longWave,src->middleWave,src->shortWave), computeBlue(src->longWave,src->middleWave,src->shortWave));
|
|
*opacity = FLOAT_TO_UINT8(src->alpha);
|
|
}
|
|
|
|
TQ_UINT8 KisLmsF32ColorSpace::difference(const TQ_UINT8 *src1U8, const TQ_UINT8 *src2U8)
|
|
{
|
|
const Pixel *src1 = reinterpret_cast<const Pixel *>(src1U8);
|
|
const Pixel *src2 = reinterpret_cast<const Pixel *>(src2U8);
|
|
|
|
return FLOAT_TO_UINT8(TQMAX(TQABS(src2->longWave - src1->longWave),
|
|
TQMAX(TQABS(src2->middleWave - src1->middleWave),
|
|
TQABS(src2->shortWave - src1->shortWave))));
|
|
}
|
|
|
|
void KisLmsF32ColorSpace::mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const
|
|
{
|
|
float totalLong = 0, totalMiddle = 0, totalShort = 0, newAlpha = 0;
|
|
|
|
while (nColors--)
|
|
{
|
|
const Pixel *pixel = reinterpret_cast<const Pixel *>(*colors);
|
|
|
|
float alpha = pixel->alpha;
|
|
float alphaTimesWeight = alpha * UINT8_TO_FLOAT(*weights);
|
|
|
|
totalLong += pixel->longWave * alphaTimesWeight;
|
|
totalMiddle += pixel->middleWave * alphaTimesWeight;
|
|
totalShort += pixel->shortWave * alphaTimesWeight;
|
|
newAlpha += alphaTimesWeight;
|
|
|
|
weights++;
|
|
colors++;
|
|
}
|
|
|
|
Q_ASSERT(newAlpha <= F32_OPACITY_OPAQUE);
|
|
|
|
Pixel *dstPixel = reinterpret_cast<Pixel *>(dst);
|
|
|
|
dstPixel->alpha = newAlpha;
|
|
|
|
if (newAlpha > EPSILON) {
|
|
totalLong = totalLong / newAlpha;
|
|
totalMiddle = totalMiddle / newAlpha;
|
|
totalShort = totalShort / newAlpha;
|
|
}
|
|
|
|
dstPixel->longWave = totalLong;
|
|
dstPixel->middleWave = totalMiddle;
|
|
dstPixel->shortWave = totalShort;
|
|
}
|
|
|
|
TQValueVector<KisChannelInfo *> KisLmsF32ColorSpace::channels() const
|
|
{
|
|
return m_channels;
|
|
}
|
|
|
|
TQ_UINT32 KisLmsF32ColorSpace::nChannels() const
|
|
{
|
|
return MAX_CHANNEL_LMSA;
|
|
}
|
|
|
|
TQ_UINT32 KisLmsF32ColorSpace::nColorChannels() const
|
|
{
|
|
return MAX_CHANNEL_LMS;
|
|
}
|
|
|
|
TQ_UINT32 KisLmsF32ColorSpace::pixelSize() const
|
|
{
|
|
return MAX_CHANNEL_LMSA * sizeof(float);
|
|
}
|
|
|
|
TQImage KisLmsF32ColorSpace::convertToTQImage(const TQ_UINT8 *dataU8, TQ_INT32 width, TQ_INT32 height,
|
|
KisProfile * /*dstProfile*/,
|
|
TQ_INT32 /*renderingIntent*/, float /*exposure*/)
|
|
|
|
{
|
|
const float *data = reinterpret_cast<const float *>(dataU8);
|
|
|
|
TQImage img = TQImage(width, height, 32, 0, TQImage::LittleEndian);
|
|
img.setAlphaBuffer(true);
|
|
|
|
TQ_INT32 i = 0;
|
|
uchar *j = img.bits();
|
|
|
|
while ( i < width * height * MAX_CHANNEL_LMSA) {
|
|
double l = *( data + i + PIXEL_LONGWAVE );
|
|
double m = *( data + i + PIXEL_MIDDLEWAVE );
|
|
double s = *( data + i + PIXEL_SHORTWAVE );
|
|
*( j + 3) = FLOAT_TO_UINT8(*( data + i + PIXEL_ALPHA ));
|
|
*( j + 2 ) = computeRed(l,m,s);
|
|
*( j + 1 ) = computeGreen(l,m,s);
|
|
*( j + 0 ) = computeBlue(l,m,s);
|
|
i += MAX_CHANNEL_LMSA;
|
|
j += MAX_CHANNEL_LMSA;
|
|
}
|
|
|
|
/*
|
|
if (srcProfile != 0 && dstProfile != 0) {
|
|
convertPixelsTo(img.bits(), srcProfile,
|
|
img.bits(), this, dstProfile,
|
|
width * height, renderingIntent);
|
|
}
|
|
*/
|
|
return img;
|
|
}
|
|
|
|
|
|
void KisLmsF32ColorSpace::compositeOver(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *maskRowStart, TQ_INT32 maskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, float opacity)
|
|
{
|
|
while (rows > 0) {
|
|
|
|
const float *src = reinterpret_cast<const float *>(srcRowStart);
|
|
float *dst = reinterpret_cast<float *>(dstRowStart);
|
|
const TQ_UINT8 *mask = maskRowStart;
|
|
TQ_INT32 columns = numColumns;
|
|
|
|
while (columns > 0) {
|
|
|
|
float srcAlpha = src[PIXEL_ALPHA];
|
|
|
|
// apply the alphamask
|
|
if (mask != 0) {
|
|
TQ_UINT8 U8_mask = *mask;
|
|
|
|
if (U8_mask != OPACITY_OPAQUE) {
|
|
srcAlpha *= UINT8_TO_FLOAT(U8_mask);
|
|
}
|
|
mask++;
|
|
}
|
|
|
|
if (srcAlpha > F32_OPACITY_TRANSPARENT + EPSILON) {
|
|
|
|
if (opacity < F32_OPACITY_OPAQUE - EPSILON) {
|
|
srcAlpha *= opacity;
|
|
}
|
|
|
|
if (srcAlpha > F32_OPACITY_OPAQUE - EPSILON) {
|
|
memcpy(dst, src, MAX_CHANNEL_LMSA * sizeof(float));
|
|
} else {
|
|
float dstAlpha = dst[PIXEL_ALPHA];
|
|
|
|
float srcBlend;
|
|
|
|
if (dstAlpha > F32_OPACITY_OPAQUE - EPSILON) {
|
|
srcBlend = srcAlpha;
|
|
} else {
|
|
float newAlpha = dstAlpha + (F32_OPACITY_OPAQUE - dstAlpha) * srcAlpha;
|
|
dst[PIXEL_ALPHA] = newAlpha;
|
|
|
|
if (newAlpha > EPSILON) {
|
|
srcBlend = srcAlpha / newAlpha;
|
|
} else {
|
|
srcBlend = srcAlpha;
|
|
}
|
|
}
|
|
|
|
if (srcBlend > F32_OPACITY_OPAQUE - EPSILON) {
|
|
memcpy(dst, src, MAX_CHANNEL_LMS * sizeof(float));
|
|
} else {
|
|
dst[PIXEL_LONGWAVE] = FLOAT_BLEND(src[PIXEL_LONGWAVE], dst[PIXEL_LONGWAVE], srcBlend);
|
|
dst[PIXEL_MIDDLEWAVE] = FLOAT_BLEND(src[PIXEL_MIDDLEWAVE], dst[PIXEL_MIDDLEWAVE], srcBlend);
|
|
dst[PIXEL_SHORTWAVE] = FLOAT_BLEND(src[PIXEL_SHORTWAVE], dst[PIXEL_SHORTWAVE], srcBlend);
|
|
}
|
|
}
|
|
}
|
|
|
|
columns--;
|
|
src += MAX_CHANNEL_LMSA;
|
|
dst += MAX_CHANNEL_LMSA;
|
|
}
|
|
|
|
rows--;
|
|
srcRowStart += srcRowStride;
|
|
dstRowStart += dstRowStride;
|
|
if(maskRowStart) {
|
|
maskRowStart += maskRowStride;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KisLmsF32ColorSpace::compositeErase(TQ_UINT8 *dst,
|
|
TQ_INT32 dstRowSize,
|
|
const TQ_UINT8 *src,
|
|
TQ_INT32 srcRowSize,
|
|
const TQ_UINT8 *srcAlphaMask,
|
|
TQ_INT32 maskRowStride,
|
|
TQ_INT32 rows,
|
|
TQ_INT32 cols,
|
|
float /*opacity*/)
|
|
{
|
|
while (rows-- > 0)
|
|
{
|
|
const Pixel *s = reinterpret_cast<const Pixel *>(src);
|
|
Pixel *d = reinterpret_cast<Pixel *>(dst);
|
|
const TQ_UINT8 *mask = srcAlphaMask;
|
|
|
|
for (TQ_INT32 i = cols; i > 0; i--, s++, d++)
|
|
{
|
|
float srcAlpha = s->alpha;
|
|
|
|
// apply the alphamask
|
|
if (mask != 0) {
|
|
TQ_UINT8 U8_mask = *mask;
|
|
|
|
if (U8_mask != OPACITY_OPAQUE) {
|
|
srcAlpha = FLOAT_BLEND(srcAlpha, F32_OPACITY_OPAQUE, UINT8_TO_FLOAT(U8_mask));
|
|
}
|
|
mask++;
|
|
}
|
|
d->alpha = srcAlpha * d->alpha;
|
|
}
|
|
|
|
dst += dstRowSize;
|
|
src += srcRowSize;
|
|
if(srcAlphaMask) {
|
|
srcAlphaMask += maskRowStride;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KisLmsF32ColorSpace::compositeCopy(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride,
|
|
const TQ_UINT8 */*maskRowStart*/, TQ_INT32 /*maskRowStride*/, TQ_INT32 rows, TQ_INT32 numColumns, float /*opacity*/)
|
|
{
|
|
while (rows > 0) {
|
|
memcpy(dstRowStart, srcRowStart, numColumns * sizeof(Pixel));
|
|
--rows;
|
|
srcRowStart += srcRowStride;
|
|
dstRowStart += dstRowStride;
|
|
}
|
|
}
|
|
|
|
void KisLmsF32ColorSpace::bitBlt(TQ_UINT8 *dst,
|
|
TQ_INT32 dstRowStride,
|
|
const TQ_UINT8 *src,
|
|
TQ_INT32 srcRowStride,
|
|
const TQ_UINT8 *mask,
|
|
TQ_INT32 maskRowStride,
|
|
TQ_UINT8 U8_opacity,
|
|
TQ_INT32 rows,
|
|
TQ_INT32 cols,
|
|
const KisCompositeOp& op)
|
|
{
|
|
float opacity = UINT8_TO_FLOAT(U8_opacity);
|
|
|
|
switch (op.op()) {
|
|
case COMPOSITE_UNDEF:
|
|
// Undefined == no composition
|
|
break;
|
|
case COMPOSITE_OVER:
|
|
compositeOver(dst, dstRowStride, src, srcRowStride, mask, maskRowStride, rows, cols, opacity);
|
|
break;
|
|
case COMPOSITE_COPY:
|
|
compositeCopy(dst, dstRowStride, src, srcRowStride, mask, maskRowStride, rows, cols, opacity);
|
|
break;
|
|
case COMPOSITE_ERASE:
|
|
compositeErase(dst, dstRowStride, src, srcRowStride, mask, maskRowStride, rows, cols, opacity);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
KisCompositeOpList KisLmsF32ColorSpace::userVisiblecompositeOps() const
|
|
{
|
|
KisCompositeOpList list;
|
|
|
|
list.append(KisCompositeOp(COMPOSITE_OVER));
|
|
return list;
|
|
}
|
|
|