File indexing completed on 2024-05-19 16:07:25
0001 /* This file is part of the KDE project 0002 * Copyright (c) 2010 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 "MorphologyEffect.h" 0021 #include "KoFilterEffectRenderContext.h" 0022 #include "KoFilterEffectLoadingContext.h" 0023 #include "KoViewConverter.h" 0024 #include "KoXmlWriter.h" 0025 #include "KoXmlReader.h" 0026 #include <klocalizedstring.h> 0027 #include <QRect> 0028 #include <QImage> 0029 #include <cmath> 0030 0031 MorphologyEffect::MorphologyEffect() 0032 : KoFilterEffect(MorphologyEffectId, i18n("Morphology")) 0033 , m_radius(0,0), m_operator(Erode) 0034 { 0035 } 0036 0037 QPointF MorphologyEffect::morphologyRadius() const 0038 { 0039 return m_radius; 0040 } 0041 0042 void MorphologyEffect::setMorphologyRadius(const QPointF &radius) 0043 { 0044 m_radius = radius; 0045 } 0046 0047 MorphologyEffect::Operator MorphologyEffect::morphologyOperator() const 0048 { 0049 return m_operator; 0050 } 0051 0052 void MorphologyEffect::setMorphologyOperator(Operator op) 0053 { 0054 m_operator = op; 0055 } 0056 0057 QImage MorphologyEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const 0058 { 0059 QImage result = image; 0060 0061 QPointF radius = context.toUserSpace(m_radius); 0062 0063 const int rx = static_cast<int>(ceil(radius.x())); 0064 const int ry = static_cast<int>(ceil(radius.y())); 0065 0066 const int w = result.width(); 0067 const int h = result.height(); 0068 0069 // setup mask 0070 const int maskSize = (1+2*rx)*(1+2*ry); 0071 int * mask = new int[maskSize]; 0072 int index = 0; 0073 for (int y = -ry; y <= ry; ++y) { 0074 for (int x = -rx; x <= rx; ++x) { 0075 mask[index] = y*w+x; 0076 index++; 0077 } 0078 } 0079 0080 int dstPixel, srcPixel; 0081 uchar s0, s1, s2, s3; 0082 const uchar * src = image.constBits(); 0083 uchar * dst = result.bits(); 0084 0085 const QRect roi = context.filterRegion().toRect(); 0086 const int minX = qMax(rx, roi.left()); 0087 const int maxX = qMin(w-rx, roi.right()); 0088 const int minY = qMax(ry, roi.top()); 0089 const int maxY = qMin(h-ry, roi.bottom()); 0090 const int defValue = m_operator == Erode ? 255 : 0; 0091 0092 uchar * d = 0; 0093 0094 for (int row = minY; row < maxY; ++row) { 0095 for (int col = minX; col < maxX; ++col) { 0096 dstPixel = row * w + col; 0097 s0 = s1 = s2 = s3 = defValue; 0098 for (int i = 0; i < maskSize; ++i) { 0099 srcPixel = dstPixel+mask[i]; 0100 const uchar *s = &src[4*srcPixel]; 0101 if (m_operator == Erode ) { 0102 s0 = qMin(s0, s[0]); 0103 s1 = qMin(s1, s[1]); 0104 s2 = qMin(s2, s[2]); 0105 s3 = qMin(s3, s[3]); 0106 } else { 0107 s0 = qMax(s0, s[0]); 0108 s1 = qMax(s1, s[1]); 0109 s2 = qMax(s2, s[2]); 0110 s3 = qMax(s3, s[3]); 0111 } 0112 } 0113 d = &dst[4*dstPixel]; 0114 d[0] = s0; 0115 d[1] = s1; 0116 d[2] = s2; 0117 d[3] = s3; 0118 } 0119 } 0120 0121 delete [] mask; 0122 0123 return result; 0124 } 0125 0126 bool MorphologyEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) 0127 { 0128 if (element.tagName() != id()) 0129 return false; 0130 0131 m_radius = QPointF(); 0132 m_operator = Erode; 0133 0134 if (element.hasAttribute("radius")) { 0135 QString radiusStr = element.attribute("radius").trimmed(); 0136 QStringList params = radiusStr.replace(',', ' ').simplified().split(' '); 0137 switch (params.count()) { 0138 case 1: 0139 m_radius.rx() = params[0].toDouble()*72./90.; 0140 m_radius.ry() = m_radius.x(); 0141 break; 0142 case 2: 0143 m_radius.rx() = params[0].toDouble()*72./90.; 0144 m_radius.ry() = params[1].toDouble()*72./90.; 0145 break; 0146 default: 0147 m_radius = QPointF(); 0148 } 0149 } 0150 0151 m_radius = context.convertFilterPrimitiveUnits(m_radius); 0152 0153 if (element.hasAttribute("operator")) { 0154 QString op = element.attribute("operator"); 0155 if (op == "dilate") 0156 m_operator = Dilate; 0157 } 0158 0159 return true; 0160 } 0161 0162 void MorphologyEffect::save(KoXmlWriter &writer) 0163 { 0164 writer.startElement(MorphologyEffectId); 0165 0166 saveCommonAttributes(writer); 0167 0168 if (m_operator != Erode ) 0169 writer.addAttribute("operator", "dilate"); 0170 if (!m_radius.isNull()) { 0171 if (m_radius.x() == m_radius.y()) { 0172 writer.addAttribute("radius", QString("%1").arg(m_radius.x())); 0173 } else { 0174 writer.addAttribute("radius", QString("%1 %2").arg(m_radius.x()).arg(m_radius.y())); 0175 } 0176 } 0177 0178 writer.endElement(); 0179 }