File indexing completed on 2024-05-12 16:34:28

0001 /* This file is part of the KDE project
0002  * Copyright (c) 2009 Jan Hambrecht <jaham@gmx.net>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Library General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 
0020 #include "CompositeEffect.h"
0021 #include "ColorChannelConversion.h"
0022 #include <KoFilterEffectRenderContext.h>
0023 #include <KoViewConverter.h>
0024 #include <KoXmlWriter.h>
0025 #include <KoXmlReader.h>
0026 #include <klocalizedstring.h>
0027 #include <QDebug>
0028 #include <QRect>
0029 #include <QPainter>
0030 
0031 CompositeEffect::CompositeEffect()
0032         : KoFilterEffect(CompositeEffectId, i18n("Composite"))
0033         , m_operation(CompositeOver)
0034 {
0035     setRequiredInputCount(2);
0036     setMaximalInputCount(2);
0037     memset(m_k, 0, 4*sizeof(qreal));
0038 }
0039 
0040 CompositeEffect::Operation CompositeEffect::operation() const
0041 {
0042     return m_operation;
0043 }
0044 
0045 void CompositeEffect::setOperation(Operation op)
0046 {
0047     m_operation = op;
0048 }
0049 
0050 const qreal * CompositeEffect::arithmeticValues() const
0051 {
0052     return m_k;
0053 }
0054 
0055 void CompositeEffect::setArithmeticValues(qreal * values)
0056 {
0057     memcpy(m_k, values, 4*sizeof(qreal));
0058 }
0059 
0060 QImage CompositeEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &) const
0061 {
0062     return image;
0063 }
0064 
0065 QImage CompositeEffect::processImages(const QVector<QImage> &images, const KoFilterEffectRenderContext &context) const
0066 {
0067     int imageCount = images.count();
0068     if (!imageCount)
0069         return QImage();
0070 
0071     QImage result = images[0];
0072     if (images.count() != 2) {
0073         return result;
0074     }
0075 
0076     if (m_operation == Arithmetic) {
0077         const QRgb *src = (QRgb*)images[1].constBits();
0078         QRgb *dst = (QRgb*)result.bits();
0079         int w = result.width();
0080 
0081         qreal sa, sr, sg, sb;
0082         qreal da, dr, dg, db;
0083         int pixel = 0;
0084 
0085         // TODO: do we have to calculate with non-premuliplied colors here ???
0086 
0087         QRect roi = context.filterRegion().toRect();
0088         for (int row = roi.top(); row < roi.bottom(); ++row) {
0089             for (int col = roi.left(); col < roi.right(); ++col) {
0090                 pixel = row * w + col;
0091                 const QRgb &s = src[pixel];
0092                 QRgb &d = dst[pixel];
0093 
0094                 sa = fromIntColor[qAlpha(s)];
0095                 sr = fromIntColor[qRed(s)];
0096                 sg = fromIntColor[qGreen(s)];
0097                 sb = fromIntColor[qBlue(s)];
0098 
0099                 da = fromIntColor[qAlpha(d)];
0100                 dr = fromIntColor[qRed(d)];
0101                 dg = fromIntColor[qGreen(d)];
0102                 db = fromIntColor[qBlue(d)];
0103 
0104                 da = m_k[0] * sa * da + m_k[1] * da + m_k[2] * sa + m_k[3];
0105                 dr = m_k[0] * sr * dr + m_k[1] * dr + m_k[2] * sr + m_k[3];
0106                 dg = m_k[0] * sg * dg + m_k[1] * dg + m_k[2] * sg + m_k[3];
0107                 db = m_k[0] * sb * db + m_k[1] * db + m_k[2] * sb + m_k[3];
0108 
0109                 da *= 255.0;
0110 
0111                 // set pre-multiplied color values on destination image
0112                 d = qRgba(static_cast<quint8>(qBound(qreal(0.0), dr * da, qreal(255.0))),
0113                           static_cast<quint8>(qBound(qreal(0.0), dg * da, qreal(255.0))),
0114                           static_cast<quint8>(qBound(qreal(0.0), db * da, qreal(255.0))),
0115                           static_cast<quint8>(qBound(qreal(0.0), da, qreal(255.0))));
0116             }
0117         }
0118     } else {
0119         QPainter painter(&result);
0120 
0121         switch (m_operation) {
0122         case CompositeOver:
0123             painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);
0124             break;
0125         case CompositeIn:
0126             painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
0127             break;
0128         case CompositeOut:
0129             painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
0130             break;
0131         case CompositeAtop:
0132             painter.setCompositionMode(QPainter::CompositionMode_DestinationAtop);
0133             break;
0134         case CompositeXor:
0135             painter.setCompositionMode(QPainter::CompositionMode_Xor);
0136             break;
0137         default:
0138             // no composition mode
0139             break;
0140         }
0141         painter.drawImage(context.filterRegion(), images[1], context.filterRegion());
0142     }
0143 
0144     return result;
0145 }
0146 
0147 bool CompositeEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &)
0148 {
0149     if (element.tagName() != id())
0150         return false;
0151 
0152     QString opStr = element.attribute("operator");
0153     if (opStr == "over") {
0154         m_operation = CompositeOver;
0155     } else if (opStr == "in") {
0156         m_operation = CompositeIn;
0157     } else if (opStr == "out") {
0158         m_operation = CompositeOut;
0159     } else if (opStr == "atop") {
0160         m_operation = CompositeAtop;
0161     } else if (opStr == "xor") {
0162         m_operation = CompositeXor;
0163     } else if (opStr == "arithmetic") {
0164         m_operation = Arithmetic;
0165         if (element.hasAttribute("k1"))
0166             m_k[0] = element.attribute("k1").toDouble();
0167         if (element.hasAttribute("k2"))
0168             m_k[1] = element.attribute("k2").toDouble();
0169         if (element.hasAttribute("k3"))
0170             m_k[2] = element.attribute("k3").toDouble();
0171         if (element.hasAttribute("k4"))
0172             m_k[3] = element.attribute("k4").toDouble();
0173     } else {
0174         return false;
0175     }
0176 
0177     if (element.hasAttribute("in2")) {
0178         if (inputs().count() == 2)
0179             setInput(1, element.attribute("in2"));
0180         else
0181             addInput(element.attribute("in2"));
0182     }
0183 
0184     return true;
0185 }
0186 
0187 void CompositeEffect::save(KoXmlWriter &writer)
0188 {
0189     writer.startElement(CompositeEffectId);
0190 
0191     saveCommonAttributes(writer);
0192 
0193     switch (m_operation) {
0194     case CompositeOver:
0195         writer.addAttribute("operator", "over");
0196         break;
0197     case CompositeIn:
0198         writer.addAttribute("operator", "in");
0199         break;
0200     case CompositeOut:
0201         writer.addAttribute("operator", "out");
0202         break;
0203     case CompositeAtop:
0204         writer.addAttribute("operator", "atop");
0205         break;
0206     case CompositeXor:
0207         writer.addAttribute("operator", "xor");
0208         break;
0209     case Arithmetic:
0210         writer.addAttribute("operator", "arithmetic");
0211         writer.addAttribute("k1", QString("%1").arg(m_k[0]));
0212         writer.addAttribute("k2", QString("%1").arg(m_k[1]));
0213         writer.addAttribute("k3", QString("%1").arg(m_k[2]));
0214         writer.addAttribute("k4", QString("%1").arg(m_k[3]));
0215         break;
0216     }
0217 
0218     writer.addAttribute("in2", inputs().at(1));
0219 
0220     writer.endElement();
0221 }