Files
libreoffice/vcl/source/gdi/virdev.cxx
Tomaž Vajngerl 69b6ab1f8d tdf#100164 change scaling unit to precentage for *.5x factors
Currently we support DPI scaling by a integer factor. This commit
changes that to percentage so we can have scaling factors like
1.5x or 1.25x. This is useful with 2.7k monitors that are in
between standard DPI and HiDPI. Thresholding was adjusted to scale
to 1.5x when DPI is between 120 and 168 DPI.

The old method GetDPIScaleFactor has been changed to return a
float value insted of int. Sometimes it is however more accurate
to use GetDPIScalePercentage which was added in this commit.

Change-Id: Iaecee793ff3d5084d00adeebbcf5d7368c580882
Reviewed-on: https://gerrit.libreoffice.org/30379
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Tested-by: Tomaž Vajngerl <quikee@gmail.com>
2016-10-29 21:47:41 +00:00

564 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 "salinst.hxx"
#include "salgdi.hxx"
#include "salvd.hxx"
#include "outdev.h"
#include "PhysicalFontCollection.hxx"
#include "svdata.hxx"
#include <vcl/ITiledRenderable.hxx>
namespace vcl
{
ITiledRenderable::~ITiledRenderable()
{
}
}
using namespace ::com::sun::star::uno;
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) );
mpGraphics->setAntiAliasB2DDraw(bool(mnAntialiasing & AntialiasingFlags::EnableB2dDraw));
}
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,
long nDX, long nDY, DeviceFormat eFormat, const SystemGraphicsData *pData )
{
SAL_INFO( "vcl.virdev", "ImplInitVirDev(" << nDX << "," << nDY << "," << static_cast<int>(eFormat) << ")" );
bool bErase = nDX > 0 && nDY > 0;
if ( nDX < 1 )
nDX = 1;
if ( nDY < 1 )
nDY = 1;
ImplSVData* pSVData = ImplGetSVData();
if ( !pOutDev )
pOutDev = ImplGetDefaultWindow();
if( !pOutDev )
return;
SalGraphics* pGraphics;
if ( !pOutDev->mpGraphics )
(void)pOutDev->AcquireGraphics();
pGraphics = pOutDev->mpGraphics;
if ( pGraphics )
mpVirDev = pSVData->mpDefInst->CreateVirtualDevice(pGraphics, nDX, nDY, eFormat, 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(
OUString( "Could not create system bitmap!" ),
css::uno::Reference< css::uno::XInterface >() );
}
meFormat = eFormat;
switch (meFormat)
{
case DeviceFormat::BITMASK:
mnBitCount = 1;
break;
default:
mnBitCount = pOutDev->GetBitCount();
break;
}
mnOutWidth = nDX;
mnOutHeight = nDY;
mbScreenComp = true;
meAlphaFormat = DeviceFormat::NONE;
if (meFormat == DeviceFormat::BITMASK)
SetAntialiasing( AntialiasingFlags::DisableText );
if ( pOutDev->GetOutDevType() == OUTDEV_PRINTER )
mbScreenComp = false;
else if ( pOutDev->GetOutDevType() == OUTDEV_VIRDEV )
mbScreenComp = static_cast<const VirtualDevice*>(pOutDev)->mbScreenComp;
meOutDevType = OUTDEV_VIRDEV;
mbDevOutput = true;
mpFontCollection = pSVData->maGDIData.mpScreenFontList;
mpFontCache = pSVData->maGDIData.mpScreenFontCache;
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( Color( 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;
else
pSVData->maGDIData.mpLastVirDev = this;
pSVData->maGDIData.mpFirstVirDev = this;
}
VirtualDevice::VirtualDevice(DeviceFormat eFormat)
: mpVirDev( nullptr ),
meRefDevMode( RefDevMode::NONE ),
mbForceZeroExtleadBug( false )
{
SAL_INFO( "vcl.gdi", "VirtualDevice::VirtualDevice( " << static_cast<int>(eFormat) << " )" );
ImplInitVirDev(Application::GetDefaultDevice(), 0, 0, eFormat);
}
VirtualDevice::VirtualDevice(const OutputDevice& rCompDev, DeviceFormat eFormat)
: mpVirDev( nullptr ),
meRefDevMode( RefDevMode::NONE ),
mbForceZeroExtleadBug( false )
{
SAL_INFO( "vcl.gdi", "VirtualDevice::VirtualDevice( " << static_cast<int>(eFormat) << " )" );
ImplInitVirDev(&rCompDev, 0, 0, eFormat);
}
VirtualDevice::VirtualDevice(const OutputDevice& rCompDev, DeviceFormat eFormat, DeviceFormat eAlphaFormat)
: mpVirDev( nullptr )
, meRefDevMode( RefDevMode::NONE )
, mbForceZeroExtleadBug( false )
{
SAL_INFO( "vcl.gdi",
"VirtualDevice::VirtualDevice( " << static_cast<int>(eFormat) << ", " << static_cast<int>(eAlphaFormat) << " )" );
ImplInitVirDev(&rCompDev, 0, 0, eFormat);
// Enable alpha channel
meAlphaFormat = eAlphaFormat;
}
VirtualDevice::VirtualDevice(const SystemGraphicsData *pData, const Size &rSize,
DeviceFormat eFormat)
: mpVirDev( nullptr ),
meRefDevMode( RefDevMode::NONE ),
mbForceZeroExtleadBug( false )
{
SAL_INFO( "vcl.gdi", "VirtualDevice::VirtualDevice( " << static_cast<int>(eFormat) << " )" );
ImplInitVirDev(Application::GetDefaultDevice(), rSize.Width(), rSize.Height(),
eFormat, pData);
}
VirtualDevice::~VirtualDevice()
{
SAL_INFO( "vcl.gdi", "VirtualDevice::~VirtualDevice()" );
disposeOnce();
}
void VirtualDevice::dispose()
{
SAL_INFO( "vcl.gdi", "VirtualDevice::dispose()" );
ImplSVData* pSVData = ImplGetSVData();
ReleaseGraphics();
delete mpVirDev;
// remove this VirtualDevice from the double-linked global list
if( mpPrev )
mpPrev->mpNext = mpNext;
else
pSVData->maGDIData.mpFirstVirDev = mpNext;
if( mpNext )
mpNext->mpPrev = mpPrev;
else
pSVData->maGDIData.mpLastVirDev = mpPrev;
OutputDevice::dispose();
}
bool VirtualDevice::InnerImplSetOutputSizePixel( const Size& rNewSize, bool bErase,
sal_uInt8 *const pBuffer)
{
SAL_INFO( "vcl.gdi",
"VirtualDevice::InnerImplSetOutputSizePixel( " << rNewSize.Width() << ", "
<< rNewSize.Height() << ", " << int(bErase) << " )" );
if ( !mpVirDev )
return false;
else if ( rNewSize == GetOutputSizePixel() )
{
if ( bErase )
Erase();
// Yeah, so trying to re-use a VirtualDevice but this time using a
// pre-allocated buffer won't work. Big deal.
return true;
}
bool bRet;
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
{
SalVirtualDevice* pNewVirDev;
ImplSVData* pSVData = ImplGetSVData();
// we need a graphics
if ( !mpGraphics )
{
if ( !AcquireGraphics() )
return false;
}
pNewVirDev = pSVData->mpDefInst->CreateVirtualDevice(mpGraphics, nNewWidth, nNewHeight, meFormat);
if ( pNewVirDev )
{
SalGraphics* pGraphics = pNewVirDev->AcquireGraphics();
if ( pGraphics )
{
long nWidth;
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();
delete mpVirDev;
mpVirDev = pNewVirDev;
mnOutWidth = rNewSize.Width();
mnOutHeight = rNewSize.Height();
bRet = true;
}
else
{
bRet = false;
delete pNewVirDev;
}
}
else
bRet = false;
}
return bRet;
}
// #i32109#: Fill opaque areas correctly (without relying on
// fill/linecolor state)
void VirtualDevice::ImplFillOpaqueRectangle( const Rectangle& rRect )
{
// Set line and fill color to black (->opaque),
// fill rect with that (linecolor, too, because of
// those pesky missing pixel problems)
Push( PushFlags::LINECOLOR | 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() != Color( COL_TRANSPARENT ) )
mpAlphaVDev->SetLineColor( COL_BLACK );
if( GetFillColor() != Color( COL_TRANSPARENT ) )
mpAlphaVDev->SetFillColor( COL_BLACK );
mpAlphaVDev->SetMapMode( GetMapMode() );
}
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::SetOutputSizePixelScaleOffsetAndBuffer(
const Size& rNewSize, const Fraction& rScale, const Point& rNewOffset,
sal_uInt8 *const pBuffer)
{
if (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", "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 );
}
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
if ( mpFontInstance )
{
mpFontCache->Release( mpFontInstance );
mpFontInstance = nullptr;
}
if ( mpDeviceFontList )
{
delete mpDeviceFontList;
mpDeviceFontList = nullptr;
}
if ( mpDeviceFontSizeList )
{
delete mpDeviceFontSizeList;
mpDeviceFontSizeList = nullptr;
}
// preserve global font lists
ImplSVData* pSVData = ImplGetSVData();
if( mpFontCollection && (mpFontCollection != pSVData->maGDIData.mpScreenFontList) )
delete mpFontCollection;
if( mpFontCache && (mpFontCache != pSVData->maGDIData.mpScreenFontCache) )
delete mpFontCache;
// get font list with scalable fonts only
AcquireGraphics();
mpFontCollection = pSVData->maGDIData.mpScreenFontList->Clone( false );
// prepare to use new font lists
mpFontCache = new ImplFontCache();
}
sal_uInt16 VirtualDevice::GetBitCount() const
{
return mnBitCount;
}
bool VirtualDevice::UsePolyPolygonForComplexGradient()
{
return true;
}
void VirtualDevice::Compat_ZeroExtleadBug()
{
mbForceZeroExtleadBug = true;
}
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: */