Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/gdi/gdimtf.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 <cstdlib>
21
#include <memory>
22
#include <sal/log.hxx>
23
#include <osl/diagnose.h>
24
#include <comphelper/diagnose_ex.hxx>
25
#include <tools/helpers.hxx>
26
#include <tools/stream.hxx>
27
#include <tools/vcompat.hxx>
28
#include <tools/fract.hxx>
29
#include <vcl/alpha.hxx>
30
#include <vcl/BitmapPalette.hxx>
31
#include <vcl/metaact.hxx>
32
#include <vcl/outdev.hxx>
33
#include <vcl/window.hxx>
34
#include <vcl/virdev.hxx>
35
#include <vcl/svapp.hxx>
36
#include <vcl/gdimtf.hxx>
37
#include <vcl/graphictools.hxx>
38
#include <basegfx/polygon/b2dpolygon.hxx>
39
#include <vcl/canvastools.hxx>
40
#include <vcl/mtfxmldump.hxx>
41
42
#include <vcl/TypeSerializer.hxx>
43
44
#include <com/sun/star/beans/XFastPropertySet.hpp>
45
#include <com/sun/star/rendering/MtfRenderer.hpp>
46
#include <com/sun/star/rendering/XBitmapCanvas.hpp>
47
#include <com/sun/star/rendering/XCanvas.hpp>
48
#include <comphelper/processfactory.hxx>
49
50
using namespace com::sun::star;
51
52
namespace {
53
54
struct ImplColAdjustParam
55
{
56
    std::unique_ptr<sal_uInt8[]>  pMapR;
57
    std::unique_ptr<sal_uInt8[]>  pMapG;
58
    std::unique_ptr<sal_uInt8[]>  pMapB;
59
};
60
61
struct ImplBmpAdjustParam
62
{
63
    short   nLuminancePercent;
64
    short   nContrastPercent;
65
    short   nChannelRPercent;
66
    short   nChannelGPercent;
67
    short   nChannelBPercent;
68
    double  fGamma;
69
    bool    bInvert;
70
};
71
72
struct ImplColConvertParam
73
{
74
    MtfConversion   eConversion;
75
};
76
77
struct ImplBmpConvertParam
78
{
79
    BmpConversion   eConversion;
80
};
81
82
struct ImplColMonoParam
83
{
84
    Color aColor;
85
};
86
87
struct ImplBmpMonoParam
88
{
89
    Color aColor;
90
};
91
92
struct ImplColReplaceParam
93
{
94
    std::unique_ptr<sal_uLong[]>     pMinR;
95
    std::unique_ptr<sal_uLong[]>     pMaxR;
96
    std::unique_ptr<sal_uLong[]>     pMinG;
97
    std::unique_ptr<sal_uLong[]>     pMaxG;
98
    std::unique_ptr<sal_uLong[]>     pMinB;
99
    std::unique_ptr<sal_uLong[]>     pMaxB;
100
    const Color *                  pDstCols;
101
    sal_uLong                      nCount;
102
};
103
104
struct ImplBmpReplaceParam
105
{
106
    const Color*        pSrcCols;
107
    const Color*        pDstCols;
108
    sal_uLong           nCount;
109
};
110
111
}
112
113
GDIMetaFile::GDIMetaFile() :
114
1.38M
    m_nCurrentActionElement( 0 ),
115
1.38M
    m_aPrefSize   ( 1, 1 ),
116
1.38M
    m_pPrev       ( nullptr ),
117
1.38M
    m_pNext       ( nullptr ),
118
1.38M
    m_pOutDev     ( nullptr ),
119
1.38M
    m_bPause      ( false ),
120
1.38M
    m_bRecord     ( false ),
121
1.38M
    m_bUseCanvas  ( false ),
122
1.38M
    m_bSVG        ( false )
123
1.38M
{
124
1.38M
}
125
126
GDIMetaFile::GDIMetaFile( const GDIMetaFile& rMtf ) :
127
15.5k
    m_nCurrentActionElement( rMtf.m_nCurrentActionElement ),
128
15.5k
    m_aPrefMapMode    ( rMtf.m_aPrefMapMode ),
129
15.5k
    m_aPrefSize       ( rMtf.m_aPrefSize ),
130
15.5k
    m_pPrev           ( rMtf.m_pPrev ),
131
15.5k
    m_pNext           ( rMtf.m_pNext ),
132
15.5k
    m_pOutDev         ( nullptr ),
133
15.5k
    m_bPause          ( false ),
134
15.5k
    m_bRecord         ( false ),
135
15.5k
    m_bUseCanvas      ( rMtf.m_bUseCanvas ),
136
15.5k
    m_bSVG            ( rMtf.m_bSVG )
137
15.5k
{
138
1.17M
    for( size_t i = 0, n = rMtf.GetActionSize(); i < n; ++i )
139
1.16M
    {
140
1.16M
        m_aList.push_back( rMtf.GetAction( i ) );
141
1.16M
    }
142
143
15.5k
    if( rMtf.m_bRecord )
144
0
    {
145
0
        Record( rMtf.m_pOutDev );
146
147
0
        if ( rMtf.m_bPause )
148
0
            Pause( true );
149
0
    }
150
15.5k
}
151
152
GDIMetaFile::~GDIMetaFile()
153
1.40M
{
154
1.40M
    Clear();
155
1.40M
}
156
157
bool GDIMetaFile::HasTransparentActions() const
158
0
{
159
0
    MetaAction* pCurrAct;
160
161
    // watch for transparent drawing actions
162
0
    for(pCurrAct = const_cast<GDIMetaFile*>(this)->FirstAction();
163
0
        pCurrAct;
164
0
        pCurrAct = const_cast<GDIMetaFile*>(this)->NextAction())
165
0
    {
166
        // #i10613# determine if the action is transparency capable
167
168
        // #107169# Also examine metafiles with masked bitmaps in
169
        // detail. Further down, this is optimized in such a way
170
        // that there's no unnecessary painting of masked bitmaps
171
        // (which are _always_ subdivided into rectangular regions
172
        // of uniform opacity): if a masked bitmap is printed over
173
        // empty background, we convert to a plain bitmap with
174
        // white background.
175
0
        if (pCurrAct->IsTransparent())
176
0
            return true;
177
0
    }
178
179
0
    return false;
180
0
}
181
182
size_t GDIMetaFile::GetActionSize() const
183
282k
{
184
282k
    return m_aList.size();
185
282k
}
186
187
MetaAction* GDIMetaFile::GetAction( size_t nAction ) const
188
15.3M
{
189
15.3M
    return (nAction < m_aList.size()) ? m_aList[ nAction ].get() : nullptr;
190
15.3M
}
191
192
MetaAction* GDIMetaFile::FirstAction()
193
7.78k
{
194
7.78k
    m_nCurrentActionElement = 0;
195
7.78k
    return m_aList.empty() ? nullptr : m_aList[ 0 ].get();
196
7.78k
}
197
198
MetaAction* GDIMetaFile::NextAction()
199
8.46M
{
200
8.46M
    return ( m_nCurrentActionElement + 1 < m_aList.size() ) ? m_aList[ ++m_nCurrentActionElement ].get() : nullptr;
201
8.46M
}
202
203
void GDIMetaFile::ReplaceAction( rtl::Reference<MetaAction> pAction, size_t nAction )
204
0
{
205
0
    if ( nAction >= m_aList.size() )
206
0
    {
207
0
        return;
208
0
    }
209
0
    m_aList[nAction] = std::move(pAction);
210
0
}
211
212
GDIMetaFile& GDIMetaFile::operator=( const GDIMetaFile& rMtf )
213
100k
{
214
100k
    if( this != &rMtf )
215
100k
    {
216
100k
        Clear();
217
218
        // Increment RefCount of MetaActions
219
7.32M
        for( size_t i = 0, n = rMtf.GetActionSize(); i < n; ++i )
220
7.22M
        {
221
7.22M
            m_aList.push_back( rMtf.GetAction( i ) );
222
7.22M
        }
223
224
100k
        m_aPrefMapMode = rMtf.m_aPrefMapMode;
225
100k
        m_aPrefSize = rMtf.m_aPrefSize;
226
100k
        m_pPrev = rMtf.m_pPrev;
227
100k
        m_pNext = rMtf.m_pNext;
228
100k
        m_pOutDev = nullptr;
229
100k
        m_bPause = false;
230
100k
        m_bRecord = false;
231
100k
        m_bUseCanvas = rMtf.m_bUseCanvas;
232
100k
        m_bSVG = rMtf.m_bSVG;
233
234
100k
        if( rMtf.m_bRecord )
235
0
        {
236
0
            Record( rMtf.m_pOutDev );
237
238
0
            if( rMtf.m_bPause )
239
0
                Pause( true );
240
0
        }
241
100k
    }
242
243
100k
    return *this;
244
100k
}
245
246
bool GDIMetaFile::operator==( const GDIMetaFile& rMtf ) const
247
0
{
248
0
    const size_t    nObjCount = m_aList.size();
249
0
    bool        bRet = false;
250
251
0
    if( this == &rMtf )
252
0
        bRet = true;
253
0
    else if( rMtf.GetActionSize()  == nObjCount &&
254
0
             rMtf.GetPrefSize()    == m_aPrefSize &&
255
0
             rMtf.GetPrefMapMode() == m_aPrefMapMode )
256
0
    {
257
0
        bRet = true;
258
259
0
        for( size_t n = 0; n < nObjCount; n++ )
260
0
        {
261
0
            if( m_aList[ n ] != rMtf.GetAction( n ) )
262
0
            {
263
0
                bRet = false;
264
0
                break;
265
0
            }
266
0
        }
267
0
    }
268
269
0
    return bRet;
270
0
}
271
272
void GDIMetaFile::Clear()
273
1.50M
{
274
1.50M
    if( m_bRecord )
275
428
        Stop();
276
277
1.50M
    m_aList.clear();
278
1.50M
}
279
280
void GDIMetaFile::Linker( OutputDevice* pOut, bool bLink )
281
150k
{
282
150k
    if( bLink )
283
75.1k
    {
284
75.1k
        m_pNext = nullptr;
285
75.1k
        m_pPrev = pOut->GetConnectMetaFile();
286
75.1k
        pOut->SetConnectMetaFile( this );
287
288
75.1k
        if( m_pPrev )
289
0
            m_pPrev->m_pNext = this;
290
75.1k
    }
291
75.1k
    else
292
75.1k
    {
293
75.1k
        if( m_pNext )
294
0
        {
295
0
            m_pNext->m_pPrev = m_pPrev;
296
297
0
            if( m_pPrev )
298
0
                m_pPrev->m_pNext = m_pNext;
299
0
        }
300
75.1k
        else
301
75.1k
        {
302
75.1k
            if( m_pPrev )
303
0
                m_pPrev->m_pNext = nullptr;
304
305
75.1k
            pOut->SetConnectMetaFile( m_pPrev );
306
75.1k
        }
307
308
75.1k
        m_pPrev = nullptr;
309
75.1k
        m_pNext = nullptr;
310
75.1k
    }
311
150k
}
312
313
void GDIMetaFile::Record( OutputDevice* pOut )
314
75.1k
{
315
75.1k
    if( m_bRecord )
316
0
        Stop();
317
318
75.1k
    m_nCurrentActionElement = m_aList.empty() ? 0 : (m_aList.size() - 1);
319
75.1k
    m_pOutDev = pOut;
320
75.1k
    m_bRecord = true;
321
75.1k
    Linker( pOut, true );
322
75.1k
}
323
324
void GDIMetaFile::Play( GDIMetaFile& rMtf )
325
26
{
326
26
    if (m_bRecord || rMtf.m_bRecord)
327
0
        return;
328
329
26
    MetaAction* pAction = GetCurAction();
330
26
    const size_t nObjCount = m_aList.size();
331
332
26
    rMtf.UseCanvas( rMtf.GetUseCanvas() || m_bUseCanvas );
333
26
    rMtf.setSVG( rMtf.getSVG() || m_bSVG );
334
335
1.54k
    for( size_t nCurPos = m_nCurrentActionElement; nCurPos < nObjCount; nCurPos++ )
336
1.51k
    {
337
1.51k
        if( pAction )
338
1.51k
        {
339
1.51k
            rMtf.AddAction( pAction );
340
1.51k
        }
341
342
1.51k
        pAction = NextAction();
343
1.51k
    }
344
26
}
345
346
void GDIMetaFile::Play(OutputDevice& rOut, size_t nPos)
347
11.3k
{
348
11.3k
    if( m_bRecord )
349
0
        return;
350
351
11.3k
    MetaAction* pAction = GetCurAction();
352
11.3k
    const size_t nObjCount = m_aList.size();
353
11.3k
    size_t  nSyncCount = rOut.GetSyncCount();
354
355
11.3k
    if( nPos > nObjCount )
356
11.3k
        nPos = nObjCount;
357
358
    // #i23407# Set backwards-compatible text language and layout mode
359
    // This is necessary, since old metafiles don't even know of these
360
    // recent add-ons. Newer metafiles must of course explicitly set
361
    // those states.
362
11.3k
    auto popIt = rOut.ScopedPush(vcl::PushFlags::TEXTLAYOUTMODE | vcl::PushFlags::TEXTLANGUAGE);
363
11.3k
    rOut.SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
364
11.3k
    rOut.SetDigitLanguage(LANGUAGE_SYSTEM);
365
366
11.3k
    SAL_INFO( "vcl.gdi", "GDIMetaFile::Play on device of size: " << rOut.GetOutputSizePixel().Width() << " " << rOut.GetOutputSizePixel().Height());
367
368
11.3k
    if (!ImplPlayWithRenderer(rOut, Point(0,0), rOut.GetOutputSize())) {
369
11.3k
        size_t  i  = 0;
370
2.40M
        for( size_t nCurPos = m_nCurrentActionElement; nCurPos < nPos; nCurPos++ )
371
2.39M
        {
372
2.39M
            if( pAction )
373
2.39M
            {
374
2.39M
                pAction->Execute(&rOut);
375
376
                // flush output from time to time
377
2.39M
                if( i++ > nSyncCount )
378
0
                {
379
0
                    rOut.Flush();
380
0
                    i = 0;
381
0
                }
382
2.39M
            }
383
384
2.39M
            pAction = NextAction();
385
2.39M
        }
386
11.3k
    }
387
11.3k
}
388
389
bool GDIMetaFile::ImplPlayWithRenderer(OutputDevice& rOut, const Point& rPos, Size rLogicDestSize)
390
12.1k
{
391
12.1k
    if (!m_bUseCanvas)
392
12.1k
        return false;
393
394
4
    Size rDestSize(rOut.LogicToPixel(rLogicDestSize));
395
396
4
    const vcl::Window* win = rOut.GetOwnerWindow();
397
398
4
    if (!win)
399
4
        win = Application::GetActiveTopWindow();
400
4
    if (!win)
401
4
        win = Application::GetFirstTopLevelWindow();
402
403
4
    if (!win)
404
0
        return false;
405
406
4
    try
407
4
    {
408
4
        uno::Reference<rendering::XCanvas> xCanvas = win->GetOutDev()->GetCanvas ();
409
410
4
        if (!xCanvas.is())
411
4
            return false;
412
413
0
        Size aSize (rDestSize.Width () + 1, rDestSize.Height () + 1);
414
0
        uno::Reference<rendering::XBitmap> xBitmap = xCanvas->getDevice ()->createCompatibleAlphaBitmap (vcl::unotools::integerSize2DFromSize( aSize));
415
0
        if( xBitmap.is () )
416
0
        {
417
0
            uno::Reference< rendering::XBitmapCanvas > xBitmapCanvas( xBitmap, uno::UNO_QUERY );
418
0
            if( xBitmapCanvas.is() )
419
0
            {
420
0
                const uno::Reference< uno::XComponentContext >& xContext = comphelper::getProcessComponentContext();
421
0
                uno::Reference< rendering::XMtfRenderer > xMtfRenderer = rendering::MtfRenderer::createWithBitmapCanvas( xContext, xBitmapCanvas );
422
423
0
                xBitmapCanvas->clear();
424
0
                uno::Reference< beans::XFastPropertySet > xMtfFastPropertySet( xMtfRenderer, uno::UNO_QUERY );
425
0
                if( xMtfFastPropertySet.is() )
426
                    // set this metafile to the renderer to
427
                    // speedup things (instead of copying data to
428
                    // sequence of bytes passed to renderer)
429
0
                    xMtfFastPropertySet->setFastPropertyValue( 0, uno::Any( reinterpret_cast<sal_Int64>( this ) ) );
430
431
0
                xMtfRenderer->draw( rDestSize.Width(), rDestSize.Height() );
432
433
0
                Bitmap aBitmap;
434
0
                if( aBitmap.Create( xBitmapCanvas, aSize ) )
435
0
                {
436
0
                    if (rOut.GetMapMode().GetMapUnit() == MapUnit::MapPixel)
437
0
                        rOut.DrawBitmap( rPos, aBitmap );
438
0
                    else
439
0
                        rOut.DrawBitmap( rPos, rLogicDestSize, aBitmap );
440
0
                    return true;
441
0
                }
442
0
            }
443
0
        }
444
0
    }
445
4
    catch (const uno::RuntimeException& )
446
4
    {
447
0
        throw; // runtime errors are fatal
448
0
    }
449
4
    catch (const uno::Exception&)
450
4
    {
451
        // ignore errors, no way of reporting them here
452
0
        TOOLS_WARN_EXCEPTION("vcl.gdi", "GDIMetaFile::ImplPlayWithRenderer");
453
0
    }
454
455
0
    return false;
456
4
}
457
458
void GDIMetaFile::Play(OutputDevice& rOut, const Point& rPos,
459
                       const Size& rSize)
460
978
{
461
978
    MapMode aDrawMap( GetPrefMapMode() );
462
978
    Size    aDestSize(rOut.LogicToPixel(rSize));
463
464
978
    if (aDestSize.Width() <= 0 || aDestSize.Height() <= 0)
465
96
        return;
466
467
882
    if (aDestSize.Width() > std::numeric_limits<sal_Int32>::max() ||
468
861
        aDestSize.Height() > std::numeric_limits<sal_Int32>::max())
469
40
        return;
470
471
842
    GDIMetaFile* pMtf = rOut.GetConnectMetaFile();
472
473
842
    if (ImplPlayWithRenderer(rOut, rPos, rSize))
474
0
        return;
475
476
842
    Size aTmpPrefSize(rOut.LogicToPixel(GetPrefSize(), aDrawMap));
477
478
842
    if( !aTmpPrefSize.Width() )
479
436
        aTmpPrefSize.setWidth( aDestSize.Width() );
480
481
842
    if( !aTmpPrefSize.Height() )
482
285
        aTmpPrefSize.setHeight( aDestSize.Height() );
483
484
842
    Fraction aScaleX( aDestSize.Width(), aTmpPrefSize.Width() );
485
842
    Fraction aScaleY( aDestSize.Height(), aTmpPrefSize.Height() );
486
487
842
    aScaleX *= aDrawMap.GetScaleX();
488
842
    aScaleY *= aDrawMap.GetScaleY();
489
    // try reducing inaccurary first and abandon if the scaling
490
    // still cannot be achieved
491
842
    if (TooLargeScaleForMapMode(aScaleX, rOut.GetDPIX()))
492
208
        aScaleX.ReduceInaccurate(10);
493
842
    if (TooLargeScaleForMapMode(aScaleY, rOut.GetDPIY()))
494
327
        aScaleY.ReduceInaccurate(10);
495
842
    if (TooLargeScaleForMapMode(aScaleX, rOut.GetDPIX()) ||
496
838
        TooLargeScaleForMapMode(aScaleY, rOut.GetDPIY()))
497
14
    {
498
14
        SAL_WARN("vcl", "GDIMetaFile Scaling is too high");
499
14
        return;
500
14
    }
501
502
828
    aDrawMap.SetScaleX(aScaleX);
503
828
    aDrawMap.SetScaleY(aScaleY);
504
505
    // #i47260# Convert logical output position to offset within
506
    // the metafile's mapmode. Therefore, disable pixel offset on
507
    // outdev, it's inverse mnOutOffLogicX/Y is calculated for a
508
    // different mapmode (the one currently set on rOut, that is)
509
    // - thus, aDrawMap's origin would generally be wrong. And
510
    // even _if_ aDrawMap is similar to pOutDev's current mapmode,
511
    // it's _still_ undesirable to have pixel offset unequal zero,
512
    // because one would still get round-off errors (the
513
    // round-trip error for LogicToPixel( PixelToLogic() ) was the
514
    // reason for having pixel offset in the first place).
515
828
    const Size aOldOffset(rOut.GetPixelOffset());
516
828
    const Size aEmptySize;
517
828
    rOut.SetPixelOffset(aEmptySize);
518
828
    aDrawMap.SetOrigin(rOut.PixelToLogic(rOut.LogicToPixel(rPos), aDrawMap));
519
828
    rOut.SetPixelOffset(aOldOffset);
520
521
828
    auto popIt = rOut.ScopedPush();
522
523
828
    bool bIsRecord = (pMtf && pMtf->IsRecord());
524
828
    rOut.SetMetafileMapMode(aDrawMap, bIsRecord);
525
526
    // #i23407# Set backwards-compatible text language and layout mode
527
    // This is necessary, since old metafiles don't even know of these
528
    // recent add-ons. Newer metafiles must of course explicitly set
529
    // those states.
530
828
    rOut.SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
531
828
    rOut.SetDigitLanguage(LANGUAGE_SYSTEM);
532
533
828
    Play(rOut);
534
828
}
535
536
void GDIMetaFile::Pause( bool _bPause )
537
0
{
538
0
    if( !m_bRecord )
539
0
        return;
540
541
0
    if( _bPause )
542
0
    {
543
0
        if( !m_bPause )
544
0
            Linker( m_pOutDev, false );
545
0
    }
546
0
    else
547
0
    {
548
0
        if( m_bPause )
549
0
            Linker( m_pOutDev, true );
550
0
    }
551
552
0
    m_bPause = _bPause;
553
0
}
554
555
void GDIMetaFile::Stop()
556
96.6k
{
557
96.6k
    if( m_bRecord )
558
75.1k
    {
559
75.1k
        m_bRecord = false;
560
561
75.1k
        if( !m_bPause )
562
75.1k
            Linker( m_pOutDev, false );
563
0
        else
564
0
            m_bPause = false;
565
75.1k
    }
566
96.6k
}
567
568
void GDIMetaFile::WindStart()
569
81.6k
{
570
81.6k
    if( !m_bRecord )
571
81.6k
        m_nCurrentActionElement = 0;
572
81.6k
}
573
574
void GDIMetaFile::WindPrev()
575
0
{
576
0
    if( !m_bRecord )
577
0
        if ( m_nCurrentActionElement > 0 )
578
0
            --m_nCurrentActionElement;
579
0
}
580
581
void GDIMetaFile::AddAction(const rtl::Reference<MetaAction>& pAction)
582
16.6M
{
583
16.6M
    m_aList.push_back( pAction );
584
585
16.6M
    if( m_pPrev )
586
0
    {
587
0
        m_pPrev->AddAction( pAction );
588
0
    }
589
16.6M
}
590
591
void GDIMetaFile::AddAction(const rtl::Reference<MetaAction>& pAction, size_t nPos)
592
0
{
593
0
    if ( nPos < m_aList.size() )
594
0
    {
595
0
        m_aList.insert( m_aList.begin() + nPos, pAction );
596
0
    }
597
0
    else
598
0
    {
599
0
        m_aList.push_back( pAction );
600
0
    }
601
602
0
    if( m_pPrev )
603
0
    {
604
0
        m_pPrev->AddAction( pAction, nPos );
605
0
    }
606
0
}
607
608
void GDIMetaFile::push_back(const rtl::Reference<MetaAction>& pAction)
609
0
{
610
0
    m_aList.push_back( pAction );
611
0
}
612
613
void GDIMetaFile::Mirror( BmpMirrorFlags nMirrorFlags )
614
0
{
615
0
    const Size  aOldPrefSize( GetPrefSize() );
616
0
    tools::Long        nMoveX, nMoveY;
617
0
    double      fScaleX, fScaleY;
618
619
0
    if( nMirrorFlags & BmpMirrorFlags::Horizontal )
620
0
    {
621
0
        nMoveX = std::abs( aOldPrefSize.Width() ) - 1;
622
0
        fScaleX = -1.0;
623
0
    }
624
0
    else
625
0
    {
626
0
        nMoveX = 0;
627
0
        fScaleX = 1.0;
628
0
    }
629
630
0
    if( nMirrorFlags & BmpMirrorFlags::Vertical )
631
0
    {
632
0
        nMoveY = std::abs( aOldPrefSize.Height() ) - 1;
633
0
        fScaleY = -1.0;
634
0
    }
635
0
    else
636
0
    {
637
0
        nMoveY = 0;
638
0
        fScaleY = 1.0;
639
0
    }
640
641
0
    if( ( fScaleX != 1.0 ) || ( fScaleY != 1.0 ) )
642
0
    {
643
0
        Scale( fScaleX, fScaleY );
644
0
        Move( nMoveX, nMoveY );
645
0
        SetPrefSize( aOldPrefSize );
646
0
    }
647
0
}
648
649
void GDIMetaFile::Move( tools::Long nX, tools::Long nY )
650
7.52k
{
651
7.52k
    const Size      aBaseOffset( nX, nY );
652
7.52k
    Size            aOffset( aBaseOffset );
653
7.52k
    ScopedVclPtrInstance< VirtualDevice > aMapVDev;
654
655
7.52k
    aMapVDev->EnableOutput( false );
656
7.52k
    aMapVDev->SetMapMode( GetPrefMapMode() );
657
658
6.04M
    for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
659
6.04M
    {
660
6.04M
        const MetaActionType nType = pAct->GetType();
661
6.04M
        MetaAction* pModAct;
662
663
6.04M
        if( pAct->GetRefCount() > 1 )
664
0
        {
665
0
            m_aList[ m_nCurrentActionElement ] = pAct->Clone();
666
0
            pModAct = m_aList[ m_nCurrentActionElement ].get();
667
0
        }
668
6.04M
        else
669
6.04M
            pModAct = pAct;
670
671
6.04M
        if( ( MetaActionType::MAPMODE == nType ) ||
672
6.04M
            ( MetaActionType::PUSH == nType ) ||
673
5.01M
            ( MetaActionType::POP == nType ) )
674
2.05M
        {
675
2.05M
            pModAct->Execute( aMapVDev.get() );
676
2.05M
            aOffset = OutputDevice::LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev->GetMapMode() );
677
2.05M
        }
678
679
6.04M
        pModAct->Move( aOffset.Width(), aOffset.Height() );
680
6.04M
    }
681
7.52k
}
682
683
void GDIMetaFile::Move( tools::Long nX, tools::Long nY, tools::Long nDPIX, tools::Long nDPIY )
684
0
{
685
0
    const Size      aBaseOffset( nX, nY );
686
0
    Size            aOffset( aBaseOffset );
687
0
    ScopedVclPtrInstance< VirtualDevice > aMapVDev;
688
689
0
    aMapVDev->EnableOutput( false );
690
0
    aMapVDev->SetReferenceDevice( nDPIX, nDPIY );
691
0
    aMapVDev->SetMapMode( GetPrefMapMode() );
692
693
0
    for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
694
0
    {
695
0
        const MetaActionType nType = pAct->GetType();
696
0
        MetaAction* pModAct;
697
698
0
        if( pAct->GetRefCount() > 1 )
699
0
        {
700
0
            m_aList[ m_nCurrentActionElement ] = pAct->Clone();
701
0
            pModAct = m_aList[ m_nCurrentActionElement ].get();
702
0
        }
703
0
        else
704
0
            pModAct = pAct;
705
706
0
        if( ( MetaActionType::MAPMODE == nType ) ||
707
0
            ( MetaActionType::PUSH == nType ) ||
708
0
            ( MetaActionType::POP == nType ) )
709
0
        {
710
0
            pModAct->Execute( aMapVDev.get() );
711
0
            if( aMapVDev->GetMapMode().GetMapUnit() == MapUnit::MapPixel )
712
0
            {
713
0
                aOffset = aMapVDev->LogicToPixel( aBaseOffset, GetPrefMapMode() );
714
0
                MapMode aMap( aMapVDev->GetMapMode() );
715
0
                aOffset.setWidth( static_cast<tools::Long>(aOffset.Width() * static_cast<double>(aMap.GetScaleX())) );
716
0
                aOffset.setHeight( static_cast<tools::Long>(aOffset.Height() * static_cast<double>(aMap.GetScaleY())) );
717
0
            }
718
0
            else
719
0
                aOffset = OutputDevice::LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev->GetMapMode() );
720
0
        }
721
722
0
        pModAct->Move( aOffset.Width(), aOffset.Height() );
723
0
    }
724
0
}
725
726
void GDIMetaFile::ScaleActions(double const fScaleX, double const fScaleY)
727
132
{
728
13.3k
    for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
729
13.1k
    {
730
13.1k
        MetaAction* pModAct;
731
732
13.1k
        if( pAct->GetRefCount() > 1 )
733
13.1k
        {
734
13.1k
            m_aList[ m_nCurrentActionElement ] = pAct->Clone();
735
13.1k
            pModAct = m_aList[ m_nCurrentActionElement ].get();
736
13.1k
        }
737
0
        else
738
0
            pModAct = pAct;
739
740
13.1k
        pModAct->Scale( fScaleX, fScaleY );
741
13.1k
    }
742
132
}
743
744
void GDIMetaFile::Scale( double fScaleX, double fScaleY )
745
132
{
746
132
    ScaleActions(fScaleX, fScaleY);
747
748
132
    m_aPrefSize.setWidth(basegfx::fround<tools::Long>(m_aPrefSize.Width() * fScaleX));
749
132
    m_aPrefSize.setHeight(basegfx::fround<tools::Long>(m_aPrefSize.Height() * fScaleY));
750
132
}
751
752
void GDIMetaFile::Scale( const Fraction& rScaleX, const Fraction& rScaleY )
753
132
{
754
132
    Scale( static_cast<double>(rScaleX), static_cast<double>(rScaleY) );
755
132
}
756
757
void GDIMetaFile::Clip( const tools::Rectangle& i_rClipRect )
758
0
{
759
0
    tools::Rectangle aCurRect( i_rClipRect );
760
0
    ScopedVclPtrInstance< VirtualDevice > aMapVDev;
761
762
0
    aMapVDev->EnableOutput( false );
763
0
    aMapVDev->SetMapMode( GetPrefMapMode() );
764
765
0
    for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
766
0
    {
767
0
        const MetaActionType nType = pAct->GetType();
768
769
0
        if( ( MetaActionType::MAPMODE == nType ) ||
770
0
            ( MetaActionType::PUSH == nType ) ||
771
0
            ( MetaActionType::POP == nType ) )
772
0
        {
773
0
            pAct->Execute( aMapVDev.get() );
774
0
            aCurRect = OutputDevice::LogicToLogic( i_rClipRect, GetPrefMapMode(), aMapVDev->GetMapMode() );
775
0
        }
776
0
        else if( nType == MetaActionType::CLIPREGION )
777
0
        {
778
0
            MetaClipRegionAction* pOldAct = static_cast<MetaClipRegionAction*>(pAct);
779
0
            vcl::Region aNewReg( aCurRect );
780
0
            if( pOldAct->IsClipping() )
781
0
                aNewReg.Intersect( pOldAct->GetRegion() );
782
0
            MetaClipRegionAction* pNewAct = new MetaClipRegionAction( std::move(aNewReg), true );
783
0
            m_aList[ m_nCurrentActionElement ] = pNewAct;
784
0
        }
785
0
    }
786
0
}
787
788
Point GDIMetaFile::ImplGetRotatedPoint( const Point& rPt, const Point& rRotatePt,
789
                                        const Size& rOffset, double fSin, double fCos )
790
0
{
791
0
    const tools::Long nX = rPt.X() - rRotatePt.X();
792
0
    const tools::Long nY = rPt.Y() - rRotatePt.Y();
793
794
0
    return { basegfx::fround<tools::Long>(fCos * nX + fSin * nY) + rRotatePt.X() + rOffset.Width(),
795
0
             basegfx::fround<tools::Long>(fCos * nY - fSin * nX) + rRotatePt.Y() + rOffset.Height() };
796
0
}
797
798
tools::Polygon GDIMetaFile::ImplGetRotatedPolygon( const tools::Polygon& rPoly, const Point& rRotatePt,
799
                                                   const Size& rOffset, double fSin, double fCos )
800
0
{
801
0
    tools::Polygon aRet( rPoly );
802
803
0
    aRet.Rotate( rRotatePt, fSin, fCos );
804
0
    aRet.Move( rOffset.Width(), rOffset.Height() );
805
806
0
    return aRet;
807
0
}
808
809
tools::PolyPolygon GDIMetaFile::ImplGetRotatedPolyPolygon( const tools::PolyPolygon& rPolyPoly, const Point& rRotatePt,
810
                                                    const Size& rOffset, double fSin, double fCos )
811
0
{
812
0
    tools::PolyPolygon aRet( rPolyPoly );
813
814
0
    aRet.Rotate( rRotatePt, fSin, fCos );
815
0
    aRet.Move( rOffset.Width(), rOffset.Height() );
816
817
0
    return aRet;
818
0
}
819
820
void GDIMetaFile::ImplAddGradientEx( GDIMetaFile&         rMtf,
821
                                     const OutputDevice&  rMapDev,
822
                                     const tools::PolyPolygon&   rPolyPoly,
823
                                     const Gradient&      rGrad     )
824
0
{
825
    // Generate comment, GradientEx and Gradient actions (within DrawGradient)
826
0
    ScopedVclPtrInstance< VirtualDevice > aVDev(rMapDev, DeviceFormat::WITHOUT_ALPHA);
827
0
    aVDev->EnableOutput( false );
828
0
    GDIMetaFile aGradMtf;
829
830
0
    aGradMtf.Record( aVDev.get() );
831
0
    aVDev->DrawGradient( rPolyPoly, rGrad );
832
0
    aGradMtf.Stop();
833
834
0
    size_t i, nAct( aGradMtf.GetActionSize() );
835
0
    for( i=0; i < nAct; ++i )
836
0
    {
837
0
        MetaAction* pMetaAct = aGradMtf.GetAction( i );
838
0
        rMtf.AddAction( pMetaAct );
839
0
    }
840
0
}
841
842
void GDIMetaFile::Rotate( Degree10 nAngle10 )
843
0
{
844
0
    nAngle10 %= 3600_deg10;
845
0
    nAngle10 = ( nAngle10 < 0_deg10 ) ? ( Degree10(3599) + nAngle10 ) : nAngle10;
846
847
0
    if( !nAngle10 )
848
0
        return;
849
850
0
    GDIMetaFile     aMtf;
851
0
    ScopedVclPtrInstance< VirtualDevice > aMapVDev(DeviceFormat::WITH_ALPHA);
852
0
    const double    fAngle = toRadians(nAngle10);
853
0
    const double    fSin = sin( fAngle );
854
0
    const double    fCos = cos( fAngle );
855
0
    tools::Rectangle aRect( Point(), GetPrefSize() );
856
0
    tools::Polygon aPoly( aRect );
857
858
0
    aPoly.Rotate( Point(), fSin, fCos );
859
860
0
    aMapVDev->EnableOutput( false );
861
0
    aMapVDev->SetMapMode( GetPrefMapMode() );
862
863
0
    const tools::Rectangle aNewBound( aPoly.GetBoundRect() );
864
865
0
    const Point aOrigin( GetPrefMapMode().GetOrigin().X(), GetPrefMapMode().GetOrigin().Y() );
866
0
    const Size  aOffset( -aNewBound.Left(), -aNewBound.Top() );
867
868
0
    Point     aRotAnchor( aOrigin );
869
0
    Size      aRotOffset( aOffset );
870
871
0
    for( MetaAction* pAction = FirstAction(); pAction; pAction = NextAction() )
872
0
    {
873
0
        const MetaActionType nActionType = pAction->GetType();
874
875
0
        switch( nActionType )
876
0
        {
877
0
            case MetaActionType::PIXEL:
878
0
            {
879
0
                MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
880
0
                aMtf.AddAction( new MetaPixelAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
881
0
                                                                          pAct->GetColor() ) );
882
0
            }
883
0
            break;
884
885
0
            case MetaActionType::POINT:
886
0
            {
887
0
                MetaPointAction* pAct = static_cast<MetaPointAction*>(pAction);
888
0
                aMtf.AddAction( new MetaPointAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
889
0
            }
890
0
            break;
891
892
0
            case MetaActionType::LINE:
893
0
            {
894
0
                MetaLineAction* pAct = static_cast<MetaLineAction*>(pAction);
895
0
                aMtf.AddAction( new MetaLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
896
0
                                                    ImplGetRotatedPoint( pAct->GetEndPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
897
0
                                                    pAct->GetLineInfo() ) );
898
0
            }
899
0
            break;
900
901
0
            case MetaActionType::RECT:
902
0
            {
903
0
                MetaRectAction* pAct = static_cast<MetaRectAction*>(pAction);
904
0
                aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( tools::Polygon(pAct->GetRect()), aRotAnchor, aRotOffset, fSin, fCos ) ) );
905
0
            }
906
0
            break;
907
908
0
            case MetaActionType::ROUNDRECT:
909
0
            {
910
0
                MetaRoundRectAction*    pAct = static_cast<MetaRoundRectAction*>(pAction);
911
0
                const tools::Polygon aRoundRectPoly( pAct->GetRect(), pAct->GetHorzRound(), pAct->GetVertRound() );
912
913
0
                aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aRoundRectPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
914
0
            }
915
0
            break;
916
917
0
            case MetaActionType::ELLIPSE:
918
0
            {
919
0
                MetaEllipseAction*      pAct = static_cast<MetaEllipseAction*>(pAction);
920
0
                const tools::Polygon aEllipsePoly( pAct->GetRect().Center(), pAct->GetRect().GetWidth() >> 1, pAct->GetRect().GetHeight() >> 1 );
921
922
0
                aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aEllipsePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
923
0
            }
924
0
            break;
925
926
0
            case MetaActionType::ARC:
927
0
            {
928
0
                MetaArcAction*  pAct = static_cast<MetaArcAction*>(pAction);
929
0
                const tools::Polygon aArcPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Arc );
930
931
0
                aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aArcPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
932
0
            }
933
0
            break;
934
935
0
            case MetaActionType::PIE:
936
0
            {
937
0
                MetaPieAction*  pAct = static_cast<MetaPieAction*>(pAction);
938
0
                const tools::Polygon aPiePoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Pie );
939
940
0
                aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aPiePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
941
0
            }
942
0
            break;
943
944
0
            case MetaActionType::CHORD:
945
0
            {
946
0
                MetaChordAction*    pAct = static_cast<MetaChordAction*>(pAction);
947
0
                const tools::Polygon aChordPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Chord );
948
949
0
                aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aChordPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
950
0
            }
951
0
            break;
952
953
0
            case MetaActionType::POLYLINE:
954
0
            {
955
0
                MetaPolyLineAction* pAct = static_cast<MetaPolyLineAction*>(pAction);
956
0
                aMtf.AddAction( new MetaPolyLineAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->GetLineInfo() ) );
957
0
            }
958
0
            break;
959
960
0
            case MetaActionType::POLYGON:
961
0
            {
962
0
                MetaPolygonAction* pAct = static_cast<MetaPolygonAction*>(pAction);
963
0
                aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
964
0
            }
965
0
            break;
966
967
0
            case MetaActionType::POLYPOLYGON:
968
0
            {
969
0
                MetaPolyPolygonAction* pAct = static_cast<MetaPolyPolygonAction*>(pAction);
970
0
                aMtf.AddAction( new MetaPolyPolygonAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
971
0
            }
972
0
            break;
973
974
0
            case MetaActionType::TEXT:
975
0
            {
976
0
                MetaTextAction* pAct = static_cast<MetaTextAction*>(pAction);
977
0
                aMtf.AddAction( new MetaTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
978
0
                                                                         pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) );
979
0
            }
980
0
            break;
981
982
0
            case MetaActionType::TEXTARRAY:
983
0
            {
984
0
                MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pAction);
985
0
                aMtf.AddAction(new MetaTextArrayAction(
986
0
                    ImplGetRotatedPoint(pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos),
987
0
                    pAct->GetText(), pAct->GetDXArray(), pAct->GetKashidaArray(), pAct->GetIndex(),
988
0
                    pAct->GetLen(), pAct->GetLayoutContextIndex(), pAct->GetLayoutContextLen()));
989
0
            }
990
0
            break;
991
992
0
            case MetaActionType::STRETCHTEXT:
993
0
            {
994
0
                MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pAction);
995
0
                aMtf.AddAction( new MetaStretchTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
996
0
                                                                                pAct->GetWidth(), pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) );
997
0
            }
998
0
            break;
999
1000
0
            case MetaActionType::TEXTLINE:
1001
0
            {
1002
0
                MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pAction);
1003
0
                aMtf.AddAction( new MetaTextLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
1004
0
                                                                             pAct->GetWidth(), pAct->GetStrikeout(), pAct->GetUnderline(), pAct->GetOverline() ) );
1005
0
            }
1006
0
            break;
1007
1008
0
            case MetaActionType::BMPSCALE:
1009
0
            {
1010
0
                MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
1011
0
                tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetPoint(), pAct->GetSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
1012
0
                tools::Rectangle           aBmpRect( aBmpPoly.GetBoundRect() );
1013
0
                Bitmap            aBmp( pAct->GetBitmap() );
1014
1015
0
                aBmp.Rotate( nAngle10, COL_TRANSPARENT );
1016
0
                aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(),
1017
0
                                                          aBmp ) );
1018
0
            }
1019
0
            break;
1020
1021
0
            case MetaActionType::BMPSCALEPART:
1022
0
            {
1023
0
                MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
1024
0
                tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
1025
0
                tools::Rectangle               aBmpRect( aBmpPoly.GetBoundRect() );
1026
0
                Bitmap                aBmp( pAct->GetBitmap() );
1027
1028
0
                aBmp.Crop( tools::Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) );
1029
0
                aBmp.Rotate( nAngle10, COL_TRANSPARENT );
1030
1031
0
                aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmp ) );
1032
0
            }
1033
0
            break;
1034
1035
0
            case MetaActionType::BMPEXSCALE:
1036
0
            {
1037
0
                MetaBmpExScaleAction*   pAct = static_cast<MetaBmpExScaleAction*>(pAction);
1038
0
                tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetPoint(), pAct->GetSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
1039
0
                tools::Rectangle               aBmpRect( aBmpPoly.GetBoundRect() );
1040
0
                Bitmap                aBmp( pAct->GetBitmap() );
1041
1042
0
                aBmp.Rotate( nAngle10, COL_TRANSPARENT );
1043
1044
0
                aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmp ) );
1045
0
            }
1046
0
            break;
1047
1048
0
            case MetaActionType::BMPEXSCALEPART:
1049
0
            {
1050
0
                MetaBmpExScalePartAction*   pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
1051
0
                tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
1052
0
                tools::Rectangle                   aBmpRect( aBmpPoly.GetBoundRect() );
1053
0
                Bitmap                    aBmp( pAct->GetBitmap() );
1054
1055
0
                aBmp.Crop( tools::Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) );
1056
0
                aBmp.Rotate( nAngle10, COL_TRANSPARENT );
1057
1058
0
                aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmp ) );
1059
0
            }
1060
0
            break;
1061
1062
0
            case MetaActionType::GRADIENT:
1063
0
            {
1064
0
                MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
1065
1066
0
                ImplAddGradientEx( aMtf, *aMapVDev,
1067
0
                                   tools::PolyPolygon(ImplGetRotatedPolygon( tools::Polygon(pAct->GetRect()), aRotAnchor, aRotOffset, fSin, fCos )),
1068
0
                                   pAct->GetGradient() );
1069
0
            }
1070
0
            break;
1071
1072
0
            case MetaActionType::GRADIENTEX:
1073
0
            {
1074
0
                MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1075
0
                aMtf.AddAction( new MetaGradientExAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1076
0
                                                          pAct->GetGradient() ) );
1077
0
            }
1078
0
            break;
1079
1080
            // Handle gradientex comment block correctly
1081
0
            case MetaActionType::COMMENT:
1082
0
            {
1083
0
                MetaCommentAction* pCommentAct = static_cast<MetaCommentAction*>(pAction);
1084
0
                if( pCommentAct->GetComment() == "XGRAD_SEQ_BEGIN" )
1085
0
                {
1086
0
                    int nBeginComments( 1 );
1087
0
                    pAction = NextAction();
1088
1089
                    // skip everything, except gradientex action
1090
0
                    while( pAction )
1091
0
                    {
1092
0
                        const MetaActionType nType = pAction->GetType();
1093
1094
0
                        if( MetaActionType::GRADIENTEX == nType )
1095
0
                        {
1096
                            // Add rotated gradientex
1097
0
                            MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1098
0
                            ImplAddGradientEx( aMtf, *aMapVDev,
1099
0
                                               ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1100
0
                                               pAct->GetGradient() );
1101
0
                        }
1102
0
                        else if( MetaActionType::COMMENT == nType)
1103
0
                        {
1104
0
                            MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pAction);
1105
0
                            if( pAct->GetComment() == "XGRAD_SEQ_END" )
1106
0
                            {
1107
                                // handle nested blocks
1108
0
                                --nBeginComments;
1109
1110
                                // gradientex comment block: end reached, done.
1111
0
                                if( !nBeginComments )
1112
0
                                    break;
1113
0
                            }
1114
0
                            else if( pAct->GetComment() == "XGRAD_SEQ_BEGIN" )
1115
0
                            {
1116
                                // handle nested blocks
1117
0
                                ++nBeginComments;
1118
0
                            }
1119
1120
0
                        }
1121
1122
0
                        pAction =NextAction();
1123
0
                    }
1124
0
                }
1125
0
                else
1126
0
                {
1127
0
                    bool bPathStroke = (pCommentAct->GetComment() == "XPATHSTROKE_SEQ_BEGIN");
1128
0
                    if ( bPathStroke || pCommentAct->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1129
0
                    {
1130
0
                        if ( pCommentAct->GetDataSize() )
1131
0
                        {
1132
0
                            SvMemoryStream aMemStm( const_cast<sal_uInt8 *>(pCommentAct->GetData()), pCommentAct->GetDataSize(), StreamMode::READ );
1133
0
                            SvMemoryStream aDest;
1134
0
                            if ( bPathStroke )
1135
0
                            {
1136
0
                                SvtGraphicStroke aStroke;
1137
0
                                ReadSvtGraphicStroke( aMemStm, aStroke );
1138
0
                                tools::Polygon aPath;
1139
0
                                aStroke.getPath( aPath );
1140
0
                                aStroke.setPath( ImplGetRotatedPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) );
1141
0
                                WriteSvtGraphicStroke( aDest, aStroke );
1142
0
                                aMtf.AddAction( new MetaCommentAction( "XPATHSTROKE_SEQ_BEGIN"_ostr, 0,
1143
0
                                                    static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) );
1144
0
                            }
1145
0
                            else
1146
0
                            {
1147
0
                                SvtGraphicFill aFill;
1148
0
                                ReadSvtGraphicFill( aMemStm, aFill );
1149
0
                                tools::PolyPolygon aPath;
1150
0
                                aFill.getPath( aPath );
1151
0
                                aFill.setPath( ImplGetRotatedPolyPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) );
1152
0
                                WriteSvtGraphicFill( aDest, aFill );
1153
0
                                aMtf.AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN"_ostr, 0,
1154
0
                                                    static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) );
1155
0
                            }
1156
0
                        }
1157
0
                    }
1158
0
                    else if ( pCommentAct->GetComment() == "XPATHSTROKE_SEQ_END"
1159
0
                           || pCommentAct->GetComment() == "XPATHFILL_SEQ_END" )
1160
0
                    {
1161
0
                        pAction->Execute( aMapVDev.get() );
1162
0
                        aMtf.AddAction( pAction );
1163
0
                    }
1164
0
                }
1165
0
            }
1166
0
            break;
1167
1168
0
            case MetaActionType::HATCH:
1169
0
            {
1170
0
                MetaHatchAction*    pAct = static_cast<MetaHatchAction*>(pAction);
1171
0
                Hatch               aHatch( pAct->GetHatch() );
1172
1173
0
                aHatch.SetAngle( aHatch.GetAngle() + nAngle10 );
1174
0
                aMtf.AddAction( new MetaHatchAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1175
0
                                                                                aHatch ) );
1176
0
            }
1177
0
            break;
1178
1179
0
            case MetaActionType::Transparent:
1180
0
            {
1181
0
                MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pAction);
1182
0
                aMtf.AddAction( new MetaTransparentAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1183
0
                                                                                      pAct->GetTransparence() ) );
1184
0
            }
1185
0
            break;
1186
1187
0
            case MetaActionType::FLOATTRANSPARENT:
1188
0
            {
1189
0
                MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
1190
0
                GDIMetaFile                 aTransMtf( pAct->GetGDIMetaFile() );
1191
0
                tools::Polygon aMtfPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetPoint(), pAct->GetSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
1192
0
                tools::Rectangle                   aMtfRect( aMtfPoly.GetBoundRect() );
1193
1194
0
                aTransMtf.Rotate( nAngle10 );
1195
0
                aMtf.AddAction( new MetaFloatTransparentAction( aTransMtf, aMtfRect.TopLeft(), aMtfRect.GetSize(),
1196
0
                                                                pAct->GetGradient() ) );
1197
0
            }
1198
0
            break;
1199
1200
0
            case MetaActionType::EPS:
1201
0
            {
1202
0
                MetaEPSAction*  pAct = static_cast<MetaEPSAction*>(pAction);
1203
0
                GDIMetaFile     aEPSMtf( pAct->GetSubstitute() );
1204
0
                tools::Polygon aEPSPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetPoint(), pAct->GetSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
1205
0
                tools::Rectangle       aEPSRect( aEPSPoly.GetBoundRect() );
1206
1207
0
                aEPSMtf.Rotate( nAngle10 );
1208
0
                aMtf.AddAction( new MetaEPSAction( aEPSRect.TopLeft(), aEPSRect.GetSize(),
1209
0
                                                   pAct->GetLink(), aEPSMtf ) );
1210
0
            }
1211
0
            break;
1212
1213
0
            case MetaActionType::CLIPREGION:
1214
0
            {
1215
0
                MetaClipRegionAction* pAct = static_cast<MetaClipRegionAction*>(pAction);
1216
1217
0
                if( pAct->IsClipping() && pAct->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1218
0
                    aMtf.AddAction( new MetaClipRegionAction( vcl::Region( ImplGetRotatedPolyPolygon( pAct->GetRegion().GetAsPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ), true ) );
1219
0
                else
1220
0
                {
1221
0
                    aMtf.AddAction( pAction );
1222
0
                }
1223
0
            }
1224
0
            break;
1225
1226
0
            case MetaActionType::ISECTRECTCLIPREGION:
1227
0
            {
1228
0
                MetaISectRectClipRegionAction*  pAct = static_cast<MetaISectRectClipRegionAction*>(pAction);
1229
0
                aMtf.AddAction( new MetaISectRegionClipRegionAction(vcl::Region(
1230
0
                    ImplGetRotatedPolygon( tools::Polygon(pAct->GetRect()), aRotAnchor,
1231
0
                        aRotOffset, fSin, fCos )) ) );
1232
0
            }
1233
0
            break;
1234
1235
0
            case MetaActionType::ISECTREGIONCLIPREGION:
1236
0
            {
1237
0
                MetaISectRegionClipRegionAction*    pAct = static_cast<MetaISectRegionClipRegionAction*>(pAction);
1238
0
                const vcl::Region&                  rRegion = pAct->GetRegion();
1239
1240
0
                if( rRegion.HasPolyPolygonOrB2DPolyPolygon() )
1241
0
                    aMtf.AddAction( new MetaISectRegionClipRegionAction( vcl::Region( ImplGetRotatedPolyPolygon( rRegion.GetAsPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) ) );
1242
0
                else
1243
0
                {
1244
0
                    aMtf.AddAction( pAction );
1245
0
                }
1246
0
            }
1247
0
            break;
1248
1249
0
            case MetaActionType::REFPOINT:
1250
0
            {
1251
0
                MetaRefPointAction* pAct = static_cast<MetaRefPointAction*>(pAction);
1252
0
                aMtf.AddAction( new MetaRefPointAction( ImplGetRotatedPoint( pAct->GetRefPoint(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->IsSetting() ) );
1253
0
            }
1254
0
            break;
1255
1256
0
            case MetaActionType::FONT:
1257
0
            {
1258
0
                MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
1259
0
                vcl::Font       aFont( pAct->GetFont() );
1260
1261
0
                aFont.SetOrientation( aFont.GetOrientation() + nAngle10 );
1262
0
                aMtf.AddAction( new MetaFontAction( std::move(aFont) ) );
1263
0
            }
1264
0
            break;
1265
1266
0
            case MetaActionType::BMP:
1267
0
            case MetaActionType::BMPEX:
1268
0
            case MetaActionType::MASK:
1269
0
            case MetaActionType::MASKSCALE:
1270
0
            case MetaActionType::MASKSCALEPART:
1271
0
            case MetaActionType::WALLPAPER:
1272
0
            case MetaActionType::TEXTRECT:
1273
0
            case MetaActionType::MOVECLIPREGION:
1274
0
            {
1275
0
                OSL_FAIL( "GDIMetaFile::Rotate(): unsupported action" );
1276
0
            }
1277
0
            break;
1278
1279
0
            default:
1280
0
            {
1281
0
                pAction->Execute( aMapVDev.get() );
1282
0
                aMtf.AddAction( pAction );
1283
1284
                // update rotation point and offset, if necessary
1285
0
                if( ( MetaActionType::MAPMODE == nActionType ) ||
1286
0
                    ( MetaActionType::PUSH == nActionType ) ||
1287
0
                    ( MetaActionType::POP == nActionType ) )
1288
0
                {
1289
0
                    aRotAnchor = OutputDevice::LogicToLogic( aOrigin, m_aPrefMapMode, aMapVDev->GetMapMode() );
1290
0
                    aRotOffset = OutputDevice::LogicToLogic( aOffset, m_aPrefMapMode, aMapVDev->GetMapMode() );
1291
0
                }
1292
0
            }
1293
0
            break;
1294
0
        }
1295
0
    }
1296
1297
0
    aMtf.m_aPrefMapMode = m_aPrefMapMode;
1298
0
    aMtf.m_aPrefSize = aNewBound.GetSize();
1299
1300
0
    *this = aMtf;
1301
1302
0
}
1303
1304
static void ImplActionBounds( tools::Rectangle& o_rOutBounds,
1305
                              const tools::Rectangle& i_rInBounds,
1306
                              const std::vector<tools::Rectangle>& i_rClipStack )
1307
0
{
1308
0
    tools::Rectangle aBounds( i_rInBounds );
1309
0
    if( ! i_rInBounds.IsEmpty() && ! i_rClipStack.empty() && ! i_rClipStack.back().IsEmpty() )
1310
0
        aBounds.Intersection( i_rClipStack.back() );
1311
0
    if(  aBounds.IsEmpty() )
1312
0
        return;
1313
1314
0
    if( ! o_rOutBounds.IsEmpty() )
1315
0
        o_rOutBounds.Union( aBounds );
1316
0
    else
1317
0
        o_rOutBounds = aBounds;
1318
0
}
1319
1320
tools::Rectangle GDIMetaFile::GetBoundRect( OutputDevice& i_rReference ) const
1321
0
{
1322
0
    ScopedVclPtrInstance< VirtualDevice > aMapVDev( i_rReference, DeviceFormat::WITH_ALPHA );
1323
1324
0
    aMapVDev->EnableOutput( false );
1325
0
    aMapVDev->SetMapMode( GetPrefMapMode() );
1326
1327
0
    std::vector<tools::Rectangle> aClipStack( 1, tools::Rectangle() );
1328
0
    std::vector<vcl::PushFlags> aPushFlagStack;
1329
1330
0
    tools::Rectangle aBound;
1331
0
    const sal_uLong nCount(GetActionSize());
1332
1333
0
    for(sal_uLong a(0); a < nCount; a++)
1334
0
    {
1335
0
        MetaAction* pAction = GetAction(a);
1336
0
        const MetaActionType nActionType = pAction->GetType();
1337
1338
0
        switch( nActionType )
1339
0
        {
1340
0
        case MetaActionType::PIXEL:
1341
0
        {
1342
0
            MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
1343
0
            ImplActionBounds( aBound,
1344
0
                              tools::Rectangle( OutputDevice::LogicToLogic( pAct->GetPoint(), aMapVDev->GetMapMode(), GetPrefMapMode() ),
1345
0
                                       aMapVDev->PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ),
1346
0
                             aClipStack );
1347
0
        }
1348
0
        break;
1349
1350
0
        case MetaActionType::POINT:
1351
0
        {
1352
0
            MetaPointAction* pAct = static_cast<MetaPointAction*>(pAction);
1353
0
            ImplActionBounds( aBound,
1354
0
                              tools::Rectangle( OutputDevice::LogicToLogic( pAct->GetPoint(), aMapVDev->GetMapMode(), GetPrefMapMode() ),
1355
0
                                       aMapVDev->PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ),
1356
0
                             aClipStack );
1357
0
        }
1358
0
        break;
1359
1360
0
        case MetaActionType::LINE:
1361
0
        {
1362
0
            MetaLineAction* pAct = static_cast<MetaLineAction*>(pAction);
1363
0
            Point aP1( pAct->GetStartPoint() ), aP2( pAct->GetEndPoint() );
1364
0
            tools::Rectangle aRect( aP1, aP2 );
1365
0
            aRect.Normalize();
1366
1367
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1368
0
        }
1369
0
        break;
1370
1371
0
        case MetaActionType::RECT:
1372
0
        {
1373
0
            MetaRectAction* pAct = static_cast<MetaRectAction*>(pAction);
1374
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1375
0
        }
1376
0
        break;
1377
1378
0
        case MetaActionType::ROUNDRECT:
1379
0
        {
1380
0
            MetaRoundRectAction*    pAct = static_cast<MetaRoundRectAction*>(pAction);
1381
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1382
0
        }
1383
0
        break;
1384
1385
0
        case MetaActionType::ELLIPSE:
1386
0
        {
1387
0
            MetaEllipseAction*      pAct = static_cast<MetaEllipseAction*>(pAction);
1388
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1389
0
        }
1390
0
        break;
1391
1392
0
        case MetaActionType::ARC:
1393
0
        {
1394
0
            MetaArcAction*  pAct = static_cast<MetaArcAction*>(pAction);
1395
            // FIXME: this is imprecise
1396
            // e.g. for small arcs the whole rectangle is WAY too large
1397
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1398
0
        }
1399
0
        break;
1400
1401
0
        case MetaActionType::PIE:
1402
0
        {
1403
0
            MetaPieAction*  pAct = static_cast<MetaPieAction*>(pAction);
1404
            // FIXME: this is imprecise
1405
            // e.g. for small arcs the whole rectangle is WAY too large
1406
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1407
0
        }
1408
0
        break;
1409
1410
0
        case MetaActionType::CHORD:
1411
0
        {
1412
0
            MetaChordAction*    pAct = static_cast<MetaChordAction*>(pAction);
1413
            // FIXME: this is imprecise
1414
            // e.g. for small arcs the whole rectangle is WAY too large
1415
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1416
0
        }
1417
0
        break;
1418
1419
0
        case MetaActionType::POLYLINE:
1420
0
        {
1421
0
            MetaPolyLineAction* pAct = static_cast<MetaPolyLineAction*>(pAction);
1422
0
            tools::Rectangle aRect( pAct->GetPolygon().GetBoundRect() );
1423
1424
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1425
0
        }
1426
0
        break;
1427
1428
0
        case MetaActionType::POLYGON:
1429
0
        {
1430
0
            MetaPolygonAction* pAct = static_cast<MetaPolygonAction*>(pAction);
1431
0
            tools::Rectangle aRect( pAct->GetPolygon().GetBoundRect() );
1432
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1433
0
        }
1434
0
        break;
1435
1436
0
        case MetaActionType::POLYPOLYGON:
1437
0
        {
1438
0
            MetaPolyPolygonAction* pAct = static_cast<MetaPolyPolygonAction*>(pAction);
1439
0
            tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1440
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1441
0
        }
1442
0
        break;
1443
1444
0
        case MetaActionType::TEXT:
1445
0
        {
1446
0
            MetaTextAction* pAct = static_cast<MetaTextAction*>(pAction);
1447
0
            tools::Rectangle aRect;
1448
            // hdu said base = index
1449
0
            aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen() );
1450
0
            Point aPt( pAct->GetPoint() );
1451
0
            aRect.Move( aPt.X(), aPt.Y() );
1452
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1453
0
        }
1454
0
        break;
1455
1456
0
        case MetaActionType::TEXTARRAY:
1457
0
        {
1458
0
            MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pAction);
1459
0
            tools::Rectangle aRect;
1460
            // hdu said base = index
1461
0
            aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(),
1462
0
                                       0, pAct->GetDXArray(), pAct->GetKashidaArray() );
1463
0
            Point aPt( pAct->GetPoint() );
1464
0
            aRect.Move( aPt.X(), aPt.Y() );
1465
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1466
0
        }
1467
0
        break;
1468
1469
0
        case MetaActionType::STRETCHTEXT:
1470
0
        {
1471
0
            MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pAction);
1472
0
            tools::Rectangle aRect;
1473
            // hdu said base = index
1474
0
            aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(),
1475
0
                                       pAct->GetWidth() );
1476
0
            Point aPt( pAct->GetPoint() );
1477
0
            aRect.Move( aPt.X(), aPt.Y() );
1478
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1479
0
        }
1480
0
        break;
1481
1482
0
        case MetaActionType::TEXTLINE:
1483
0
        {
1484
0
            MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pAction);
1485
            // measure a test string to get ascend and descent right
1486
0
            static constexpr OUStringLiteral pStr = u"\u00c4g";
1487
0
            OUString aStr( pStr );
1488
1489
0
            tools::Rectangle aRect;
1490
0
            aMapVDev->GetTextBoundRect( aRect, aStr, 0, 0, aStr.getLength() );
1491
0
            Point aPt( pAct->GetStartPoint() );
1492
0
            aRect.Move( aPt.X(), aPt.Y() );
1493
0
            aRect.SetRight( aRect.Left() + pAct->GetWidth() );
1494
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1495
0
        }
1496
0
        break;
1497
1498
0
        case MetaActionType::BMPSCALE:
1499
0
        {
1500
0
            MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
1501
0
            tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1502
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1503
0
        }
1504
0
        break;
1505
1506
0
        case MetaActionType::BMPSCALEPART:
1507
0
        {
1508
0
            MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
1509
0
            tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1510
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1511
0
        }
1512
0
        break;
1513
1514
0
        case MetaActionType::BMPEXSCALE:
1515
0
        {
1516
0
            MetaBmpExScaleAction*   pAct = static_cast<MetaBmpExScaleAction*>(pAction);
1517
0
            tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1518
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1519
0
        }
1520
0
        break;
1521
1522
0
        case MetaActionType::BMPEXSCALEPART:
1523
0
        {
1524
0
            MetaBmpExScalePartAction*   pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
1525
0
            tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1526
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1527
0
        }
1528
0
        break;
1529
1530
0
        case MetaActionType::GRADIENT:
1531
0
        {
1532
0
            MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
1533
0
            tools::Rectangle aRect( pAct->GetRect() );
1534
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1535
0
        }
1536
0
        break;
1537
1538
0
        case MetaActionType::GRADIENTEX:
1539
0
        {
1540
0
            MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1541
0
            tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1542
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1543
0
        }
1544
0
        break;
1545
1546
0
        case MetaActionType::COMMENT:
1547
0
        {
1548
            // nothing to do
1549
0
        };
1550
0
        break;
1551
1552
0
        case MetaActionType::HATCH:
1553
0
        {
1554
0
            MetaHatchAction*    pAct = static_cast<MetaHatchAction*>(pAction);
1555
0
            tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1556
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1557
0
        }
1558
0
        break;
1559
1560
0
        case MetaActionType::Transparent:
1561
0
        {
1562
0
            MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pAction);
1563
0
            tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1564
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1565
0
        }
1566
0
        break;
1567
1568
0
        case MetaActionType::FLOATTRANSPARENT:
1569
0
        {
1570
0
            MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
1571
            // MetaFloatTransparentAction is defined limiting its content Metafile
1572
            // to its geometry definition(Point, Size), so use these directly
1573
0
            const tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1574
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1575
0
        }
1576
0
        break;
1577
1578
0
        case MetaActionType::EPS:
1579
0
        {
1580
0
            MetaEPSAction*  pAct = static_cast<MetaEPSAction*>(pAction);
1581
0
            tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1582
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1583
0
        }
1584
0
        break;
1585
1586
0
        case MetaActionType::CLIPREGION:
1587
0
        {
1588
0
            MetaClipRegionAction* pAct = static_cast<MetaClipRegionAction*>(pAction);
1589
0
            if( pAct->IsClipping() )
1590
0
                aClipStack.back() = OutputDevice::LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev->GetMapMode(), GetPrefMapMode() );
1591
0
            else
1592
0
                aClipStack.back() = tools::Rectangle();
1593
0
        }
1594
0
        break;
1595
1596
0
        case MetaActionType::ISECTRECTCLIPREGION:
1597
0
        {
1598
0
            MetaISectRectClipRegionAction* pAct = static_cast<MetaISectRectClipRegionAction*>(pAction);
1599
0
            tools::Rectangle aRect( OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ) );
1600
0
            if( aClipStack.back().IsEmpty() )
1601
0
                aClipStack.back() = aRect;
1602
0
            else
1603
0
                aClipStack.back().Intersection( aRect );
1604
0
        }
1605
0
        break;
1606
1607
0
        case MetaActionType::ISECTREGIONCLIPREGION:
1608
0
        {
1609
0
            MetaISectRegionClipRegionAction*    pAct = static_cast<MetaISectRegionClipRegionAction*>(pAction);
1610
0
            tools::Rectangle aRect( OutputDevice::LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ) );
1611
0
            if( aClipStack.back().IsEmpty() )
1612
0
                aClipStack.back() = aRect;
1613
0
            else
1614
0
                aClipStack.back().Intersection( aRect );
1615
0
        }
1616
0
        break;
1617
1618
0
        case MetaActionType::BMP:
1619
0
        {
1620
0
            MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pAction);
1621
0
            tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmap().GetSizePixel() ) );
1622
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1623
0
        }
1624
0
        break;
1625
1626
0
        case MetaActionType::BMPEX:
1627
0
        {
1628
0
            MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pAction);
1629
0
            tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmap().GetSizePixel() ) );
1630
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1631
0
        }
1632
0
        break;
1633
1634
0
        case MetaActionType::MASK:
1635
0
        {
1636
0
            MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pAction);
1637
0
            tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmap().GetSizePixel() ) );
1638
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1639
0
        }
1640
0
        break;
1641
1642
0
        case MetaActionType::MASKSCALE:
1643
0
        {
1644
0
            MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
1645
0
            tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1646
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1647
0
        }
1648
0
        break;
1649
1650
0
        case MetaActionType::MASKSCALEPART:
1651
0
        {
1652
0
            MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
1653
0
            tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1654
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1655
0
        }
1656
0
        break;
1657
1658
0
        case MetaActionType::WALLPAPER:
1659
0
        {
1660
0
            MetaWallpaperAction* pAct = static_cast<MetaWallpaperAction*>(pAction);
1661
0
            tools::Rectangle aRect( pAct->GetRect() );
1662
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1663
0
        }
1664
0
        break;
1665
1666
0
        case MetaActionType::TEXTRECT:
1667
0
        {
1668
0
            MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pAction);
1669
0
            tools::Rectangle aRect( pAct->GetRect() );
1670
0
            ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
1671
0
        }
1672
0
        break;
1673
1674
0
        case MetaActionType::MOVECLIPREGION:
1675
0
        {
1676
0
            MetaMoveClipRegionAction* pAct = static_cast<MetaMoveClipRegionAction*>(pAction);
1677
0
            if( ! aClipStack.back().IsEmpty() )
1678
0
            {
1679
0
                Size aDelta( pAct->GetHorzMove(), pAct->GetVertMove() );
1680
0
                aDelta = OutputDevice::LogicToLogic( aDelta, aMapVDev->GetMapMode(), GetPrefMapMode() );
1681
0
                aClipStack.back().Move( aDelta.Width(), aDelta.Width() );
1682
0
            }
1683
0
        }
1684
0
        break;
1685
1686
0
        default:
1687
0
            {
1688
0
                pAction->Execute( aMapVDev.get() );
1689
1690
0
                if( nActionType == MetaActionType::PUSH )
1691
0
                {
1692
0
                    MetaPushAction* pAct = static_cast<MetaPushAction*>(pAction);
1693
0
                    aPushFlagStack.push_back( pAct->GetFlags() );
1694
0
                    if( aPushFlagStack.back() & vcl::PushFlags::CLIPREGION )
1695
0
                    {
1696
0
                        tools::Rectangle aRect( aClipStack.back() );
1697
0
                        aClipStack.push_back( aRect );
1698
0
                    }
1699
0
                }
1700
0
                else if( nActionType == MetaActionType::POP )
1701
0
                {
1702
                    // sanity check
1703
0
                    if( ! aPushFlagStack.empty() )
1704
0
                    {
1705
0
                        if( aPushFlagStack.back() & vcl::PushFlags::CLIPREGION )
1706
0
                        {
1707
0
                            if( aClipStack.size() > 1 )
1708
0
                                aClipStack.pop_back();
1709
0
                        }
1710
0
                        aPushFlagStack.pop_back();
1711
0
                    }
1712
0
                }
1713
0
            }
1714
0
            break;
1715
0
        }
1716
0
    }
1717
0
    return aBound;
1718
0
}
1719
1720
Color GDIMetaFile::ImplColAdjustFnc( const Color& rColor, const void* pColParam )
1721
0
{
1722
0
    return Color( ColorAlpha, rColor.GetAlpha(),
1723
0
                  static_cast<const ImplColAdjustParam*>(pColParam)->pMapR[ rColor.GetRed() ],
1724
0
                  static_cast<const ImplColAdjustParam*>(pColParam)->pMapG[ rColor.GetGreen() ],
1725
0
                  static_cast<const ImplColAdjustParam*>(pColParam)->pMapB[ rColor.GetBlue() ] );
1726
1727
0
}
1728
1729
Bitmap GDIMetaFile::ImplBmpAdjustFnc( const Bitmap& rBmp, const void* pBmpParam )
1730
0
{
1731
0
    const ImplBmpAdjustParam*   p = static_cast<const ImplBmpAdjustParam*>(pBmpParam);
1732
0
    Bitmap                      aRet( rBmp );
1733
1734
0
    aRet.Adjust( p->nLuminancePercent, p->nContrastPercent,
1735
0
                 p->nChannelRPercent, p->nChannelGPercent, p->nChannelBPercent,
1736
0
                 p->fGamma, p->bInvert );
1737
1738
0
    return aRet;
1739
0
}
1740
1741
Color GDIMetaFile::ImplColConvertFnc( const Color& rColor, const void* pColParam )
1742
0
{
1743
0
    sal_uInt8 cLum = rColor.GetLuminance();
1744
1745
0
    if( MtfConversion::N1BitThreshold == static_cast<const ImplColConvertParam*>(pColParam)->eConversion )
1746
0
        cLum = ( cLum < 128 ) ? 0 : 255;
1747
1748
0
    return Color( ColorAlpha, rColor.GetAlpha(), cLum, cLum, cLum );
1749
0
}
1750
1751
Bitmap GDIMetaFile::ImplBmpConvertFnc( const Bitmap& rBmp, const void* pBmpParam )
1752
0
{
1753
0
    Bitmap aRet( rBmp );
1754
1755
0
    aRet.Convert( static_cast<const ImplBmpConvertParam*>(pBmpParam)->eConversion );
1756
1757
0
    return aRet;
1758
0
}
1759
1760
Color GDIMetaFile::ImplColMonoFnc( const Color&, const void* pColParam )
1761
0
{
1762
0
    return static_cast<const ImplColMonoParam*>(pColParam)->aColor;
1763
0
}
1764
1765
Bitmap GDIMetaFile::ImplBmpMonoFnc( const Bitmap& rBmp, const void* pBmpParam )
1766
0
{
1767
0
    BitmapPalette aPal( 3 );
1768
0
    aPal[ 0 ] = COL_BLACK;
1769
0
    aPal[ 1 ] = COL_WHITE;
1770
0
    aPal[ 2 ] = static_cast<const ImplBmpMonoParam*>(pBmpParam)->aColor;
1771
1772
0
    Bitmap aBmp(rBmp.GetSizePixel(), vcl::PixelFormat::N8_BPP, &aPal);
1773
0
    aBmp.Erase( static_cast<const ImplBmpMonoParam*>(pBmpParam)->aColor );
1774
1775
0
    if( rBmp.HasAlpha() )
1776
0
        return Bitmap( aBmp, rBmp.CreateAlphaMask() );
1777
0
    else
1778
0
        return aBmp;
1779
0
}
1780
1781
Color GDIMetaFile::ImplColReplaceFnc( const Color& rColor, const void* pColParam )
1782
0
{
1783
0
    const sal_uLong nR = rColor.GetRed(), nG = rColor.GetGreen(), nB = rColor.GetBlue();
1784
1785
0
    for( sal_uLong i = 0; i < static_cast<const ImplColReplaceParam*>(pColParam)->nCount; i++ )
1786
0
    {
1787
0
        if( ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinR[ i ] <= nR ) &&
1788
0
            ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxR[ i ] >= nR ) &&
1789
0
            ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinG[ i ] <= nG ) &&
1790
0
            ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxG[ i ] >= nG ) &&
1791
0
            ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinB[ i ] <= nB ) &&
1792
0
            ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxB[ i ] >= nB ) )
1793
0
        {
1794
0
            return static_cast<const ImplColReplaceParam*>(pColParam)->pDstCols[ i ];
1795
0
        }
1796
0
    }
1797
1798
0
    return rColor;
1799
0
}
1800
1801
Bitmap GDIMetaFile::ImplBmpReplaceFnc( const Bitmap& rBmp, const void* pBmpParam )
1802
0
{
1803
0
    const ImplBmpReplaceParam*  p = static_cast<const ImplBmpReplaceParam*>(pBmpParam);
1804
0
    Bitmap                      aRet( rBmp );
1805
1806
0
    aRet.Replace( p->pSrcCols, p->pDstCols, p->nCount, nullptr );
1807
1808
0
    return aRet;
1809
0
}
1810
1811
void GDIMetaFile::ImplExchangeColors( ColorExchangeFnc pFncCol, const void* pColParam,
1812
                                      BmpExchangeFnc pFncBmp, const void* pBmpParam )
1813
0
{
1814
0
    GDIMetaFile aMtf;
1815
1816
0
    aMtf.m_aPrefSize = m_aPrefSize;
1817
0
    aMtf.m_aPrefMapMode = m_aPrefMapMode;
1818
0
    aMtf.m_bUseCanvas = m_bUseCanvas;
1819
0
    aMtf.m_bSVG = m_bSVG;
1820
1821
0
    for( MetaAction* pAction = FirstAction(); pAction; pAction = NextAction() )
1822
0
    {
1823
0
        const MetaActionType nType = pAction->GetType();
1824
1825
0
        switch( nType )
1826
0
        {
1827
0
            case MetaActionType::PIXEL:
1828
0
            {
1829
0
                MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
1830
0
                aMtf.push_back( new MetaPixelAction( pAct->GetPoint(), pFncCol( pAct->GetColor(), pColParam ) ) );
1831
0
            }
1832
0
            break;
1833
1834
0
            case MetaActionType::LINECOLOR:
1835
0
            {
1836
0
                MetaLineColorAction* pAct = static_cast<MetaLineColorAction*>(pAction);
1837
1838
0
                if( pAct->IsSetting() )
1839
0
                    pAct = new MetaLineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1840
1841
0
                aMtf.push_back( pAct );
1842
0
            }
1843
0
            break;
1844
1845
0
            case MetaActionType::FILLCOLOR:
1846
0
            {
1847
0
                MetaFillColorAction* pAct = static_cast<MetaFillColorAction*>(pAction);
1848
1849
0
                if( pAct->IsSetting() )
1850
0
                    pAct = new MetaFillColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1851
1852
0
                aMtf.push_back( pAct );
1853
0
            }
1854
0
            break;
1855
1856
0
            case MetaActionType::TEXTCOLOR:
1857
0
            {
1858
0
                MetaTextColorAction* pAct = static_cast<MetaTextColorAction*>(pAction);
1859
0
                aMtf.push_back( new MetaTextColorAction( pFncCol( pAct->GetColor(), pColParam ) ) );
1860
0
            }
1861
0
            break;
1862
1863
0
            case MetaActionType::TEXTFILLCOLOR:
1864
0
            {
1865
0
                MetaTextFillColorAction* pAct = static_cast<MetaTextFillColorAction*>(pAction);
1866
1867
0
                if( pAct->IsSetting() )
1868
0
                    pAct = new MetaTextFillColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1869
1870
0
                aMtf.push_back( pAct );
1871
0
            }
1872
0
            break;
1873
1874
0
            case MetaActionType::TEXTLINECOLOR:
1875
0
            {
1876
0
                MetaTextLineColorAction* pAct = static_cast<MetaTextLineColorAction*>(pAction);
1877
1878
0
                if( pAct->IsSetting() )
1879
0
                    pAct = new MetaTextLineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1880
1881
0
                aMtf.push_back( pAct );
1882
0
            }
1883
0
            break;
1884
1885
0
            case MetaActionType::OVERLINECOLOR:
1886
0
            {
1887
0
                MetaOverlineColorAction* pAct = static_cast<MetaOverlineColorAction*>(pAction);
1888
1889
0
                if( pAct->IsSetting() )
1890
0
                    pAct = new MetaOverlineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1891
1892
0
                aMtf.push_back( pAct );
1893
0
            }
1894
0
            break;
1895
1896
0
            case MetaActionType::FONT:
1897
0
            {
1898
0
                MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
1899
0
                vcl::Font       aFont( pAct->GetFont() );
1900
1901
0
                aFont.SetColor( pFncCol( aFont.GetColor(), pColParam ) );
1902
0
                aFont.SetFillColor( pFncCol( aFont.GetFillColor(), pColParam ) );
1903
0
                aMtf.push_back( new MetaFontAction( std::move(aFont) ) );
1904
0
            }
1905
0
            break;
1906
1907
0
            case MetaActionType::WALLPAPER:
1908
0
            {
1909
0
                MetaWallpaperAction*    pAct = static_cast<MetaWallpaperAction*>(pAction);
1910
0
                Wallpaper               aWall( pAct->GetWallpaper() );
1911
0
                const tools::Rectangle&        rRect = pAct->GetRect();
1912
1913
0
                aWall.SetColor( pFncCol( aWall.GetColor(), pColParam ) );
1914
1915
0
                if( aWall.IsBitmap() )
1916
0
                    aWall.SetBitmap( pFncBmp( aWall.GetBitmap(), pBmpParam ) );
1917
1918
0
                if( aWall.IsGradient() )
1919
0
                {
1920
0
                    Gradient aGradient( aWall.GetGradient() );
1921
1922
0
                    aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
1923
0
                    aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
1924
0
                    aWall.SetGradient( aGradient );
1925
0
                }
1926
1927
0
                aMtf.push_back( new MetaWallpaperAction( rRect, std::move(aWall) ) );
1928
0
            }
1929
0
            break;
1930
1931
0
            case MetaActionType::BMP:
1932
0
            case MetaActionType::BMPEX:
1933
0
            case MetaActionType::MASK:
1934
0
            {
1935
0
                OSL_FAIL( "Don't use bitmap actions of this type in metafiles!" );
1936
0
            }
1937
0
            break;
1938
1939
0
            case MetaActionType::BMPSCALE:
1940
0
            {
1941
0
                MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
1942
0
                aMtf.push_back( new MetaBmpScaleAction( pAct->GetPoint(), pAct->GetSize(),
1943
0
                                      pFncBmp( pAct->GetBitmap(), pBmpParam ).CreateColorBitmap() ) );
1944
0
            }
1945
0
            break;
1946
1947
0
            case MetaActionType::BMPSCALEPART:
1948
0
            {
1949
0
                MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
1950
0
                aMtf.push_back( new MetaBmpScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
1951
0
                                                    pAct->GetSrcPoint(), pAct->GetSrcSize(),
1952
0
                                                    pFncBmp( pAct->GetBitmap(), pBmpParam ).CreateColorBitmap() )
1953
0
                                                );
1954
0
            }
1955
0
            break;
1956
1957
0
            case MetaActionType::BMPEXSCALE:
1958
0
            {
1959
0
                MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
1960
0
                aMtf.push_back( new MetaBmpExScaleAction( pAct->GetPoint(), pAct->GetSize(),
1961
0
                                                          pFncBmp( pAct->GetBitmap(), pBmpParam ) )
1962
0
                                                        );
1963
0
            }
1964
0
            break;
1965
1966
0
            case MetaActionType::BMPEXSCALEPART:
1967
0
            {
1968
0
                MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
1969
0
                aMtf.push_back( new MetaBmpExScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
1970
0
                                                              pAct->GetSrcPoint(), pAct->GetSrcSize(),
1971
0
                                                              pFncBmp( pAct->GetBitmap(), pBmpParam ) )
1972
0
                                                            );
1973
0
            }
1974
0
            break;
1975
1976
0
            case MetaActionType::MASKSCALE:
1977
0
            {
1978
0
                MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pAction);
1979
0
                aMtf.push_back( new MetaMaskScaleAction( pAct->GetPoint(), pAct->GetSize(),
1980
0
                                                         pAct->GetBitmap(),
1981
0
                                                         pFncCol( pAct->GetColor(), pColParam ) )
1982
0
                                                       );
1983
0
            }
1984
0
            break;
1985
1986
0
            case MetaActionType::MASKSCALEPART:
1987
0
            {
1988
0
                MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
1989
0
                aMtf.push_back( new MetaMaskScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
1990
0
                                                             pAct->GetSrcPoint(), pAct->GetSrcSize(),
1991
0
                                                             pAct->GetBitmap(),
1992
0
                                                             pFncCol( pAct->GetColor(), pColParam ) )
1993
0
                                                           );
1994
0
            }
1995
0
            break;
1996
1997
0
            case MetaActionType::GRADIENT:
1998
0
            {
1999
0
                MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
2000
0
                Gradient            aGradient( pAct->GetGradient() );
2001
2002
0
                aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
2003
0
                aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
2004
0
                aMtf.push_back( new MetaGradientAction( pAct->GetRect(), std::move(aGradient) ) );
2005
0
            }
2006
0
            break;
2007
2008
0
            case MetaActionType::GRADIENTEX:
2009
0
            {
2010
0
                MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
2011
0
                Gradient              aGradient( pAct->GetGradient() );
2012
2013
0
                aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
2014
0
                aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
2015
0
                aMtf.push_back( new MetaGradientExAction( pAct->GetPolyPolygon(), std::move(aGradient) ) );
2016
0
            }
2017
0
            break;
2018
2019
0
            case MetaActionType::HATCH:
2020
0
            {
2021
0
                MetaHatchAction*    pAct = static_cast<MetaHatchAction*>(pAction);
2022
0
                Hatch               aHatch( pAct->GetHatch() );
2023
2024
0
                aHatch.SetColor( pFncCol( aHatch.GetColor(), pColParam ) );
2025
0
                aMtf.push_back( new MetaHatchAction( pAct->GetPolyPolygon(), aHatch ) );
2026
0
            }
2027
0
            break;
2028
2029
0
            case MetaActionType::FLOATTRANSPARENT:
2030
0
            {
2031
0
                MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
2032
0
                GDIMetaFile                 aTransMtf( pAct->GetGDIMetaFile() );
2033
2034
0
                aTransMtf.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam );
2035
0
                aMtf.push_back( new MetaFloatTransparentAction( aTransMtf,
2036
0
                                                                pAct->GetPoint(), pAct->GetSize(),
2037
0
                                                                pAct->GetGradient() )
2038
0
                                                              );
2039
0
            }
2040
0
            break;
2041
2042
0
            case MetaActionType::EPS:
2043
0
            {
2044
0
                MetaEPSAction*  pAct = static_cast<MetaEPSAction*>(pAction);
2045
0
                GDIMetaFile     aSubst( pAct->GetSubstitute() );
2046
2047
0
                aSubst.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam );
2048
0
                aMtf.push_back( new MetaEPSAction( pAct->GetPoint(), pAct->GetSize(),
2049
0
                                                   pAct->GetLink(), aSubst )
2050
0
                                                 );
2051
0
            }
2052
0
            break;
2053
2054
0
            default:
2055
0
            {
2056
0
                aMtf.push_back( pAction );
2057
0
            }
2058
0
            break;
2059
0
        }
2060
0
    }
2061
2062
0
    *this = aMtf;
2063
0
}
2064
2065
void GDIMetaFile::Adjust( short nLuminancePercent, short nContrastPercent,
2066
                          short nChannelRPercent, short nChannelGPercent,
2067
                          short nChannelBPercent, double fGamma, bool bInvert, bool msoBrightness )
2068
0
{
2069
    // nothing to do? => return quickly
2070
0
    if( !(nLuminancePercent || nContrastPercent ||
2071
0
          nChannelRPercent || nChannelGPercent || nChannelBPercent ||
2072
0
          ( fGamma != 1.0 ) || bInvert) )
2073
0
        return;
2074
2075
0
    double              fM, fROff, fGOff, fBOff, fOff;
2076
0
    ImplColAdjustParam  aColParam;
2077
0
    ImplBmpAdjustParam  aBmpParam;
2078
2079
0
    aColParam.pMapR.reset(new sal_uInt8[ 256 ]);
2080
0
    aColParam.pMapG.reset(new sal_uInt8[ 256 ]);
2081
0
    aColParam.pMapB.reset(new sal_uInt8[ 256 ]);
2082
2083
    // calculate slope
2084
0
    if( nContrastPercent >= 0 )
2085
0
        fM = 128.0 / ( 128.0 - 1.27 * std::clamp( nContrastPercent, short(0), short(100) ) );
2086
0
    else
2087
0
        fM = ( 128.0 + 1.27 * std::clamp( nContrastPercent, short(-100), short(0) ) ) / 128.0;
2088
2089
0
    if(!msoBrightness)
2090
        // total offset = luminance offset + contrast offset
2091
0
        fOff = std::clamp( nLuminancePercent, short(-100), short(100) ) * 2.55 + 128.0 - fM * 128.0;
2092
0
    else
2093
0
        fOff = std::clamp( nLuminancePercent, short(-100), short(100) ) * 2.55;
2094
2095
    // channel offset = channel offset  + total offset
2096
0
    fROff = nChannelRPercent * 2.55 + fOff;
2097
0
    fGOff = nChannelGPercent * 2.55 + fOff;
2098
0
    fBOff = nChannelBPercent * 2.55 + fOff;
2099
2100
    // calculate gamma value
2101
0
    fGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma );
2102
0
    const bool bGamma = ( fGamma != 1.0 );
2103
2104
    // create mapping table
2105
0
    for( tools::Long nX = 0; nX < 256; nX++ )
2106
0
    {
2107
0
        if(!msoBrightness)
2108
0
        {
2109
0
            aColParam.pMapR[nX] = basegfx::fround<sal_uInt8>(nX * fM + fROff);
2110
0
            aColParam.pMapG[nX] = basegfx::fround<sal_uInt8>(nX * fM + fGOff);
2111
0
            aColParam.pMapB[nX] = basegfx::fround<sal_uInt8>(nX * fM + fBOff);
2112
0
        }
2113
0
        else
2114
0
        {
2115
0
            aColParam.pMapR[nX] = basegfx::fround<sal_uInt8>((nX+fROff/2-128) * fM + 128 + fROff/2);
2116
0
            aColParam.pMapG[nX] = basegfx::fround<sal_uInt8>((nX+fGOff/2-128) * fM + 128 + fGOff/2);
2117
0
            aColParam.pMapB[nX] = basegfx::fround<sal_uInt8>((nX+fBOff/2-128) * fM + 128 + fBOff/2);
2118
0
        }
2119
0
        if( bGamma )
2120
0
        {
2121
0
            aColParam.pMapR[ nX ] = GAMMA( aColParam.pMapR[ nX ], fGamma );
2122
0
            aColParam.pMapG[ nX ] = GAMMA( aColParam.pMapG[ nX ], fGamma );
2123
0
            aColParam.pMapB[ nX ] = GAMMA( aColParam.pMapB[ nX ], fGamma );
2124
0
        }
2125
2126
0
        if( bInvert )
2127
0
        {
2128
0
            aColParam.pMapR[ nX ] = ~aColParam.pMapR[ nX ];
2129
0
            aColParam.pMapG[ nX ] = ~aColParam.pMapG[ nX ];
2130
0
            aColParam.pMapB[ nX ] = ~aColParam.pMapB[ nX ];
2131
0
        }
2132
0
    }
2133
2134
0
    aBmpParam.nLuminancePercent = nLuminancePercent;
2135
0
    aBmpParam.nContrastPercent = nContrastPercent;
2136
0
    aBmpParam.nChannelRPercent = nChannelRPercent;
2137
0
    aBmpParam.nChannelGPercent = nChannelGPercent;
2138
0
    aBmpParam.nChannelBPercent = nChannelBPercent;
2139
0
    aBmpParam.fGamma = fGamma;
2140
0
    aBmpParam.bInvert = bInvert;
2141
2142
    // do color adjustment
2143
0
    ImplExchangeColors( ImplColAdjustFnc, &aColParam, ImplBmpAdjustFnc, &aBmpParam );
2144
0
}
2145
2146
void GDIMetaFile::Convert( MtfConversion eConversion )
2147
0
{
2148
0
    ImplColConvertParam aColParam;
2149
0
    ImplBmpConvertParam aBmpParam;
2150
2151
0
    aColParam.eConversion = eConversion;
2152
0
    aBmpParam.eConversion = ( MtfConversion::N1BitThreshold == eConversion ) ? BmpConversion::N1BitThreshold : BmpConversion::N8BitGreys;
2153
2154
0
    ImplExchangeColors( ImplColConvertFnc, &aColParam, ImplBmpConvertFnc, &aBmpParam );
2155
0
}
2156
2157
void GDIMetaFile::ReplaceColors( const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount )
2158
0
{
2159
0
    ImplColReplaceParam aColParam;
2160
0
    ImplBmpReplaceParam aBmpParam;
2161
2162
0
    aColParam.pMinR.reset(new sal_uLong[ nColorCount ]);
2163
0
    aColParam.pMaxR.reset(new sal_uLong[ nColorCount ]);
2164
0
    aColParam.pMinG.reset(new sal_uLong[ nColorCount ]);
2165
0
    aColParam.pMaxG.reset(new sal_uLong[ nColorCount ]);
2166
0
    aColParam.pMinB.reset(new sal_uLong[ nColorCount ]);
2167
0
    aColParam.pMaxB.reset(new sal_uLong[ nColorCount ]);
2168
2169
0
    for( sal_uLong i = 0; i < nColorCount; i++ )
2170
0
    {
2171
0
        tools::Long        nVal;
2172
2173
0
        nVal = pSearchColors[ i ].GetRed();
2174
0
        aColParam.pMinR[ i ] = static_cast<sal_uLong>(std::max( nVal, tools::Long(0) ));
2175
0
        aColParam.pMaxR[ i ] = static_cast<sal_uLong>(std::min( nVal, tools::Long(255) ));
2176
2177
0
        nVal = pSearchColors[ i ].GetGreen();
2178
0
        aColParam.pMinG[ i ] = static_cast<sal_uLong>(std::max( nVal, tools::Long(0) ));
2179
0
        aColParam.pMaxG[ i ] = static_cast<sal_uLong>(std::min( nVal, tools::Long(255) ));
2180
2181
0
        nVal = pSearchColors[ i ].GetBlue();
2182
0
        aColParam.pMinB[ i ] = static_cast<sal_uLong>(std::max( nVal, tools::Long(0) ));
2183
0
        aColParam.pMaxB[ i ] = static_cast<sal_uLong>(std::min( nVal, tools::Long(255) ));
2184
0
    }
2185
2186
0
    aColParam.pDstCols = pReplaceColors;
2187
0
    aColParam.nCount = nColorCount;
2188
2189
0
    aBmpParam.pSrcCols = pSearchColors;
2190
0
    aBmpParam.pDstCols = pReplaceColors;
2191
0
    aBmpParam.nCount = nColorCount;
2192
2193
0
    ImplExchangeColors( ImplColReplaceFnc, &aColParam, ImplBmpReplaceFnc, &aBmpParam );
2194
0
};
2195
2196
GDIMetaFile GDIMetaFile::GetMonochromeMtf( const Color& rColor ) const
2197
0
{
2198
0
    GDIMetaFile aRet( *this );
2199
2200
0
    ImplColMonoParam    aColParam;
2201
0
    ImplBmpMonoParam    aBmpParam;
2202
2203
0
    aColParam.aColor = rColor;
2204
0
    aBmpParam.aColor = rColor;
2205
2206
0
    aRet.ImplExchangeColors( ImplColMonoFnc, &aColParam, ImplBmpMonoFnc, &aBmpParam );
2207
2208
0
    return aRet;
2209
0
}
2210
2211
sal_uLong GDIMetaFile::GetSizeBytes() const
2212
3.23k
{
2213
3.23k
    sal_uLong nSizeBytes = 0;
2214
2215
394k
    for( size_t i = 0, nObjCount = GetActionSize(); i < nObjCount; ++i )
2216
391k
    {
2217
391k
        MetaAction* pAction = GetAction( i );
2218
2219
        // default action size is set to 32 (=> not the exact value)
2220
391k
        nSizeBytes += 32;
2221
2222
        // add sizes for large action content
2223
391k
        switch( pAction->GetType() )
2224
391k
        {
2225
0
            case MetaActionType::BMP:            nSizeBytes += static_cast<MetaBmpAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2226
596
            case MetaActionType::BMPSCALE:       nSizeBytes += static_cast<MetaBmpScaleAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2227
797
            case MetaActionType::BMPSCALEPART:   nSizeBytes += static_cast<MetaBmpScalePartAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2228
2229
213
            case MetaActionType::BMPEX:          nSizeBytes += static_cast<MetaBmpExAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2230
229
            case MetaActionType::BMPEXSCALE:     nSizeBytes += static_cast<MetaBmpExScaleAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2231
553
            case MetaActionType::BMPEXSCALEPART: nSizeBytes += static_cast<MetaBmpExScalePartAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2232
2233
382
            case MetaActionType::MASK:           nSizeBytes += static_cast<MetaMaskAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2234
754
            case MetaActionType::MASKSCALE:      nSizeBytes += static_cast<MetaMaskScaleAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2235
534
            case MetaActionType::MASKSCALEPART:  nSizeBytes += static_cast<MetaMaskScalePartAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2236
2237
1.01k
            case MetaActionType::POLYLINE:       nSizeBytes += static_cast<MetaPolyLineAction*>( pAction )->GetPolygon().GetSize() * sizeof( Point ); break;
2238
4.29k
            case MetaActionType::POLYGON:        nSizeBytes += static_cast<MetaPolygonAction*>( pAction )->GetPolygon().GetSize() * sizeof( Point ); break;
2239
326
            case MetaActionType::POLYPOLYGON:
2240
326
            {
2241
326
                const tools::PolyPolygon& rPolyPoly = static_cast<MetaPolyPolygonAction*>( pAction )->GetPolyPolygon();
2242
2243
348
                for( sal_uInt16 n = 0; n < rPolyPoly.Count(); ++n )
2244
22
                    nSizeBytes += ( rPolyPoly[ n ].GetSize() * sizeof( Point ) );
2245
326
            }
2246
326
            break;
2247
2248
111
            case MetaActionType::TEXT:        nSizeBytes += static_cast<MetaTextAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2249
18
            case MetaActionType::STRETCHTEXT: nSizeBytes += static_cast<MetaStretchTextAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2250
2.44k
            case MetaActionType::TEXTRECT:    nSizeBytes += static_cast<MetaTextRectAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2251
3.86k
            case MetaActionType::TEXTARRAY:
2252
3.86k
            {
2253
3.86k
                MetaTextArrayAction* pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
2254
2255
3.86k
                nSizeBytes += ( pTextArrayAction->GetText().getLength() * sizeof( sal_Unicode ) );
2256
2257
3.86k
                if( !pTextArrayAction->GetDXArray().empty() )
2258
2.74k
                    nSizeBytes += ( pTextArrayAction->GetLen() << 2 );
2259
3.86k
            }
2260
3.86k
            break;
2261
375k
            default: break;
2262
391k
        }
2263
391k
    }
2264
2265
3.23k
    return nSizeBytes;
2266
3.23k
}
2267
2268
bool GDIMetaFile::CreateThumbnail(Bitmap& rBitmap, BmpConversion eColorConversion, BmpScaleFlag nScaleFlag) const
2269
0
{
2270
    // initialization seems to be complicated but is used to avoid rounding errors
2271
0
    ScopedVclPtrInstance< VirtualDevice > aVDev;
2272
    // set Enable to tease the rendering down the code paths which use B2DPolygon and
2273
    // avoid integer overflows on scaling tools::Polygon, e.g. moz1545040-1.svg
2274
    // note: this is similar to DocumentToGraphicRenderer::renderToGraphic
2275
0
    aVDev->SetAntialiasing(AntialiasingFlags::Enable | aVDev->GetAntialiasing());
2276
0
    const Point     aNullPt;
2277
0
    const Point     aTLPix( aVDev->LogicToPixel( aNullPt, GetPrefMapMode() ) );
2278
0
    const Point     aBRPix( aVDev->LogicToPixel( Point( GetPrefSize().Width() - 1, GetPrefSize().Height() - 1 ), GetPrefMapMode() ) );
2279
0
    Size            aDrawSize( aVDev->LogicToPixel( GetPrefSize(), GetPrefMapMode() ) );
2280
0
    Size            aSizePix( std::abs( aBRPix.X() - aTLPix.X() ) + 1, std::abs( aBRPix.Y() - aTLPix.Y() ) + 1 );
2281
0
    sal_uInt32      nMaximumExtent = 512;
2282
2283
0
    if (!rBitmap.IsEmpty())
2284
0
        rBitmap.SetEmpty();
2285
2286
    // determine size that has the same aspect ratio as image size and
2287
    // fits into the rectangle determined by nMaximumExtent
2288
0
    if ( aSizePix.Width() && aSizePix.Height()
2289
0
      && ( sal::static_int_cast< tools::ULong >(aSizePix.Width()) >
2290
0
               nMaximumExtent ||
2291
0
           sal::static_int_cast< tools::ULong >(aSizePix.Height()) >
2292
0
               nMaximumExtent ) )
2293
0
    {
2294
0
        const Size  aOldSizePix( aSizePix );
2295
0
        double      fWH = static_cast< double >( aSizePix.Width() ) / aSizePix.Height();
2296
2297
0
        if ( fWH <= 1.0 )
2298
0
        {
2299
0
            aSizePix.setWidth(basegfx::fround<tools::Long>(nMaximumExtent * fWH));
2300
0
            aSizePix.setHeight( nMaximumExtent );
2301
0
        }
2302
0
        else
2303
0
        {
2304
0
            aSizePix.setWidth( nMaximumExtent );
2305
0
            aSizePix.setHeight(basegfx::fround<tools::Long>(nMaximumExtent / fWH));
2306
0
        }
2307
2308
0
        aDrawSize.setWidth( basegfx::fround<tools::Long>( ( static_cast< double >( aDrawSize.Width() ) * aSizePix.Width() ) / aOldSizePix.Width() ) );
2309
0
        aDrawSize.setHeight( basegfx::fround<tools::Long>( ( static_cast< double >( aDrawSize.Height() ) * aSizePix.Height() ) / aOldSizePix.Height() ) );
2310
0
    }
2311
2312
    // draw image(s) into VDev and get resulting image
2313
    // do it 4x larger to be able to scale it down & get beautiful antialias
2314
0
    Size aAntialiasSize(aSizePix.Width() * 4, aSizePix.Height() * 4);
2315
0
    if (aVDev->SetOutputSizePixel(aAntialiasSize))
2316
0
    {
2317
        // antialias: provide 4x larger size, and then scale down the result
2318
0
        Size aAntialias(aDrawSize.Width() * 4, aDrawSize.Height() * 4);
2319
2320
        // draw metafile into VDev
2321
0
        const_cast<GDIMetaFile *>(this)->WindStart();
2322
0
        const_cast<GDIMetaFile *>(this)->Play(*aVDev, Point(), aAntialias);
2323
2324
        // get paint bitmap
2325
0
        Bitmap aBitmap( aVDev->GetBitmap( aNullPt, aVDev->GetOutputSizePixel() ) );
2326
2327
        // scale down the image to the desired size - use the input scaler for the scaling operation
2328
0
        aBitmap.Scale(aDrawSize, nScaleFlag);
2329
2330
        // convert to desired bitmap color format
2331
0
        Size aSize(aBitmap.GetSizePixel());
2332
0
        if (aSize.Width() && aSize.Height())
2333
0
            aBitmap.Convert(eColorConversion);
2334
2335
0
        rBitmap = std::move(aBitmap);
2336
0
    }
2337
2338
0
    return !rBitmap.IsEmpty();
2339
0
}
2340
2341
void GDIMetaFile::UseCanvas( bool _bUseCanvas )
2342
261
{
2343
261
    m_bUseCanvas = _bUseCanvas;
2344
261
}
2345
2346
void GDIMetaFile::dumpAsXml(const char* pFileName) const
2347
0
{
2348
0
    SvFileStream aStream(pFileName ? OUString::fromUtf8(pFileName) : u"file:///tmp/metafile.xml"_ustr,
2349
0
            StreamMode::STD_READWRITE | StreamMode::TRUNC);
2350
    assert(aStream.good());
2351
0
    MetafileXmlDump aDumper;
2352
0
    aDumper.dump(*this, aStream);
2353
0
}
2354
2355
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */