Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/graphic/GraphicObject.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 <sal/config.h>
21
22
#include <algorithm>
23
24
#include <o3tl/string_view.hxx>
25
#include <osl/diagnose.h>
26
#include <tools/fract.hxx>
27
#include <tools/helpers.hxx>
28
#include <tools/mapunit.hxx>
29
#include <utility>
30
#include <vcl/animate/Animation.hxx>
31
#include <vcl/animate/AnimationFrame.hxx>
32
#include <vcl/svapp.hxx>
33
#include <vcl/metaact.hxx>
34
#include <vcl/GraphicObject.hxx>
35
#include <vcl/GraphicLoader.hxx>
36
#include <vcl/outdev.hxx>
37
#include <vcl/rendercontext/DrawModeFlags.hxx>
38
39
#include <com/sun/star/container/XNameContainer.hpp>
40
#include <com/sun/star/beans/XPropertySet.hpp>
41
#include <com/sun/star/graphic/XGraphic.hpp>
42
#include <memory>
43
44
45
using namespace css;
46
using com::sun::star::uno::Reference;
47
using com::sun::star::uno::XInterface;
48
using com::sun::star::uno::UNO_QUERY;
49
using com::sun::star::uno::Sequence;
50
using com::sun::star::container::XNameContainer;
51
using com::sun::star::beans::XPropertySet;
52
53
0
#define WATERMARK_LUM_OFFSET        50
54
0
#define WATERMARK_CON_OFFSET        -70
55
56
namespace vcl::graphic
57
{
58
59
void SearchForGraphics(uno::Reference<uno::XInterface> const & xInterface,
60
                       std::vector<uno::Reference<css::graphic::XGraphic>> & raGraphicList)
61
0
{
62
0
    uno::Reference<beans::XPropertySet> xPropertySet(xInterface, UNO_QUERY);
63
0
    if (xPropertySet.is())
64
0
    {
65
0
        if (xPropertySet->getPropertySetInfo()->hasPropertyByName(u"ImageURL"_ustr))
66
0
        {
67
0
            OUString sURL;
68
0
            xPropertySet->getPropertyValue(u"ImageURL"_ustr) >>= sURL;
69
0
            if (!sURL.isEmpty() && !GraphicObject::isGraphicObjectUniqueIdURL(sURL))
70
0
            {
71
0
                Graphic aGraphic = vcl::graphic::loadFromURL(sURL);
72
0
                if (!aGraphic.IsNone())
73
0
                {
74
0
                    raGraphicList.push_back(aGraphic.GetXGraphic());
75
0
                }
76
0
            }
77
0
        } else if (xPropertySet->getPropertySetInfo()->hasPropertyByName(u"Graphic"_ustr))
78
0
        {
79
0
            uno::Reference<css::graphic::XGraphic> xGraphic;
80
0
            xPropertySet->getPropertyValue(u"Graphic"_ustr) >>= xGraphic;
81
0
            if (xGraphic.is())
82
0
            {
83
0
                raGraphicList.push_back(xGraphic);
84
0
            }
85
0
        }
86
0
    }
87
0
    Reference<XNameContainer> xContainer(xInterface, UNO_QUERY);
88
0
    if (xContainer.is())
89
0
    {
90
0
        const css::uno::Sequence<OUString> aElementNames = xContainer->getElementNames();
91
0
        for (OUString const & rName : aElementNames)
92
0
        {
93
0
            uno::Reference<XInterface> xInnerInterface;
94
0
            xContainer->getByName(rName) >>= xInnerInterface;
95
0
            SearchForGraphics(xInnerInterface, raGraphicList);
96
0
        }
97
0
    }
98
0
}
99
100
} // end namespace vcl::graphic
101
102
namespace
103
{
104
105
bool lclDrawObj(OutputDevice& rOut, const Point& rPt, const Size& rSz,
106
                GraphicObject const & rObj, const GraphicAttr& rAttr)
107
0
{
108
0
    Point   aPt( rPt );
109
0
    Size    aSz( rSz );
110
0
    bool    bRet = false;
111
112
0
    if( ( rObj.GetType() == GraphicType::Bitmap ) || ( rObj.GetType() == GraphicType::GdiMetafile ) )
113
0
    {
114
        // simple output of transformed graphic
115
0
        const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) );
116
117
0
        if( aGraphic.IsSupportedGraphic() )
118
0
        {
119
0
            const Degree10 nRot10 = rAttr.GetRotation() % 3600_deg10;
120
121
0
            if( nRot10 )
122
0
            {
123
0
                tools::Polygon aPoly( tools::Rectangle( aPt, aSz ) );
124
125
0
                aPoly.Rotate( aPt, nRot10 );
126
0
                const tools::Rectangle aRotBoundRect( aPoly.GetBoundRect() );
127
0
                aPt = aRotBoundRect.TopLeft();
128
0
                aSz = aRotBoundRect.GetSize();
129
0
            }
130
131
0
            aGraphic.Draw(rOut, aPt, aSz);
132
0
        }
133
134
0
        bRet = true;
135
0
    }
