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 }