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 }