blob: 748a690734b685e286ec8e1a63bc425b7e0c155e [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.
*
*************************************************************/
// 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