Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svgio/source/svgreader/svgfecompositenode.cxx
Line
Count
Source
1
/*
2
 * This file is part of the LibreOffice project.
3
 *
4
 * This Source Code Form is subject to the terms of the Mozilla Public
5
 * License, v. 2.0. If a copy of the MPL was not distributed with this
6
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
 *
8
 * This file incorporates work covered by the following license notice:
9
 *
10
 *   Licensed to the Apache Software Foundation (ASF) under one or more
11
 *   contributor license agreements. See the NOTICE file distributed
12
 *   with this work for additional information regarding copyright
13
 *   ownership. The ASF licenses this file to you under the Apache
14
 *   License, Version 2.0 (the "License"); you may not use this file
15
 *   except in compliance with the License. You may obtain a copy of
16
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
17
 */
18
19
#include <svgfecompositenode.hxx>
20
#include <o3tl/string_view.hxx>
21
#include <basegfx/polygon/b2dpolygontools.hxx>
22
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
23
#include <basegfx/polygon/b2dpolypolygon.hxx>
24
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
25
#include <drawinglayer/processor2d/contourextractor2d.hxx>
26
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
27
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
28
#include <vcl/bitmap/BitmapArithmeticBlendFilter.hxx>
29
#include <drawinglayer/converters.hxx>
30
#include <basegfx/matrix/b2dhommatrixtools.hxx>
31
#include <vcl/BitmapWriteAccess.hxx>
32
#include <vcl/BitmapTools.hxx>
33
34
namespace svgio::svgreader
35
{
36
SvgFeCompositeNode::SvgFeCompositeNode(SvgDocument& rDocument, SvgNode* pParent)
37
0
    : SvgFilterNode(SVGToken::FeComposite, rDocument, pParent)
38
0
    , maOperator(Operator::Over)
39
0
{
40
0
}
41
42
0
SvgFeCompositeNode::~SvgFeCompositeNode() {}
43
44
void SvgFeCompositeNode::parseAttribute(SVGToken aSVGToken, const OUString& aContent)
45
0
{
46
    // parse own
47
0
    switch (aSVGToken)
48
0
    {
49
0
        case SVGToken::Style:
50
0
        {
51
0
            readLocalCssStyle(aContent);
52
0
            break;
53
0
        }
54
0
        case SVGToken::In:
55
0
        {
56
0
            maIn = aContent.trim();
57
0
            break;
58
0
        }
59
0
        case SVGToken::In2:
60
0
        {
61
0
            maIn2 = aContent.trim();
62
0
            break;
63
0
        }
64
0
        case SVGToken::Result:
65
0
        {
66
0
            maResult = aContent.trim();
67
0
            break;
68
0
        }
69
0
        case SVGToken::Operator:
70
0
        {
71
0
            if (!aContent.isEmpty())
72
0
            {
73
0
                if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"over"))
74
0
                {
75
0
                    maOperator = Operator::Over;
76
0
                }
77
0
                else if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"in"))
78
0
                {
79
0
                    maOperator = Operator::In;
80
0
                }
81
0
                else if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"out"))
82
0
                {
83
0
                    maOperator = Operator::Out;
84
0
                }
85
0
                else if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"xor"))
86
0
                {
87
0
                    maOperator = Operator::Xor;
88
0
                }
89
0
                else if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"atop"))
90
0
                {
91
0
                    maOperator = Operator::Atop;
92
0
                }
93
0
                else if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"arithmetic"))
94
0
                {
95
0
                    maOperator = Operator::Arithmetic;
96
0
                }
97
0
            }
98
0
            break;
99
0
        }
100
0
        case SVGToken::K1:
101
0
        {
102
0
            SvgNumber aNum;
103
104
0
            if (readSingleNumber(aContent, aNum))
105
0
            {
106
0
                maK1 = aNum;
107
0
            }
108
0
            break;
109
0
        }
110
0
        case SVGToken::K2:
111
0
        {
112
0
            SvgNumber aNum;
113
114
0
            if (readSingleNumber(aContent, aNum))
115
0
            {
116
0
                maK2 = aNum;
117
0
            }
118
0
            break;
119
0
        }
120
0
        case SVGToken::K3:
121
0
        {
122
0
            SvgNumber aNum;
123
124
0
            if (readSingleNumber(aContent, aNum))
125
0
            {
126
0
                maK3 = aNum;
127
0
            }
128
0
            break;
129
0
        }
130
0
        case SVGToken::K4:
131
0
        {
132
0
            SvgNumber aNum;
133
134
0
            if (readSingleNumber(aContent, aNum))
135
0
            {
136
0
                maK4 = aNum;
137
0
            }
138
0
            break;
139
0
        }
140
0
        default:
141
0
        {
142
0
            break;
143
0
        }
144
0
    }
145
0
}
146
147
void SvgFeCompositeNode::apply(drawinglayer::primitive2d::Primitive2DContainer& rTarget,
148
                               const SvgFilterNode* pParent) const
149
0
{
150
0
    const drawinglayer::primitive2d::Primitive2DContainer* pSource
151
0
        = pParent->findGraphicSource(maIn);
152
0
    const drawinglayer::primitive2d::Primitive2DContainer* pSource2
153
0
        = pParent->findGraphicSource(maIn2);
154
155
0
    if (maOperator != Operator::Arithmetic)
156
0
    {
157
0
        basegfx::B2DPolyPolygon aPolyPolygon, aPolyPolygon2;
158
159
        // Process maIn2 first
160
0
        if (pSource2)
161
0
        {
162
0
            rTarget.append(*pSource2);
163
0
            drawinglayer::processor2d::ContourExtractor2D aExtractor(
164
0
                drawinglayer::geometry::ViewInformation2D(), true);
165
0
            aExtractor.process(*pSource2);
166
0
            const basegfx::B2DPolyPolygonVector& rResult(aExtractor.getExtractedContour());
167
0
            aPolyPolygon2 = basegfx::utils::mergeToSinglePolyPolygon(rResult);
168
0
        }
169
170
0
        if (pSource)
171
0
        {
172
0
            rTarget.append(*pSource);
173
0
            drawinglayer::processor2d::ContourExtractor2D aExtractor(
174
0
                drawinglayer::geometry::ViewInformation2D(), true);
175
0
            aExtractor.process(*pSource);
176
0
            const basegfx::B2DPolyPolygonVector& rResult(aExtractor.getExtractedContour());
177
0
            aPolyPolygon = basegfx::utils::mergeToSinglePolyPolygon(rResult);
178
0
        }
179
180
0
        basegfx::B2DPolyPolygon aResult;
181
0
        if (maOperator == Operator::Over)
182
0
        {
183
0
            aResult = basegfx::utils::solvePolygonOperationOr(aPolyPolygon, aPolyPolygon2);
184
0
        }
185
0
        else if (maOperator == Operator::Out)
186
0
        {
187
0
            aResult = basegfx::utils::solvePolygonOperationDiff(aPolyPolygon, aPolyPolygon2);
188
0
        }
189
0
        else if (maOperator == Operator::In)
190
0
        {
191
0
            aResult = basegfx::utils::solvePolygonOperationAnd(aPolyPolygon, aPolyPolygon2);
192
0
        }
193
0
        else if (maOperator == Operator::Xor)
194
0
        {
195
0
            aResult = basegfx::utils::solvePolygonOperationXor(aPolyPolygon, aPolyPolygon2);
196
0
        }
197
0
        else if (maOperator == Operator::Atop)
198
0
        {
199
            // Atop is the union of In and Out.
200
            // The parts of in2 graphic that do not overlap with the in graphic stay untouched.
201
0
            aResult = basegfx::utils::solvePolygonOperationDiff(aPolyPolygon2, aPolyPolygon);
202
0
            aResult.append(basegfx::utils::solvePolygonOperationAnd(aPolyPolygon, aPolyPolygon2));
203
0
        }
204
205
0
        rTarget = drawinglayer::primitive2d::Primitive2DContainer{
206
0
            new drawinglayer::primitive2d::MaskPrimitive2D(std::move(aResult), std::move(rTarget))
207
0
        };
208
209
0
        pParent->addGraphicSourceToMapper(maResult, rTarget);
210
0
    }
211
0
    else
212
0
    {
213
0
        basegfx::B2DRange aRange, aRange2;
214
0
        const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
215
0
        if (pSource)
216
0
        {
217
0
            aRange = pSource->getB2DRange(aViewInformation2D);
218
0
        }
219
220
0
        if (pSource2)
221
0
        {
222
0
            aRange2 = pSource2->getB2DRange(aViewInformation2D);
223
0
        }
224
225
0
        const basegfx::B2DRange aBaseRange(0, 0, std::max(aRange.getMaxX(), aRange2.getMaxX()),
226
0
                                           std::max(aRange.getMaxY(), aRange2.getMaxY()));
227
228
0
        Bitmap aBmp, aBmp2;
229
230
0
        if (pSource)
231
0
        {
232
0
            drawinglayer::primitive2d::Primitive2DContainer aSource(*pSource);
233
0
            aBmp = drawinglayer::convertToBitmap(std::move(aSource), aViewInformation2D,
234
0
                                                 aBaseRange.getWidth(), aBaseRange.getHeight(),
235
0
                                                 aBaseRange.getWidth() * aBaseRange.getHeight());
236
0
        }
237
0
        else
238
0
        {
239
0
            aBmp = drawinglayer::convertToBitmap(std::move(rTarget), aViewInformation2D,
240
0
                                                 aBaseRange.getWidth(), aBaseRange.getHeight(),
241
0
                                                 aBaseRange.getWidth() * aBaseRange.getHeight());
242
0
        }
243
244
0
        if (pSource2)
245
0
        {
246
0
            drawinglayer::primitive2d::Primitive2DContainer aSource(*pSource2);
247
0
            aBmp2 = drawinglayer::convertToBitmap(std::move(aSource), aViewInformation2D,
248
0
                                                  aBaseRange.getWidth(), aBaseRange.getHeight(),
249
0
                                                  aBaseRange.getWidth() * aBaseRange.getHeight());
250
0
        }
251
0
        else
252
0
        {
253
0
            aBmp2 = drawinglayer::convertToBitmap(std::move(rTarget), aViewInformation2D,
254
0
                                                  aBaseRange.getWidth(), aBaseRange.getHeight(),
255
0
                                                  aBaseRange.getWidth() * aBaseRange.getHeight());
256
0
        }
257
258
0
        BitmapArithmeticBlendFilter aArithmeticFilter(aBmp2, maK1.getNumber(), maK2.getNumber(),
259
0
                                                      maK3.getNumber(), maK4.getNumber());
260
0
        Bitmap aResBmp = aArithmeticFilter.execute(aBmp);
261
262
0
        const drawinglayer::primitive2d::Primitive2DReference xRef(
263
0
            new drawinglayer::primitive2d::BitmapPrimitive2D(
264
0
                aResBmp, basegfx::utils::createScaleTranslateB2DHomMatrix(
265
0
                             aBaseRange.getRange(), aBaseRange.getMinimum())));
266
0
        rTarget = drawinglayer::primitive2d::Primitive2DContainer{ xRef };
267
0
    }
268
0
}
269
270
} // end of namespace svgio::svgreader
271
272
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */