blob: f275ac3db23758739333f26312979e0d1d91a89d [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "precompiled_sd.hxx"
#include "SlsLayeredDevice.hxx"
#include <vcl/window.hxx>
#include <vcl/virdev.hxx>
#include <boost/bind.hpp>
#include <boost/function.hpp>
namespace sd { namespace slidesorter { namespace view {
namespace {
static const sal_Int32 gnMaximumLayerCount = 8;
class LayerInvalidator : public ILayerInvalidator
{
public:
LayerInvalidator (
const ::boost::shared_ptr<LayeredDevice>& rpLayeredDevice,
const SharedSdWindow& rpTargetWindow,
const int nLayer)
: mpLayeredDevice(rpLayeredDevice),
mpTargetWindow(rpTargetWindow),
mnLayer(nLayer)
{
}
virtual void Invalidate (const Rectangle& rInvalidationBox)
{
mpLayeredDevice->Invalidate(rInvalidationBox, mnLayer);
mpTargetWindow->Invalidate(rInvalidationBox);
}
private:
const ::boost::shared_ptr<LayeredDevice> mpLayeredDevice;
SharedSdWindow mpTargetWindow;
const int mnLayer;
};
void DeviceCopy (
OutputDevice& rTargetDevice,
OutputDevice& rSourceDevice,
const Rectangle& rBox)
{
rTargetDevice.DrawOutDev(
rBox.TopLeft(),
rBox.GetSize(),
rBox.TopLeft(),
rBox.GetSize(),
rSourceDevice);
}
void ForAllRectangles (const Region& rRegion, ::boost::function<void(const Rectangle&)> aFunction)
{
OSL_ASSERT(aFunction);
RectangleVector aRectangles;
rRegion.GetRegionRectangles(aRectangles);
if(0 == aRectangles.size())
{
aFunction(Rectangle());
}
else
{
for(RectangleVector::const_iterator aRectIter(aRectangles.begin()); aRectIter != aRectangles.end(); aRectIter++)
{
aFunction(*aRectIter);
}
//Region aMutableRegionCopy (rRegion);
//RegionHandle aHandle(aMutableRegionCopy.BeginEnumRects());
//Rectangle aBox;
//while (aMutableRegionCopy.GetEnumRects(aHandle, aBox))
// aFunction(aBox);
//aMutableRegionCopy.EndEnumRects(aHandle);
}
}
class Layer : private ::boost::noncopyable
{
public:
Layer (void);
~Layer (void);
void Initialize (const SharedSdWindow& rpTargetWindow);
void InvalidateRectangle (const Rectangle& rInvalidationBox);
void InvalidateRegion (const Region& rInvalidationRegion);
void Validate (const MapMode& rMapMode);
void Repaint (
OutputDevice& rTargetDevice,
const Rectangle& rRepaintRectangle);
void Resize (const Size& rSize);
void AddPainter (const SharedILayerPainter& rpPainter);
void RemovePainter (const SharedILayerPainter& rpPainter);
bool HasPainter (void) const;
void Dispose (void);
private:
::boost::shared_ptr<VirtualDevice> mpLayerDevice;
::std::vector<SharedILayerPainter> maPainters;
Region maInvalidationRegion;
void ValidateRectangle (const Rectangle& rBox);
};
typedef ::boost::shared_ptr<Layer> SharedLayer;
} // end of anonymous namespace
class LayeredDevice::LayerContainer : public ::std::vector<SharedLayer>
{
public:
LayerContainer (void) {}
~LayerContainer (void) {}
};
//===== LayeredDevice =========================================================
LayeredDevice::LayeredDevice (const SharedSdWindow& rpTargetWindow)
: mpTargetWindow(rpTargetWindow),
mpLayers(new LayerContainer()),
mpBackBuffer(new VirtualDevice(*mpTargetWindow)),
maSavedMapMode(rpTargetWindow->GetMapMode())
{
mpBackBuffer->SetOutputSizePixel(mpTargetWindow->GetSizePixel());
}
LayeredDevice::~LayeredDevice (void)
{
}
void LayeredDevice::Invalidate (
const Rectangle& rInvalidationArea,
const sal_Int32 nLayer)
{
if (nLayer<0 || size_t(nLayer)>=mpLayers->size())
{
OSL_ASSERT(nLayer>=0 && size_t(nLayer)<mpLayers->size());
return;
}
(*mpLayers)[nLayer]->InvalidateRectangle(rInvalidationArea);
}
void LayeredDevice::InvalidateAllLayers (const Rectangle& rInvalidationArea)
{
for (sal_uInt32 nLayer=0; nLayer<mpLayers->size(); ++nLayer)
(*mpLayers)[nLayer]->InvalidateRectangle(rInvalidationArea);
}
void LayeredDevice::InvalidateAllLayers (const Region& rInvalidationRegion)
{
for (sal_uInt32 nLayer=0; nLayer<mpLayers->size(); ++nLayer)
(*mpLayers)[nLayer]->InvalidateRegion(rInvalidationRegion);
}
void LayeredDevice::RegisterPainter (
const SharedILayerPainter& rpPainter,
const sal_Int32 nLayer)
{
OSL_ASSERT(mpLayers);
if ( ! rpPainter)
{
OSL_ASSERT(rpPainter);
return;
}
if (nLayer<0 || nLayer>=gnMaximumLayerCount)
{
OSL_ASSERT(nLayer>=0 && nLayer<gnMaximumLayerCount);
return;
}
// Provide the layers.
if (sal_uInt32(nLayer) >= mpLayers->size())
{
const sal_Int32 nOldLayerCount (mpLayers->size());
mpLayers->resize(nLayer+1);
for (size_t nIndex=nOldLayerCount; nIndex<mpLayers->size(); ++nIndex)
(*mpLayers)[nIndex].reset(new Layer());
}
(*mpLayers)[nLayer]->AddPainter(rpPainter);
if (nLayer == 0)
(*mpLayers)[nLayer]->Initialize(mpTargetWindow);
rpPainter->SetLayerInvalidator(
SharedILayerInvalidator(new LayerInvalidator(shared_from_this(),mpTargetWindow,nLayer)));
}
void LayeredDevice::RemovePainter (
const SharedILayerPainter& rpPainter,
const sal_Int32 nLayer)
{
if ( ! rpPainter)
{
OSL_ASSERT(rpPainter);
return;
}
if (nLayer<0 || size_t(nLayer)>=mpLayers->size())
{
OSL_ASSERT(nLayer>=0 && size_t(nLayer)<mpLayers->size());
return;
}
rpPainter->SetLayerInvalidator(SharedILayerInvalidator());
(*mpLayers)[nLayer]->RemovePainter(rpPainter);
// Remove top most layers that do not contain any painters.
while ( ! mpLayers->empty() && ! mpLayers->back()->HasPainter())
mpLayers->erase(mpLayers->end()-1);
}
bool LayeredDevice::HasPainter (const sal_Int32 nLayer)
{
return nLayer>=0
&& sal_uInt32(nLayer)<mpLayers->size()
&& (*mpLayers)[nLayer]->HasPainter();
}
void LayeredDevice::Repaint (const Region& rRepaintRegion)
{
// Validate the contents of all layers (that have their own devices.)
::std::for_each(
mpLayers->begin(),
mpLayers->end(),
::boost::bind(&Layer::Validate, _1, mpTargetWindow->GetMapMode()));
ForAllRectangles(rRepaintRegion, ::boost::bind(&LayeredDevice::RepaintRectangle, this, _1));
}
void LayeredDevice::RepaintRectangle (const Rectangle& rRepaintRectangle)
{
if (mpLayers->size() == 0)
return;
else if (mpLayers->size() == 1)
{
// Just copy the main layer into the target device.
(*mpLayers)[0]->Repaint(*mpTargetWindow, rRepaintRectangle);
}
else
{
// Paint all layers first into the back buffer (to avoid flickering
// due to synchronous paints) and then copy that into the target
// device.
mpBackBuffer->SetMapMode(mpTargetWindow->GetMapMode());
::std::for_each(
mpLayers->begin(),
mpLayers->end(),
::boost::bind(&Layer::Repaint, _1, ::boost::ref(*mpBackBuffer), rRepaintRectangle));
DeviceCopy(*mpTargetWindow, *mpBackBuffer, rRepaintRectangle);
}
}
void LayeredDevice::Resize (void)
{
const Size aSize (mpTargetWindow->GetSizePixel());
mpBackBuffer->SetOutputSizePixel(aSize);
::std::for_each(mpLayers->begin(), mpLayers->end(), ::boost::bind(&Layer::Resize, _1, aSize));
}
void LayeredDevice::Dispose (void)
{
::std::for_each(mpLayers->begin(), mpLayers->end(), ::boost::bind(&Layer::Dispose, _1));
mpLayers->clear();
}
bool LayeredDevice::HandleMapModeChange (void)
{
const MapMode& rMapMode (mpTargetWindow->GetMapMode());
if (maSavedMapMode == rMapMode)
return false;
const Rectangle aLogicWindowBox (
mpTargetWindow->PixelToLogic(Rectangle(Point(0,0), mpTargetWindow->GetSizePixel())));
if (maSavedMapMode.GetScaleX() != rMapMode.GetScaleX()
|| maSavedMapMode.GetScaleY() != rMapMode.GetScaleY()
|| maSavedMapMode.GetMapUnit() != rMapMode.GetMapUnit())
{
// When the scale has changed then we have to paint everything.
InvalidateAllLayers(aLogicWindowBox);
}
else if (maSavedMapMode.GetOrigin() != rMapMode.GetOrigin())
{
// Window has been scrolled. Adapt contents of backbuffers and
// layer devices.
const Point aDelta (rMapMode.GetOrigin() - maSavedMapMode.GetOrigin());
mpBackBuffer->CopyArea(
aLogicWindowBox.TopLeft(),
mpTargetWindow->PixelToLogic(Point(0,0), maSavedMapMode),
aLogicWindowBox.GetSize());
// Invalidate the area(s) that have been exposed.
const Rectangle aWindowBox (Point(0,0), mpTargetWindow->GetSizePixel());
if (aDelta.Y() < 0)
InvalidateAllLayers(mpTargetWindow->PixelToLogic(Rectangle(
aWindowBox.Left(),
aWindowBox.Bottom()+aDelta.Y(),
aWindowBox.Right(),
aWindowBox.Bottom())));
else if (aDelta.Y() > 0)
InvalidateAllLayers(mpTargetWindow->PixelToLogic(Rectangle(
aWindowBox.Left(),
aWindowBox.Top(),
aWindowBox.Right(),
aWindowBox.Top()+aDelta.Y())));
if (aDelta.X() < 0)
InvalidateAllLayers(mpTargetWindow->PixelToLogic(Rectangle(
aWindowBox.Right()+aDelta.X(),
aWindowBox.Top(),
aWindowBox.Right(),
aWindowBox.Bottom())));
else if (aDelta.X() > 0)
InvalidateAllLayers(mpTargetWindow->PixelToLogic(Rectangle(
aWindowBox.Left(),
aWindowBox.Top(),
aWindowBox.Left()+aDelta.X(),
aWindowBox.Bottom())));
}
else
{
// Can this happen? Lets trigger a warning when it does.
OSL_ASSERT(false);
}
maSavedMapMode = rMapMode;
return true;
}
//===== Layer =================================================================
Layer::Layer (void)
: mpLayerDevice(),
maPainters(),
maInvalidationRegion()
{
}
Layer::~Layer (void)
{
}
void Layer::Initialize (const SharedSdWindow& rpTargetWindow)
{
#if 0
(void)rpTargetWindow;
#else
if ( ! mpLayerDevice)
{
mpLayerDevice.reset(new VirtualDevice(*rpTargetWindow));
mpLayerDevice->SetOutputSizePixel(rpTargetWindow->GetSizePixel());
}
#endif
}
void Layer::InvalidateRectangle (const Rectangle& rInvalidationBox)
{
maInvalidationRegion.Union(rInvalidationBox);
}
void Layer::InvalidateRegion (const Region& rInvalidationRegion)
{
maInvalidationRegion.Union(rInvalidationRegion);
}
void Layer::Validate (const MapMode& rMapMode)
{
if (mpLayerDevice && ! maInvalidationRegion.IsEmpty())
{
Region aRegion (maInvalidationRegion);
maInvalidationRegion.SetEmpty();
mpLayerDevice->SetMapMode(rMapMode);
ForAllRectangles(
aRegion,
::boost::bind(&Layer::ValidateRectangle, this, _1));
}
}
void Layer::ValidateRectangle (const Rectangle& rBox)
{
if ( ! mpLayerDevice)
return;
const Region aSavedClipRegion (mpLayerDevice->GetClipRegion());
mpLayerDevice->IntersectClipRegion(rBox);
for (::std::vector<SharedILayerPainter>::const_iterator
iPainter(maPainters.begin()),
iEnd(maPainters.end());
iPainter!=iEnd;
++iPainter)
{
(*iPainter)->Paint(*mpLayerDevice, rBox);
}
mpLayerDevice->SetClipRegion(aSavedClipRegion);
}
void Layer::Repaint (
OutputDevice& rTargetDevice,
const Rectangle& rRepaintRectangle)
{
if (mpLayerDevice)
{
DeviceCopy(rTargetDevice, *mpLayerDevice, rRepaintRectangle);
}
else
{
::std::for_each(
maPainters.begin(),
maPainters.end(),
::boost::bind(&ILayerPainter::Paint,
_1,
::boost::ref(rTargetDevice),
rRepaintRectangle));
}
}
void Layer::Resize (const Size& rSize)
{
if (mpLayerDevice)
{
mpLayerDevice->SetOutputSizePixel(rSize);
maInvalidationRegion = Rectangle(Point(0,0), rSize);
}
}
void Layer::AddPainter (const SharedILayerPainter& rpPainter)
{
OSL_ASSERT(::std::find(maPainters.begin(), maPainters.end(), rpPainter) == maPainters.end());
maPainters.push_back(rpPainter);
}
void Layer::RemovePainter (const SharedILayerPainter& rpPainter)
{
const ::std::vector<SharedILayerPainter>::iterator iPainter (
::std::find(maPainters.begin(), maPainters.end(), rpPainter));
if (iPainter != maPainters.end())
{
maPainters.erase(iPainter);
}
else
{
DBG_ASSERT(false,"LayeredDevice::RemovePainter called for painter that is not registered");
}
}
bool Layer::HasPainter (void) const
{
return !maPainters.empty();
}
void Layer::Dispose (void)
{
maPainters.clear();
}
} } } // end of namespace ::sd::slidesorter::view