/* * Copyright (c) 2006 Cyrille Berger * * 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_ycbcr_u16_colorspace.h" #include #include const TQ_INT32 MAX_CHANNEL_YCbCr = 3; const TQ_INT32 MAX_CHANNEL_YCbCrA = 4; KisYCbCrU16ColorSpace::KisYCbCrU16ColorSpace(KisColorSpaceFactoryRegistry* parent, KisProfile* /*p*/) : KisU16BaseColorSpace(KisID("YCbCrAU16", i18n("YCbCr (16-bit integer/channel)")), TYPE_YCbCr_16, icSigYCbCrData, parent, 0) { m_channels.push_back(new KisChannelInfo(i18n("Y"), "Y", PIXEL_Y * sizeof(TQ_UINT16), KisChannelInfo::COLOR, KisChannelInfo::UINT16, sizeof(TQ_UINT16))); m_channels.push_back(new KisChannelInfo(i18n("Cb"), "Cb", PIXEL_Cb * sizeof(TQ_UINT16), KisChannelInfo::COLOR, KisChannelInfo::UINT16, sizeof(TQ_UINT16))); m_channels.push_back(new KisChannelInfo(i18n("Cr"), "Cr", PIXEL_Cr * sizeof(TQ_UINT16), KisChannelInfo::COLOR, KisChannelInfo::UINT16, sizeof(TQ_UINT16))); m_channels.push_back(new KisChannelInfo(i18n("Alpha"), "A", PIXEL_ALPHA * sizeof(TQ_UINT16), KisChannelInfo::ALPHA, KisChannelInfo::UINT16, sizeof(TQ_UINT16))); m_alphaPos = PIXEL_ALPHA * sizeof(TQ_UINT16); KisAbstractColorSpace::init(); } KisYCbCrU16ColorSpace::~KisYCbCrU16ColorSpace() { } void KisYCbCrU16ColorSpace::setPixel(TQ_UINT8 *dst, TQ_UINT16 Y, TQ_UINT16 Cb, TQ_UINT16 Cr, TQ_UINT16 alpha) const { Pixel *dstPixel = reinterpret_cast(dst); dstPixel->Y = Y; dstPixel->Cb = Cb; dstPixel->Cr = Cr; dstPixel->alpha = alpha; } void KisYCbCrU16ColorSpace::getPixel(const TQ_UINT8 *src, TQ_UINT16 *Y, TQ_UINT16 *Cb, TQ_UINT16 *Cr, TQ_UINT16 *alpha) const { const Pixel *srcPixel = reinterpret_cast(src); *Y = srcPixel->Y; *Cb = srcPixel->Cb; *Cr = srcPixel->Cr; *alpha = srcPixel->alpha; } void KisYCbCrU16ColorSpace::fromTQColor(const TQColor& c, TQ_UINT8 *dstU8, KisProfile * profile ) { if(getProfile()) { KisU16BaseColorSpace::fromTQColor(c, dstU8, profile); } else { Pixel *dst = reinterpret_cast(dstU8); dst->Y = computeY( c.red(), c.green(), c.blue()); dst->Cb = computeCb( c.red(), c.green(), c.blue()); dst->Cr = computeCr( c.red(), c.green(), c.blue()); } } void KisYCbCrU16ColorSpace::fromTQColor(const TQColor& c, TQ_UINT8 opacity, TQ_UINT8 *dstU8, KisProfile * profile ) { if(getProfile()) { KisU16BaseColorSpace::fromTQColor(c, opacity, dstU8, profile); } else { Pixel *dst = reinterpret_cast(dstU8); dst->Y = computeY( c.red(), c.green(), c.blue()); dst->Cb = computeCb( c.red(), c.green(), c.blue()); dst->Cr = computeCr( c.red(), c.green(), c.blue()); dst->alpha = opacity; } } void KisYCbCrU16ColorSpace::toTQColor(const TQ_UINT8 *srcU8, TQColor *c, KisProfile * profile) { if(getProfile()) { KisU16BaseColorSpace::toTQColor(srcU8, c, profile); } else { const Pixel *src = reinterpret_cast(srcU8); c->setRgb(computeRed(src->Y,src->Cb,src->Cr) >> 8, computeGreen(src->Y,src->Cb,src->Cr) >> 8, computeBlue(src->Y,src->Cb,src->Cr) >> 8); } } void KisYCbCrU16ColorSpace::toTQColor(const TQ_UINT8 *srcU8, TQColor *c, TQ_UINT8 *opacity, KisProfile * profile) { if(getProfile()) { KisU16BaseColorSpace::toTQColor(srcU8, c, opacity, profile); } else { const Pixel *src = reinterpret_cast(srcU8); c->setRgb(computeRed(src->Y,src->Cb,src->Cr) >> 8, computeGreen(src->Y,src->Cb,src->Cr) >> 8, computeBlue(src->Y,src->Cb,src->Cr) >> 8); *opacity = src->alpha; } } TQ_UINT8 KisYCbCrU16ColorSpace::difference(const TQ_UINT8 *src1U8, const TQ_UINT8 *src2U8) { if(getProfile()) return KisU16BaseColorSpace::difference(src1U8, src2U8); const Pixel *src1 = reinterpret_cast(src1U8); const Pixel *src2 = reinterpret_cast(src2U8); return TQMAX(TQABS(src2->Y - src1->Y), TQMAX(TQABS(src2->Cb - src1->Cb), TQABS(src2->Cr - src1->Cr))) >> 8; } void KisYCbCrU16ColorSpace::mixColors(const TQ_UINT8 **colors, const TQ_UINT8 *weights, TQ_UINT32 nColors, TQ_UINT8 *dst) const { TQ_UINT16 totalY = 0, totalCb = 0, totalCr = 0, newAlpha = 0; while (nColors--) { const Pixel *pixel = reinterpret_cast(*colors); TQ_UINT16 alpha = pixel->alpha; float alphaTimesWeight = alpha * *weights; totalY += (TQ_UINT16)(pixel->Y * alphaTimesWeight); totalCb += (TQ_UINT16)(pixel->Cb * alphaTimesWeight); totalCr += (TQ_UINT16)(pixel->Cr * alphaTimesWeight); newAlpha += (TQ_UINT16)(alphaTimesWeight); weights++; colors++; } Pixel *dstPixel = reinterpret_cast(dst); dstPixel->alpha = newAlpha; if (newAlpha > 0) { totalY = totalY / newAlpha; totalCb = totalCb / newAlpha; totalCr = totalCr / newAlpha; } dstPixel->Y = totalY; dstPixel->Cb = totalCb; dstPixel->Cr = totalCr; } TQValueVector KisYCbCrU16ColorSpace::channels() const { return m_channels; } TQ_UINT32 KisYCbCrU16ColorSpace::nChannels() const { return MAX_CHANNEL_YCbCrA; } TQ_UINT32 KisYCbCrU16ColorSpace::nColorChannels() const { return MAX_CHANNEL_YCbCr; } TQ_UINT32 KisYCbCrU16ColorSpace::pixelSize() const { return MAX_CHANNEL_YCbCrA * sizeof(TQ_UINT16); } TQImage KisYCbCrU16ColorSpace::convertToTQImage(const TQ_UINT8 *data, TQ_INT32 width, TQ_INT32 height, KisProfile * dstProfile, TQ_INT32 renderingIntent, float exposure ) { if(getProfile()) return KisU16BaseColorSpace::convertToTQImage( data, width, height, dstProfile, renderingIntent, exposure); 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_YCbCrA) { TQ_UINT16 Y = *( data + i + PIXEL_Y ); TQ_UINT16 Cb = *( data + i + PIXEL_Cb ); TQ_UINT16 Cr = *( data + i + PIXEL_Cr ); *( j + 3) = *( data + i + PIXEL_ALPHA ) >> 8; *( j + 2 ) = computeRed(Y,Cb,Cr) >> 8; *( j + 1 ) = computeGreen(Y,Cb,Cr) >> 8; *( j + 0 ) = computeBlue(Y,Cb,Cr) >> 8; i += MAX_CHANNEL_YCbCrA; j += 4; } return img; } void KisYCbCrU16ColorSpace::bitBlt(TQ_UINT8 *dst, TQ_INT32 dstRowStride, const TQ_UINT8 *src, TQ_INT32 srcRowStride, const TQ_UINT8 *srcAlphaMask, TQ_INT32 maskRowStride, TQ_UINT8 opacity, TQ_INT32 rows, TQ_INT32 cols, const KisCompositeOp& op) { switch (op.op()) { case COMPOSITE_UNDEF: // Undefined == no composition break; case COMPOSITE_OVER: compositeOver(dst, dstRowStride, src, srcRowStride, srcAlphaMask, maskRowStride, rows, cols, opacity); break; case COMPOSITE_COPY: compositeCopy(dst, dstRowStride, src, srcRowStride, srcAlphaMask, maskRowStride, rows, cols, opacity); break; case COMPOSITE_ERASE: compositeErase(dst, dstRowStride, src, srcRowStride, srcAlphaMask, maskRowStride, rows, cols, opacity); break; default: break; } } void KisYCbCrU16ColorSpace::compositeOver(TQ_UINT8 *dstRowStart, TQ_INT32 dstRowStride, const TQ_UINT8 *srcRowStart, TQ_INT32 srcRowStride, const TQ_UINT8 *maskRowStart, TQ_INT32 maskRowStride, TQ_INT32 rows, TQ_INT32 numColumns, TQ_UINT8 opacity) { while (rows > 0) { const TQ_UINT16 *src = reinterpret_cast(srcRowStart); TQ_UINT16 *dst = reinterpret_cast(dstRowStart); const TQ_UINT8 *mask = maskRowStart; TQ_INT32 columns = numColumns; while (columns > 0) { TQ_UINT16 srcAlpha = src[PIXEL_ALPHA]; // apply the alphamask if (mask != 0) { TQ_UINT8 U8_mask = *mask; if (U8_mask != OPACITY_OPAQUE) { srcAlpha = UINT16_MULT(srcAlpha, UINT8_TO_UINT16(U8_mask)); } mask++; } if (srcAlpha != U16_OPACITY_TRANSPARENT) { if (opacity != OPACITY_OPAQUE) { srcAlpha = UINT16_MULT(srcAlpha, opacity); } if (srcAlpha == U16_OPACITY_OPAQUE) { memcpy(dst, src, MAX_CHANNEL_YCbCrA * 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, MAX_CHANNEL_YCbCr * sizeof(TQ_UINT16)); } else { dst[PIXEL_Y] = UINT16_BLEND(src[PIXEL_Y], dst[PIXEL_Y], srcBlend); dst[PIXEL_Cb] = UINT16_BLEND(src[PIXEL_Cb], dst[PIXEL_Cb], srcBlend); dst[PIXEL_Cr] = UINT16_BLEND(src[PIXEL_Cr], dst[PIXEL_Cr], srcBlend); } } } columns--; src += MAX_CHANNEL_YCbCrA; dst += MAX_CHANNEL_YCbCrA; } rows--; srcRowStart += srcRowStride; dstRowStart += dstRowStride; if(maskRowStart) { maskRowStart += maskRowStride; } } } void KisYCbCrU16ColorSpace::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, TQ_UINT8 /*opacity*/) { while (rows-- > 0) { const Pixel *s = reinterpret_cast(src); Pixel *d = reinterpret_cast(dst); const TQ_UINT8 *mask = srcAlphaMask; for (TQ_INT32 i = cols; i > 0; i--, s++, d++) { TQ_UINT16 srcAlpha = s->alpha; // apply the alphamask if (mask != 0) { TQ_UINT8 U8_mask = *mask; if (U8_mask != OPACITY_OPAQUE) { srcAlpha = UINT16_BLEND(srcAlpha, U16_OPACITY_OPAQUE, UINT8_TO_UINT16(U8_mask)); } mask++; } d->alpha = UINT16_MULT(srcAlpha, d->alpha); } dst += dstRowSize; src += srcRowSize; if(srcAlphaMask) { srcAlphaMask += maskRowStride; } } } KisCompositeOpList KisYCbCrU16ColorSpace::userVisiblecompositeOps() const { KisCompositeOpList list; list.append(KisCompositeOp(COMPOSITE_OVER)); return list; }