office-gobmx/vcl/source/gdi/virdev.cxx
Luboš Luňák 91b98efc2a make VirtualDevice::SetOutputSizePixelScaleOffsetAndBuffer() LOK-only
Because it's used only for LOK, and SvpSalVirtualDevice::CreateSurface()
otherwise wouldn't know whether to apply LOK DPI settings or not
(since this might be called for LOK tiled painting, when it should,
or it might be called from somewhere else while LOK is active, in which
case this should be handled normally). Getting that mismatched can cause
things like https://github.com/CollaboraOnline/online/issues/4834 .

Change-Id: I1df7b8a169c8ef2e799731a6695a032948536582
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135588
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
2022-06-13 10:36:35 +02:00

524 lines
16 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <comphelper/lok.hxx>
#include <sal/log.hxx>
#include <tools/debug.hxx>
#include <vcl/pdfextoutdevdata.hxx>
#include <vcl/virdev.hxx>
#include <ImplOutDevData.hxx>
#include <font/PhysicalFontCollection.hxx>
#include <font/PhysicalFontFaceCollection.hxx>
#include <impfontcache.hxx>
#include <salinst.hxx>
#include <salgdi.hxx>
#include <salvd.hxx>
#include <svdata.hxx>
using namespace ::com::sun::star::uno;
bool VirtualDevice::CanEnableNativeWidget() const
{
const vcl::ExtOutDevData* pOutDevData(GetExtOutDevData());
const vcl::PDFExtOutDevData* pPDFData(dynamic_cast<const vcl::PDFExtOutDevData*>(pOutDevData));
return pPDFData == nullptr;
}
bool VirtualDevice::AcquireGraphics() const
{
DBG_TESTSOLARMUTEX();
if ( mpGraphics )
return true;
mbInitLineColor = true;
mbInitFillColor = true;
mbInitFont = true;
mbInitTextColor = true;
mbInitClipRegion = true;
ImplSVData* pSVData = ImplGetSVData();
if ( mpVirDev )
{
mpGraphics = mpVirDev->AcquireGraphics();
// if needed retry after releasing least recently used virtual device graphics
while ( !mpGraphics )
{
if ( !pSVData->maGDIData.mpLastVirGraphics )
break;
pSVData->maGDIData.mpLastVirGraphics->ReleaseGraphics();
mpGraphics = mpVirDev->AcquireGraphics();
}
// update global LRU list of virtual device graphics
if ( mpGraphics )
{
mpNextGraphics = pSVData->maGDIData.mpFirstVirGraphics;
pSVData->maGDIData.mpFirstVirGraphics = const_cast<VirtualDevice*>(this);
if ( mpNextGraphics )
mpNextGraphics->mpPrevGraphics = const_cast<VirtualDevice*>(this);
if ( !pSVData->maGDIData.mpLastVirGraphics )
pSVData->maGDIData.mpLastVirGraphics = const_cast<VirtualDevice*>(this);
}
}
if ( mpGraphics )
{
mpGraphics->SetXORMode( (RasterOp::Invert == meRasterOp) || (RasterOp::Xor == meRasterOp), RasterOp::Invert == meRasterOp );
mpGraphics->setAntiAlias(bool(mnAntialiasing & AntialiasingFlags::Enable));
}
return mpGraphics != nullptr;
}
void VirtualDevice::ReleaseGraphics( bool bRelease )
{
DBG_TESTSOLARMUTEX();
if ( !mpGraphics )
return;
// release the fonts of the physically released graphics device
if ( bRelease )
ImplReleaseFonts();
ImplSVData* pSVData = ImplGetSVData();
VirtualDevice* pVirDev = this;
if ( bRelease )
pVirDev->mpVirDev->ReleaseGraphics( mpGraphics );
// remove from global LRU list of virtual device graphics
if ( mpPrevGraphics )
mpPrevGraphics->mpNextGraphics = mpNextGraphics;
else
pSVData->maGDIData.mpFirstVirGraphics = mpNextGraphics;
if ( mpNextGraphics )
mpNextGraphics->mpPrevGraphics = mpPrevGraphics;
else
pSVData->maGDIData.mpLastVirGraphics = mpPrevGraphics;
mpGraphics = nullptr;
mpPrevGraphics = nullptr;
mpNextGraphics = nullptr;
}
void VirtualDevice::ImplInitVirDev( const OutputDevice* pOutDev,
tools::Long nDX, tools::Long nDY, const SystemGraphicsData *pData )
{
SAL_INFO( "vcl.virdev", "ImplInitVirDev(" << nDX << "," << nDY << ")" );
meRefDevMode = RefDevMode::NONE;
mbForceZeroExtleadBug = false;
mnBitCount = 0;
mbScreenComp = false;
bool bErase = nDX > 0 && nDY > 0;
if ( nDX < 1 )
nDX = 1;
if ( nDY < 1 )
nDY = 1;
ImplSVData* pSVData = ImplGetSVData();
if ( !pOutDev )
pOutDev = ImplGetDefaultWindow()->GetOutDev();
if( !pOutDev )
return;
SalGraphics* pGraphics;
if ( !pOutDev->mpGraphics )
(void)pOutDev->AcquireGraphics();
pGraphics = pOutDev->mpGraphics;
if ( pGraphics )
mpVirDev = pSVData->mpDefInst->CreateVirtualDevice(*pGraphics, nDX, nDY, meFormat, pData);
else
mpVirDev = nullptr;
if ( !mpVirDev )
{
// do not abort but throw an exception, may be the current thread terminates anyway (plugin-scenario)
throw css::uno::RuntimeException(
"Could not create system bitmap!",
css::uno::Reference< css::uno::XInterface >() );
}
mnBitCount = pOutDev->GetBitCount();
mnOutWidth = nDX;
mnOutHeight = nDY;
mbScreenComp = pOutDev->IsScreenComp();
mbDevOutput = true;
mxFontCollection = pSVData->maGDIData.mxScreenFontList;
mxFontCache = pSVData->maGDIData.mxScreenFontCache;
mnDPIX = pOutDev->mnDPIX;
mnDPIY = pOutDev->mnDPIY;
mnDPIScalePercentage = pOutDev->mnDPIScalePercentage;
maFont = pOutDev->maFont;
if( maTextColor != pOutDev->maTextColor )
{
maTextColor = pOutDev->maTextColor;
mbInitTextColor = true;
}
// virtual devices have white background by default
SetBackground( Wallpaper( COL_WHITE ) );
// #i59283# don't erase user-provided surface
if( !pData && bErase)
Erase();
// register VirDev in the list
mpNext = pSVData->maGDIData.mpFirstVirDev;
mpPrev = nullptr;
if ( mpNext )
mpNext->mpPrev = this;
pSVData->maGDIData.mpFirstVirDev = this;
}
VirtualDevice::VirtualDevice(const OutputDevice* pCompDev, DeviceFormat eFormat,
DeviceFormat eAlphaFormat, OutDevType eOutDevType)
: OutputDevice(eOutDevType)
, meFormat(eFormat)
, meAlphaFormat(eAlphaFormat)
{
SAL_INFO( "vcl.virdev", "VirtualDevice::VirtualDevice( " << static_cast<int>(eFormat)
<< ", " << static_cast<int>(eAlphaFormat)
<< ", " << static_cast<int>(eOutDevType) << " )" );
ImplInitVirDev(pCompDev ? pCompDev : Application::GetDefaultDevice(), 0, 0);
}
VirtualDevice::VirtualDevice(const SystemGraphicsData& rData, const Size &rSize,
DeviceFormat eFormat)
: OutputDevice(OUTDEV_VIRDEV)
, meFormat(eFormat)
, meAlphaFormat(DeviceFormat::NONE)
{
SAL_INFO( "vcl.virdev", "VirtualDevice::VirtualDevice( " << static_cast<int>(eFormat) << " )" );
ImplInitVirDev(Application::GetDefaultDevice(), rSize.Width(), rSize.Height(), &rData);
}
VirtualDevice::~VirtualDevice()
{
SAL_INFO( "vcl.virdev", "VirtualDevice::~VirtualDevice()" );
disposeOnce();
}
void VirtualDevice::dispose()
{
SAL_INFO( "vcl.virdev", "VirtualDevice::dispose()" );
ImplSVData* pSVData = ImplGetSVData();
ReleaseGraphics();
mpVirDev.reset();
// remove this VirtualDevice from the double-linked global list
if( mpPrev )
mpPrev->mpNext = mpNext;
else
pSVData->maGDIData.mpFirstVirDev = mpNext;
if( mpNext )
mpNext->mpPrev = mpPrev;
OutputDevice::dispose();
}
bool VirtualDevice::InnerImplSetOutputSizePixel( const Size& rNewSize, bool bErase,
sal_uInt8 *const pBuffer)
{
SAL_INFO( "vcl.virdev",
"VirtualDevice::InnerImplSetOutputSizePixel( " << rNewSize.Width() << ", "
<< rNewSize.Height() << ", " << int(bErase) << " )" );
if ( !mpVirDev )
return false;
else if ( rNewSize == GetOutputSizePixel() )
{
if ( bErase )
Erase();
SAL_INFO( "vcl.virdev", "Trying to re-use a VirtualDevice but this time using a pre-allocated buffer");
return true;
}
bool bRet;
tools::Long nNewWidth = rNewSize.Width(), nNewHeight = rNewSize.Height();
if ( nNewWidth < 1 )
nNewWidth = 1;
if ( nNewHeight < 1 )
nNewHeight = 1;
if ( bErase )
{
if ( pBuffer )
bRet = mpVirDev->SetSizeUsingBuffer( nNewWidth, nNewHeight, pBuffer );
else
bRet = mpVirDev->SetSize( nNewWidth, nNewHeight );
if ( bRet )
{
mnOutWidth = rNewSize.Width();
mnOutHeight = rNewSize.Height();
Erase();
}
}
else
{
std::unique_ptr<SalVirtualDevice> pNewVirDev;
ImplSVData* pSVData = ImplGetSVData();
// we need a graphics
if ( !mpGraphics && !AcquireGraphics() )
return false;
assert(mpGraphics);
pNewVirDev = pSVData->mpDefInst->CreateVirtualDevice(*mpGraphics, nNewWidth, nNewHeight, meFormat);
if ( pNewVirDev )
{
SalGraphics* pGraphics = pNewVirDev->AcquireGraphics();
if ( pGraphics )
{
tools::Long nWidth;
tools::Long nHeight;
if ( mnOutWidth < nNewWidth )
nWidth = mnOutWidth;
else
nWidth = nNewWidth;
if ( mnOutHeight < nNewHeight )
nHeight = mnOutHeight;
else
nHeight = nNewHeight;
SalTwoRect aPosAry(0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight);
pGraphics->CopyBits( aPosAry, *mpGraphics, *this, *this );
pNewVirDev->ReleaseGraphics( pGraphics );
ReleaseGraphics();
mpVirDev = std::move(pNewVirDev);
mnOutWidth = rNewSize.Width();
mnOutHeight = rNewSize.Height();
bRet = true;
}
else
{
bRet = false;
}
}
else
bRet = false;
}
return bRet;
}
// #i32109#: Fill opaque areas correctly (without relying on
// fill/linecolor state)
void VirtualDevice::ImplFillOpaqueRectangle( const tools::Rectangle& rRect )
{
// Set line and fill color to black (->opaque),
// fill rect with that (linecolor, too, because of
// those pesky missing pixel problems)
Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
SetLineColor( COL_BLACK );
SetFillColor( COL_BLACK );
DrawRect( rRect );
Pop();
}
bool VirtualDevice::ImplSetOutputSizePixel( const Size& rNewSize, bool bErase,
sal_uInt8 *const pBuffer)
{
if( InnerImplSetOutputSizePixel(rNewSize, bErase, pBuffer) )
{
if (meAlphaFormat != DeviceFormat::NONE)
{
// #110958# Setup alpha bitmap
if(mpAlphaVDev && mpAlphaVDev->GetOutputSizePixel() != rNewSize)
{
mpAlphaVDev.disposeAndClear();
}
if( !mpAlphaVDev )
{
mpAlphaVDev = VclPtr<VirtualDevice>::Create(*this, meAlphaFormat);
mpAlphaVDev->InnerImplSetOutputSizePixel(rNewSize, bErase, nullptr);
}
// TODO: copy full outdev state to new one, here. Also needed in outdev2.cxx:DrawOutDev
if( GetLineColor() != COL_TRANSPARENT )
mpAlphaVDev->SetLineColor( COL_BLACK );
if( GetFillColor() != COL_TRANSPARENT )
mpAlphaVDev->SetFillColor( COL_BLACK );
mpAlphaVDev->SetMapMode( GetMapMode() );
mpAlphaVDev->SetAntialiasing( GetAntialiasing() );
mpAlphaVDev->SetTextRenderModeForResolutionIndependentLayout(GetTextRenderModeForResolutionIndependentLayout());
}
return true;
}
return false;
}
void VirtualDevice::EnableRTL( bool bEnable )
{
// virdevs default to not mirroring, they will only be set to mirroring
// under rare circumstances in the UI, eg the valueset control
// because each virdev has its own SalGraphics we can safely switch the SalGraphics here
// ...hopefully
if( AcquireGraphics() )
mpGraphics->SetLayout( bEnable ? SalLayoutFlags::BiDiRtl : SalLayoutFlags::NONE );
OutputDevice::EnableRTL(bEnable);
}
bool VirtualDevice::SetOutputSizePixel( const Size& rNewSize, bool bErase )
{
return ImplSetOutputSizePixel(rNewSize, bErase, nullptr);
}
bool VirtualDevice::SetOutputSizePixelScaleOffsetAndLOKBuffer(
const Size& rNewSize, const Fraction& rScale, const Point& rNewOffset,
sal_uInt8 *const pBuffer)
{
// If this is ever needed for something else than LOK, changes will
// be needed in SvpSalVirtualDevice::CreateSurface() .
assert(comphelper::LibreOfficeKit::isActive());
assert(pBuffer);
MapMode mm = GetMapMode();
mm.SetOrigin( rNewOffset );
mm.SetScaleX( rScale );
mm.SetScaleY( rScale );
SetMapMode( mm );
return ImplSetOutputSizePixel(rNewSize, true, pBuffer);
}
void VirtualDevice::SetReferenceDevice( RefDevMode i_eRefDevMode )
{
sal_Int32 nDPIX = 600, nDPIY = 600;
switch( i_eRefDevMode )
{
case RefDevMode::NONE:
default:
SAL_WARN( "vcl.virdev", "VDev::SetRefDev illegal argument!" );
break;
case RefDevMode::Dpi600:
nDPIX = nDPIY = 600;
break;
case RefDevMode::MSO1:
nDPIX = nDPIY = 6*1440;
break;
case RefDevMode::PDF1:
nDPIX = nDPIY = 720;
break;
}
ImplSetReferenceDevice( i_eRefDevMode, nDPIX, nDPIY );
}
void VirtualDevice::SetReferenceDevice( sal_Int32 i_nDPIX, sal_Int32 i_nDPIY )
{
ImplSetReferenceDevice( RefDevMode::Custom, i_nDPIX, i_nDPIY );
}
bool VirtualDevice::IsVirtual() const
{
return true;
}
void VirtualDevice::ImplSetReferenceDevice( RefDevMode i_eRefDevMode, sal_Int32 i_nDPIX, sal_Int32 i_nDPIY )
{
mnDPIX = i_nDPIX;
mnDPIY = i_nDPIY;
mnDPIScalePercentage = 100;
EnableOutput( false ); // prevent output on reference device
mbScreenComp = false;
// invalidate currently selected fonts
mbInitFont = true;
mbNewFont = true;
// avoid adjusting font lists when already in refdev mode
RefDevMode nOldRefDevMode = meRefDevMode;
meRefDevMode = i_eRefDevMode;
if( nOldRefDevMode != RefDevMode::NONE )
return;
// the reference device should have only scalable fonts
// => clean up the original font lists before getting new ones
mpFontInstance.clear();
mpFontFaceCollection.reset();
// preserve global font lists
ImplSVData* pSVData = ImplGetSVData();
mxFontCollection.reset();
mxFontCache.reset();
// get font list with scalable fonts only
(void)AcquireGraphics();
mxFontCollection = pSVData->maGDIData.mxScreenFontList->Clone();
// prepare to use new font lists
mxFontCache = std::make_shared<ImplFontCache>();
}
sal_uInt16 VirtualDevice::GetBitCount() const
{
return mnBitCount;
}
bool VirtualDevice::UsePolyPolygonForComplexGradient()
{
return true;
}
void VirtualDevice::Compat_ZeroExtleadBug()
{
mbForceZeroExtleadBug = true;
}
tools::Long VirtualDevice::GetFontExtLeading() const
{
#ifdef UNX
// backwards compatible line metrics after fixing #i60945#
if ( mbForceZeroExtleadBug )
return 0;
#endif
return mpFontInstance->mxFontMetric->GetExternalLeading();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */