/************************************************************** * * 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. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_svx.hxx" #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////// namespace sdr { namespace overlay { void OverlayManagerBuffered::ImpPrepareBufferDevice() { // compare size of maBufferDevice with size of visible area if(maBufferDevice.GetOutputSizePixel() != getOutputDevice().GetOutputSizePixel()) { // set new buffer size, copy as much content as possible (use bool parameter for vcl). // Newly uncovered regions will be repainted. maBufferDevice.SetOutputSizePixel(getOutputDevice().GetOutputSizePixel(), false); } // compare the MapModes for zoom/scroll changes if(maBufferDevice.GetMapMode() != getOutputDevice().GetMapMode()) { const bool bZoomed( maBufferDevice.GetMapMode().GetScaleX() != getOutputDevice().GetMapMode().GetScaleX() || maBufferDevice.GetMapMode().GetScaleY() != getOutputDevice().GetMapMode().GetScaleY()); if(!bZoomed) { const Point& rOriginOld = maBufferDevice.GetMapMode().GetOrigin(); const Point& rOriginNew = getOutputDevice().GetMapMode().GetOrigin(); const bool bScrolled(rOriginOld != rOriginNew); if(bScrolled) { // get pixel bounds const Point aOriginOldPixel(maBufferDevice.LogicToPixel(rOriginOld)); const Point aOriginNewPixel(maBufferDevice.LogicToPixel(rOriginNew)); const Size aOutputSizePixel(maBufferDevice.GetOutputSizePixel()); // remember and switch off MapMode const bool bMapModeWasEnabled(maBufferDevice.IsMapModeEnabled()); maBufferDevice.EnableMapMode(false); // scroll internally buffered stuff const Point aDestinationOffsetPixel(aOriginNewPixel - aOriginOldPixel); maBufferDevice.DrawOutDev( aDestinationOffsetPixel, aOutputSizePixel, // destination Point(), aOutputSizePixel); // source // restore MapMode maBufferDevice.EnableMapMode(bMapModeWasEnabled); // scroll remembered region, too. if(!maBufferRememberedRangePixel.isEmpty()) { const basegfx::B2IPoint aIPointDestinationOffsetPixel(aDestinationOffsetPixel.X(), aDestinationOffsetPixel.Y()); const basegfx::B2IPoint aNewMinimum(maBufferRememberedRangePixel.getMinimum() + aIPointDestinationOffsetPixel); const basegfx::B2IPoint aNewMaximum(maBufferRememberedRangePixel.getMaximum() + aIPointDestinationOffsetPixel); maBufferRememberedRangePixel = basegfx::B2IRange(aNewMinimum, aNewMaximum); } } } // copy new MapMode maBufferDevice.SetMapMode(getOutputDevice().GetMapMode()); } // #i29186# maBufferDevice.SetDrawMode(getOutputDevice().GetDrawMode()); maBufferDevice.SetSettings(getOutputDevice().GetSettings()); maBufferDevice.SetAntialiasing(getOutputDevice().GetAntialiasing()); } void OverlayManagerBuffered::ImpRestoreBackground() const { const Rectangle aRegionRectanglePixel( maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(), maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY()); const Region aRegionPixel(aRegionRectanglePixel); ImpRestoreBackground(aRegionPixel); } void OverlayManagerBuffered::ImpRestoreBackground(const Region& rRegionPixel) const { // local region Region aRegionPixel(rRegionPixel); RegionHandle aRegionHandle(aRegionPixel.BeginEnumRects()); Rectangle aRegionRectanglePixel; // MapModes off const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled()); const bool bMapModeWasEnabledSource(maBufferDevice.IsMapModeEnabled()); getOutputDevice().EnableMapMode(false); ((OverlayManagerBuffered*)this)->maBufferDevice.EnableMapMode(false); while(aRegionPixel.GetEnumRects(aRegionHandle, aRegionRectanglePixel)) { #ifdef DBG_UTIL // #i72754# possible graphical region test only with non-pro static bool bDoPaintForVisualControl(false); if(bDoPaintForVisualControl) { getOutputDevice().SetLineColor(COL_LIGHTGREEN); getOutputDevice().SetFillColor(); getOutputDevice().DrawRect(aRegionRectanglePixel); } #endif // restore the area const Point aTopLeft(aRegionRectanglePixel.TopLeft()); const Size aSize(aRegionRectanglePixel.GetSize()); getOutputDevice().DrawOutDev( aTopLeft, aSize, // destination aTopLeft, aSize, // source maBufferDevice); } aRegionPixel.EndEnumRects(aRegionHandle); // restore MapModes getOutputDevice().EnableMapMode(bMapModeWasEnabledDest); ((OverlayManagerBuffered*)this)->maBufferDevice.EnableMapMode(bMapModeWasEnabledSource); } void OverlayManagerBuffered::ImpSaveBackground(const Region& rRegion, OutputDevice* pPreRenderDevice) { // prepare source OutputDevice& rSource = (pPreRenderDevice) ? *pPreRenderDevice : getOutputDevice(); // Ensure buffer is valid ImpPrepareBufferDevice(); // build region which needs to be copied Region aRegion(rSource.LogicToPixel(rRegion)); // limit to PaintRegion if it's a window. This will be evtl. the expanded one, // but always the exact redraw area if(OUTDEV_WINDOW == rSource.GetOutDevType()) { Window& rWindow = (Window&)rSource; Region aPaintRegionPixel = rWindow.LogicToPixel(rWindow.GetPaintRegion()); aRegion.Intersect(aPaintRegionPixel); // #i72754# Make sure content is completetly rendered, the window // will be used as source of a DrawOutDev soon rWindow.Flush(); } // also limit to buffer size const Rectangle aBufferDeviceRectanglePixel = Rectangle(Point(), maBufferDevice.GetOutputSizePixel()); aRegion.Intersect(aBufferDeviceRectanglePixel); // prepare to iterate over the rectangles from the region in pixels RegionHandle aRegionHandle(aRegion.BeginEnumRects()); Rectangle aRegionRectanglePixel; // MapModes off const bool bMapModeWasEnabledDest(rSource.IsMapModeEnabled()); const bool bMapModeWasEnabledSource(maBufferDevice.IsMapModeEnabled()); rSource.EnableMapMode(false); maBufferDevice.EnableMapMode(false); while(aRegion.GetEnumRects(aRegionHandle, aRegionRectanglePixel)) { // for each rectangle, save the area Point aTopLeft(aRegionRectanglePixel.TopLeft()); Size aSize(aRegionRectanglePixel.GetSize()); maBufferDevice.DrawOutDev( aTopLeft, aSize, // destination aTopLeft, aSize, // source rSource); #ifdef DBG_UTIL // #i72754# possible graphical region test only with non-pro static bool bDoPaintForVisualControl(false); if(bDoPaintForVisualControl) { const bool bMapModeWasEnabledTest(getOutputDevice().IsMapModeEnabled()); getOutputDevice().EnableMapMode(false); getOutputDevice().SetLineColor(COL_LIGHTRED); getOutputDevice().SetFillColor(); getOutputDevice().DrawRect(aRegionRectanglePixel); getOutputDevice().EnableMapMode(bMapModeWasEnabledTest); } static bool bDoSaveForVisualControl(false); if(bDoSaveForVisualControl) { const Bitmap aBitmap(maBufferDevice.GetBitmap(aTopLeft, aSize)); SvFileStream aNew((const String&)String(ByteString( "c:\\test.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC); aNew << aBitmap; } #endif } aRegion.EndEnumRects(aRegionHandle); // restore MapModes rSource.EnableMapMode(bMapModeWasEnabledDest); maBufferDevice.EnableMapMode(bMapModeWasEnabledSource); } IMPL_LINK(OverlayManagerBuffered, ImpBufferTimerHandler, AutoTimer*, /*pTimer*/) { // stop timer maBufferTimer.Stop(); if(!maBufferRememberedRangePixel.isEmpty()) { // logic size for impDrawMember call basegfx::B2DRange aBufferRememberedRangeLogic( maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(), maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY()); aBufferRememberedRangeLogic.transform(getOutputDevice().GetInverseViewTransformation()); // prepare cursor handling const bool bTargetIsWindow(OUTDEV_WINDOW == rmOutputDevice.GetOutDevType()); bool bCursorWasEnabled(false); // #i80730# switch off VCL cursor during overlay refresh if(bTargetIsWindow) { Window& rWindow = static_cast< Window& >(rmOutputDevice); Cursor* pCursor = rWindow.GetCursor(); if(pCursor && pCursor->IsVisible()) { pCursor->Hide(); bCursorWasEnabled = true; } } if(DoRefreshWithPreRendering()) { // #i73602# ensure valid and sized maOutputBufferDevice const Size aDestinationSizePixel(maBufferDevice.GetOutputSizePixel()); const Size aOutputBufferSizePixel(maOutputBufferDevice.GetOutputSizePixel()); if(aDestinationSizePixel != aOutputBufferSizePixel) { maOutputBufferDevice.SetOutputSizePixel(aDestinationSizePixel); } maOutputBufferDevice.SetMapMode(getOutputDevice().GetMapMode()); maOutputBufferDevice.EnableMapMode(false); maOutputBufferDevice.SetDrawMode(maBufferDevice.GetDrawMode()); maOutputBufferDevice.SetSettings(maBufferDevice.GetSettings()); maOutputBufferDevice.SetAntialiasing(maBufferDevice.GetAntialiasing()); // calculate sizes Rectangle aRegionRectanglePixel( maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(), maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY()); // truncate aRegionRectanglePixel to destination pixel size, more does // not need to be prepared since destination is a buffer for a window. So, // maximum size indirectly shall be limited to getOutputDevice().GetOutputSizePixel() if(aRegionRectanglePixel.Left() < 0L) { aRegionRectanglePixel.Left() = 0L; } if(aRegionRectanglePixel.Top() < 0L) { aRegionRectanglePixel.Top() = 0L; } if(aRegionRectanglePixel.Right() > aDestinationSizePixel.getWidth()) { aRegionRectanglePixel.Right() = aDestinationSizePixel.getWidth(); } if(aRegionRectanglePixel.Bottom() > aDestinationSizePixel.getHeight()) { aRegionRectanglePixel.Bottom() = aDestinationSizePixel.getHeight(); } // get sizes const Point aTopLeft(aRegionRectanglePixel.TopLeft()); const Size aSize(aRegionRectanglePixel.GetSize()); { const bool bMapModeWasEnabledDest(maBufferDevice.IsMapModeEnabled()); maBufferDevice.EnableMapMode(false); maOutputBufferDevice.DrawOutDev( aTopLeft, aSize, // destination aTopLeft, aSize, // source maBufferDevice); // restore MapModes maBufferDevice.EnableMapMode(bMapModeWasEnabledDest); } // paint overlay content for remembered region, use // method from base class directly maOutputBufferDevice.EnableMapMode(true); OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic, maOutputBufferDevice); maOutputBufferDevice.EnableMapMode(false); // copy to output { const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled()); getOutputDevice().EnableMapMode(false); getOutputDevice().DrawOutDev( aTopLeft, aSize, // destination aTopLeft, aSize, // source maOutputBufferDevice); // debug /*getOutputDevice().SetLineColor(COL_RED); getOutputDevice().SetFillColor(); getOutputDevice().DrawRect(Rectangle(aTopLeft, aSize));*/ // restore MapModes getOutputDevice().EnableMapMode(bMapModeWasEnabledDest); } } else { // Restore all rectangles for remembered region from buffer ImpRestoreBackground(); // paint overlay content for remembered region, use // method from base class directly OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic, getOutputDevice()); } // VCL hack for transparent child windows // Problem is e.g. a radiobuttion form control in life mode. The used window // is a transparence vcl childwindow. This flag only allows the parent window to // paint into the child windows area, but there is no mechanism which takes // care for a repaint of the child window. A transparent child window is NOT // a window which always keeps it's content consistent over the parent, but it's // more like just a paint flag for the parent. // To get the update, the windows in question are updated manulally here. if(bTargetIsWindow) { Window& rWindow = static_cast< Window& >(rmOutputDevice); if(rWindow.IsChildTransparentModeEnabled() && rWindow.GetChildCount()) { const Rectangle aRegionRectanglePixel( maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(), maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY()); for(sal_uInt16 a(0); a < rWindow.GetChildCount(); a++) { Window* pCandidate = rWindow.GetChild(a); if(pCandidate && pCandidate->IsPaintTransparent()) { const Rectangle aCandidatePosSizePixel(pCandidate->GetPosPixel(), pCandidate->GetSizePixel()); if(aCandidatePosSizePixel.IsOver(aRegionRectanglePixel)) { pCandidate->Invalidate(INVALIDATE_NOTRANSPARENT|INVALIDATE_CHILDREN); pCandidate->Update(); } } } } } // #i80730# restore visibility of VCL cursor if(bCursorWasEnabled) { Window& rWindow = static_cast< Window& >(rmOutputDevice); Cursor* pCursor = rWindow.GetCursor(); if(pCursor) { // check if cursor still exists. It may have been deleted from someone pCursor->Show(); } } // forget remembered Region maBufferRememberedRangePixel.reset(); } return 0; } OverlayManagerBuffered::OverlayManagerBuffered( OutputDevice& rOutputDevice, bool bRefreshWithPreRendering) : OverlayManager(rOutputDevice), mbRefreshWithPreRendering(bRefreshWithPreRendering) { // Init timer maBufferTimer.SetTimeout(1); maBufferTimer.SetTimeoutHdl(LINK(this, OverlayManagerBuffered, ImpBufferTimerHandler)); } OverlayManagerBuffered::~OverlayManagerBuffered() { // Clear timer maBufferTimer.Stop(); if(!maBufferRememberedRangePixel.isEmpty()) { // Restore all rectangles for remembered region from buffer ImpRestoreBackground(); } } void OverlayManagerBuffered::completeRedraw(const Region& rRegion, OutputDevice* pPreRenderDevice) const { if(!rRegion.IsEmpty()) { // save new background ((OverlayManagerBuffered*)this)->ImpSaveBackground(rRegion, pPreRenderDevice); } // call parent OverlayManager::completeRedraw(rRegion, pPreRenderDevice); } void OverlayManagerBuffered::flush() { // call timer handler direct ImpBufferTimerHandler(0); } // #i68597# part of content gets copied, react on it void OverlayManagerBuffered::copyArea(const Point& rDestPt, const Point& rSrcPt, const Size& rSrcSize) { // scroll local buffered area maBufferDevice.CopyArea(rDestPt, rSrcPt, rSrcSize); } void OverlayManagerBuffered::restoreBackground(const Region& rRegion) const { // restore const Region aRegionPixel(getOutputDevice().LogicToPixel(rRegion)); ImpRestoreBackground(aRegionPixel); // call parent OverlayManager::restoreBackground(rRegion); } void OverlayManagerBuffered::invalidateRange(const basegfx::B2DRange& rRange) { if(!rRange.isEmpty()) { // buffered output, do not invalidate but use the timer // to trigger a timer event for refresh maBufferTimer.Start(); // add the discrete range to the remembered region // #i75163# use double precision and floor/ceil rounding to get overlapped pixel region, even // when the given logic region has a width/height of 0.0. This does NOT work with LogicToPixel // since it just transforms the top left and bottom right points equally without taking // discrete pixel coverage into account. An empty B2DRange and thus empty logic Rectangle translated // to an also empty discrete pixel rectangle - what is wrong. basegfx::B2DRange aDiscreteRange(rRange); aDiscreteRange.transform(getOutputDevice().GetViewTransformation()); if(maDrawinglayerOpt.IsAntiAliasing()) { // assume AA needs one pixel more and invalidate one pixel more const double fDiscreteOne(getDiscreteOne()); const basegfx::B2IPoint aTopLeft( (sal_Int32)floor(aDiscreteRange.getMinX() - fDiscreteOne), (sal_Int32)floor(aDiscreteRange.getMinY() - fDiscreteOne)); const basegfx::B2IPoint aBottomRight( (sal_Int32)ceil(aDiscreteRange.getMaxX() + fDiscreteOne), (sal_Int32)ceil(aDiscreteRange.getMaxY() + fDiscreteOne)); maBufferRememberedRangePixel.expand(aTopLeft); maBufferRememberedRangePixel.expand(aBottomRight); } else { const basegfx::B2IPoint aTopLeft((sal_Int32)floor(aDiscreteRange.getMinX()), (sal_Int32)floor(aDiscreteRange.getMinY())); const basegfx::B2IPoint aBottomRight((sal_Int32)ceil(aDiscreteRange.getMaxX()), (sal_Int32)ceil(aDiscreteRange.getMaxY())); maBufferRememberedRangePixel.expand(aTopLeft); maBufferRememberedRangePixel.expand(aBottomRight); } } } void OverlayManagerBuffered::SetRefreshWithPreRendering(bool bNew) { if((bool)mbRefreshWithPreRendering != bNew) { mbRefreshWithPreRendering = bNew; } } } // end of namespace overlay } // end of namespace sdr ////////////////////////////////////////////////////////////////////////////// // eof