File indexing completed on 2024-05-12 16:34:26
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 "BlendEffect.h" 0021 #include "ColorChannelConversion.h" 0022 #include <KoFilterEffectRenderContext.h> 0023 #include <KoXmlWriter.h> 0024 #include <KoXmlReader.h> 0025 #include <klocalizedstring.h> 0026 #include <QRect> 0027 #include <QImage> 0028 #include <math.h> 0029 0030 BlendEffect::BlendEffect() 0031 : KoFilterEffect(BlendEffectId, i18n("Blend")) 0032 , m_blendMode(Normal) 0033 { 0034 setRequiredInputCount(2); 0035 setMaximalInputCount(2); 0036 } 0037 0038 BlendEffect::BlendMode BlendEffect::blendMode() const 0039 { 0040 return m_blendMode; 0041 } 0042 0043 void BlendEffect::setBlendMode(BlendMode blendMode) 0044 { 0045 m_blendMode = blendMode; 0046 } 0047 0048 QImage BlendEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const 0049 { 0050 Q_UNUSED(context); 0051 return image; 0052 } 0053 0054 QImage BlendEffect::processImages(const QVector<QImage> &images, const KoFilterEffectRenderContext &context) const 0055 { 0056 int imageCount = images.count(); 0057 if (!imageCount) 0058 return QImage(); 0059 0060 QImage result = images[0]; 0061 if (images.count() != 2) { 0062 return result; 0063 } 0064 const QRgb *src = (const QRgb*)images[1].constBits(); 0065 QRgb *dst = (QRgb*)result.bits(); 0066 int w = result.width(); 0067 0068 qreal sa, sr, sg, sb; 0069 qreal da, dr, dg, db; 0070 int pixel = 0; 0071 0072 QRect roi = context.filterRegion().toRect(); 0073 for (int row = roi.top(); row < roi.bottom(); ++row) { 0074 for (int col = roi.left(); col < roi.right(); ++col) { 0075 pixel = row * w + col; 0076 const QRgb &s = src[pixel]; 0077 QRgb &d = dst[pixel]; 0078 0079 sa = fromIntColor[qAlpha(s)]; 0080 sr = fromIntColor[qRed(s)]; 0081 sg = fromIntColor[qGreen(s)]; 0082 sb = fromIntColor[qBlue(s)]; 0083 0084 da = fromIntColor[qAlpha(d)]; 0085 dr = fromIntColor[qRed(d)]; 0086 dg = fromIntColor[qGreen(d)]; 0087 db = fromIntColor[qBlue(d)]; 0088 0089 switch (m_blendMode) { 0090 case Normal: 0091 dr = (qreal(1.0) - da) * sr + dr; 0092 dg = (qreal(1.0) - da) * sg + dg; 0093 db = (qreal(1.0) - da) * sb + db; 0094 break; 0095 case Multiply: 0096 dr = (qreal(1.0) - da) * sr + (qreal(1.0) - sa) * dr + dr * sr; 0097 dg = (qreal(1.0) - da) * sg + (qreal(1.0) - sa) * dg + dg * sg; 0098 db = (qreal(1.0) - da) * sb + (qreal(1.0) - sa) * db + db * sb; 0099 break; 0100 case Screen: 0101 dr = sr + dr - dr * sr; 0102 dg = sg + dg - dg * sg; 0103 db = sb + db - db * sb; 0104 break; 0105 case Darken: 0106 dr = qMin((qreal(1.0) - da) * sr + dr, (qreal(1.0) - sa) * dr + sr); 0107 dg = qMin((qreal(1.0) - da) * sg + dg, (qreal(1.0) - sa) * dg + sg); 0108 db = qMin((qreal(1.0) - da) * sb + db, (qreal(1.0) - sa) * db + sb); 0109 break; 0110 case Lighten: 0111 dr = qMax((qreal(1.0) - da) * sr + dr, (qreal(1.0) - sa) * dr + sr); 0112 dg = qMax((qreal(1.0) - da) * sg + dg, (qreal(1.0) - sa) * dg + sg); 0113 db = qMax((qreal(1.0) - da) * sb + db, (qreal(1.0) - sa) * db + sb); 0114 break; 0115 } 0116 da = qreal(1.0) - (qreal(1.0) - da) * (qreal(1.0) - sa); 0117 0118 d = qRgba(static_cast<quint8>(qBound(qreal(0.0), dr * qreal(255.0), qreal(255.0))), 0119 static_cast<quint8>(qBound(qreal(0.0), dg * qreal(255.0), qreal(255.0))), 0120 static_cast<quint8>(qBound(qreal(0.0), db * qreal(255.0), qreal(255.0))), 0121 static_cast<quint8>(qBound(qreal(0.0), da * qreal(255.0), qreal(255.0)))); 0122 } 0123 } 0124 0125 return result; 0126 } 0127 0128 bool BlendEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &) 0129 { 0130 if (element.tagName() != id()) 0131 return false; 0132 0133 m_blendMode = Normal; // default blend mode 0134 0135 QString modeStr = element.attribute("mode"); 0136 if (!modeStr.isEmpty()) { 0137 if (modeStr == "multiply") 0138 m_blendMode = Multiply; 0139 else if (modeStr == "screen") 0140 m_blendMode = Screen; 0141 else if (modeStr == "darken") 0142 m_blendMode = Darken; 0143 else if (modeStr == "lighten") 0144 m_blendMode = Lighten; 0145 } 0146 0147 if (element.hasAttribute("in2")) { 0148 if (inputs().count() == 2) 0149 setInput(1, element.attribute("in2")); 0150 else 0151 addInput(element.attribute("in2")); 0152 } 0153 0154 return true; 0155 } 0156 0157 void BlendEffect::save(KoXmlWriter &writer) 0158 { 0159 writer.startElement(BlendEffectId); 0160 0161 saveCommonAttributes(writer); 0162 0163 switch (m_blendMode) { 0164 case Normal: 0165 writer.addAttribute("mode", "normal"); 0166 break; 0167 case Multiply: 0168 writer.addAttribute("mode", "multiply"); 0169 break; 0170 case Screen: 0171 writer.addAttribute("mode", "screen"); 0172 break; 0173 case Darken: 0174 writer.addAttribute("mode", "darken"); 0175 break; 0176 case Lighten: 0177 writer.addAttribute("mode", "lighten"); 0178 break; 0179 } 0180 0181 writer.addAttribute("in2", inputs().at(1)); 0182 0183 writer.endElement(); 0184 }