| /************************************************************** |
| * |
| * 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_drawinglayer.hxx" |
| |
| #include <vclhelperbufferdevice.hxx> |
| #include <basegfx/range/b2drange.hxx> |
| #include <vcl/bitmapex.hxx> |
| #include <basegfx/matrix/b2dhommatrix.hxx> |
| #include <tools/stream.hxx> |
| #include <vcl/timer.hxx> |
| #include <comphelper/broadcasthelper.hxx> |
| #include <vcl/lazydelete.hxx> |
| #include <vcl/dibtools.hxx> |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // buffered VDev usage |
| |
| namespace |
| { |
| typedef ::std::vector< VirtualDevice* > aBuffers; |
| |
| class VDevBuffer : public Timer, protected comphelper::OBaseMutex |
| { |
| private: |
| // available buffers |
| aBuffers maFreeBuffers; |
| |
| // allocated/used buffers (remembered to allow deleteing them in destructor) |
| aBuffers maUsedBuffers; |
| |
| public: |
| VDevBuffer(); |
| virtual ~VDevBuffer(); |
| |
| VirtualDevice* alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, bool bMono); |
| void free(VirtualDevice& rDevice); |
| |
| // Timer virtuals |
| virtual void Timeout(); |
| }; |
| |
| VDevBuffer::VDevBuffer() |
| : Timer(), |
| maFreeBuffers(), |
| maUsedBuffers() |
| { |
| SetTimeout(10L * 1000L); // ten seconds |
| } |
| |
| VDevBuffer::~VDevBuffer() |
| { |
| ::osl::MutexGuard aGuard(m_aMutex); |
| Stop(); |
| |
| while(!maFreeBuffers.empty()) |
| { |
| delete *(maFreeBuffers.end() - 1); |
| maFreeBuffers.pop_back(); |
| } |
| |
| while(!maUsedBuffers.empty()) |
| { |
| delete *(maUsedBuffers.end() - 1); |
| maUsedBuffers.pop_back(); |
| } |
| } |
| |
| VirtualDevice* VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, bool bMono) |
| { |
| ::osl::MutexGuard aGuard(m_aMutex); |
| VirtualDevice* pRetval = 0; |
| |
| if(!maFreeBuffers.empty()) |
| { |
| bool bOkay(false); |
| aBuffers::iterator aFound(maFreeBuffers.end()); |
| |
| for(aBuffers::iterator a(maFreeBuffers.begin()); a != maFreeBuffers.end(); a++) |
| { |
| OSL_ENSURE(*a, "Empty pointer in VDevBuffer (!)"); |
| |
| if((bMono && 1 == (*a)->GetBitCount()) || (!bMono && (*a)->GetBitCount() > 1)) |
| { |
| // candidate is valid due to bit depth |
| if(aFound != maFreeBuffers.end()) |
| { |
| // already found |
| if(bOkay) |
| { |
| // found is valid |
| const bool bCandidateOkay((*a)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*a)->GetOutputHeightPixel() >= rSizePixel.getHeight()); |
| |
| if(bCandidateOkay) |
| { |
| // found and candidate are valid |
| const sal_uLong aSquare((*aFound)->GetOutputWidthPixel() * (*aFound)->GetOutputHeightPixel()); |
| const sal_uLong aCandidateSquare((*a)->GetOutputWidthPixel() * (*a)->GetOutputHeightPixel()); |
| |
| if(aCandidateSquare < aSquare) |
| { |
| // candidate is valid and smaller, use it |
| aFound = a; |
| } |
| } |
| else |
| { |
| // found is valid, candidate is not. Keep found |
| } |
| } |
| else |
| { |
| // found is invalid, use candidate |
| aFound = a; |
| bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight(); |
| } |
| } |
| else |
| { |
| // none yet, use candidate |
| aFound = a; |
| bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight(); |
| } |
| } |
| } |
| |
| if(aFound != maFreeBuffers.end()) |
| { |
| pRetval = *aFound; |
| maFreeBuffers.erase(aFound); |
| |
| if(bOkay) |
| { |
| if(bClear) |
| { |
| pRetval->Erase(Rectangle(0, 0, rSizePixel.getWidth(), rSizePixel.getHeight())); |
| } |
| } |
| else |
| { |
| pRetval->SetOutputSizePixel(rSizePixel, bClear); |
| } |
| } |
| } |
| |
| // no success yet, create new buffer |
| if(!pRetval) |
| { |
| pRetval = (bMono) ? new VirtualDevice(rOutDev, 1) : new VirtualDevice(rOutDev); |
| pRetval->SetOutputSizePixel(rSizePixel, bClear); |
| } |
| else |
| { |
| // reused, reset some values |
| pRetval->SetMapMode(); |
| } |
| |
| // remember allocated buffer |
| maUsedBuffers.push_back(pRetval); |
| |
| return pRetval; |
| } |
| |
| void VDevBuffer::free(VirtualDevice& rDevice) |
| { |
| ::osl::MutexGuard aGuard(m_aMutex); |
| const aBuffers::iterator aUsedFound(::std::find(maUsedBuffers.begin(), maUsedBuffers.end(), &rDevice)); |
| OSL_ENSURE(aUsedFound != maUsedBuffers.end(), "OOps, non-registered buffer freed (!)"); |
| |
| maUsedBuffers.erase(aUsedFound); |
| maFreeBuffers.push_back(&rDevice); |
| Start(); |
| } |
| |
| void VDevBuffer::Timeout() |
| { |
| ::osl::MutexGuard aGuard(m_aMutex); |
| |
| while(!maFreeBuffers.empty()) |
| { |
| delete *(maFreeBuffers.end() - 1); |
| maFreeBuffers.pop_back(); |
| } |
| } |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // support for rendering Bitmap and BitmapEx contents |
| |
| namespace drawinglayer |
| { |
| // static global VDev buffer for the VclProcessor2D's (VclMetafileProcessor2D and VclPixelProcessor2D) |
| VDevBuffer& getVDevBuffer() |
| { |
| // secure global instance with Vcl's safe desroyer of external (seen by |
| // library base) stuff, the remembered VDevs need to be deleted before |
| // Vcl's deinit |
| static vcl::DeleteOnDeinit< VDevBuffer > aVDevBuffer(new VDevBuffer()); |
| return *aVDevBuffer.get(); |
| } |
| |
| impBufferDevice::impBufferDevice( |
| OutputDevice& rOutDev, |
| const basegfx::B2DRange& rRange, |
| bool bAddOffsetToMapping) |
| : mrOutDev(rOutDev), |
| mpContent(0), |
| mpMask(0), |
| mpAlpha(0) |
| { |
| basegfx::B2DRange aRangePixel(rRange); |
| aRangePixel.transform(mrOutDev.GetViewTransformation()); |
| const Rectangle aRectPixel( |
| (sal_Int32)floor(aRangePixel.getMinX()), (sal_Int32)floor(aRangePixel.getMinY()), |
| (sal_Int32)ceil(aRangePixel.getMaxX()), (sal_Int32)ceil(aRangePixel.getMaxY())); |
| const Point aEmptyPoint; |
| maDestPixel = Rectangle(aEmptyPoint, mrOutDev.GetOutputSizePixel()); |
| maDestPixel.Intersection(aRectPixel); |
| |
| if(isVisible()) |
| { |
| mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), false, false); |
| |
| // #i93485# assert when copying from window to VDev is used |
| OSL_ENSURE(mrOutDev.GetOutDevType() != OUTDEV_WINDOW, |
| "impBufferDevice render helper: Copying from Window to VDev, this should be avoided (!)"); |
| |
| const bool bWasEnabledSrc(mrOutDev.IsMapModeEnabled()); |
| mrOutDev.EnableMapMode(false); |
| mpContent->DrawOutDev(aEmptyPoint, maDestPixel.GetSize(), maDestPixel.TopLeft(), maDestPixel.GetSize(), mrOutDev); |
| mrOutDev.EnableMapMode(bWasEnabledSrc); |
| |
| MapMode aNewMapMode(mrOutDev.GetMapMode()); |
| |
| if(bAddOffsetToMapping) |
| { |
| const Point aLogicTopLeft(mrOutDev.PixelToLogic(maDestPixel.TopLeft())); |
| aNewMapMode.SetOrigin(Point(-aLogicTopLeft.X(), -aLogicTopLeft.Y())); |
| } |
| |
| mpContent->SetMapMode(aNewMapMode); |
| |
| // copy AA flag for new target |
| mpContent->SetAntialiasing(mrOutDev.GetAntialiasing()); |
| } |
| } |
| |
| impBufferDevice::~impBufferDevice() |
| { |
| if(mpContent) |
| { |
| getVDevBuffer().free(*mpContent); |
| } |
| |
| if(mpMask) |
| { |
| getVDevBuffer().free(*mpMask); |
| } |
| |
| if(mpAlpha) |
| { |
| getVDevBuffer().free(*mpAlpha); |
| } |
| } |
| |
| void impBufferDevice::paint(double fTrans) |
| { |
| if(isVisible()) |
| { |
| const Point aEmptyPoint; |
| const Size aSizePixel(maDestPixel.GetSize()); |
| const bool bWasEnabledDst(mrOutDev.IsMapModeEnabled()); |
| static bool bDoSaveForVisualControl(false); |
| |
| mrOutDev.EnableMapMode(false); |
| mpContent->EnableMapMode(false); |
| Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel)); |
| |
| if(bDoSaveForVisualControl) |
| { |
| SvFileStream aNew((const String&)String(ByteString( "c:\\content.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC); |
| WriteDIB(aContent, aNew, false, true); |
| } |
| |
| if(mpAlpha) |
| { |
| mpAlpha->EnableMapMode(false); |
| const AlphaMask aAlphaMask(mpAlpha->GetBitmap(aEmptyPoint, aSizePixel)); |
| |
| if(bDoSaveForVisualControl) |
| { |
| SvFileStream aNew((const String&)String(ByteString( "c:\\transparence.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC); |
| WriteDIB(aAlphaMask.GetBitmap(), aNew, false, true); |
| } |
| |
| mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask)); |
| } |
| else if(mpMask) |
| { |
| mpMask->EnableMapMode(false); |
| const Bitmap aMask(mpMask->GetBitmap(aEmptyPoint, aSizePixel)); |
| |
| if(bDoSaveForVisualControl) |
| { |
| SvFileStream aNew((const String&)String(ByteString( "c:\\mask.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC); |
| WriteDIB(aMask, aNew, false, true); |
| } |
| |
| mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aMask)); |
| } |
| else if(0.0 != fTrans) |
| { |
| sal_uInt8 nMaskValue((sal_uInt8)basegfx::fround(fTrans * 255.0)); |
| const AlphaMask aAlphaMask(aSizePixel, &nMaskValue); |
| mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask)); |
| } |
| else |
| { |
| mrOutDev.DrawBitmap(maDestPixel.TopLeft(), aContent); |
| } |
| |
| mrOutDev.EnableMapMode(bWasEnabledDst); |
| } |
| } |
| |
| VirtualDevice& impBufferDevice::getContent() |
| { |
| OSL_ENSURE(mpContent, "impBufferDevice: No content, check isVisible() before accessing (!)"); |
| return *mpContent; |
| } |
| |
| VirtualDevice& impBufferDevice::getMask() |
| { |
| OSL_ENSURE(mpContent, "impBufferDevice: No content, check isVisible() before accessing (!)"); |
| if(!mpMask) |
| { |
| mpMask = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, true); |
| mpMask->SetMapMode(mpContent->GetMapMode()); |
| |
| // do NOT copy AA flag for mask! |
| } |
| |
| return *mpMask; |
| } |
| |
| VirtualDevice& impBufferDevice::getTransparence() |
| { |
| OSL_ENSURE(mpContent, "impBufferDevice: No content, check isVisible() before accessing (!)"); |
| if(!mpAlpha) |
| { |
| mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, false); |
| mpAlpha->SetMapMode(mpContent->GetMapMode()); |
| |
| // copy AA flag for new target; masking needs to be smooth |
| mpAlpha->SetAntialiasing(mpContent->GetAntialiasing()); |
| } |
| |
| return *mpAlpha; |
| } |
| } // end of namespace drawinglayer |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // eof |