136
137
0
    return bRet;
138
0
}
139
140
void lclImplAdjust( Bitmap& rBmp, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
141
0
{
142
0
    GraphicAttr aAttr( rAttr );
143
144
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
145
0
    {
146
0
        switch( aAttr.GetDrawMode() )
147
0
        {
148
0
            case GraphicDrawMode::Mono:
149
0
                rBmp.Convert( BmpConversion::N1BitThreshold );
150
0
            break;
151
152
0
            case GraphicDrawMode::Greys:
153
0
                rBmp.Convert( BmpConversion::N8BitGreys );
154
0
            break;
155
156
0
            case GraphicDrawMode::Watermark:
157
0
            {
158
0
                aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
159
0
                aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
160
0
            }
161
0
            break;
162
163
0
            default:
164
0
            break;
165
0
        }
166
0
    }
167
168
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
169
0
    {
170
0
        rBmp.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
171
0
                       aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
172
0
                       aAttr.GetGamma(), aAttr.IsInvert() );
173
0
    }
174
175
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
176
0
    {
177
0
        rBmp.Mirror( aAttr.GetMirrorFlags() );
178
0
    }
179
180
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
181
0
    {
182
0
        rBmp.Rotate( aAttr.GetRotation(), COL_TRANSPARENT );
183
0
    }
184
185
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
186
0
    {
187
0
        rBmp.AdjustTransparency(255 - aAttr.GetAlpha());
188
0
    }
189
0
}
190
191
void lclImplAdjust( GDIMetaFile& rMtf, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
192
0
{
193
0
    GraphicAttr aAttr( rAttr );
194
195
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
196
0
    {
197
0
        switch( aAttr.GetDrawMode() )
198
0
        {
199
0
            case GraphicDrawMode::Mono:
200
0
                rMtf.Convert( MtfConversion::N1BitThreshold );
201
0
            break;
202
203
0
            case GraphicDrawMode::Greys:
204
0
                rMtf.Convert( MtfConversion::N8BitGreys );
205
0
            break;
206
207
0
            case GraphicDrawMode::Watermark:
208
0
            {
209
0
                aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
210
0
                aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
211
0
            }
212
0
            break;
213
214
0
            default:
215
0
            break;
216
0
        }
217
0
    }
218
219
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
220
0
    {
221
0
        rMtf.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
222
0
                     aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
223
0
                     aAttr.GetGamma(), aAttr.IsInvert() );
224
0
    }
225
226
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
227
0
    {
228
0
        rMtf.Mirror( aAttr.GetMirrorFlags() );
229
0
    }
230
231
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
232
0
    {
233
0
        rMtf.Rotate( aAttr.GetRotation() );
234
0
    }
235
236
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
237
0
    {
238
0
        OSL_FAIL( "Missing implementation: Mtf-Transparency" );
239
0
    }
240
0
}
241
242
void lclImplAdjust( Animation& rAnimation, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
243
0
{
244
0
    GraphicAttr aAttr( rAttr );
245
246
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
247
0
    {
248
0
        switch( aAttr.GetDrawMode() )
249
0
        {
250
0
            case GraphicDrawMode::Mono:
251
0
                rAnimation.Convert( BmpConversion::N1BitThreshold );
252
0
            break;
253
254
0
            case GraphicDrawMode::Greys:
255
0
                rAnimation.Convert( BmpConversion::N8BitGreys );
256
0
            break;
257
258
0
            case GraphicDrawMode::Watermark:
259
0
            {
260
0
                aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
261
0
                aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
262
0
            }
263
0
            break;
264
265
0
            default:
266
0
            break;
267
0
        }
268
0
    }
269
270
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
271
0
    {
272
0
        rAnimation.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
273
0
                           aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
274
0
                           aAttr.GetGamma(), aAttr.IsInvert() );
275
0
    }
276
277
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
278
0
    {
279
0
        rAnimation.Mirror( aAttr.GetMirrorFlags() );
280
0
    }
281
282
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
283
0
    {
284
0
        OSL_FAIL( "Missing implementation: Animation-Rotation" );
285
0
    }
286
287
0
    if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
288
0
    {
289
0
        OSL_FAIL( "Missing implementation: Animation-Transparency" );
290
0
    }
291
0
}
292
293
} // end anonymous namespace
294
295
struct GrfSimpleCacheObj
296
{
297
    Graphic     maGraphic;
298
    GraphicAttr maAttr;
299
300
                GrfSimpleCacheObj( Graphic aGraphic, const GraphicAttr& rAttr ) :
301
0
                    maGraphic(std::move( aGraphic )), maAttr( rAttr ) {}
302
};
303
304
GraphicObject::GraphicObject()
305
216k
{
306
216k
}
307
308
GraphicObject::GraphicObject(Graphic aGraphic)
309
188k
    : maGraphic(std::move(aGraphic))
