File indexing completed on 2024-05-12 16:34:27
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 "ColorMatrixEffect.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 const int MatrixSize = 20; 0031 const int MatrixRows = 4; 0032 const int MatrixCols = 5; 0033 0034 ColorMatrixEffect::ColorMatrixEffect() 0035 : KoFilterEffect(ColorMatrixEffectId, i18n("Color Matrix")) 0036 , m_type(Matrix) 0037 { 0038 setIdentity(); 0039 } 0040 0041 void ColorMatrixEffect::setIdentity() 0042 { 0043 // set identity matrix 0044 m_matrix.resize(MatrixSize); 0045 for (int r = 0; r < MatrixRows; ++r) { 0046 for (int c = 0; c < MatrixCols; ++c) { 0047 m_matrix[r*MatrixCols+c] = r == c ? 1.0 : 0.0; 0048 } 0049 } 0050 } 0051 0052 ColorMatrixEffect::Type ColorMatrixEffect::type() const 0053 { 0054 return m_type; 0055 } 0056 0057 int ColorMatrixEffect::colorMatrixSize() 0058 { 0059 return MatrixSize; 0060 } 0061 0062 int ColorMatrixEffect::colorMatrixRowCount() 0063 { 0064 return MatrixRows; 0065 } 0066 0067 int ColorMatrixEffect::colorMatrixColumnCount() 0068 { 0069 return MatrixCols; 0070 } 0071 0072 QVector<qreal> ColorMatrixEffect::colorMatrix() const 0073 { 0074 return m_matrix; 0075 } 0076 0077 void ColorMatrixEffect::setColorMatrix(const QVector<qreal> &colorMatrix) 0078 { 0079 if (colorMatrix.count() == MatrixSize) 0080 m_matrix = colorMatrix; 0081 m_type = Matrix; 0082 } 0083 0084 void ColorMatrixEffect::setSaturate(qreal value) 0085 { 0086 m_type = Saturate; 0087 m_value = qBound(qreal(0.0), value, qreal(1.0)); 0088 0089 setIdentity(); 0090 0091 m_matrix[0] = 0.213 + 0.787 * value; 0092 m_matrix[1] = 0.715 - 0.715 * value; 0093 m_matrix[2] = 0.072 - 0.072 * value; 0094 0095 m_matrix[5] = 0.213 - 0.213 * value; 0096 m_matrix[6] = 0.715 + 0.285 * value; 0097 m_matrix[7] = 0.072 - 0.072 * value; 0098 0099 m_matrix[10] = 0.213 - 0.213 * value; 0100 m_matrix[11] = 0.715 - 0.715 * value; 0101 m_matrix[12] = 0.072 + 0.928 * value; 0102 } 0103 0104 qreal ColorMatrixEffect::saturate() const 0105 { 0106 if (m_type == Saturate) 0107 return m_value; 0108 else 0109 return 1.0; 0110 } 0111 0112 void ColorMatrixEffect::setHueRotate(qreal value) 0113 { 0114 m_type = HueRotate; 0115 m_value = value; 0116 0117 const qreal rad = m_value * M_PI / 180.0; 0118 const qreal c = cos(rad); 0119 const qreal s = sin(rad); 0120 0121 setIdentity(); 0122 0123 m_matrix[0] = 0.213 + 0.787 * c - 0.213 * s; 0124 m_matrix[1] = 0.715 - 0.715 * c - 0.715 * s; 0125 m_matrix[2] = 0.072 - 0.072 * c + 0.928 * s; 0126 0127 m_matrix[5] = 0.213 - 0.213 * c + 0.143 * s; 0128 m_matrix[6] = 0.715 + 0.285 * c + 0.140 * s; 0129 m_matrix[7] = 0.072 - 0.072 * c - 0.283 * s; 0130 0131 m_matrix[10] = 0.213 - 0.213 * c - 0.787 * s; 0132 m_matrix[11] = 0.715 - 0.715 * c + 0.715 * s; 0133 m_matrix[12] = 0.072 + 0.928 * c + 0.072 * s; 0134 } 0135 0136 qreal ColorMatrixEffect::hueRotate() const 0137 { 0138 if (m_type == HueRotate) 0139 return m_value; 0140 else 0141 return 0.0; 0142 } 0143 0144 void ColorMatrixEffect::setLuminanceAlpha() 0145 { 0146 m_type = LuminanceAlpha; 0147 0148 memset(m_matrix.data(), 0, MatrixSize*sizeof(qreal)); 0149 0150 m_matrix[15] = 0.2125; 0151 m_matrix[16] = 0.7154; 0152 m_matrix[17] = 0.0721; 0153 m_matrix[18] = 0.0; 0154 } 0155 0156 QImage ColorMatrixEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const 0157 { 0158 QImage result = image; 0159 0160 const QRgb *src = (const QRgb*)image.constBits(); 0161 QRgb *dst = (QRgb*)result.bits(); 0162 int w = result.width(); 0163 0164 const qreal * m = m_matrix.data(); 0165 qreal sa, sr, sg, sb; 0166 qreal da, dr, dg, db; 0167 0168 QRect roi = context.filterRegion().toRect(); 0169 for (int row = roi.top(); row < roi.bottom(); ++row) { 0170 for (int col = roi.left(); col < roi.right(); ++col) { 0171 const QRgb &s = src[row*w+col]; 0172 sa = fromIntColor[qAlpha(s)]; 0173 sr = fromIntColor[qRed(s)]; 0174 sg = fromIntColor[qGreen(s)]; 0175 sb = fromIntColor[qBlue(s)]; 0176 // the matrix is applied to non-premultiplied color values 0177 // so we have to convert colors by dividing by alpha value 0178 if (sa > 0.0 && sa < 1.0) { 0179 sr /= sa; 0180 sb /= sa; 0181 sg /= sa; 0182 } 0183 0184 // apply matrix to color values 0185 dr = m[ 0] * sr + m[ 1] * sg + m[ 2] * sb + m[ 3] * sa + m[ 4]; 0186 dg = m[ 5] * sr + m[ 6] * sg + m[ 7] * sb + m[ 8] * sa + m[ 9]; 0187 db = m[10] * sr + m[11] * sg + m[12] * sb + m[13] * sa + m[14]; 0188 da = m[15] * sr + m[16] * sg + m[17] * sb + m[18] * sa + m[19]; 0189 0190 // the new alpha value 0191 da *= 255.0; 0192 0193 // set pre-multiplied color values on destination image 0194 dst[row*w+col] = qRgba(static_cast<quint8>(qBound(qreal(0.0), dr * da, qreal(255.0))), 0195 static_cast<quint8>(qBound(qreal(0.0), dg * da, qreal(255.0))), 0196 static_cast<quint8>(qBound(qreal(0.0), db * da, qreal(255.0))), 0197 static_cast<quint8>(qBound(qreal(0.0), da, qreal(255.0)))); 0198 } 0199 } 0200 0201 return result; 0202 } 0203 0204 bool ColorMatrixEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &) 0205 { 0206 if (element.tagName() != id()) 0207 return false; 0208 0209 QString typeStr = element.attribute("type"); 0210 if (typeStr.isEmpty()) 0211 return false; 0212 0213 QString valueStr = element.attribute("values"); 0214 0215 setIdentity(); 0216 m_type = Matrix; 0217 0218 if (typeStr == "matrix") { 0219 // values are separated by whitespace and/or comma 0220 QStringList values = valueStr.trimmed().split(QRegExp("(\\s+|,)"), QString::SkipEmptyParts); 0221 if (values.count() == MatrixSize) { 0222 for (int i = 0; i < MatrixSize; ++i) { 0223 m_matrix[i] = values[i].toDouble(); 0224 } 0225 } 0226 } else if (typeStr == "saturate") { 0227 if (!valueStr.isEmpty()) { 0228 setSaturate(valueStr.toDouble()); 0229 } 0230 } else if (typeStr == "hueRotate") { 0231 if (!valueStr.isEmpty()) { 0232 setHueRotate(valueStr.toDouble()); 0233 } 0234 } else if (typeStr == "luminanceToAlpha") { 0235 setLuminanceAlpha(); 0236 } else { 0237 return false; 0238 } 0239 0240 return true; 0241 } 0242 0243 void ColorMatrixEffect::save(KoXmlWriter &writer) 0244 { 0245 writer.startElement(ColorMatrixEffectId); 0246 0247 saveCommonAttributes(writer); 0248 0249 switch (m_type) { 0250 case Matrix: { 0251 writer.addAttribute("type", "matrix"); 0252 QString matrix; 0253 for (int r = 0; r < MatrixRows; ++r) { 0254 for (int c = 0; c < MatrixCols; ++c) { 0255 matrix += QString("%1 ").arg(m_matrix[r*MatrixCols+c]); 0256 } 0257 } 0258 writer.addAttribute("values", matrix); 0259 } 0260 break; 0261 case Saturate: 0262 writer.addAttribute("type", "saturate"); 0263 writer.addAttribute("values", QString("%1").arg(m_value)); 0264 break; 0265 case HueRotate: 0266 writer.addAttribute("type", "hueRotate"); 0267 writer.addAttribute("values", QString("%1").arg(m_value)); 0268 break; 0269 case LuminanceAlpha: 0270 writer.addAttribute("type", "luminanceToAlpha"); 0271 break; 0272 } 0273 0274 writer.endElement(); 0275 }