Coverage Report

Created: 2026-10-14 09:33

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