310
188k
{
311
188k
}
312
313
GraphicObject::GraphicObject(const GraphicObject& rGraphicObj)
314
213k
    : maGraphic(rGraphicObj.GetGraphic())
315
213k
    , maAttr(rGraphicObj.maAttr)
316
213k
    , maUserData(rGraphicObj.maUserData)
317
213k
{
318
213k
}
319
320
GraphicObject::~GraphicObject()
321
618k
{
322
618k
}
323
324
GraphicType GraphicObject::GetType() const
325
121k
{
326
121k
    return maGraphic.GetType();
327
121k
}
328
329
Size GraphicObject::GetPrefSize() const
330
0
{
331
0
    return maGraphic.GetPrefSize();
332
0
}
333
334
MapMode GraphicObject::GetPrefMapMode() const
335
0
{
336
0
    return maGraphic.GetPrefMapMode();
337
0
}
338
339
bool GraphicObject::IsTransparent() const
340
270
{
341
270
    return maGraphic.IsTransparent();
342
270
}
343
344
bool GraphicObject::IsAnimated() const
345
4.13k
{
346
4.13k
    return maGraphic.IsAnimated();
347
4.13k
}
348
349
bool GraphicObject::IsEPS() const
350
0
{
351
0
    return maGraphic.IsEPS();
352
0
}
353
354
bool GraphicObject::ImplGetCropParams(const OutputDevice& rOut, Point& rPt, Size& rSz, const GraphicAttr* pAttr,
355
                                      tools::PolyPolygon& rClipPolyPoly, bool& bRectClipRegion) const
356
0
{
357
0
    bool bRet = false;
358
359
0
    if( GetType() != GraphicType::NONE )
360
0
    {
361
0
        tools::Polygon aClipPoly( tools::Rectangle( rPt, rSz ) );
362
0
        const Degree10  nRot10 = pAttr->GetRotation() % 3600_deg10;
363
0
        const Point     aOldOrigin( rPt );
364
0
        const MapMode   aMap100( MapUnit::Map100thMM );
365
0
        Size            aSize100;
366
0
        tools::Long            nTotalWidth, nTotalHeight;
367
368
0
        if( nRot10 )
369
0
        {
370
0
            aClipPoly.Rotate( rPt, nRot10 );
371
0
            bRectClipRegion = false;
372
0
        }
373
0
        else
374
0
            bRectClipRegion = true;
375
376
0
        rClipPolyPoly = tools::PolyPolygon(aClipPoly);
377
378
0
        if (maGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
379
0
            aSize100 = Application::GetDefaultDevice()->PixelToLogic( maGraphic.GetPrefSize(), aMap100 );
380
0
        else
381
0
        {
382
0
            MapMode m(maGraphic.GetPrefMapMode());
383
0
            aSize100 = rOut.LogicToLogic( maGraphic.GetPrefSize(), &m, &aMap100 );
384
0
        }
385
386
0
        nTotalWidth = aSize100.Width() - pAttr->GetLeftCrop() - pAttr->GetRightCrop();
387
0
        nTotalHeight = aSize100.Height() - pAttr->GetTopCrop() - pAttr->GetBottomCrop();
388
389
0
        if( !aSize100.IsEmpty() && nTotalWidth > 0 && nTotalHeight > 0 )
390
0
        {
391
0
            double fScale = static_cast<double>(aSize100.Width()) / nTotalWidth;
392
0
            const tools::Long nNewLeft = basegfx::fround<tools::Long>( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Horizontal ) ? pAttr->GetRightCrop() : pAttr->GetLeftCrop() ) * -fScale );
393
0
            const tools::Long nNewRight = nNewLeft + basegfx::fround<tools::Long>( aSize100.Width() * fScale ) - 1;
394
395
0
            fScale = static_cast<double>(rSz.Width()) / aSize100.Width();
396
0
            rPt.AdjustX(basegfx::fround<tools::Long>(nNewLeft * fScale));
397
0
            rSz.setWidth(basegfx::fround<tools::Long>((nNewRight - nNewLeft + 1) * fScale));
398
399
0
            fScale = static_cast<double>(aSize100.Height()) / nTotalHeight;
400
0
            const tools::Long nNewTop = basegfx::fround<tools::Long>( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Vertical ) ? pAttr->GetBottomCrop() : pAttr->GetTopCrop() ) * -fScale );
401
0
            const tools::Long nNewBottom = nNewTop + basegfx::fround<tools::Long>( aSize100.Height() * fScale ) - 1;
402
403
0
            fScale = static_cast<double>(rSz.Height()) / aSize100.Height();
404
0
            rPt.AdjustY(basegfx::fround<tools::Long>(nNewTop * fScale));
405
0
            rSz.setHeight(basegfx::fround<tools::Long>((nNewBottom - nNewTop + 1) * fScale));
406
407
0
            if( nRot10 )
408
0
            {
409
0
                tools::Polygon aOriginPoly( 1 );
410
411
0
                aOriginPoly[ 0 ] = rPt;
412
0
                aOriginPoly.Rotate( aOldOrigin, nRot10 );
413
0
                rPt = aOriginPoly[ 0 ];
414
0
            }
415
416
0
            bRet = true;
417
0
        }
418
0
    }
419
420
0
    return bRet;
421
0
}
422
423
GraphicObject& GraphicObject::operator=( const GraphicObject& rGraphicObj )
424
0
{
425
0
    if( &rGraphicObj != this )
426
0
    {
427
0
        mxSimpleCache.reset();
428
0
        maGraphic = rGraphicObj.GetGraphic();
429
0
        maAttr = rGraphicObj.maAttr;
430
0
        maUserData = rGraphicObj.maUserData;
431
0
    }
432
433
0
    return *this;
434
0
}
435
436
bool GraphicObject::operator==( const GraphicObject& rGraphicObj ) const
437
78.5k
{
438
78.5k
    return rGraphicObj.maGraphic == maGraphic
439
36.8k
        && rGraphicObj.maAttr == maAttr;
440
78.5k
}
441
442
OString GraphicObject::GetUniqueID() const
443
0
{
444
0
    return GetGraphic().getUniqueID();
445
0
}
446
447
void GraphicObject::SetAttr( const GraphicAttr& rAttr )
448
72
{
449
72
    maAttr = rAttr;
450
451
72
    if (mxSimpleCache && (mxSimpleCache->maAttr != rAttr))
452
0
        mxSimpleCache.reset();
453
72
}
454
455
void GraphicObject::SetUserData()
456
9.60k
{
457
9.60k
    maUserData.clear();
458
9.60k
}
459
460
void GraphicObject::SetUserData( const OUString& rUserData )
461
0
{
462
0
    maUserData = rUserData;
463
0
}
464
465
bool GraphicObject::Draw(OutputDevice& rOut, const Point& rPt, const Size& rSz,
466
                         const GraphicAttr* pAttr) const
467
0
{
468
0
    GraphicAttr         aAttr( pAttr ? *pAttr : GetAttr() );
469
0
    Point               aPt( rPt );
470
0
    Size                aSz( rSz );
471
0
    const DrawModeFlags nOldDrawMode = rOut.GetDrawMode();
472
0
    bool                bCropped = aAttr.IsCropped();
473
0
    bool bRet;
474
475
0
    rOut.SetDrawMode(nOldDrawMode & ~DrawModeFlags( DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient ));
476
477
    // mirrored horizontally
478
0
    if( aSz.Width() < 0 )
479
0
    {
480
0
        aPt.AdjustX(aSz.Width() + 1 );
481
0
        aSz.setWidth( -aSz.Width() );
482
0
        aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Horizontal );
483
0
    }
484
485
    // mirrored vertically
486
0
    if( aSz.Height() < 0 )
487
0
    {
488
0
        aPt.AdjustY(aSz.Height() + 1 );
489
0
        aSz.setHeight( -aSz.Height() );
490
0
        aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Vertical );
491
0
    }
492
493
0
    if( bCropped )
494
0
    {
495
0
        tools::PolyPolygon aClipPolyPoly;
496
0
        bool        bRectClip;
497
0
        const bool  bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip);
498
499
0
        rOut.Push(vcl::PushFlags::CLIPREGION);
500
501
0
        if( bCrop )
502
0
        {
503
0
            if( bRectClip )
504
0
            {
505
                // #i29534# Store crop rect for later forwarding to
506
                // PDF writer
507
0
                tools::Rectangle aCropRect = aClipPolyPoly.GetBoundRect();
508
0
                rOut.IntersectClipRegion(aCropRect);
509
0
            }
510
0
            else
511
0
            {
512
0
                rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly));
513
0
            }
514
0
        }
515
0
    }
516
517
0
    bRet = lclDrawObj(rOut, aPt, aSz, *this, aAttr);
518
519
0
    if( bCropped )
520
0
        rOut.Pop();
521
522
0
    rOut.SetDrawMode( nOldDrawMode );
523
524
0
    return bRet;
525
0
}
526
527
void GraphicObject::DrawTiled(OutputDevice& rOut, const tools::Rectangle& rArea, const Size& rSize,
528
                              const Size& rOffset, int nTileCacheSize1D)
529
0
{
530
0
    if (rSize.IsEmpty())
531
0
        return;
532
533
0
    const MapMode   aOutMapMode(rOut.GetMapMode());
534
    // #106258# Clamp size to 1 for zero values. This is okay, since
535
    // logical size of zero is handled above already
536
0
    const Size      aOutTileSize( ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Width() ),
537
0
                                  ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Height() ) );
538
539
    //#i69780 clip final tile size to a sane max size
540
0
    while ((static_cast<sal_Int64>(rSize.Width()) * nTileCacheSize1D) > SAL_MAX_UINT16)
541
0
        nTileCacheSize1D /= 2;
542
0
    while ((static_cast<sal_Int64>(rSize.Height()) * nTileCacheSize1D) > SAL_MAX_UINT16)
543
0
        nTileCacheSize1D /= 2;
544
545
0
    ImplDrawTiled(rOut, rArea, aOutTileSize, rOffset, nullptr, nTileCacheSize1D);
546
0
}
547
548
bool GraphicObject::StartAnimation(OutputDevice& rOut, const Point& rPt, const Size& rSz,
549
                                   tools::Long nRendererId,
550
                                   OutputDevice* pFirstFrameOutDev)
551
0
{
552
0
    bool bRet = false;
553
554
0
    GetGraphic();
555
556
0
    const GraphicAttr aAttr( GetAttr() );
557
558
0
    if (IsAnimated())
559
0
    {
560
0
        Point   aPt( rPt );
561
0
        Size    aSz( rSz );
562
0
        bool    bCropped = aAttr.IsCropped();
563
564
0
        if( bCropped )
565
0
        {
566
0
            tools::PolyPolygon aClipPolyPoly;
567
0
            bool        bRectClip;
568
0
            const bool  bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip);
569
570
0
            rOut.Push(vcl::PushFlags::CLIPREGION);
571
572
0
            if( bCrop )
573
0
            {
574
0
                if( bRectClip )
575
0
                    rOut.IntersectClipRegion(aClipPolyPoly.GetBoundRect());
576
0
                else
577
0
                    rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly));
578
0
            }
579
0
        }
580
581
0
        if (!mxSimpleCache || (mxSimpleCache->maAttr != aAttr) || pFirstFrameOutDev)
582
0
        {
583
0
            mxSimpleCache.reset(new GrfSimpleCacheObj(GetTransformedGraphic(&aAttr), aAttr));
584
0
            mxSimpleCache->maGraphic.SetAnimationNotifyHdl(GetGraphic().GetAnimationNotifyHdl());
585
0
        }
586
587
0
        mxSimpleCache->maGraphic.StartAnimation(rOut, aPt, aSz, nRendererId, pFirstFrameOutDev);
588
589
0
        if( bCropped )
590
0
            rOut.Pop();
591
592
0
        bRet = true;
593
0
    }
594
0
    else
595
0
        bRet = Draw(rOut, rPt, rSz, &aAttr);
596
597
0
    return bRet;
598
0
}
599
600
void GraphicObject::StopAnimation( const OutputDevice* pOut, tools::Long nRendererId )
601
0
{
602
0
    if (mxSimpleCache)
603
0
        mxSimpleCache->maGraphic.StopAnimation(pOut, nRendererId);
604
0
}
605
606
const Graphic& GraphicObject::GetGraphic() const
607
442k
{
608
442k
    return maGraphic;
609
442k
}
610
611
void GraphicObject::SetGraphic( const Graphic& rGraphic)
612
210k
{
613
210k
    maGraphic = rGraphic;
614
210k
}
615
616
Graphic GraphicObject::GetTransformedGraphic( const Size& rDestSize, const MapMode& rDestMap, const GraphicAttr& rAttr ) const
617
0
{
618
    // #104550# Extracted from svx/source/svdraw/svdograf.cxx
619
0
    Graphic             aTransGraphic( GetGraphic() );
620
0
    const GraphicType   eType = GetType();
621
0
    const Size          aSrcSize( aTransGraphic.GetPrefSize() );
622
623
    // #104115# Convert the crop margins to graphic object mapmode
624
0
    const MapMode aMapGraph( aTransGraphic.GetPrefMapMode() );
625
0
    const MapMode aMap100( MapUnit::Map100thMM );
626
627
0
    Size aCropLeftTop;
628
0
    Size aCropRightBottom;
629
630
0
    if( GraphicType::GdiMetafile == eType )
631
0
    {
632
0
        GDIMetaFile aMtf( aTransGraphic.GetGDIMetaFile() );
633
634
0
        if (aMapGraph.GetMapUnit() == MapUnit::MapPixel)
635
0
        {
636
            // crops are in 1/100th mm -> to aMapGraph -> to MapUnit::MapPixel
637
0
            aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
638
0
                Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
639
0
                aMap100);
640
0
            aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
641
0
                Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
642
0
                aMap100);
643
0
        }
644
0
        else
645
0
        {
646
            // crops are in GraphicObject units -> to aMapGraph
647
0
            aCropLeftTop = OutputDevice::LogicToLogic(
648
0
                Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
649
0
                aMap100,
650
0
                aMapGraph);
651
0
            aCropRightBottom = OutputDevice::LogicToLogic(
652
0
                Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
653
0
                aMap100,
654
0
                aMapGraph);
655
0
        }
656
657
        // #104115# If the metafile is cropped, give it a special
658
        // treatment: clip against the remaining area, scale up such
659
        // that this area later fills the desired size, and move the
660
        // origin to the upper left edge of that area.
661
0
        if( rAttr.IsCropped() )
662
0
        {
663
0
            const MapMode aMtfMapMode( aMtf.GetPrefMapMode() );
664
665
0
            tools::Rectangle aClipRect( aMtfMapMode.GetOrigin().X() + aCropLeftTop.Width(),
666
0
                                 aMtfMapMode.GetOrigin().Y() + aCropLeftTop.Height(),
667
0
                                 aMtfMapMode.GetOrigin().X() + aSrcSize.Width() - aCropRightBottom.Width(),
668
0
                                 aMtfMapMode.GetOrigin().Y() + aSrcSize.Height() - aCropRightBottom.Height() );
669
670
            // #104115# To correctly crop rotated metafiles, clip by view rectangle
671
0
            aMtf.AddAction( new MetaISectRectClipRegionAction( aClipRect ), 0 );
672
673
            // #104115# To crop the metafile, scale larger than the output rectangle
674
0
            aMtf.Scale( static_cast<double>(rDestSize.Width()) / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()),
675
0
                        static_cast<double>(rDestSize.Height()) / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) );
676
677
            // #104115# Adapt the pref size by hand (scale changes it
678
            // proportionally, but we want it to be smaller than the
679
            // former size, to crop the excess out)
680
0
            aMtf.SetPrefSize( Size( static_cast<tools::Long>(static_cast<double>(rDestSize.Width()) *  (1.0 + (aCropLeftTop.Width() + aCropRightBottom.Width()) / aSrcSize.Width())  + .5),
681
0
                                    static_cast<tools::Long>(static_cast<double>(rDestSize.Height()) * (1.0 + (aCropLeftTop.Height() + aCropRightBottom.Height()) / aSrcSize.Height()) + .5) ) );
682
683
            // #104115# Adapt the origin of the new mapmode, such that it
684
            // is shifted to the place where the cropped output starts
685
0
            Point aNewOrigin( static_cast<tools::Long>(static_cast<double>(aMtfMapMode.GetOrigin().X()) + rDestSize.Width() * aCropLeftTop.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()) + .5),
686
0
                              static_cast<tools::Long>(static_cast<double>(aMtfMapMode.GetOrigin().Y()) + rDestSize.Height() * aCropLeftTop.Height() / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) + .5) );
687
0
            MapMode aNewMap( rDestMap );
688
0
            aNewMap.SetOrigin( OutputDevice::LogicToLogic(aNewOrigin, aMtfMapMode, rDestMap) );
689
0
            aMtf.SetPrefMapMode( aNewMap );
690
0
        }
691
0
        else
692
0
        {
693
0
            aMtf.Scale( double(rDestSize.Width()) / aSrcSize.Width(), double(rDestSize.Height()) / aSrcSize.Height() );
694
0
            aMtf.SetPrefMapMode( rDestMap );
695
0
        }
696
697
0
        aTransGraphic = aMtf;
698
0
    }
699
0
    else if( GraphicType::Bitmap == eType )
700
0
    {
701
0
        Bitmap aBitmap( aTransGraphic.GetBitmap() );
702
0
        tools::Rectangle aCropRect;
703
704
        // convert crops to pixel
705
0
        if(rAttr.IsCropped())
706
0
        {
707
0
            if (aMapGraph.GetMapUnit() == MapUnit::MapPixel)
708
0
            {
709
                // crops are in 1/100th mm -> to MapUnit::MapPixel
710
0
                aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
711
0
                    Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
712
0
                    aMap100);
713
0
                aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
714
0
                    Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
715
0
                    aMap100);
716
0
            }
717
0
            else
718
0
            {
719
                // crops are in GraphicObject units -> to MapUnit::MapPixel
720
0
                aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
721
0
                    Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
722
0
                    aMapGraph);
723
0
                aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
724
0
                    Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
725
0
                    aMapGraph);
726
0
            }
727
728
            // convert from prefmapmode to pixel
729
0
            Size aSrcSizePixel(
730
0
                Application::GetDefaultDevice()->LogicToPixel(
731
0
                    aSrcSize,
732
0
                    aMapGraph));
733
734
0
            if(rAttr.IsCropped()
735
0
                && (aSrcSizePixel.Width() != aBitmap.GetSizePixel().Width() || aSrcSizePixel.Height() != aBitmap.GetSizePixel().Height())
736
0
                && aSrcSizePixel.Width())
737
0
            {
738
                // the size in pixels calculated from Graphic's internal MapMode (aTransGraphic.GetPrefMapMode())
739
                // and its internal size (aTransGraphic.GetPrefSize()) is different from its real pixel size.
740
                // This can be interpreted as this values to be set wrong, but needs to be corrected since e.g.
741
                // existing cropping is calculated based on this logic values already.
742
                // aBitmapEx.Scale(aSrcSizePixel);
743
744
                // another possibility is to adapt the values created so far with a factor; this
745
                // will keep the original Bitmap untouched and thus quality will not change
746
                // caution: convert to double first, else pretty big errors may occur
747
0
                const double fFactorX(static_cast<double>(aBitmap.GetSizePixel().Width()) / aSrcSizePixel.Width());
748
0
                const double fFactorY(static_cast<double>(aBitmap.GetSizePixel().Height()) / aSrcSizePixel.Height());
749
750
0
                aCropLeftTop.setWidth( basegfx::fround<tools::Long>(aCropLeftTop.Width() * fFactorX) );
751
0
                aCropLeftTop.setHeight( basegfx::fround<tools::Long>(aCropLeftTop.Height() * fFactorY) );
752
0
                aCropRightBottom.setWidth( basegfx::fround<tools::Long>(aCropRightBottom.Width() * fFactorX) );
753
0
                aCropRightBottom.setHeight( basegfx::fround<tools::Long>(aCropRightBottom.Height() * fFactorY) );
754
755
0
                aSrcSizePixel = aBitmap.GetSizePixel();
756
0
            }
757
758
            // setup crop rectangle in pixel
759
0
            aCropRect = tools::Rectangle( aCropLeftTop.Width(), aCropLeftTop.Height(),
760
0
                                 aSrcSizePixel.Width() - aCropRightBottom.Width(),
761
0
                                 aSrcSizePixel.Height() - aCropRightBottom.Height() );
762
0
        }
763
764
        // #105641# Also crop animations
765
0
        if( aTransGraphic.IsAnimated() )
766
0
        {
767
0
            Animation aAnim( aTransGraphic.GetAnimation() );
768
769
0
            for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame )
770
0
            {
771
0
                AnimationFrame aAnimationFrame( aAnim.Get( nFrame ) );
772
773
0
                if( !aCropRect.Contains( tools::Rectangle(aAnimationFrame.maPositionPixel, aAnimationFrame.maSizePixel) ) )
774
0
                {
775
                    // setup actual cropping (relative to frame position)
776
0
                    tools::Rectangle aCropRectRel( aCropRect );
777
0
                    aCropRectRel.Move( -aAnimationFrame.maPositionPixel.X(),
778
0
                                       -aAnimationFrame.maPositionPixel.Y() );
779
780
                    // cropping affects this frame, apply it then
781
                    // do _not_ apply enlargement, this is done below
782
0
                    ImplTransformBitmap( aAnimationFrame.maBitmap, rAttr, Size(), Size(),
783
0
                                         aCropRectRel, rDestSize, false );
784
785
0
                    aAnim.Replace( aAnimationFrame, nFrame );
786
0
                }
787
                // else: bitmap completely within crop area,
788
                // i.e. nothing is cropped away
789
0
            }
790
791
            // now, apply enlargement (if any) through global animation size
792
0
            if( aCropLeftTop.Width() < 0 ||
793
0
                aCropLeftTop.Height() < 0 ||
794
0
                aCropRightBottom.Width() < 0 ||
795
0
                aCropRightBottom.Height() < 0 )
796
0
            {
797
0
                Size aNewSize( aAnim.GetDisplaySizePixel() );
798
0
                aNewSize.AdjustWidth(aCropRightBottom.Width() < 0 ? -aCropRightBottom.Width() : 0 );
799
0
                aNewSize.AdjustWidth(aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0 );
800
0
                aNewSize.AdjustHeight(aCropRightBottom.Height() < 0 ? -aCropRightBottom.Height() : 0 );
801
0
                aNewSize.AdjustHeight(aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );
802
0
                aAnim.SetDisplaySizePixel( aNewSize );
803
0
            }
804
805
            // if topleft has changed, we must move all frames to the
806
            // right and bottom, resp.
807
0
            if( aCropLeftTop.Width() < 0 ||
808
0
                aCropLeftTop.Height() < 0 )
809
0
            {
810
0
                Point aPosOffset( aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0,
811
0
                                  aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );
812
813
0
                for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame )
814
0
                {
815
0
                    AnimationFrame aAnimationFrame( aAnim.Get( nFrame ) );
816
817
0
                    aAnimationFrame.maPositionPixel += aPosOffset;
818
819
0
                    aAnim.Replace( aAnimationFrame, nFrame );
820
0
                }
821
0
            }
822
823
0
            aTransGraphic = aAnim;
824
0
        }
825
0
        else
826
0
        {
827
0
            ImplTransformBitmap( aBitmap, rAttr, aCropLeftTop, aCropRightBottom,
828
0
                                 aCropRect, rDestSize, true );
829
830
0
            aTransGraphic = aBitmap;
831
0
        }
832
833
0
        aTransGraphic.SetPrefSize( rDestSize );
834
0
        aTransGraphic.SetPrefMapMode( rDestMap );
835
0
    }
836
837
0
    GraphicObject aGrfObj( aTransGraphic );
838
0
    aTransGraphic = aGrfObj.GetTransformedGraphic( &rAttr );
839
840
0
    return aTransGraphic;
841
0
}
842
843
Graphic GraphicObject::GetTransformedGraphic( const GraphicAttr* pAttr ) const
844
0
{
845
0
    GetGraphic();
846
847
0
    Graphic     aGraphic;
848
0
    GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
849
850
0
    if (maGraphic.IsSupportedGraphic())
851
0
    {
852
0
        if( aAttr.IsSpecialDrawMode() || aAttr.IsAdjusted() || aAttr.IsMirrored() || aAttr.IsRotated() || aAttr.IsTransparent() )
853
0
        {
854
0
            if( GetType() == GraphicType::Bitmap )
855
0
            {
856
0
                if( IsAnimated() )
857
0
                {
858
0
                    Animation aAnimation( maGraphic.GetAnimation() );
859
0
                    lclImplAdjust( aAnimation, aAttr, GraphicAdjustmentFlags::ALL );
860
0
                    aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount());
861
0
                    aGraphic = aAnimation;
862
0
                }
863
0
                else
864
0
                {
865
0
                    Bitmap aBmp( maGraphic.GetBitmap() );
866
0
                    lclImplAdjust( aBmp, aAttr, GraphicAdjustmentFlags::ALL );
867
0
                    aGraphic = aBmp;
868
0
                }
869
0
            }
870
0
            else
871
0
            {
872
0
                GDIMetaFile aMtf( maGraphic.GetGDIMetaFile() );
873
0
                lclImplAdjust( aMtf, aAttr, GraphicAdjustmentFlags::ALL );
874
0
                aGraphic = aMtf;
875
0
            }
876
0
        }
877
0
        else
878
0
        {
879
0
            if( ( GetType() == GraphicType::Bitmap ) && IsAnimated() )
880
0
            {
881
0
                Animation aAnimation( maGraphic.GetAnimation() );
882
0
                aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount());
883
0
                aGraphic = aAnimation;
884
0
            }
885
0
            else
886
0
                aGraphic = maGraphic;
887
0
        }
888
0
    }
889
890
0
    return aGraphic;
891
0
}
892
893
bool GraphicObject::isGraphicObjectUniqueIdURL(std::u16string_view rURL)
894
0
{
895
0
    return o3tl::starts_with(rURL, u"vnd.sun.star.GraphicObject:");
896
0
}
897
898
// calculate scalings between real image size and logic object size. This
899
// is necessary since the crop values are relative to original bitmap size
900
basegfx::B2DVector GraphicObject::calculateCropScaling(
901
    double fWidth,
902
    double fHeight,
903
    double fLeftCrop,
904
    double fTopCrop,
905
    double fRightCrop,
906
    double fBottomCrop) const
907
0
{
908
0
    const MapMode aMapMode100thmm(MapUnit::Map100thMM);
909
0
    Size aBitmapSize(GetPrefSize());
910
0
    double fFactorX(1.0);
911
0
    double fFactorY(1.0);
912
913
0
    if(MapUnit::MapPixel == GetPrefMapMode().GetMapUnit())
914
0
    {
915
0
        aBitmapSize = Application::GetDefaultDevice()->PixelToLogic(aBitmapSize, aMapMode100thmm);
916
0
    }
917
0
    else
918
0
    {
919
0
        aBitmapSize = OutputDevice::LogicToLogic(aBitmapSize, GetPrefMapMode(), aMapMode100thmm);
920
0
    }
921
922
0
    const double fDivX(aBitmapSize.Width() - fLeftCrop - fRightCrop);
923
0
    const double fDivY(aBitmapSize.Height() - fTopCrop - fBottomCrop);
924
925
0
    if(!basegfx::fTools::equalZero(fDivX))
926
0
    {
927
0
        fFactorX = fabs(fWidth) / fDivX;
928
0
    }
929
930
0
    if(!basegfx::fTools::equalZero(fDivY))
931
0
    {
932
0
        fFactorY = fabs(fHeight) / fDivY;
933
0
    }
934
935
0
    return basegfx::B2DVector(fFactorX,fFactorY);
936
0
}
937
938
939
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */