361 lines
14 KiB
C++
361 lines
14 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 <svx/sdr/overlay/overlaymanager.hxx>
|
|
#include <basegfx/range/b2drange.hxx>
|
|
#include <comphelper/propertyvalue.hxx>
|
|
#include <tools/gen.hxx>
|
|
#include <vcl/canvastools.hxx>
|
|
#include <vcl/outdev.hxx>
|
|
#include <vcl/window.hxx>
|
|
#include <svx/sdr/overlay/overlayobject.hxx>
|
|
#include <basegfx/matrix/b2dhommatrix.hxx>
|
|
#include <drawinglayer/processor2d/baseprocessor2d.hxx>
|
|
#include <drawinglayer/processor2d/processor2dtools.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <memory>
|
|
|
|
|
|
using namespace com::sun::star;
|
|
|
|
|
|
namespace sdr::overlay
|
|
{
|
|
void OverlayManager::ImpDrawMembers(const basegfx::B2DRange& rRange, OutputDevice& rDestinationDevice) const
|
|
{
|
|
const sal_uInt32 nSize(maOverlayObjects.size());
|
|
|
|
if(!nSize)
|
|
return;
|
|
|
|
const AntialiasingFlags nOriginalAA(rDestinationDevice.GetAntialiasing());
|
|
const bool bIsAntiAliasing(getCurrentViewInformation2D().getUseAntiAliasing());
|
|
// tdf#150622 for High Contrast we typically force colors to a single pair Fore/Back,
|
|
// but it seems reasonable to allow overlays to use the selection color
|
|
// taken from the system High Contrast settings
|
|
const DrawModeFlags nOriginalDrawMode(rDestinationDevice.GetDrawMode());
|
|
|
|
// create processor
|
|
std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
|
|
rDestinationDevice,
|
|
getCurrentViewInformation2D()));
|
|
|
|
for(const auto& rpOverlayObject : maOverlayObjects)
|
|
{
|
|
OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)");
|
|
const OverlayObject& rCandidate = *rpOverlayObject;
|
|
|
|
if(rCandidate.isVisible())
|
|
{
|
|
const drawinglayer::primitive2d::Primitive2DContainer aSequence = rCandidate.getOverlayObjectPrimitive2DSequence();
|
|
|
|
if(!aSequence.empty())
|
|
{
|
|
if(rRange.overlaps(rCandidate.getBaseRange()))
|
|
{
|
|
if(bIsAntiAliasing && rCandidate.allowsAntiAliase())
|
|
{
|
|
rDestinationDevice.SetAntialiasing(nOriginalAA | AntialiasingFlags::Enable);
|
|
}
|
|
else
|
|
{
|
|
rDestinationDevice.SetAntialiasing(nOriginalAA & ~AntialiasingFlags::Enable);
|
|
}
|
|
|
|
const bool bIsHighContrastSelection = rCandidate.isHighContrastSelection();
|
|
if (bIsHighContrastSelection)
|
|
{
|
|
// overrule DrawMode settings
|
|
rDestinationDevice.SetDrawMode(nOriginalDrawMode | DrawModeFlags::SettingsForSelection);
|
|
}
|
|
|
|
pProcessor->process(aSequence);
|
|
|
|
if (bIsHighContrastSelection)
|
|
{
|
|
// restore DrawMode settings
|
|
rDestinationDevice.SetDrawMode(nOriginalDrawMode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pProcessor.reset();
|
|
|
|
// restore AA settings
|
|
rDestinationDevice.SetAntialiasing(nOriginalAA);
|
|
}
|
|
|
|
void OverlayManager::ImpStripeDefinitionChanged()
|
|
{
|
|
const sal_uInt32 nSize(maOverlayObjects.size());
|
|
|
|
if(nSize)
|
|
{
|
|
for(const auto& rpOverlayObject : maOverlayObjects)
|
|
{
|
|
OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)");
|
|
OverlayObject& rCandidate = *rpOverlayObject;
|
|
rCandidate.stripeDefinitionHasChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
double OverlayManager::getDiscreteOne() const
|
|
{
|
|
if(basegfx::fTools::equalZero(mfDiscreteOne))
|
|
{
|
|
const basegfx::B2DVector aDiscreteInLogic(getOutputDevice().GetInverseViewTransformation() * basegfx::B2DVector(1.0, 0.0));
|
|
const_cast< OverlayManager* >(this)->mfDiscreteOne = aDiscreteInLogic.getLength();
|
|
}
|
|
|
|
return mfDiscreteOne;
|
|
}
|
|
|
|
OverlayManager::OverlayManager(OutputDevice& rOutputDevice)
|
|
: mrOutputDevice(rOutputDevice),
|
|
maStripeColorA(COL_BLACK),
|
|
maStripeColorB(COL_WHITE),
|
|
mnStripeLengthPixel(5),
|
|
mfDiscreteOne(0.0)
|
|
{
|
|
// Set Property 'ReducedDisplayQuality' to true to allow simpler interaction
|
|
// visualisations. Note: Currently will use reduced quality for 3d scene soft renderer
|
|
uno::Sequence< beans::PropertyValue > xProperties{
|
|
comphelper::makePropertyValue(u"ReducedDisplayQuality"_ustr, true)
|
|
};
|
|
maViewInformation2D = drawinglayer::geometry::createViewInformation2D(xProperties);
|
|
}
|
|
|
|
rtl::Reference<OverlayManager> OverlayManager::create(OutputDevice& rOutputDevice)
|
|
{
|
|
return rtl::Reference<OverlayManager>(new OverlayManager(rOutputDevice));
|
|
}
|
|
|
|
drawinglayer::geometry::ViewInformation2D const & OverlayManager::getCurrentViewInformation2D() const
|
|
{
|
|
if(getOutputDevice().GetViewTransformation() != maViewTransformation)
|
|
{
|
|
basegfx::B2DRange aViewRange(maViewInformation2D.getViewport());
|
|
|
|
if(OUTDEV_WINDOW == getOutputDevice().GetOutDevType())
|
|
{
|
|
const Size aOutputSizePixel(getOutputDevice().GetOutputSizePixel());
|
|
|
|
// only set when we *have* an output size, else let aViewRange
|
|
// stay on empty
|
|
if(aOutputSizePixel.Width() && aOutputSizePixel.Height())
|
|
{
|
|
aViewRange = basegfx::B2DRange(0.0, 0.0, aOutputSizePixel.getWidth(), aOutputSizePixel.getHeight());
|
|
aViewRange.transform(getOutputDevice().GetInverseViewTransformation());
|
|
}
|
|
}
|
|
|
|
OverlayManager* pThis = const_cast< OverlayManager* >(this);
|
|
|
|
pThis->maViewTransformation = getOutputDevice().GetViewTransformation();
|
|
drawinglayer::geometry::ViewInformation2D aViewInformation(maViewInformation2D);
|
|
aViewInformation.setViewTransformation(maViewTransformation);
|
|
aViewInformation.setViewport(aViewRange);
|
|
pThis->maViewInformation2D = std::move(aViewInformation);
|
|
|
|
pThis->mfDiscreteOne = 0.0;
|
|
}
|
|
|
|
return maViewInformation2D;
|
|
}
|
|
|
|
void OverlayManager::impApplyRemoveActions(OverlayObject& rTarget)
|
|
{
|
|
// handle evtl. animation
|
|
if(rTarget.allowsAnimation())
|
|
{
|
|
// remove from event chain
|
|
RemoveEvent(&rTarget);
|
|
}
|
|
|
|
// make invisible
|
|
invalidateRange(rTarget.getBaseRange());
|
|
|
|
// clear manager
|
|
rTarget.mpOverlayManager = nullptr;
|
|
}
|
|
|
|
void OverlayManager::impApplyAddActions(OverlayObject& rTarget)
|
|
{
|
|
// set manager
|
|
rTarget.mpOverlayManager = this;
|
|
|
|
// make visible
|
|
invalidateRange(rTarget.getBaseRange());
|
|
|
|
// handle evtl. animation
|
|
if(rTarget.allowsAnimation())
|
|
{
|
|
// Trigger at current time to get alive. This will do the
|
|
// object-specific next time calculation and hand over adding
|
|
// again to the scheduler to the animated object, too. This works for
|
|
// a paused or non-paused animator.
|
|
rTarget.Trigger(GetTime());
|
|
}
|
|
}
|
|
|
|
OverlayManager::~OverlayManager()
|
|
{
|
|
// The OverlayManager is not the owner of the OverlayObjects
|
|
// and thus will not delete them, but remove them. Profit here
|
|
// from knowing that all will be removed
|
|
const sal_uInt32 nSize(maOverlayObjects.size());
|
|
|
|
if(nSize)
|
|
{
|
|
for(const auto& rpOverlayObject : maOverlayObjects)
|
|
{
|
|
OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)");
|
|
OverlayObject& rCandidate = *rpOverlayObject;
|
|
impApplyRemoveActions(rCandidate);
|
|
}
|
|
|
|
// erase vector
|
|
maOverlayObjects.clear();
|
|
}
|
|
}
|
|
|
|
void OverlayManager::completeRedraw(const vcl::Region& rRegion, OutputDevice* pPreRenderDevice) const
|
|
{
|
|
if(rRegion.IsEmpty() || maOverlayObjects.empty())
|
|
return;
|
|
|
|
// check for changed MapModes. That may influence the
|
|
// logical size of pixel based OverlayObjects (like BitmapHandles)
|
|
//ImpCheckMapModeChange();
|
|
|
|
// paint members
|
|
const tools::Rectangle aRegionBoundRect(rRegion.GetBoundRect());
|
|
const basegfx::B2DRange aRegionRange = vcl::unotools::b2DRectangleFromRectangle(aRegionBoundRect);
|
|
|
|
OutputDevice& rTarget = pPreRenderDevice ? *pPreRenderDevice : getOutputDevice();
|
|
ImpDrawMembers(aRegionRange, rTarget);
|
|
}
|
|
|
|
void OverlayManager::flush()
|
|
{
|
|
// default has nothing to do
|
|
}
|
|
|
|
void OverlayManager::add(OverlayObject& rOverlayObject)
|
|
{
|
|
OSL_ENSURE(nullptr == rOverlayObject.mpOverlayManager, "OverlayObject is added twice to an OverlayManager (!)");
|
|
|
|
// add to the end of chain to preserve display order in paint
|
|
maOverlayObjects.push_back(&rOverlayObject);
|
|
|
|
// execute add actions
|
|
impApplyAddActions(rOverlayObject);
|
|
}
|
|
|
|
void OverlayManager::remove(OverlayObject& rOverlayObject)
|
|
{
|
|
OSL_ENSURE(rOverlayObject.mpOverlayManager == this, "OverlayObject is removed from wrong OverlayManager (!)");
|
|
|
|
// execute remove actions
|
|
impApplyRemoveActions(rOverlayObject);
|
|
|
|
// remove from vector
|
|
const OverlayObjectVector::iterator aFindResult = ::std::find(maOverlayObjects.begin(), maOverlayObjects.end(), &rOverlayObject);
|
|
const bool bFound(aFindResult != maOverlayObjects.end());
|
|
OSL_ENSURE(bFound, "OverlayObject NOT found at OverlayManager (!)");
|
|
|
|
if(bFound)
|
|
{
|
|
maOverlayObjects.erase(aFindResult);
|
|
}
|
|
}
|
|
|
|
tools::Rectangle OverlayManager::RangeToInvalidateRectangle(const basegfx::B2DRange& rRange) const
|
|
{
|
|
if (rRange.isEmpty()) {
|
|
return {};
|
|
}
|
|
if (getCurrentViewInformation2D().getUseAntiAliasing())
|
|
{
|
|
// assume AA needs one pixel more and invalidate one pixel more
|
|
const double fDiscreteOne(getDiscreteOne());
|
|
const tools::Rectangle aInvalidateRectangle(
|
|
static_cast<tools::Long>(floor(rRange.getMinX() - fDiscreteOne)),
|
|
static_cast<tools::Long>(floor(rRange.getMinY() - fDiscreteOne)),
|
|
static_cast<tools::Long>(ceil(rRange.getMaxX() + fDiscreteOne)),
|
|
static_cast<tools::Long>(ceil(rRange.getMaxY() + fDiscreteOne)));
|
|
return aInvalidateRectangle;
|
|
}
|
|
else
|
|
{
|
|
// #i77674# transform to rectangle. Use floor/ceil to get all covered
|
|
// discrete pixels, see #i75163# and OverlayManagerBuffered::invalidateRange
|
|
const tools::Rectangle aInvalidateRectangle(
|
|
static_cast<sal_Int32>(floor(rRange.getMinX())), static_cast<sal_Int32>(floor(rRange.getMinY())),
|
|
static_cast<sal_Int32>(ceil(rRange.getMaxX())), static_cast<sal_Int32>(ceil(rRange.getMaxY())));
|
|
return aInvalidateRectangle;
|
|
}
|
|
}
|
|
|
|
void OverlayManager::invalidateRange(const basegfx::B2DRange& rRange)
|
|
{
|
|
if (OUTDEV_WINDOW == getOutputDevice().GetOutDevType())
|
|
{
|
|
tools::Rectangle aInvalidateRectangle(RangeToInvalidateRectangle(rRange));
|
|
// simply invalidate
|
|
getOutputDevice().GetOwnerWindow()->Invalidate(aInvalidateRectangle, InvalidateFlags::NoErase);
|
|
}
|
|
}
|
|
|
|
// stripe support ColA
|
|
void OverlayManager::setStripeColorA(Color aNew)
|
|
{
|
|
if(aNew != maStripeColorA)
|
|
{
|
|
maStripeColorA = aNew;
|
|
ImpStripeDefinitionChanged();
|
|
}
|
|
}
|
|
|
|
// stripe support ColB
|
|
void OverlayManager::setStripeColorB(Color aNew)
|
|
{
|
|
if(aNew != maStripeColorB)
|
|
{
|
|
maStripeColorB = aNew;
|
|
ImpStripeDefinitionChanged();
|
|
}
|
|
}
|
|
|
|
// stripe support StripeLengthPixel
|
|
void OverlayManager::setStripeLengthPixel(sal_uInt32 nNew)
|
|
{
|
|
if(nNew != mnStripeLengthPixel)
|
|
{
|
|
mnStripeLengthPixel = nNew;
|
|
ImpStripeDefinitionChanged();
|
|
}
|
|
}
|
|
|
|
} // end of namespace
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|