| /************************************************************** |
| * |
| * 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 |