Coverage Report

Created: 2026-10-14 09:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/oox/source/drawingml/fillproperties.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <drawingml/fillproperties.hxx>
21
22
#include <iterator>
23
24
#include <comphelper/propertyvalue.hxx>
25
#include <drawingml/graphicproperties.hxx>
26
#include <vcl/alpha.hxx>
27
#include <vcl/graph.hxx>
28
#include <vcl/bitmap/BitmapMonochromeFilter.hxx>
29
#include <vcl/gfxlink.hxx>
30
#include <docmodel/uno/UnoComplexColor.hxx>
31
#include <docmodel/uno/UnoGradientTools.hxx>
32
#include <basegfx/utils/gradienttools.hxx>
33
34
#include <com/sun/star/beans/XPropertySet.hpp>
35
#include <com/sun/star/awt/Gradient2.hpp>
36
#include <com/sun/star/text/GraphicCrop.hpp>
37
#include <com/sun/star/awt/Size.hpp>
38
#include <com/sun/star/drawing/BitmapMode.hpp>
39
#include <com/sun/star/drawing/ColorMode.hpp>
40
#include <com/sun/star/drawing/FillStyle.hpp>
41
#include <com/sun/star/drawing/RectanglePoint.hpp>
42
#include <com/sun/star/graphic/XGraphicTransformer.hpp>
43
#include <oox/helper/graphichelper.hxx>
44
#include <oox/drawingml/drawingmltypes.hxx>
45
#include <oox/drawingml/shapepropertymap.hxx>
46
#include <drawingml/hatchmap.hxx>
47
#include <oox/token/namespaces.hxx>
48
#include <oox/token/properties.hxx>
49
#include <oox/token/tokens.hxx>
50
#include <osl/diagnose.h>
51
#include <sal/log.hxx>
52
53
#include <frozen/bits/defines.h>
54
#include <frozen/bits/elsa_std.h>
55
#include <frozen/unordered_map.h>
56
57
58
using namespace ::com::sun::star;
59
using namespace ::com::sun::star::drawing;
60
using namespace ::com::sun::star::graphic;
61
62
using ::com::sun::star::uno::Reference;
63
using ::com::sun::star::uno::Exception;
64
using ::com::sun::star::uno::UNO_QUERY_THROW;
65
using ::com::sun::star::geometry::IntegerRectangle2D;
66
67
namespace oox::drawingml {
68
69
namespace {
70
71
Reference< XGraphic > lclCheckAndApplyDuotoneTransform(const BlipFillProperties& aBlipProps, uno::Reference<graphic::XGraphic> const & xGraphic,
72
                                                       const GraphicHelper& rGraphicHelper, const ::Color nPhClr)
73
2.42k
{
74
2.42k
    if (aBlipProps.maDuotoneColors[0].isUsed() && aBlipProps.maDuotoneColors[1].isUsed())
75
7
    {
76
7
        ::Color nColor1 = aBlipProps.maDuotoneColors[0].getColor( rGraphicHelper, nPhClr );
77
7
        ::Color nColor2 = aBlipProps.maDuotoneColors[1].getColor( rGraphicHelper, nPhClr );
78
79
7
        uno::Reference<graphic::XGraphicTransformer> xTransformer(aBlipProps.mxFillGraphic, uno::UNO_QUERY);
80
7
        if (xTransformer.is())
81
7
            return xTransformer->applyDuotone(xGraphic, sal_Int32(nColor1), sal_Int32(nColor2));
82
7
    }
83
2.41k
    return xGraphic;
84
2.42k
}
85
86
Reference< XGraphic > lclRotateGraphic(uno::Reference<graphic::XGraphic> const & xGraphic, Degree10 nRotation)
87
0
{
88
0
    ::Graphic aGraphic(xGraphic);
89
0
    ::Graphic aReturnGraphic;
90
91
0
    assert (aGraphic.GetType() == GraphicType::Bitmap);
92
93
0
    Bitmap aBitmap(aGraphic.GetBitmap());
94
0
    const ::Color aColor(0x00);
95
0
    aBitmap.Rotate(nRotation, aColor);
96
0
    aReturnGraphic = ::Graphic(aBitmap);
97
0
    aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
98
99
0
    return aReturnGraphic.GetXGraphic();
100
0
}
101
102
using Quotients = std::tuple<double, double, double, double>;
103
Quotients getQuotients(geometry::IntegerRectangle2D aRelRect, double hDiv, double vDiv)
104
0
{
105
0
    return { aRelRect.X1 / hDiv, aRelRect.Y1 / vDiv, aRelRect.X2 / hDiv, aRelRect.Y2 / vDiv };
106
0
}
107
108
// ECMA-376 Part 1 20.1.8.55 srcRect (Source Rectangle)
109
std::optional<Quotients> CropQuotientsFromSrcRect(geometry::IntegerRectangle2D aSrcRect)
110
0
{
111
0
    aSrcRect.X1 = std::max(aSrcRect.X1, sal_Int32(0));
112
0
    aSrcRect.X2 = std::max(aSrcRect.X2, sal_Int32(0));
113
0
    aSrcRect.Y1 = std::max(aSrcRect.Y1, sal_Int32(0));
114
0
    aSrcRect.Y2 = std::max(aSrcRect.Y2, sal_Int32(0));
115
0
    if (aSrcRect.X1 + aSrcRect.X2 >= MAX_PERCENT || aSrcRect.Y1 + aSrcRect.Y2 >= MAX_PERCENT)
116
0
        return {}; // Cropped everything
117
0
    return getQuotients(aSrcRect, MAX_PERCENT, MAX_PERCENT);
118
0
}
119
120
// ECMA-376 Part 1 20.1.8.30 fillRect (Fill Rectangle)
121
std::optional<Quotients> CropQuotientsFromFillRect(geometry::IntegerRectangle2D aFillRect)
122
0
{
123
0
    aFillRect.X1 = std::min(aFillRect.X1, sal_Int32(0));
124
0
    aFillRect.X2 = std::min(aFillRect.X2, sal_Int32(0));
125
0
    aFillRect.Y1 = std::min(aFillRect.Y1, sal_Int32(0));
126
0
    aFillRect.Y2 = std::min(aFillRect.Y2, sal_Int32(0));
127
    // Negative divisor and negative relative offset give positive value wanted in lclCropGraphic
128
0
    return getQuotients(aFillRect, -MAX_PERCENT + aFillRect.X1 + aFillRect.X2,
129
0
                        -MAX_PERCENT + aFillRect.Y1 + aFillRect.Y2);
130
0
}
131
132
// Crops a piece of the bitmap. lclCropGraphic doesn't handle growing.
133
Reference<XGraphic> lclCropGraphic(uno::Reference<graphic::XGraphic> const& xGraphic,
134
                                   std::optional<Quotients> quotients)
135
0
{
136
0
    ::Graphic aGraphic(xGraphic);
137
0
    assert (aGraphic.GetType() == GraphicType::Bitmap);
138
139
0
    Bitmap aBitmap;
140
0
    if (quotients)
141
0
    {
142
0
        aBitmap = aGraphic.GetBitmap();
143
144
0
        const Size bmpSize = aBitmap.GetSizePixel();
145
0
        const auto& [qx1, qy1, qx2, qy2] = *quotients;
146
0
        const tools::Long l = std::round(bmpSize.Width() * qx1);
147
0
        const tools::Long t = std::round(bmpSize.Height() * qy1);
148
0
        const tools::Long r = std::round(bmpSize.Width() * qx2);
149
0
        const tools::Long b = std::round(bmpSize.Height() * qy2);
150
151
0
        aBitmap.Crop({ l, t, bmpSize.Width() - r - 1, bmpSize.Height() - b - 1 });
152
0
    }
153
154
0
    ::Graphic aReturnGraphic(aBitmap);
155
0
    aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
156
157
0
    return aReturnGraphic.GetXGraphic();
158
0
}
159
160
Reference< XGraphic > lclMirrorGraphic(uno::Reference<graphic::XGraphic> const & xGraphic, bool bFlipH, bool bFlipV)
161
0
{
162
0
    ::Graphic aGraphic(xGraphic);
163
0
    ::Graphic aReturnGraphic;
164
165
0
    assert (aGraphic.GetType() == GraphicType::Bitmap);
166
167
0
    Bitmap aBitmap(aGraphic.GetBitmap());
168
0
    BmpMirrorFlags nMirrorFlags = BmpMirrorFlags::NONE;
169
170
0
    if(bFlipH)
171
0
        nMirrorFlags |= BmpMirrorFlags::Horizontal;
172
0
    if(bFlipV)
173
0
        nMirrorFlags |= BmpMirrorFlags::Vertical;
174
175
0
    aBitmap.Mirror(nMirrorFlags);
176
177
0
    aReturnGraphic = ::Graphic(aBitmap);
178
0
    aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
179
180
0
    return aReturnGraphic.GetXGraphic();
181
0
}
182
183
Reference< XGraphic > lclGreysScaleGraphic(uno::Reference<graphic::XGraphic> const & xGraphic)
184
0
{
185
0
    ::Graphic aGraphic(xGraphic);
186
0
    ::Graphic aReturnGraphic;
187
188
0
    assert (aGraphic.GetType() == GraphicType::Bitmap);
189
190
0
    Bitmap aBitmap(aGraphic.GetBitmap());
191
0
    aBitmap.Convert(BmpConversion::N8BitGreys);
192
193
0
    aReturnGraphic = ::Graphic(aBitmap);
194
0
    aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
195
196
0
    return aReturnGraphic.GetXGraphic();
197
0
}
198
199
/// Applies the graphic Black&White (Monochrome) effect with the imported threshold
200
Reference<XGraphic> lclApplyBlackWhiteEffect(const BlipFillProperties& aBlipProps,
201
                                             const uno::Reference<graphic::XGraphic>& xGraphic)
202
0
{
203
0
    const auto& oBiLevelThreshold = aBlipProps.moBiLevelThreshold;
204
0
    if (oBiLevelThreshold.has_value())
205
0
    {
206
0
        sal_uInt8 nThreshold
207
0
            = static_cast<sal_uInt8>(oBiLevelThreshold.value() * 255 / MAX_PERCENT);
208
209
0
        ::Graphic aGraphic(xGraphic);
210
0
        ::Graphic aReturnGraphic;
211
212
0
        Bitmap aBitmap(aGraphic.GetBitmap());
213
0
        if (aBitmap.HasAlpha())
214
0
        {
215
0
            auto [aTmpBmp, aMask] = aBitmap.SplitIntoColorAndAlpha();
216
217
0
            BitmapFilter::Filter(aTmpBmp, BitmapMonochromeFilter{ nThreshold });
218
219
0
            aReturnGraphic = ::Graphic(Bitmap(aTmpBmp, aMask));
220
0
        }
221
0
        else
222
0
        {
223
0
            BitmapFilter::Filter(aBitmap, BitmapMonochromeFilter{ nThreshold });
224
0
            aReturnGraphic = ::Graphic(aBitmap);
225
0
        }
226
0
        aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
227
0
        return aReturnGraphic.GetXGraphic();
228
0
    }
229
0
    return xGraphic;
230
0
}
231
232
Reference< XGraphic > lclCheckAndApplyChangeColorTransform(const BlipFillProperties &aBlipProps, uno::Reference<graphic::XGraphic> const & xGraphic,
233
                                                           const GraphicHelper& rGraphicHelper, const ::Color nPhClr)
234
2.40k
{
235
2.40k
    if( aBlipProps.maColorChangeFrom.isUsed() && aBlipProps.maColorChangeTo.isUsed() )
236
0
    {
237
0
        ::Color nFromColor = aBlipProps.maColorChangeFrom.getColor( rGraphicHelper, nPhClr );
238
0
        ::Color nToColor = aBlipProps.maColorChangeTo.getColor( rGraphicHelper, nPhClr );
239
0
        if ( (nFromColor != nToColor) || aBlipProps.maColorChangeTo.hasTransparency() )
240
0
        {
241
0
            sal_Int16 nToTransparence = aBlipProps.maColorChangeTo.getTransparency();
242
0
            sal_Int8 nToAlpha = static_cast< sal_Int8 >( (100 - nToTransparence) * 2.55 );
243
244
0
            sal_uInt8 nTolerance = 9;
245
0
            Graphic aGraphic{ xGraphic };
246
0
            if( aGraphic.IsGfxLink() )
247
0
            {
248
                // tdf#149670: Try to guess tolerance depending on image format
249
0
                switch (aGraphic.GetGfxLink().GetType())
250
0
                {
251
0
                    case GfxLinkType::NativeJpg:
252
0
                        nTolerance = 15;
253
0
                        break;
254
0
                    case GfxLinkType::NativePng:
255
0
                    case GfxLinkType::NativeTif:
256
0
                        nTolerance = 1;
257
0
                        break;
258
0
                    case GfxLinkType::NativeBmp:
259
0
                        nTolerance = 0;
260
0
                        break;
261
0
                    default:
262
0
                        break;
263
0
                }
264
0
            }
265
266
0
            const ColorChangeKey aKey(aGraphic.ImplGetSharedImpGraphic(), static_cast<sal_Int32>(nFromColor), static_cast<sal_Int32>(nToColor), nToTransparence);
267
0
            Reference<XGraphic> xCachedGraphic = rGraphicHelper.getCachedColorChangeGraphic(aKey);
268
0
            if (xCachedGraphic.is())
269
0
                return xCachedGraphic;
270
271
0
            uno::Reference<graphic::XGraphicTransformer> xTransformer(aBlipProps.mxFillGraphic, uno::UNO_QUERY);
272
0
            if (xTransformer.is())
273
0
            {
274
0
                uno::Reference<graphic::XGraphic> xResult = xTransformer->colorChange(xGraphic, static_cast<sal_Int32>(nFromColor), nTolerance, static_cast<sal_Int32>(nToColor), nToAlpha);
275
0
                rGraphicHelper.addGraphicToColorChangeCache(aKey, xResult);
276
0
                return xResult;
277
0
            }
278
0
        }
279
0
    }
280
2.40k
    return xGraphic;
281
2.40k
}
282
283
uno::Reference<graphic::XGraphic> applyBrightnessContrast(uno::Reference<graphic::XGraphic> const & xGraphic, sal_Int32 brightness, sal_Int32 contrast)
284
0
{
285
0
    uno::Reference<graphic::XGraphicTransformer> xTransformer(xGraphic, uno::UNO_QUERY);
286
0
    if (xTransformer.is())
287
0
        return xTransformer->applyBrightnessContrast(xGraphic, brightness, contrast, true);
288
0
    return xGraphic;
289
0
}
290
291
BitmapMode lclGetBitmapMode( sal_Int32 nToken )
292
21
{
293
21
    OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
294
21
    switch( nToken )
295
21
    {
296
21
        case XML_tile:      return BitmapMode_REPEAT;
297
0
        case XML_stretch:   return BitmapMode_STRETCH;
298
21
    }
299
300
    // tdf#128596 Default value is XML_tile for MSO.
301
0
    return BitmapMode_REPEAT;
302
21
}
303
304
RectanglePoint lclGetRectanglePoint( sal_Int32 nToken )
305
21
{
306
21
    OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
307
21
    switch( nToken )
308
21
    {
309
21
        case XML_tl:    return RectanglePoint_LEFT_TOP;
310
0
        case XML_t:     return RectanglePoint_MIDDLE_TOP;
311
0
        case XML_tr:    return RectanglePoint_RIGHT_TOP;
312
0
        case XML_l:     return RectanglePoint_LEFT_MIDDLE;
313
0
        case XML_ctr:   return RectanglePoint_MIDDLE_MIDDLE;
314
0
        case XML_r:     return RectanglePoint_RIGHT_MIDDLE;
315
0
        case XML_bl:    return RectanglePoint_LEFT_BOTTOM;
316
0
        case XML_b:     return RectanglePoint_MIDDLE_BOTTOM;
317
0
        case XML_br:    return RectanglePoint_RIGHT_BOTTOM;
318
21
    }
319
0
    return RectanglePoint_LEFT_TOP;
320
21
}
321
322
awt::Size lclGetOriginalSize( const GraphicHelper& rGraphicHelper, const Reference< XGraphic >& rxGraphic )
323
21
{
324
21
    awt::Size aSizeHmm( 0, 0 );
325
21
    try
326
21
    {
327
21
        Reference< beans::XPropertySet > xGraphicPropertySet( rxGraphic, UNO_QUERY_THROW );
328
21
        if( xGraphicPropertySet->getPropertyValue( u"Size100thMM"_ustr ) >>= aSizeHmm )
329
21
        {
330
21
            if( !aSizeHmm.Width && !aSizeHmm.Height )
331
14
            {   // MAPMODE_PIXEL USED :-(
332
14
                awt::Size aSourceSizePixel( 0, 0 );
333
14
                if( xGraphicPropertySet->getPropertyValue( u"SizePixel"_ustr ) >>= aSourceSizePixel )
334
14
                    aSizeHmm = rGraphicHelper.convertScreenPixelToHmm( aSourceSizePixel );
335
14
            }
336
21
        }
337
21
    }
338
21
    catch( Exception& )
339
21
    {
340
0
    }
341
21
    return aSizeHmm;
342
21
}
343
344
} // namespace
345
346
void GradientFillProperties::assignUsed( const GradientFillProperties& rSourceProps )
347
10.2M
{
348
10.2M
    if( !rSourceProps.maGradientStops.empty() )
349
2.28k
        maGradientStops = rSourceProps.maGradientStops;
350
10.2M
    assignIfUsed( moFillToRect, rSourceProps.moFillToRect );
351
10.2M
    assignIfUsed( moTileRect, rSourceProps.moTileRect );
352
10.2M
    assignIfUsed( moGradientPath, rSourceProps.moGradientPath );
353
10.2M
    assignIfUsed( moShadeAngle, rSourceProps.moShadeAngle );
354
10.2M
    assignIfUsed( moShadeFlip, rSourceProps.moShadeFlip );
355
10.2M
    assignIfUsed( moShadeScaled, rSourceProps.moShadeScaled );
356
10.2M
    assignIfUsed( moRotateWithShape, rSourceProps.moRotateWithShape );
357
10.2M
}
358
359
void PatternFillProperties::assignUsed( const PatternFillProperties& rSourceProps )
360
10.2M
{
361
10.2M
    maPattFgColor.assignIfUsed( rSourceProps.maPattFgColor );
362
10.2M
    maPattBgColor.assignIfUsed( rSourceProps.maPattBgColor );
363
10.2M
    assignIfUsed( moPattPreset, rSourceProps.moPattPreset );
364
10.2M
}
365
366
void BlipFillProperties::assignUsed( const BlipFillProperties& rSourceProps )
367
10.2M
{
368
10.2M
    if(rSourceProps.mxFillGraphic.is())
369
11
        mxFillGraphic = rSourceProps.mxFillGraphic;
370
10.2M
    assignIfUsed( moBitmapMode, rSourceProps.moBitmapMode );
371
10.2M
    assignIfUsed( moFillRect, rSourceProps.moFillRect );
372
10.2M
    assignIfUsed( moTileOffsetX, rSourceProps.moTileOffsetX );
373
10.2M
    assignIfUsed( moTileOffsetY, rSourceProps.moTileOffsetY );
374
10.2M
    assignIfUsed( moTileScaleX, rSourceProps.moTileScaleX );
375
10.2M
    assignIfUsed( moTileScaleY, rSourceProps.moTileScaleY );
376
10.2M
    assignIfUsed( moTileAlign, rSourceProps.moTileAlign );
377
10.2M
    assignIfUsed( moTileFlip, rSourceProps.moTileFlip );
378
10.2M
    assignIfUsed( moRotateWithShape, rSourceProps.moRotateWithShape );
379
10.2M
    assignIfUsed( moColorEffect, rSourceProps.moColorEffect );
380
10.2M
    assignIfUsed( moBrightness, rSourceProps.moBrightness );
381
10.2M
    assignIfUsed( moContrast, rSourceProps.moContrast );
382
10.2M
    assignIfUsed( moBiLevelThreshold, rSourceProps.moBiLevelThreshold );
383
10.2M
    maColorChangeFrom.assignIfUsed( rSourceProps.maColorChangeFrom );
384
10.2M
    maColorChangeTo.assignIfUsed( rSourceProps.maColorChangeTo );
385
10.2M
    maDuotoneColors[0].assignIfUsed( rSourceProps.maDuotoneColors[0] );
386
10.2M
    maDuotoneColors[1].assignIfUsed( rSourceProps.maDuotoneColors[1] );
387
10.2M
    maEffect.assignUsed( rSourceProps.maEffect );
388
10.2M
    assignIfUsed(moAlphaModFix, rSourceProps.moAlphaModFix);
389
10.2M
}
390
391
void FillProperties::assignUsed( const FillProperties& rSourceProps )
392
10.2M
{
393
10.2M
    assignIfUsed( moFillType, rSourceProps.moFillType );
394
10.2M
    maFillColor.assignIfUsed( rSourceProps.maFillColor );
395
10.2M
    assignIfUsed( moUseBgFill, rSourceProps.moUseBgFill );
396
10.2M
    maGradientProps.assignUsed( rSourceProps.maGradientProps );
397
10.2M
    maPatternProps.assignUsed( rSourceProps.maPatternProps );
398
10.2M
    maBlipProps.assignUsed( rSourceProps.maBlipProps );
399
10.2M
}
400
401
Color FillProperties::getBestSolidColor() const
402
420k
{
403
420k
    Color aSolidColor;
404
420k
    if( moFillType.has_value() ) switch( moFillType.value() )
405
416k
    {
406
349k
        case XML_solidFill:
407
349k
            aSolidColor = maFillColor;
408
349k
        break;
409
206
        case XML_gradFill:
410
206
            if( !maGradientProps.maGradientStops.empty() )
411
206
            {
412
206
                GradientFillProperties::GradientStopMap::const_iterator aGradientStop =
413
206
                    maGradientProps.maGradientStops.begin();
414
206
                if (maGradientProps.maGradientStops.size() > 2)
415
206
                    ++aGradientStop;
416
206
                aSolidColor = aGradientStop->second;
417
206
            }
418
206
        break;
419
0
        case XML_pattFill:
420
0
            aSolidColor = maPatternProps.maPattBgColor.isUsed() ? maPatternProps.maPattBgColor : maPatternProps.maPattFgColor;
421
0
        break;
422
416k
    }
423
420k
    return aSolidColor;
424
420k
}
425
426
void FillProperties::pushToPropMap(ShapePropertyMap& rPropMap, const GraphicHelper& rGraphicHelper,
427
                                   sal_Int32 nShapeRotation, ::Color nPhClr,
428
                                   const css::awt::Size& rSize, sal_Int16 nPhClrTheme, bool bFlipH,
429
                                   bool bFlipV, bool bIsCustomShape) const
430
81.9k
{
431
81.9k
    if( !moFillType.has_value() )
432
0
        return;
433
434
81.9k
    FillStyle eFillStyle = FillStyle_NONE;
435
81.9k
    OSL_ASSERT((moFillType.value() & sal_Int32(0xFFFF0000))==0);
436
81.9k
    switch( moFillType.value() )
437
81.9k
    {
438
62.0k
        case XML_noFill:
439
62.0k
        {
440
62.0k
            eFillStyle = FillStyle_NONE;
441
62.0k
            rPropMap.setProperty(ShapeProperty::FillUseSlideBackground, moUseBgFill.value_or(false));
442
62.0k
        }
443
62.0k
        break;
444
445
17.7k
        case XML_solidFill:
446
17.7k
            if( maFillColor.isUsed() )
447
17.7k
            {
448
17.7k
                ::Color aFillColor = maFillColor.getColor(rGraphicHelper, nPhClr);
449
17.7k
                rPropMap.setProperty(ShapeProperty::FillColor, aFillColor);
450
17.7k
                if( maFillColor.hasTransparency() )
451
291
                    rPropMap.setProperty( ShapeProperty::FillTransparency, maFillColor.getTransparency() );
452
453
17.7k
                model::ComplexColor aComplexColor;
454
                // It's a theme color if there's no explicit color given in the
455
                // input, and the resolved color is the same as the placeholder
456
                // color.
457
17.7k
                if (aFillColor == nPhClr && !moFillType.has_value())
458
0
                {
459
0
                    aComplexColor.setThemeColor(model::convertToThemeColorType(nPhClrTheme));
460
0
                }
461
17.7k
                else
462
17.7k
                {
463
17.7k
                    aComplexColor = maFillColor.createComplexColor(rGraphicHelper, nPhClrTheme);
464
17.7k
                    OUString sColorName = getBestSolidColor().getSchemeColorName();
465
17.7k
                    sal_Int32 nToken = Color::getColorMapToken(sColorName);
466
17.7k
                    if (nToken != -1)
467
3.36k
                    {
468
3.36k
                        rGraphicHelper.getSchemeColorToken(nToken);
469
3.36k
                        model::ThemeColorType eThemeColorType = schemeTokenToThemeColorType(nToken);
470
3.36k
                        aComplexColor.setThemeColor(eThemeColorType);
471
3.36k
                    }
472
17.7k
                }
473
17.7k
                rPropMap.setProperty(PROP_FillComplexColor, model::color::createXComplexColor(aComplexColor));
474
475
17.7k
                eFillStyle = FillStyle_SOLID;
476
17.7k
            }
477
17.7k
        break;
478
479
2.12k
        case XML_gradFill:
480
            // do not create gradient struct if property is not supported...
481
2.12k
            if( rPropMap.supportsProperty( ShapeProperty::FillGradient ) )
482
2.12k
            {
483
                // prepare ColorStops
484
2.12k
                basegfx::BColorStops aColorStops;
485
2.12k
                basegfx::BColorStops aTransparencyStops;
486
2.12k
                bool bContainsTransparency(false);
487
488
                // convert to BColorStops, check for contained transparency
489
2.12k
                for (const auto& rCandidate : maGradientProps.maGradientStops)
490
4.51k
                {
491
                    // TODO: oox::drawingml::Color does not support transforms
492
                    // (lumMod, etc). So those get lost at this point.
493
4.51k
                    const ::Color aColor(rCandidate.second.getColor(rGraphicHelper, nPhClr));
494
4.51k
                    aColorStops.addStop(rCandidate.first, aColor.getBColor());
495
4.51k
                    bContainsTransparency = bContainsTransparency || rCandidate.second.hasTransparency();
496
4.51k
                }
497
498
                // if we have transparency, convert to BColorStops
499
2.12k
                if (bContainsTransparency)
500
7
                {
501
7
                    for (const auto& rCandidate : maGradientProps.maGradientStops)
502
14
                    {
503
14
                        const double fTrans(rCandidate.second.getTransparency() * (1.0/100.0));
504
14
                        aTransparencyStops.addStop(rCandidate.first, basegfx::BColor(fTrans, fTrans, fTrans));
505
14
                    }
506
7
                }
507
508
                // prepare BGradient with some defaults
509
                // CAUTION: This used awt::Gradient2 before whose empty constructor
510
                //          (see workdir/UnoApiHeadersTarget/offapi/normal/com/sun/
511
                //          star/awt/Gradient.hpp) initializes all to zeros, so reflect
512
                //          this here. OTOH set all that were set, e.g. Start/EndIntens
513
                //          were set to 100, so just use default of BGradient constructor
514
2.12k
                basegfx::BGradient aGradient(
515
2.12k
                    aColorStops,
516
2.12k
                    awt::GradientStyle_LINEAR,
517
2.12k
                    Degree10(900),
518
2.12k
                    0,  // border
519
2.12k
                    0,  // OfsX -> 0, not 50 (!)
520
2.12k
                    0); // OfsY -> 0, not 50 (!)
521
522
                // "rotate with shape" set to false -> do not rotate
523
2.12k
                if (!maGradientProps.moRotateWithShape.value_or(true))
524
16
                {
525
16
                    nShapeRotation = 0;
526
16
                }
527
528
2.12k
                if (maGradientProps.moGradientPath.has_value())
529
28
                {
530
28
                    IntegerRectangle2D aFillToRect = maGradientProps.moFillToRect.value_or( IntegerRectangle2D( 0, 0, MAX_PERCENT, MAX_PERCENT ) );
531
28
                    sal_Int32 nCenterX = (MAX_PERCENT + aFillToRect.X1 - aFillToRect.X2) / 2;
532
28
                    aGradient.SetXOffset(getLimitedValue<sal_Int16, sal_Int32>(
533
28
                        nCenterX / PER_PERCENT, 0, 100));
534
28
                    sal_Int32 nCenterY = (MAX_PERCENT + aFillToRect.Y1 - aFillToRect.Y2) / 2;
535
28
                    aGradient.SetYOffset(getLimitedValue<sal_Int16, sal_Int32>(
536
28
                        nCenterY / PER_PERCENT, 0, 100));
537
538
                    // FIXME tdf#166140: Size of gradient is smaller than in MSO
539
28
                    if( maGradientProps.moGradientPath.value() == XML_circle )
540
12
                    {
541
12
                        aGradient.SetGradientStyle(awt::GradientStyle_RADIAL);
542
12
                    }
543
16
                    else
544
16
                    {
545
                        // XML_rect or XML_shape, but the latter is not implemented.
546
16
                        aGradient.SetGradientStyle(awt::GradientStyle_RECT);
547
16
                    }
548
549
28
                    aColorStops.reverseColorStops();
550
28
                    aGradient.SetColorStops(aColorStops);
551
28
                    aTransparencyStops.reverseColorStops();
552
28
                }
553
2.10k
                else if (!maGradientProps.maGradientStops.empty())
554
2.10k
                {
555
                    // aGradient.SetGradientStyle(awt::GradientStyle_LINEAR);
556
2.10k
                    sal_Int32 nShadeAngle(maGradientProps.moShadeAngle.value_or( 0 ));
557
                    // Adjust for flips
558
2.10k
                    if ( bFlipH )
559
4
                        nShadeAngle = 180*60000 - nShadeAngle;
560
2.10k
                    if ( bFlipV )
561
4
                        nShadeAngle = -nShadeAngle;
562
2.10k
                    const sal_Int32 nDmlAngle = nShadeAngle + nShapeRotation;
563
564
                    // convert DrawingML angle (in 1/60000 degrees) to API angle (in 1/10 degrees)
565
2.10k
                    aGradient.SetAngle(Degree10(static_cast< sal_Int16 >( (8100 - (nDmlAngle / (PER_DEGREE / 10))) % 3600 )));
566
567
                    // If this is symmetrical, set it as an axial gradient for better UI/export.
568
                    // There were chart2 unit test failures when doing this to transparent gradients
569
                    // so just avoid that case.
570
2.10k
                    if (!bContainsTransparency)
571
2.09k
                        aGradient.tryToConvertToAxial();
572
2.10k
                }
573
574
2.12k
                if (awt::GradientStyle_RECT == aGradient.GetGradientStyle())
575
16
                {
576
                    // MCGR: tdf#155362: better support border
577
                    // CAUTION: Need to handle TransparencyStops if used
578
16
                    aGradient.tryToRecreateBorder(aTransparencyStops.empty() ? nullptr : &aTransparencyStops);
579
16
                }
580
581
                // push gradient or named gradient to property map
582
2.12k
                if (rPropMap.setProperty(ShapeProperty::FillGradient, model::gradient::createUnoGradient2(aGradient)))
583
2.12k
                {
584
2.12k
                    eFillStyle = FillStyle_GRADIENT;
585
2.12k
                }
586
587
                // push gradient transparency to property map if it exists
588
2.12k
                if (!aTransparencyStops.empty())
589
7
                {
590
7
                    aGradient.SetColorStops(aTransparencyStops);
591
7
                    rPropMap.setProperty(ShapeProperty::GradientTransparency, model::gradient::createUnoGradient2(aGradient));
592
7
                }
593
2.12k
            }
594
2.12k
        break;
595
596
23
        case XML_blipFill:
597
            // do not start complex graphic transformation if property is not supported...
598
23
            if (maBlipProps.mxFillGraphic.is() && rPropMap.supportsProperty(ShapeProperty::FillBitmap))
599
21
            {
600
21
                uno::Reference<graphic::XGraphic> xGraphic = lclCheckAndApplyDuotoneTransform(maBlipProps, maBlipProps.mxFillGraphic, rGraphicHelper, nPhClr);
601
                // TODO: "rotate with shape" is not possible with our current core
602
603
21
                if (xGraphic.is())
604
21
                {
605
21
                    if (maBlipProps.moColorEffect.value_or(XML_TOKEN_INVALID) == XML_grayscl)
606
0
                        xGraphic = lclGreysScaleGraphic(xGraphic);
607
608
21
                    if (rPropMap.supportsProperty(ShapeProperty::FillBitmapName) &&
609
21
                        rPropMap.setProperty(ShapeProperty::FillBitmapName, xGraphic))
610
21
                    {
611
21
                        eFillStyle = FillStyle_BITMAP;
612
21
                    }
613
0
                    else if (rPropMap.setProperty(ShapeProperty::FillBitmap, xGraphic))
614
0
                    {
615
0
                        eFillStyle = FillStyle_BITMAP;
616
0
                    }
617
21
                }
618
619
                // set other bitmap properties, if bitmap has been inserted into the map
620
21
                if( eFillStyle == FillStyle_BITMAP )
621
21
                {
622
                    // bitmap mode (single, repeat, stretch)
623
21
                    BitmapMode eBitmapMode = lclGetBitmapMode( maBlipProps.moBitmapMode.value_or( XML_TOKEN_INVALID ) );
624
625
                    // additional settings for repeated bitmap
626
21
                    if( eBitmapMode == BitmapMode_REPEAT )
627
21
                    {
628
                        // anchor position inside bitmap
629
21
                        RectanglePoint eRectPoint = lclGetRectanglePoint( maBlipProps.moTileAlign.value_or( XML_tl ) );
630
21
                        rPropMap.setProperty( ShapeProperty::FillBitmapRectanglePoint, eRectPoint );
631
632
21
                        awt::Size aOriginalSize = lclGetOriginalSize(rGraphicHelper, maBlipProps.mxFillGraphic);
633
21
                        if( (aOriginalSize.Width > 0) && (aOriginalSize.Height > 0) )
634
21
                        {
635
                            // size of one bitmap tile (given as 1/1000 percent of bitmap size), convert to 1/100 mm
636
21
                            double fScaleX = maBlipProps.moTileScaleX.value_or( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
637
21
                            sal_Int32 nFillBmpSizeX = getLimitedValue< sal_Int32, double >( aOriginalSize.Width * fScaleX, 1, SAL_MAX_INT32 );
638
21
                            rPropMap.setProperty( ShapeProperty::FillBitmapSizeX, nFillBmpSizeX );
639
21
                            double fScaleY = maBlipProps.moTileScaleY.value_or( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
640
21
                            sal_Int32 nFillBmpSizeY = getLimitedValue< sal_Int32, double >( aOriginalSize.Height * fScaleY, 1, SAL_MAX_INT32 );
641
21
                            rPropMap.setProperty( ShapeProperty::FillBitmapSizeY, nFillBmpSizeY );
642
643
21
                            awt::Size aBmpSize(nFillBmpSizeX, nFillBmpSizeY);
644
                            // offset of the first bitmap tile (given as EMUs), convert to percent
645
21
                            sal_Int16 nTileOffsetX = getDoubleIntervalValue< sal_Int16 >(std::round(maBlipProps.moTileOffsetX.value_or( 0 ) / 3.6 / aBmpSize.Width), 0, 100 );
646
21
                            rPropMap.setProperty( ShapeProperty::FillBitmapOffsetX, nTileOffsetX );
647
21
                            sal_Int16 nTileOffsetY = getDoubleIntervalValue< sal_Int16 >(std::round(maBlipProps.moTileOffsetY.value_or( 0 ) / 3.6 / aBmpSize.Height), 0, 100 );
648
21
                            rPropMap.setProperty( ShapeProperty::FillBitmapOffsetY, nTileOffsetY );
649
21
                        }
650
21
                    }
651
0
                    else if ( eBitmapMode == BitmapMode_STRETCH && maBlipProps.moFillRect.has_value() )
652
0
                    {
653
0
                        geometry::IntegerRectangle2D aFillRect( maBlipProps.moFillRect.value() );
654
0
                        awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
655
0
                        if ( aOriginalSize.Width && aOriginalSize.Height )
656
0
                        {
657
0
                            text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
658
0
                            if ( aFillRect.X1 )
659
0
                                aGraphCrop.Left = o3tl::convert(aFillRect.X1, aOriginalSize.Width, MAX_PERCENT);
660
0
                            if ( aFillRect.Y1 )
661
0
                                aGraphCrop.Top = o3tl::convert(aFillRect.Y1, aOriginalSize.Height, MAX_PERCENT);
662
0
                            if ( aFillRect.X2 )
663
0
                                aGraphCrop.Right = o3tl::convert(aFillRect.X2, aOriginalSize.Width, MAX_PERCENT);
664
0
                            if ( aFillRect.Y2 )
665
0
                                aGraphCrop.Bottom = o3tl::convert(aFillRect.Y2, aOriginalSize.Height, MAX_PERCENT);
666
667
0
                            bool bHasCropValues = aGraphCrop.Left != 0 || aGraphCrop.Right !=0 || aGraphCrop.Top != 0 || aGraphCrop.Bottom != 0;
668
                            // Negative GraphicCrop values mean "crop" here.
669
0
                            bool bNeedCrop = aGraphCrop.Left <= 0 && aGraphCrop.Right <= 0 && aGraphCrop.Top <= 0 && aGraphCrop.Bottom <= 0;
670
671
0
                            if (bHasCropValues)
672
0
                            {
673
0
                                if (bIsCustomShape && bNeedCrop)
674
0
                                {
675
                                    // Physically crop the image
676
                                    // In this case, don't set the PROP_GraphicCrop because that
677
                                    // would lead to applying the crop twice after roundtrip
678
0
                                    xGraphic = lclCropGraphic(xGraphic, CropQuotientsFromFillRect(aFillRect));
679
0
                                    if (rPropMap.supportsProperty(ShapeProperty::FillBitmapName))
680
0
                                        rPropMap.setProperty(ShapeProperty::FillBitmapName, xGraphic);
681
0
                                    else
682
0
                                        rPropMap.setProperty(ShapeProperty::FillBitmap, xGraphic);
683
0
                                }
684
0
                                else if ((aFillRect.X1 != 0 && aFillRect.X2 != 0
685
0
                                          && aFillRect.X1 != aFillRect.X2)
686
0
                                         || (aFillRect.Y1 != 0 && aFillRect.Y2 != 0
687
0
                                             && aFillRect.Y1 != aFillRect.Y2))
688
0
                                {
689
0
                                    rPropMap.setProperty(PROP_GraphicCrop, aGraphCrop);
690
0
                                }
691
0
                                else
692
0
                                {
693
0
                                    double nL = aFillRect.X1 / static_cast<double>(MAX_PERCENT);
694
0
                                    double nT = aFillRect.Y1 / static_cast<double>(MAX_PERCENT);
695
0
                                    double nR = aFillRect.X2 / static_cast<double>(MAX_PERCENT);
696
0
                                    double nB = aFillRect.Y2 / static_cast<double>(MAX_PERCENT);
697
698
0
                                    sal_Int32 nSizeX;
699
0
                                    if (nL || nR)
700
0
                                        nSizeX = rSize.Width * (1 - (nL + nR));
701
0
                                    else
702
0
                                        nSizeX = rSize.Width;
703
0
                                    rPropMap.setProperty(ShapeProperty::FillBitmapSizeX, nSizeX);
704
705
0
                                    sal_Int32 nSizeY;
706
0
                                    if (nT || nB)
707
0
                                        nSizeY = rSize.Height * (1 - (nT + nB));
708
0
                                    else
709
0
                                        nSizeY = rSize.Height;
710
0
                                    rPropMap.setProperty(ShapeProperty::FillBitmapSizeY, nSizeY);
711
712
0
                                    RectanglePoint eRectPoint;
713
0
                                    if (!aFillRect.X1 && aFillRect.X2)
714
0
                                    {
715
0
                                        if (!aFillRect.Y1 && aFillRect.Y2)
716
0
                                            eRectPoint = lclGetRectanglePoint(XML_tl);
717
0
                                        else if (aFillRect.Y1 && !aFillRect.Y2)
718
0
                                            eRectPoint = lclGetRectanglePoint(XML_bl);
719
0
                                        else
720
0
                                            eRectPoint = lclGetRectanglePoint(XML_l);
721
0
                                    }
722
0
                                    else if (aFillRect.X1 && !aFillRect.X2)
723
0
                                    {
724
0
                                        if (!aFillRect.Y1 && aFillRect.Y2)
725
0
                                            eRectPoint = lclGetRectanglePoint(XML_tr);
726
0
                                        else if (aFillRect.Y1 && !aFillRect.Y2)
727
0
                                            eRectPoint = lclGetRectanglePoint(XML_br);
728
0
                                        else
729
0
                                            eRectPoint = lclGetRectanglePoint(XML_r);
730
0
                                    }
731
0
                                    else
732
0
                                    {
733
0
                                        if (!aFillRect.Y1 && aFillRect.Y2)
734
0
                                            eRectPoint = lclGetRectanglePoint(XML_t);
735
0
                                        else if (aFillRect.Y1 && !aFillRect.Y2)
736
0
                                            eRectPoint = lclGetRectanglePoint(XML_b);
737
0
                                        else
738
0
                                            eRectPoint = lclGetRectanglePoint(XML_ctr);
739
0
                                    }
740
0
                                    rPropMap.setProperty(ShapeProperty::FillBitmapRectanglePoint, eRectPoint);
741
0
                                    eBitmapMode = BitmapMode_NO_REPEAT;
742
0
                                }
743
0
                            }
744
0
                        }
745
0
                    }
746
21
                    rPropMap.setProperty(ShapeProperty::FillBitmapMode, eBitmapMode);
747
21
                }
748
749
21
                if (maBlipProps.moAlphaModFix.has_value())
750
0
                    rPropMap.setProperty(ShapeProperty::FillTransparency, static_cast<sal_Int16>(100 - (maBlipProps.moAlphaModFix.value() / PER_PERCENT)));
751
21
            }
752
23
        break;
753
754
0
        case XML_pattFill:
755
0
        {
756
0
            if( rPropMap.supportsProperty( ShapeProperty::FillHatch ) )
757
0
            {
758
0
                Color aColor( maPatternProps.maPattFgColor );
759
0
                if( aColor.isUsed() && maPatternProps.moPattPreset.has_value() )
760
0
                {
761
0
                    eFillStyle = FillStyle_HATCH;
762
0
                    rPropMap.setProperty( ShapeProperty::FillHatch, createHatch( maPatternProps.moPattPreset.value(), aColor.getColor( rGraphicHelper, nPhClr ) ) );
763
0
                    if( aColor.hasTransparency() )
764
0
                        rPropMap.setProperty( ShapeProperty::FillTransparency, aColor.getTransparency() );
765
766
                    // Set background color for hatch
767
0
                    if(maPatternProps.maPattBgColor.isUsed())
768
0
                    {
769
0
                        aColor = maPatternProps.maPattBgColor;
770
0
                        rPropMap.setProperty( ShapeProperty::FillBackground, aColor.getTransparency() != 100 );
771
0
                        rPropMap.setProperty( ShapeProperty::FillColor, aColor.getColor( rGraphicHelper, nPhClr ) );
772
0
                    }
773
0
                }
774
0
                else if ( maPatternProps.maPattBgColor.isUsed() )
775
0
                {
776
0
                    aColor = maPatternProps.maPattBgColor;
777
0
                    rPropMap.setProperty( ShapeProperty::FillColor, aColor.getColor( rGraphicHelper, nPhClr ) );
778
0
                    if( aColor.hasTransparency() )
779
0
                        rPropMap.setProperty( ShapeProperty::FillTransparency, aColor.getTransparency() );
780
0
                    eFillStyle = FillStyle_SOLID;
781
0
                }
782
0
            }
783
0
        }
784
0
        break;
785
786
0
        case XML_grpFill:
787
            // todo
788
0
            eFillStyle = FillStyle_NONE;
789
0
        break;
790
81.9k
    }
791
792
    // set final fill style property
793
81.9k
    rPropMap.setProperty( ShapeProperty::FillStyle, eFillStyle );
794
81.9k
}
795
796
void GraphicProperties::pushToPropMap( PropertyMap& rPropMap, const GraphicHelper& rGraphicHelper, bool bFlipH, bool bFlipV) const
797
24.5k
{
798
24.5k
    sal_Int16 nBrightness = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moBrightness.value_or( 0 ) / PER_PERCENT, -100, 100 );
799
24.5k
    sal_Int16 nContrast = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moContrast.value_or( 0 ) / PER_PERCENT, -100, 100 );
800
24.5k
    ColorMode eColorMode = ColorMode_STANDARD;
801
802
24.5k
    switch( maBlipProps.moColorEffect.value_or( XML_TOKEN_INVALID ) )
803
24.5k
    {
804
45
        case XML_biLevel:   eColorMode = ColorMode_MONO;    break;
805
3
        case XML_grayscl:   eColorMode = ColorMode_GREYS;   break;
806
24.5k
    }
807
808
24.5k
    if (maBlipProps.mxFillGraphic.is())
809
2.40k
    {
810
        // created transformed graphic
811
2.40k
        uno::Reference<graphic::XGraphic> xGraphic = lclCheckAndApplyChangeColorTransform(maBlipProps, maBlipProps.mxFillGraphic, rGraphicHelper, API_RGB_TRANSPARENT);
812
2.40k
        xGraphic = lclCheckAndApplyDuotoneTransform(maBlipProps, xGraphic, rGraphicHelper, API_RGB_TRANSPARENT);
813
814
2.40k
        if( eColorMode == ColorMode_MONO )
815
27
        {
816
            // ColorMode_MONO is the same with MSO's biLevel with 50000 (50%) threshold,
817
            // when threshold isn't 50000 bake the effect instead.
818
27
            if( maBlipProps.moBiLevelThreshold != 50000 )
819
0
            {
820
0
                xGraphic = lclApplyBlackWhiteEffect(maBlipProps, xGraphic);
821
0
                eColorMode = ColorMode_STANDARD;
822
0
            }
823
27
        }
824
825
2.40k
        if (eColorMode == ColorMode_STANDARD && nBrightness == 70 && nContrast == -70)
826
2
        {
827
            // map MSO 'washout' to our Watermark colormode
828
2
            eColorMode = ColorMode_WATERMARK;
829
2
            nBrightness = 0;
830
2
            nContrast = 0;
831
2
        }
832
2.39k
        else if( nBrightness != 0 && nContrast != 0 )
833
0
        {
834
            // MSO uses a different algorithm for contrast+brightness, LO applies contrast before brightness,
835
            // while MSO apparently applies half of brightness before contrast and half after. So if only
836
            // contrast or brightness need to be altered, the result is the same, but if both are involved,
837
            // there's no way to map that, so just force a conversion of the image.
838
0
            xGraphic = applyBrightnessContrast( xGraphic, nBrightness, nContrast );
839
0
            nBrightness = 0;
840
0
            nContrast = 0;
841
0
        }
842
843
        // cropping
844
2.40k
        if ( maBlipProps.moClipRect.has_value() )
845
1.32k
        {
846
1.32k
            geometry::IntegerRectangle2D oClipRect( maBlipProps.moClipRect.value() );
847
1.32k
            awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
848
849
1.32k
            if (aOriginalSize.Width <= 0 || aOriginalSize.Height <= 0)
850
377
            {
851
                // VectorGraphic Objects need the correct object size for cropping
852
377
                Graphic aGraphic(xGraphic);
853
377
                if (aGraphic.getVectorGraphicData())
854
15
                {
855
15
                    Size aPrefSize = aGraphic.GetPrefSize();
856
15
                    aOriginalSize.Height = static_cast<sal_Int32>(aPrefSize.getHeight());
857
15
                    aOriginalSize.Width = static_cast<sal_Int32>(aPrefSize.getWidth());
858
15
                }
859
377
            }
860
861
1.32k
            if (aOriginalSize.Width > 0 && aOriginalSize.Height > 0)
862
959
            {
863
959
                text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
864
959
                if ( oClipRect.X1 )
865
0
                    aGraphCrop.Left = o3tl::convert(oClipRect.X1, aOriginalSize.Width, MAX_PERCENT);
866
959
                if ( oClipRect.Y1 )
867
0
                    aGraphCrop.Top = o3tl::convert(oClipRect.Y1, aOriginalSize.Height, MAX_PERCENT);
868
959
                if ( oClipRect.X2 )
869
1
                    aGraphCrop.Right = o3tl::convert(oClipRect.X2, aOriginalSize.Width, MAX_PERCENT);
870
959
                if ( oClipRect.Y2 )
871
2
                    aGraphCrop.Bottom = o3tl::convert(oClipRect.Y2, aOriginalSize.Height, MAX_PERCENT);
872
959
                rPropMap.setProperty(PROP_GraphicCrop, aGraphCrop);
873
874
959
                if(mbIsCustomShape)
875
0
                {
876
                    // Positive GraphicCrop values mean "crop" here.
877
0
                    if (aGraphCrop.Left > 0 || aGraphCrop.Right > 0 || aGraphCrop.Top > 0 || aGraphCrop.Bottom > 0)
878
0
                        xGraphic = lclCropGraphic(xGraphic, CropQuotientsFromSrcRect(oClipRect));
879
0
                }
880
959
            }
881
1.32k
        }
882
883
2.40k
        if(mbIsCustomShape)
884
0
        {
885
            // it is a cropped graphic.
886
0
            rPropMap.setProperty(PROP_FillStyle, FillStyle_BITMAP);
887
0
            rPropMap.setProperty(PROP_FillBitmapMode, BitmapMode_STRETCH);
888
889
            // It is a bitmap filled and rotated graphic.
890
            // When custom shape is rotated, bitmap has to be rotated too.
891
            // Only in extruded mode the bitmap is transformed together with the shape
892
0
            if(rPropMap.hasProperty(PROP_RotateAngle) && !mbIsExtruded)
893
0
            {
894
0
                tools::Long nAngle = rPropMap.getProperty(PROP_RotateAngle).get<tools::Long>();
895
0
                xGraphic = lclRotateGraphic(xGraphic, Degree10(nAngle/10) );
896
0
            }
897
898
            // We have no core feature that flips graphic in the shape.
899
            // Here we are applying flip property to bitmap directly.
900
0
            if((bFlipH || bFlipV) && !mbIsExtruded)
901
0
                xGraphic = lclMirrorGraphic(xGraphic, bFlipH, bFlipV );
902
903
0
            if(eColorMode == ColorMode_GREYS)
904
0
                xGraphic = lclGreysScaleGraphic( xGraphic );
905
906
0
            rPropMap.setProperty(PROP_FillBitmap, xGraphic);
907
0
        }
908
2.40k
        else
909
2.40k
            rPropMap.setProperty(PROP_Graphic, xGraphic);
910
911
912
2.40k
        if ( maBlipProps.moAlphaModFix.has_value() )
913
0
        {
914
0
            rPropMap.setProperty(
915
0
                mbIsCustomShape ? PROP_FillTransparence : PROP_Transparency,
916
0
                static_cast<sal_Int16>(100 - (maBlipProps.moAlphaModFix.value() / PER_PERCENT)));
917
0
        }
918
2.40k
    }
919
24.5k
    rPropMap.setProperty(PROP_GraphicColorMode, eColorMode);
920
921
    // brightness and contrast
922
24.5k
    if( nBrightness != 0 )
923
1
        rPropMap.setProperty(PROP_AdjustLuminance, nBrightness);
924
24.5k
    if( nContrast != 0 )
925
1
        rPropMap.setProperty(PROP_AdjustContrast, nContrast);
926
927
    // Media content
928
24.5k
    if (!m_sMediaPackageURL.isEmpty())
929
0
    {
930
0
        rPropMap.setProperty(PROP_MediaURL, m_sMediaPackageURL);
931
0
        if (m_xMediaStream.is())
932
0
            rPropMap.setProperty(PROP_PrivateStream, m_xMediaStream);
933
0
    }
934
    // Media Type
935
24.5k
    if (!m_sMediaMimeType.isEmpty())
936
0
    {
937
0
        rPropMap.setProperty(PROP_MediaMimeType, m_sMediaMimeType);
938
0
    }
939
24.5k
}
940
941
bool ArtisticEffectProperties::isEmpty() const
942
10.3M
{
943
10.3M
    return msName.isEmpty();
944
10.3M
}
945
946
css::beans::PropertyValue ArtisticEffectProperties::getEffect()
947
0
{
948
0
    css::beans::PropertyValue aRet;
949
0
    if( msName.isEmpty() )
950
0
        return aRet;
951
952
0
    css::uno::Sequence< css::beans::PropertyValue > aSeq( maAttribs.size() + 1 );
953
0
    auto pSeq = aSeq.getArray();
954
0
    sal_uInt32 i = 0;
955
0
    for (auto const& attrib : maAttribs)
956
0
    {
957
0
        pSeq[i].Name = attrib.first;
958
0
        pSeq[i].Value = attrib.second;
959
0
        i++;
960
0
    }
961
962
0
    if( mrOleObjectInfo.maEmbeddedData.hasElements() )
963
0
    {
964
0
        css::uno::Sequence< css::beans::PropertyValue > aGraphicSeq{
965
0
            comphelper::makePropertyValue(u"Id"_ustr, mrOleObjectInfo.maProgId),
966
0
            comphelper::makePropertyValue(u"Data"_ustr, mrOleObjectInfo.maEmbeddedData)
967
0
        };
968
969
0
        pSeq[i].Name = "OriginalGraphic";
970
0
        pSeq[i].Value <<= aGraphicSeq;
971
0
    }
972
973
0
    aRet.Name = msName;
974
0
    aRet.Value <<= aSeq;
975
976
0
    return aRet;
977
0
}
978
979
void ArtisticEffectProperties::assignUsed( const ArtisticEffectProperties& rSourceProps )
980
10.2M
{
981
10.2M
    if( !rSourceProps.isEmpty() )
982
0
    {
983
0
        msName = rSourceProps.msName;
984
0
        maAttribs = rSourceProps.maAttribs;
985
0
    }
986
10.2M
}
987
988
OUString ArtisticEffectProperties::getEffectString( sal_Int32 nToken )
989
0
{
990
0
    switch( nToken )
991
0
    {
992
        // effects
993
0
        case OOX_TOKEN( a14, artisticBlur ):                return u"artisticBlur"_ustr;
994
0
        case OOX_TOKEN( a14, artisticCement ):              return u"artisticCement"_ustr;
995
0
        case OOX_TOKEN( a14, artisticChalkSketch ):         return u"artisticChalkSketch"_ustr;
996
0
        case OOX_TOKEN( a14, artisticCrisscrossEtching ):   return u"artisticCrisscrossEtching"_ustr;
997
0
        case OOX_TOKEN( a14, artisticCutout ):              return u"artisticCutout"_ustr;
998
0
        case OOX_TOKEN( a14, artisticFilmGrain ):           return u"artisticFilmGrain"_ustr;
999
0
        case OOX_TOKEN( a14, artisticGlass ):               return u"artisticGlass"_ustr;
1000
0
        case OOX_TOKEN( a14, artisticGlowDiffused ):        return u"artisticGlowDiffused"_ustr;
1001
0
        case OOX_TOKEN( a14, artisticGlowEdges ):           return u"artisticGlowEdges"_ustr;
1002
0
        case OOX_TOKEN( a14, artisticLightScreen ):         return u"artisticLightScreen"_ustr;
1003
0
        case OOX_TOKEN( a14, artisticLineDrawing ):         return u"artisticLineDrawing"_ustr;
1004
0
        case OOX_TOKEN( a14, artisticMarker ):              return u"artisticMarker"_ustr;
1005
0
        case OOX_TOKEN( a14, artisticMosiaicBubbles ):      return u"artisticMosiaicBubbles"_ustr;
1006
0
        case OOX_TOKEN( a14, artisticPaintStrokes ):        return u"artisticPaintStrokes"_ustr;
1007
0
        case OOX_TOKEN( a14, artisticPaintBrush ):          return u"artisticPaintBrush"_ustr;
1008
0
        case OOX_TOKEN( a14, artisticPastelsSmooth ):       return u"artisticPastelsSmooth"_ustr;
1009
0
        case OOX_TOKEN( a14, artisticPencilGrayscale ):     return u"artisticPencilGrayscale"_ustr;
1010
0
        case OOX_TOKEN( a14, artisticPencilSketch ):        return u"artisticPencilSketch"_ustr;
1011
0
        case OOX_TOKEN( a14, artisticPhotocopy ):           return u"artisticPhotocopy"_ustr;
1012
0
        case OOX_TOKEN( a14, artisticPlasticWrap ):         return u"artisticPlasticWrap"_ustr;
1013
0
        case OOX_TOKEN( a14, artisticTexturizer ):          return u"artisticTexturizer"_ustr;
1014
0
        case OOX_TOKEN( a14, artisticWatercolorSponge ):    return u"artisticWatercolorSponge"_ustr;
1015
0
        case OOX_TOKEN( a14, brightnessContrast ):          return u"brightnessContrast"_ustr;
1016
0
        case OOX_TOKEN( a14, colorTemperature ):            return u"colorTemperature"_ustr;
1017
0
        case OOX_TOKEN( a14, saturation ):                  return u"saturation"_ustr;
1018
0
        case OOX_TOKEN( a14, sharpenSoften ):               return u"sharpenSoften"_ustr;
1019
1020
        // attributes
1021
0
        case XML_visible:           return u"visible"_ustr;
1022
0
        case XML_trans:             return u"trans"_ustr;
1023
0
        case XML_crackSpacing:      return u"crackSpacing"_ustr;
1024
0
        case XML_pressure:          return u"pressure"_ustr;
1025
0
        case XML_numberOfShades:    return u"numberOfShades"_ustr;
1026
0
        case XML_grainSize:         return u"grainSize"_ustr;
1027
0
        case XML_intensity:         return u"intensity"_ustr;
1028
0
        case XML_smoothness:        return u"smoothness"_ustr;
1029
0
        case XML_gridSize:          return u"gridSize"_ustr;
1030
0
        case XML_pencilSize:        return u"pencilSize"_ustr;
1031
0
        case XML_size:              return u"size"_ustr;
1032
0
        case XML_brushSize:         return u"brushSize"_ustr;
1033
0
        case XML_scaling:           return u"scaling"_ustr;
1034
0
        case XML_detail:            return u"detail"_ustr;
1035
0
        case XML_bright:            return u"bright"_ustr;
1036
0
        case XML_contrast:          return u"contrast"_ustr;
1037
0
        case XML_colorTemp:         return u"colorTemp"_ustr;
1038
0
        case XML_sat:               return u"sat"_ustr;
1039
0
        case XML_amount:            return u"amount"_ustr;
1040
0
    }
1041
0
    SAL_WARN( "oox.drawingml", "ArtisticEffectProperties::getEffectString: unexpected token " << nToken );
1042
0
    return OUString();
1043
0
}
1044
1045
constexpr auto constEffectTokenForEffectNameMap = frozen::make_unordered_map<std::u16string_view, sal_Int32>(
1046
{
1047
    // effects
1048
    { u"artisticBlur", XML_artisticBlur },
1049
    { u"artisticCement", XML_artisticCement },
1050
    { u"artisticChalkSketch", XML_artisticChalkSketch },
1051
    { u"artisticCrisscrossEtching", XML_artisticCrisscrossEtching },
1052
    { u"artisticCutout", XML_artisticCutout },
1053
    { u"artisticFilmGrain", XML_artisticFilmGrain },
1054
    { u"artisticGlass", XML_artisticGlass },
1055
    { u"artisticGlowDiffused", XML_artisticGlowDiffused },
1056
    { u"artisticGlowEdges", XML_artisticGlowEdges },
1057
    { u"artisticLightScreen", XML_artisticLightScreen },
1058
    { u"artisticLineDrawing", XML_artisticLineDrawing },
1059
    { u"artisticMarker", XML_artisticMarker },
1060
    { u"artisticMosiaicBubbles", XML_artisticMosiaicBubbles },
1061
    { u"artisticPaintStrokes", XML_artisticPaintStrokes },
1062
    { u"artisticPaintBrush", XML_artisticPaintBrush },
1063
    { u"artisticPastelsSmooth", XML_artisticPastelsSmooth },
1064
    { u"artisticPencilGrayscale", XML_artisticPencilGrayscale },
1065
    { u"artisticPencilSketch", XML_artisticPencilSketch },
1066
    { u"artisticPhotocopy", XML_artisticPhotocopy },
1067
    { u"artisticPlasticWrap", XML_artisticPlasticWrap },
1068
    { u"artisticTexturizer", XML_artisticTexturizer },
1069
    { u"artisticWatercolorSponge", XML_artisticWatercolorSponge },
1070
    { u"brightnessContrast", XML_brightnessContrast },
1071
    { u"colorTemperature", XML_colorTemperature },
1072
    { u"saturation", XML_saturation },
1073
    { u"sharpenSoften", XML_sharpenSoften },
1074
1075
    // attributes
1076
    { u"visible", XML_visible },
1077
    { u"trans", XML_trans },
1078
    { u"crackSpacing", XML_crackSpacing },
1079
    { u"pressure", XML_pressure },
1080
    { u"numberOfShades", XML_numberOfShades },
1081
    { u"grainSize", XML_grainSize },
1082
    { u"intensity", XML_intensity },
1083
    { u"smoothness", XML_smoothness },
1084
    { u"gridSize", XML_gridSize },
1085
    { u"pencilSize", XML_pencilSize },
1086
    { u"size", XML_size },
1087
    { u"brushSize", XML_brushSize },
1088
    { u"scaling", XML_scaling },
1089
    { u"detail", XML_detail },
1090
    { u"bright", XML_bright },
1091
    { u"contrast", XML_contrast },
1092
    { u"colorTemp", XML_colorTemp },
1093
    { u"sat", XML_sat },
1094
    { u"amount", XML_amount }
1095
});
1096
1097
sal_Int32 ArtisticEffectProperties::getEffectToken(const OUString& sName)
1098
0
{
1099
0
    auto const aIterator = constEffectTokenForEffectNameMap.find(sName);
1100
1101
0
    if (aIterator != constEffectTokenForEffectNameMap.end())
1102
0
        return aIterator->second;
1103
1104
0
    SAL_WARN( "oox.drawingml", "ArtisticEffectProperties::getEffectToken - unexpected token name: " << sName );
1105
0
    return XML_none;
1106
0
}
1107
1108
} // namespace oox
1109
1110
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */