Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/gdi/virdev.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <sal/config.h>
21
22
#include <comphelper/lok.hxx>
23
#include <sal/log.hxx>
24
#include <tools/debug.hxx>
25
#include <tools/fract.hxx>
26
27
#include <vcl/pdfextoutdevdata.hxx>
28
#include <vcl/rendercontext/AntialiasingFlags.hxx>
29
#include <vcl/virdev.hxx>
30
31
#include <ImplOutDevData.hxx>
32
#include <font/PhysicalFontCollection.hxx>
33
#include <font/PhysicalFontFaceCollection.hxx>
34
#include <impfontcache.hxx>
35
#include <salinst.hxx>
36
#include <salgdi.hxx>
37
#include <salvd.hxx>
38
#include <svdata.hxx>
39
40
using namespace ::com::sun::star::uno;
41
42
bool VirtualDevice::CanEnableNativeWidget() const
43
0
{
44
0
    const vcl::ExtOutDevData* pOutDevData(GetExtOutDevData());
45
0
    const vcl::PDFExtOutDevData* pPDFData(dynamic_cast<const vcl::PDFExtOutDevData*>(pOutDevData));
46
0
    return pPDFData == nullptr;
47
0
}
48
49
bool VirtualDevice::AcquireGraphics() const
50
402k
{
51
402k
    DBG_TESTSOLARMUTEX();
52
53
402k
    if ( mpGraphics )
54
13.9k
        return true;
55
56
388k
    mbInitLineColor     = true;
57
388k
    mbInitFillColor     = true;
58
388k
    mbInitFont          = true;
59
388k
    mbInitTextColor     = true;
60
388k
    mbInitClipRegion    = true;
61
62
388k
    ImplSVData* pSVData = ImplGetSVData();
63
64
388k
    if ( mpVirDev )
65
388k
    {
66
388k
        mpGraphics = mpVirDev->AcquireGraphics();
67
        // if needed retry after releasing least recently used virtual device graphics
68
388k
        while ( !mpGraphics )
69
0
        {
70
0
            if ( !pSVData->maGDIData.mpLastVirGraphics )
71
0
                break;
72
0
            pSVData->maGDIData.mpLastVirGraphics->ReleaseGraphics();
73
0
            mpGraphics = mpVirDev->AcquireGraphics();
74
0
        }
75
        // update global LRU list of virtual device graphics
76
388k
        if ( mpGraphics )
77
388k
        {
78
388k
            mpNextGraphics = pSVData->maGDIData.mpFirstVirGraphics;
79
388k
            pSVData->maGDIData.mpFirstVirGraphics = const_cast<VirtualDevice*>(this);
80
388k
            if ( mpNextGraphics )
81
258k
                mpNextGraphics->mpPrevGraphics = const_cast<VirtualDevice*>(this);
82
388k
            if ( !pSVData->maGDIData.mpLastVirGraphics )
83
130k
                pSVData->maGDIData.mpLastVirGraphics = const_cast<VirtualDevice*>(this);
84
388k
        }
85
388k
    }
86
87
388k
    if ( mpGraphics )
88
388k
    {
89
388k
        mpGraphics->SetXORMode( (RasterOp::Invert == meRasterOp) || (RasterOp::Xor == meRasterOp), RasterOp::Invert == meRasterOp );
90
388k
        mpGraphics->setAntiAlias(bool(mnAntialiasing & AntialiasingFlags::Enable));
91
388k
    }
92
93
388k
    return mpGraphics != nullptr;
94
402k
}
95
96
void VirtualDevice::ReleaseGraphics( bool bRelease )
97
996k
{
98
996k
    DBG_TESTSOLARMUTEX();
99
100
996k
    if ( !mpGraphics )
101
608k
        return;
102
103
    // release the fonts of the physically released graphics device
104
388k
    if ( bRelease )
105
388k
        ImplReleaseFonts();
106
107
388k
    ImplSVData* pSVData = ImplGetSVData();
108
109
388k
    VirtualDevice* pVirDev = this;
110
111
388k
    if ( bRelease )
112
388k
        pVirDev->mpVirDev->ReleaseGraphics( mpGraphics );
113
    // remove from global LRU list of virtual device graphics
114
388k
    if ( mpPrevGraphics )
115
1.19k
        mpPrevGraphics->mpNextGraphics = mpNextGraphics;
116
387k
    else
117
387k
        pSVData->maGDIData.mpFirstVirGraphics = mpNextGraphics;
118
388k
    if ( mpNextGraphics )
119
257k
        mpNextGraphics->mpPrevGraphics = mpPrevGraphics;
120
130k
    else
121
130k
        pSVData->maGDIData.mpLastVirGraphics = mpPrevGraphics;
122
123
388k
    mpGraphics      = nullptr;
124
388k
    mpPrevGraphics  = nullptr;
125
388k
    mpNextGraphics  = nullptr;
126
388k
}
127
128
void VirtualDevice::ImplInitVirDev( const OutputDevice* pOutDev,
129
                                    tools::Long nDX, tools::Long nDY, const SystemGraphicsData *pData )
130
1.00M
{
131
1.00M
    SAL_INFO( "vcl.virdev", "ImplInitVirDev(" << nDX << "," << nDY << ")" );
132
133
1.00M
    meRefDevMode = RefDevMode::NONE;
134
1.00M
    mbForceZeroExtleadBug = false;
135
1.00M
    mnBitCount = 0;
136
1.00M
    mbScreenComp = false;
137
138
139
1.00M
    bool bErase = nDX > 0 && nDY > 0;
140
141
1.00M
    if ( nDX < 1 )
142
1.00M
        nDX = 1;
143
144
1.00M
    if ( nDY < 1 )
145
1.00M
        nDY = 1;
146
147
1.00M
    ImplSVData* pSVData = ImplGetSVData();
148
149
1.00M
    if ( !pOutDev )
150
0
        pOutDev = ImplGetDefaultWindow()->GetOutDev();
151
1.00M
    if( !pOutDev )
152
0
        return;
153
154
1.00M
    SalGraphics* pGraphics;
155
1.00M
    if ( !pOutDev->mpGraphics )
156
16.4k
        (void)pOutDev->AcquireGraphics();
157
1.00M
    pGraphics = pOutDev->mpGraphics;
158
1.00M
    if ( pGraphics )
159
1.00M
    {
160
1.00M
        if (pData)
161
0
            mpVirDev = pSVData->mpDefInst->CreateVirtualDevice(*pGraphics, nDX, nDY, meFormatAndAlpha, *pData);
162
1.00M
        else
163
1.00M
            mpVirDev = pSVData->mpDefInst->CreateVirtualDevice(*pGraphics, nDX, nDY, meFormatAndAlpha);
164
1.00M
    }
165
0
    else
166
0
        mpVirDev = nullptr;
167
1.00M
    if ( !mpVirDev )
168
0
    {
169
        // do not abort but throw an exception, may be the current thread terminates anyway (plugin-scenario)
170
0
        throw css::uno::RuntimeException(
171
0
            u"Could not create system bitmap!"_ustr,
172
0
            css::uno::Reference< css::uno::XInterface >() );
173
0
    }
174
175
1.00M
    mnBitCount = pOutDev->GetBitCount();
176
1.00M
    SetOutputWidthPixel(nDX);
177
1.00M
    SetOutputHeightPixel(nDY);
178
179
1.00M
    mbScreenComp    = pOutDev->IsScreenComp();
180
181
1.00M
    mbDevOutput     = true;
182
1.00M
    mxFontCollection = pSVData->maGDIData.mxScreenFontList;
183
1.00M
    mxFontCache     = pSVData->maGDIData.mxScreenFontCache;
184
185
1.00M
    SetDPIX(pOutDev->GetDPIX());
186
1.00M
    SetDPIY(pOutDev->GetDPIY());
187
1.00M
    SetDPIScalePercentage(pOutDev->GetDPIScalePercentage());
188
189
1.00M
    maFont          = pOutDev->maFont;
190
191
1.00M
    if( maTextColor != pOutDev->maTextColor )
192
4.01k
    {
193
4.01k
        maTextColor = pOutDev->maTextColor;
194
4.01k
        mbInitTextColor = true;
195
4.01k
    }
196
197
    // virtual devices have white background by default
198
1.00M
    SetBackground( Wallpaper( COL_WHITE ) );
199
200
    // #i59283# don't erase user-provided surface
201
1.00M
    if( !pData && bErase)
202
0
        Erase();
203
204
    // register VirDev in the list
205
1.00M
    mpNext = pSVData->maGDIData.mpFirstVirDev;
206
1.00M
    mpPrev = nullptr;
207
1.00M
    if ( mpNext )
208
831k
        mpNext->mpPrev = this;
209
1.00M
    pSVData->maGDIData.mpFirstVirDev = this;
210
1.00M
}
211
212
VirtualDevice::VirtualDevice(const OutputDevice* pCompDev, DeviceFormat eFormatAndAlpha,
213
                             OutDevType eOutDevType)
214
1.00M
    : OutputDevice(eOutDevType)
215
1.00M
    , meFormatAndAlpha(eFormatAndAlpha)
216
1.00M
{
217
1.00M
    SAL_INFO( "vcl.virdev", "VirtualDevice::VirtualDevice( " << static_cast<int>(eFormatAndAlpha)
218
1.00M
                            << ", " << static_cast<int>(eOutDevType) << " )" );
219
220
1.00M
    ImplInitVirDev(pCompDev ? pCompDev : Application::GetDefaultDevice(), 0, 0);
221
1.00M
}
VirtualDevice::VirtualDevice(OutputDevice const*, DeviceFormat, OutDevType)
Line
Count
Source
214
4.01k
    : OutputDevice(eOutDevType)
215
4.01k
    , meFormatAndAlpha(eFormatAndAlpha)
216
4.01k
{
217
4.01k
    SAL_INFO( "vcl.virdev", "VirtualDevice::VirtualDevice( " << static_cast<int>(eFormatAndAlpha)
218
4.01k
                            << ", " << static_cast<int>(eOutDevType) << " )" );
219
220
4.01k
    ImplInitVirDev(pCompDev ? pCompDev : Application::GetDefaultDevice(), 0, 0);
221
4.01k
}
VirtualDevice::VirtualDevice(OutputDevice const*, DeviceFormat, OutDevType)
Line
Count
Source
214
998k
    : OutputDevice(eOutDevType)
215
998k
    , meFormatAndAlpha(eFormatAndAlpha)
216
998k
{
217
998k
    SAL_INFO( "vcl.virdev", "VirtualDevice::VirtualDevice( " << static_cast<int>(eFormatAndAlpha)
218
998k
                            << ", " << static_cast<int>(eOutDevType) << " )" );
219
220
998k
    ImplInitVirDev(pCompDev ? pCompDev : Application::GetDefaultDevice(), 0, 0);
221
998k
}
222
223
VirtualDevice::VirtualDevice(const SystemGraphicsData& rData, const Size &rSize,
224
                             DeviceFormat eFormat)
225
0
    : OutputDevice(OUTDEV_VIRDEV)
226
0
    , meFormatAndAlpha(eFormat)
227
0
{
228
0
    SAL_INFO( "vcl.virdev", "VirtualDevice::VirtualDevice( " << static_cast<int>(eFormat) << " )" );
229
230
0
    ImplInitVirDev(Application::GetDefaultDevice(), rSize.Width(), rSize.Height(), &rData);
231
0
}
Unexecuted instantiation: VirtualDevice::VirtualDevice(SystemGraphicsData const&, Size const&, DeviceFormat)
Unexecuted instantiation: VirtualDevice::VirtualDevice(SystemGraphicsData const&, Size const&, DeviceFormat)
232
233
VirtualDevice::~VirtualDevice()
234
996k
{
235
996k
    SAL_INFO( "vcl.virdev", "VirtualDevice::~VirtualDevice()" );
236
996k
    disposeOnce();
237
996k
}
238
239
void VirtualDevice::dispose()
240
996k
{
241
996k
    SAL_INFO( "vcl.virdev", "VirtualDevice::dispose()" );
242
243
996k
    ImplSVData* pSVData = ImplGetSVData();
244
245
996k
    ReleaseGraphics();
246
247
996k
    mpVirDev.reset();
248
249
    // remove this VirtualDevice from the double-linked global list
250
996k
    if( mpPrev )
251
101k
        mpPrev->mpNext = mpNext;
252
895k
    else
253
895k
        pSVData->maGDIData.mpFirstVirDev = mpNext;
254
255
996k
    if( mpNext )
256
825k
        mpNext->mpPrev = mpPrev;
257
258
996k
    OutputDevice::dispose();
259
996k
}
260
261
bool VirtualDevice::SetOutputSizePixel(const Size& rNewSize, bool bErase,
262
                                       bool bAlphaMaskTransparent)
263
17.1k
{
264
17.1k
    SAL_INFO("vcl.virdev", "VirtualDevice::SetOutputSizePixel( " << rNewSize.Width() << ", "
265
17.1k
                                                                 << rNewSize.Height() << ", "
266
17.1k
                                                                 << int(bErase) << " )");
267
268
17.1k
    if ( !mpVirDev )
269
0
        return false;
270
17.1k
    else if ( rNewSize == GetOutputSizePixel() )
271
2
    {
272
2
        if ( bErase )
273
2
            Erase();
274
2
        SAL_INFO( "vcl.virdev", "Trying to re-use a VirtualDevice but this time using a pre-allocated buffer");
275
2
        return true;
276
2
    }
277
278
17.1k
    bool bRet;
279
17.1k
    tools::Long nNewWidth = rNewSize.Width(), nNewHeight = rNewSize.Height();
280
281
17.1k
    if ( nNewWidth < 1 )
282
0
        nNewWidth = 1;
283
284
17.1k
    if ( nNewHeight < 1 )
285
0
        nNewHeight = 1;
286
287
17.1k
    if ( bErase )
288
17.1k
    {
289
17.1k
        bRet = mpVirDev->SetSize( nNewWidth, nNewHeight, bAlphaMaskTransparent );
290
17.1k
        if ( bRet )
291
17.1k
        {
292
17.1k
            SetOutputWidthPixel(rNewSize.Width());
293
17.1k
            SetOutputHeightPixel(rNewSize.Height());
294
295
            // So, in theory, the bAlphaMaskTransparent param to the SetSize() call just above should
296
            // be initialising the data to transparent. But this only works on Linux. On Windows and macOS we
297
            // have two different kinds of problems in the VirtualDevice subclasses,
298
            // which means that it ends up being completely ineffective.
299
            // So just take the heavy handed approach here and force the data to transparent.
300
17.1k
            if (bAlphaMaskTransparent)
301
0
                DrawWallpaper(tools::Rectangle(0, 0, GetOutputWidthPixel(), GetOutputHeightPixel()), Wallpaper(COL_TRANSPARENT));
302
17.1k
            else
303
17.1k
                Erase();
304
17.1k
        }
305
17.1k
    }
306
0
    else
307
0
    {
308
        // we need a graphics
309
0
        if ( !mpGraphics && !AcquireGraphics() )
310
0
            return false;
311
312
0
        assert(mpGraphics);
313
314
0
        std::unique_ptr<SalVirtualDevice> pNewVirDev = GetSalInstance()->CreateVirtualDevice(
315
0
            *mpGraphics, nNewWidth, nNewHeight, meFormatAndAlpha, bAlphaMaskTransparent);
316
0
        if ( pNewVirDev )
317
0
        {
318
0
            SalGraphics* pGraphics = pNewVirDev->AcquireGraphics();
319
0
            if ( pGraphics )
320
0
            {
321
0
                tools::Long nWidth;
322
0
                tools::Long nHeight;
323
0
                if ( GetOutputWidthPixel() < nNewWidth )
324
0
                    nWidth = GetOutputWidthPixel();
325
0
                else
326
0
                    nWidth = nNewWidth;
327
0
                if ( GetOutputHeightPixel() < nNewHeight )
328
0
                    nHeight = GetOutputHeightPixel();
329
0
                else
330
0
                    nHeight = nNewHeight;
331
0
                SalTwoRect aPosAry(0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight);
332
0
                pGraphics->CopyBits( aPosAry, *mpGraphics, *this, *this );
333
0
                pNewVirDev->ReleaseGraphics( pGraphics );
334
0
                ReleaseGraphics();
335
0
                mpVirDev = std::move(pNewVirDev);
336
0
                SetOutputWidthPixel(rNewSize.Width());
337
0
                SetOutputHeightPixel(rNewSize.Height());
338
0
                bRet = true;
339
0
            }
340
0
            else
341
0
            {
342
0
                bRet = false;
343
0
            }
344
0
        }
345
0
        else
346
0
            bRet = false;
347
0
    }
348
349
17.1k
    return bRet;
350
17.1k
}
351
352
void VirtualDevice::EnableRTL( bool bEnable )
353
2.86k
{
354
    // virdevs default to not mirroring, they will only be set to mirroring
355
    // under rare circumstances in the UI, eg the valueset control
356
    // because each virdev has its own SalGraphics we can safely switch the SalGraphics here
357
    // ...hopefully
358
2.86k
    if( AcquireGraphics() )
359
2.86k
        mpGraphics->SetLayout( bEnable ? SalLayoutFlags::BiDiRtl : SalLayoutFlags::NONE );
360
361
2.86k
    OutputDevice::EnableRTL(bEnable);
362
2.86k
}
363
364
bool VirtualDevice::SetOutputSizePixelScaleOffsetAndLOKBuffer(
365
    const Size& rNewSize, double fScale, const Point& rNewOffset,
366
    sal_uInt8 *const pBuffer)
367
0
{
368
    // If this is ever needed for something else than LOK, changes will
369
    // be needed in SvpSalVirtualDevice::CreateSurface() .
370
0
    assert(comphelper::LibreOfficeKit::isActive());
371
0
    assert(pBuffer);
372
0
    MapMode mm = GetMapMode();
373
0
    mm.SetOrigin( rNewOffset );
374
0
    mm.SetScaleX( fScale );
375
0
    mm.SetScaleY( fScale );
376
0
    SetMapMode( mm );
377
378
0
    assert(meFormatAndAlpha == DeviceFormat::WITHOUT_ALPHA);
379
0
    assert(mpVirDev);
380
0
    assert( rNewSize != GetOutputSizePixel() &&  "Trying to re-use a VirtualDevice but this time using a pre-allocated buffer");
381
0
    assert( rNewSize.Width() >= 1 );
382
0
    assert( rNewSize.Height() >= 1 );
383
384
0
    bool bRet = mpVirDev->SetSizeUsingBuffer( rNewSize.Width(), rNewSize.Height(), pBuffer );
385
0
    if ( bRet )
386
0
    {
387
0
        SetOutputWidthPixel(rNewSize.Width());
388
0
        SetOutputHeightPixel(rNewSize.Height());
389
0
    }
390
391
0
    return bRet;
392
393
0
}
394
395
void VirtualDevice::SetReferenceDevice( RefDevMode i_eRefDevMode )
396
110k
{
397
110k
    sal_Int32 nDPIX = 600, nDPIY = 600;
398
110k
    switch( i_eRefDevMode )
399
110k
    {
400
0
    case RefDevMode::NONE:
401
0
    default:
402
0
        SAL_WARN( "vcl.virdev", "VDev::SetRefDev illegal argument!" );
403
0
        break;
404
115
    case RefDevMode::Dpi600:
405
115
        nDPIX = nDPIY = 600;
406
115
        break;
407
106k
    case RefDevMode::MSO1:
408
106k
        nDPIX = nDPIY = 6*1440;
409
106k
        break;
410
4.01k
    case RefDevMode::PDF1:
411
4.01k
        nDPIX = nDPIY = 720;
412
4.01k
        break;
413
110k
    }
414
110k
    ImplSetReferenceDevice( i_eRefDevMode, nDPIX, nDPIY );
415
110k
}
416
417
void VirtualDevice::SetReferenceDevice( sal_Int32 i_nDPIX, sal_Int32 i_nDPIY )
418
0
{
419
0
    ImplSetReferenceDevice( RefDevMode::Custom, i_nDPIX, i_nDPIY );
420
0
}
421
422
bool VirtualDevice::IsVirtual() const
423
0
{
424
0
    return true;
425
0
}
426
427
void VirtualDevice::ImplSetReferenceDevice( RefDevMode i_eRefDevMode, sal_Int32 i_nDPIX, sal_Int32 i_nDPIY )
428
110k
{
429
110k
    SetDPIX(i_nDPIX);
430
110k
    SetDPIY(i_nDPIY);
431
110k
    SetDPIScalePercentage(100);
432
433
110k
    EnableOutput( false );  // prevent output on reference device
434
110k
    mbScreenComp = false;
435
436
    // invalidate currently selected fonts
437
110k
    mbInitFont = true;
438
110k
    mbNewFont = true;
439
440
    // avoid adjusting font lists when already in refdev mode
441
110k
    RefDevMode nOldRefDevMode = meRefDevMode;
442
110k
    meRefDevMode = i_eRefDevMode;
443
110k
    if( nOldRefDevMode != RefDevMode::NONE )
444
0
        return;
445
446
    // the reference device should have only scalable fonts
447
    // => clean up the original font lists before getting new ones
448
110k
    mpFontInstance.clear();
449
110k
    mpFontFaceCollection.reset();
450
451
    // preserve global font lists
452
110k
    ImplSVData* pSVData = ImplGetSVData();
453
110k
    mxFontCollection.reset();
454
110k
    mxFontCache.reset();
455
456
    // get font list with scalable fonts only
457
110k
    (void)AcquireGraphics();
458
110k
    mxFontCollection = pSVData->maGDIData.mxScreenFontList->Clone();
459
460
    // prepare to use new font lists
461
110k
    mxFontCache = std::make_shared<ImplFontCache>();
462
110k
}
463
464
sal_uInt16 VirtualDevice::GetBitCount() const
465
1.35k
{
466
1.35k
    return mnBitCount;
467
1.35k
}
468
469
bool VirtualDevice::UsePolyPolygonForComplexGradient()
470
1.00k
{
471
1.00k
    return true;
472
1.00k
}
473
474
void VirtualDevice::Compat_ZeroExtleadBug()
475
0
{
476
0
    mbForceZeroExtleadBug = true;
477
0
}
478
479
tools::Long VirtualDevice::GetFontExtLeading() const
480
16.0M
{
481
16.0M
#ifdef UNX
482
    // backwards compatible line metrics after fixing #i60945#
483
16.0M
    if ( mbForceZeroExtleadBug )
484
0
        return 0;
485
16.0M
#endif
486
487
16.0M
    return mpFontInstance->mxFontMetric->GetExternalLeading();
488
16.0M
}
489
490
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */