~ubuntu-branches/ubuntu/precise/koffice/precise

« back to all changes in this revision

Viewing changes to karbon/plugins/filtereffects/ComponentTransferEffect.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2010-09-21 15:36:35 UTC
  • mfrom: (1.4.1 upstream) (60.2.11 maverick)
  • Revision ID: james.westby@ubuntu.com-20100921153635-6tejqkiro2u21ydi
Tags: 1:2.2.2-0ubuntu3
Add kubuntu_03_fix-crash-on-closing-sqlite-connection-2.2.2.diff and
kubuntu_04_support-large-memo-values-for-msaccess-2.2.2.diff as
recommended by upstream http://kexi-
project.org/wiki/wikiview/index.php@Kexi2.2_Patches.html#sqlite_stab
ility

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is part of the KDE project
 
2
 * Copyright (c) 2009 Jan Hambrecht <jaham@gmx.net>
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Lesser General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Library General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Lesser General Public License
 
15
 * along with this library; see the file COPYING.LIB.  If not, write to
 
16
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
17
 * Boston, MA 02110-1301, USA.
 
18
 */
 
19
 
 
20
#include "ComponentTransferEffect.h"
 
21
#include "ColorChannelConversion.h"
 
22
#include <KoFilterEffectRenderContext.h>
 
23
#include <KoXmlWriter.h>
 
24
#include <KoXmlReader.h>
 
25
#include <KLocale>
 
26
#include <QtCore/QRect>
 
27
#include <math.h>
 
28
 
 
29
ComponentTransferEffect::ComponentTransferEffect()
 
30
        : KoFilterEffect(ComponentTransferEffectId, i18n("Component transfer"))
 
31
{
 
32
}
 
33
 
 
34
ComponentTransferEffect::Function ComponentTransferEffect::function(Channel channel) const
 
35
{
 
36
    return m_data[channel].function;
 
37
}
 
38
 
 
39
void ComponentTransferEffect::setFunction(Channel channel, Function function)
 
40
{
 
41
    m_data[channel].function = function;
 
42
}
 
43
 
 
44
QList<qreal> ComponentTransferEffect::tableValues(Channel channel) const
 
45
{
 
46
    return m_data[channel].tableValues;
 
47
}
 
48
 
 
49
void ComponentTransferEffect::setTableValues(Channel channel, QList<qreal> tableValues)
 
50
{
 
51
    m_data[channel].tableValues = tableValues;
 
52
}
 
53
 
 
54
void ComponentTransferEffect::setSlope(Channel channel, qreal slope)
 
55
{
 
56
    m_data[channel].slope = slope;
 
57
}
 
58
 
 
59
qreal ComponentTransferEffect::slope(Channel channel) const
 
60
{
 
61
    return m_data[channel].slope;
 
62
}
 
63
 
 
64
void ComponentTransferEffect::setIntercept(Channel channel, qreal intercept)
 
65
{
 
66
    m_data[channel].intercept = intercept;
 
67
}
 
68
 
 
69
qreal ComponentTransferEffect::intercept(Channel channel) const
 
70
{
 
71
    return m_data[channel].intercept;
 
72
}
 
73
 
 
74
void ComponentTransferEffect::setAmplitude(Channel channel, qreal amplitude)
 
75
{
 
76
    m_data[channel].amplitude = amplitude;
 
77
}
 
78
 
 
79
qreal ComponentTransferEffect::amplitude(Channel channel) const
 
80
{
 
81
    return m_data[channel].amplitude;
 
82
}
 
83
 
 
84
void ComponentTransferEffect::setExponent(Channel channel, qreal exponent)
 
85
{
 
86
    m_data[channel].exponent = exponent;
 
87
}
 
88
 
 
89
qreal ComponentTransferEffect::exponent(Channel channel) const
 
90
{
 
91
    return m_data[channel].exponent;
 
92
}
 
93
 
 
94
void ComponentTransferEffect::setOffset(Channel channel, qreal offset)
 
95
{
 
96
    m_data[channel].offset = offset;
 
97
}
 
98
 
 
99
qreal ComponentTransferEffect::offset(Channel channel) const
 
100
{
 
101
    return m_data[channel].offset;
 
102
}
 
103
 
 
104
QImage ComponentTransferEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const
 
105
{
 
106
    QImage result = image;
 
107
 
 
108
    QRgb *src = (QRgb*)image.bits();
 
109
    QRgb *dst = (QRgb*)result.bits();
 
110
    int w = result.width();
 
111
 
 
112
    qreal sa, sr, sg, sb;
 
113
    qreal da, dr, dg, db;
 
114
    int pixel;
 
115
 
 
116
    const QRect roi = context.filterRegion().toRect();
 
117
    const int minRow = roi.top();
 
118
    const int maxRow = roi.bottom();
 
119
    const int minCol = roi.left();
 
120
    const int maxCol = roi.right();
 
121
 
 
122
    for (int row = minRow; row <= maxRow; ++row) {
 
123
        for (int col = minCol; col <= maxCol; ++col) {
 
124
            pixel = row * w + col;
 
125
            const QRgb &s = src[pixel];
 
126
 
 
127
            sa = fromIntColor[qAlpha(s)];
 
128
            sr = fromIntColor[qRed(s)];
 
129
            sg = fromIntColor[qGreen(s)];
 
130
            sb = fromIntColor[qBlue(s)];
 
131
            // the matrix is applied to non-premultiplied color values
 
132
            // so we have to convert colors by dividing by alpha value
 
133
            if (sa > 0.0 && sa < 1.0) {
 
134
                sr /= sa;
 
135
                sb /= sa;
 
136
                sg /= sa;
 
137
            }
 
138
 
 
139
            dr = transferChannel(ChannelR, sr);
 
140
            dg = transferChannel(ChannelG, sg);
 
141
            db = transferChannel(ChannelB, sb);
 
142
            da = transferChannel(ChannelA, sa);
 
143
 
 
144
            da *= 255.0;
 
145
 
 
146
            // set pre-multiplied color values on destination image
 
147
            dst[pixel] = qRgba(static_cast<quint8>(qBound((qreal) 0.0, dr * da, (qreal) 255.0)),
 
148
                               static_cast<quint8>(qBound((qreal) 0.0, dg * da, (qreal) 255.0)),
 
149
                               static_cast<quint8>(qBound((qreal) 0.0, db * da, (qreal) 255.0)),
 
150
                               static_cast<quint8>(qBound((qreal) 0.0, da, (qreal) 255.0)));
 
151
        }
 
152
    }
 
153
 
 
154
    return result;
 
155
}
 
156
 
 
157
qreal ComponentTransferEffect::transferChannel(Channel channel, qreal value) const
 
158
{
 
159
    const Data &d = m_data[channel];
 
160
 
 
161
    switch (d.function) {
 
162
    case Identity:
 
163
        return value;
 
164
    case Table: {
 
165
        qreal valueCount = d.tableValues.count() - 1;
 
166
        if (valueCount < 0.0)
 
167
            return value;
 
168
        qreal k1 = static_cast<int>(value * valueCount);
 
169
        qreal k2 = qMin(k1 + 1, valueCount);
 
170
        qreal vk1 = d.tableValues[k1];
 
171
        qreal vk2 = d.tableValues[k2];
 
172
        return vk1 + (value - static_cast<qreal>(k1) / valueCount)*valueCount *(vk2 - vk1);
 
173
    }
 
174
    case Discrete: {
 
175
        qreal valueCount = d.tableValues.count() - 1;
 
176
        if (valueCount < 0.0)
 
177
            return value;
 
178
        return d.tableValues[static_cast<int>(value*valueCount)];
 
179
    }
 
180
    case Linear:
 
181
        return d.slope * value + d.intercept;
 
182
    case Gamma:
 
183
        return d.amplitude * pow(value, d.exponent) + d.offset;
 
184
    }
 
185
 
 
186
    return value;
 
187
}
 
188
 
 
189
bool ComponentTransferEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &)
 
190
{
 
191
    if (element.tagName() != id())
 
192
        return false;
 
193
 
 
194
    // reset data
 
195
    m_data[ChannelR] = Data();
 
196
    m_data[ChannelG] = Data();
 
197
    m_data[ChannelB] = Data();
 
198
    m_data[ChannelA] = Data();
 
199
 
 
200
    for (KoXmlNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
 
201
        KoXmlElement node = n.toElement();
 
202
        if (node.tagName() == "feFuncR") {
 
203
            loadChannel(ChannelR, node);
 
204
        } else if (node.tagName() == "feFuncG") {
 
205
            loadChannel(ChannelG, node);
 
206
        } else if (node.tagName() == "feFuncB") {
 
207
            loadChannel(ChannelB, node);
 
208
        } else if (node.tagName() == "feFuncA") {
 
209
            loadChannel(ChannelA, node);
 
210
        }
 
211
    }
 
212
 
 
213
    return true;
 
214
}
 
215
 
 
216
void ComponentTransferEffect::loadChannel(Channel channel, const KoXmlElement &element)
 
217
{
 
218
    QString typeStr = element.attribute("type");
 
219
    if (typeStr.isEmpty())
 
220
        return;
 
221
 
 
222
    Data &d = m_data[channel];
 
223
 
 
224
    if (typeStr == "table" || typeStr == "discrete") {
 
225
        d.function = typeStr == "table" ? Table : Discrete;
 
226
        QString valueStr = element.attribute("tableValues");
 
227
        QStringList values = valueStr.split(QRegExp("(\\s+|,)"), QString::SkipEmptyParts);
 
228
        foreach(const QString &v, values) {
 
229
            d.tableValues.append(v.toDouble());
 
230
        }
 
231
    } else if (typeStr == "linear") {
 
232
        d.function = Linear;
 
233
        if (element.hasAttribute("slope")) {
 
234
            d.slope = element.attribute("slope").toDouble();
 
235
        }
 
236
        if (element.hasAttribute("intercept")) {
 
237
            d.intercept = element.attribute("intercept").toDouble();
 
238
        }
 
239
    } else if (typeStr == "gamma") {
 
240
        d.function = Gamma;
 
241
        if (element.hasAttribute("amplitude")) {
 
242
            d.amplitude = element.attribute("amplitude").toDouble();
 
243
        }
 
244
        if (element.hasAttribute("exponent")) {
 
245
            d.exponent = element.attribute("exponent").toDouble();
 
246
        }
 
247
        if (element.hasAttribute("offset")) {
 
248
            d.offset = element.attribute("offset").toDouble();
 
249
        }
 
250
    }
 
251
}
 
252
 
 
253
void ComponentTransferEffect::save(KoXmlWriter &writer)
 
254
{
 
255
    writer.startElement(ComponentTransferEffectId);
 
256
 
 
257
    saveCommonAttributes(writer);
 
258
 
 
259
    saveChannel(ChannelR, writer);
 
260
    saveChannel(ChannelG, writer);
 
261
    saveChannel(ChannelB, writer);
 
262
    saveChannel(ChannelA, writer);
 
263
 
 
264
    writer.endElement();
 
265
}
 
266
 
 
267
void ComponentTransferEffect::saveChannel(Channel channel, KoXmlWriter &writer)
 
268
{
 
269
    Function function = m_data[channel].function;
 
270
    // we can omit writing the transfer function when
 
271
    if (function == Identity)
 
272
        return;
 
273
 
 
274
    switch (channel) {
 
275
    case ChannelR:
 
276
        writer.startElement("feFuncR");
 
277
        break;
 
278
    case ChannelG:
 
279
        writer.startElement("feFuncG");
 
280
        break;
 
281
    case ChannelB:
 
282
        writer.startElement("feFuncB");
 
283
        break;
 
284
    case ChannelA:
 
285
        writer.startElement("feFuncA");
 
286
        break;
 
287
    }
 
288
 
 
289
    Data defaultData;
 
290
    const Data &currentData = m_data[channel];
 
291
 
 
292
    if (function == Linear) {
 
293
        writer.addAttribute("type", "linear");
 
294
        // only write non default data
 
295
        if (defaultData.slope != currentData.slope)
 
296
            writer.addAttribute("slope", QString("%1").arg(currentData.slope));
 
297
        if (defaultData.intercept != currentData.intercept)
 
298
            writer.addAttribute("intercept", QString("%1").arg(currentData.intercept));
 
299
    } else if (function == Gamma) {
 
300
        writer.addAttribute("type", "gamma");
 
301
        // only write non default data
 
302
        if (defaultData.amplitude != currentData.amplitude)
 
303
            writer.addAttribute("amplitude", QString("%1").arg(currentData.amplitude));
 
304
        if (defaultData.exponent != currentData.exponent)
 
305
            writer.addAttribute("exponent", QString("%1").arg(currentData.exponent));
 
306
        if (defaultData.offset != currentData.offset)
 
307
            writer.addAttribute("offset", QString("%1").arg(currentData.offset));
 
308
    } else {
 
309
        writer.addAttribute("type", function == Table ? "table" : "discrete");
 
310
        if (currentData.tableValues.count()) {
 
311
            QString tableStr;
 
312
            foreach(qreal v, currentData.tableValues) {
 
313
                tableStr += QString("%1 ").arg(v);
 
314
            }
 
315
            writer.addAttribute("tableValues", tableStr.trimmed());
 
316
        }
 
317
    }
 
318
 
 
319
    writer.endElement();
 
320
}