Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / drawinglayer / source / processor2d / vclhelperbufferdevice.cxx
blob8ebec79304b65dd153c3a903bc3a3f2d85dc9b2d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <algorithm>
23 #include <map>
24 #include <vector>
26 #include <vclhelperbufferdevice.hxx>
27 #include <basegfx/range/b2drange.hxx>
28 #include <vcl/bitmapex.hxx>
29 #include <basegfx/matrix/b2dhommatrix.hxx>
30 #include <tools/stream.hxx>
31 #include <vcl/timer.hxx>
32 #include <comphelper/broadcasthelper.hxx>
33 #include <vcl/lazydelete.hxx>
34 #include <vcl/dibtools.hxx>
37 // buffered VDev usage
39 namespace
41 typedef ::std::vector< VclPtr<VirtualDevice> > aBuffers;
43 class VDevBuffer : public Timer, protected comphelper::OBaseMutex
45 private:
46 // available buffers
47 aBuffers maFreeBuffers;
49 // allocated/used buffers (remembered to allow deleting them in destructor)
50 aBuffers maUsedBuffers;
52 // remember what outputdevice was the template passed to VirtualDevice::Create
53 // so we can test if that OutputDevice was disposed before reusing a
54 // virtualdevice because that isn't safe to do at least for Gtk2
55 std::map< VclPtr<VirtualDevice>, VclPtr<OutputDevice> > maDeviceTemplates;
57 public:
58 VDevBuffer();
59 virtual ~VDevBuffer();
61 VirtualDevice* alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, bool bMonoChrome);
62 void free(VirtualDevice& rDevice);
64 // Timer virtuals
65 virtual void Invoke() override;
68 VDevBuffer::VDevBuffer()
69 : Timer(),
70 maFreeBuffers(),
71 maUsedBuffers()
73 SetTimeout(10L * 1000L); // ten seconds
76 VDevBuffer::~VDevBuffer()
78 ::osl::MutexGuard aGuard(m_aMutex);
79 Stop();
81 while(!maFreeBuffers.empty())
83 (*(maFreeBuffers.end() - 1)).disposeAndClear();
84 maFreeBuffers.pop_back();
87 while(!maUsedBuffers.empty())
89 (*(maUsedBuffers.end() - 1)).disposeAndClear();
90 maUsedBuffers.pop_back();
94 VirtualDevice* VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, bool bMonoChrome)
96 ::osl::MutexGuard aGuard(m_aMutex);
97 VirtualDevice* pRetval = nullptr;
99 sal_Int32 nBits = bMonoChrome ? 1 : rOutDev.GetBitCount();
101 bool bOkay(false);
102 if(!maFreeBuffers.empty())
104 aBuffers::iterator aFound(maFreeBuffers.end());
106 for(aBuffers::iterator a(maFreeBuffers.begin()); a != maFreeBuffers.end(); ++a)
108 assert(*a && "Empty pointer in VDevBuffer (!)");
110 if (nBits == (*a)->GetBitCount())
112 // candidate is valid due to bit depth
113 if(aFound != maFreeBuffers.end())
115 // already found
116 if(bOkay)
118 // found is valid
119 const bool bCandidateOkay((*a)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*a)->GetOutputHeightPixel() >= rSizePixel.getHeight());
121 if(bCandidateOkay)
123 // found and candidate are valid
124 const sal_uLong aSquare((*aFound)->GetOutputWidthPixel() * (*aFound)->GetOutputHeightPixel());
125 const sal_uLong aCandidateSquare((*a)->GetOutputWidthPixel() * (*a)->GetOutputHeightPixel());
127 if(aCandidateSquare < aSquare)
129 // candidate is valid and smaller, use it
130 aFound = a;
133 else
135 // found is valid, candidate is not. Keep found
138 else
140 // found is invalid, use candidate
141 aFound = a;
142 bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight();
145 else
147 // none yet, use candidate
148 aFound = a;
149 bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight();
154 if(aFound != maFreeBuffers.end())
156 pRetval = *aFound;
157 maFreeBuffers.erase(aFound);
161 if (pRetval)
163 // found a suitable cached virtual device, but the
164 // outputdevice it was based on has been disposed,
165 // drop it and create a new one instead as reusing
166 // such devices is unsafe under at least Gtk2
167 if (maDeviceTemplates[pRetval]->isDisposed())
169 maDeviceTemplates.erase(pRetval);
170 pRetval = nullptr;
172 else
174 if (bOkay)
176 if (bClear)
178 pRetval->Erase(Rectangle(0, 0, rSizePixel.getWidth(), rSizePixel.getHeight()));
181 else
183 pRetval->SetOutputSizePixel(rSizePixel, bClear);
188 // no success yet, create new buffer
189 if(!pRetval)
191 pRetval = VclPtr<VirtualDevice>::Create(rOutDev, bMonoChrome ? DeviceFormat::BITMASK : DeviceFormat::DEFAULT);
192 maDeviceTemplates[pRetval] = &rOutDev;
193 pRetval->SetOutputSizePixel(rSizePixel, bClear);
195 else
197 // reused, reset some values
198 pRetval->SetMapMode();
199 pRetval->SetRasterOp(ROP_OVERPAINT);
202 // remember allocated buffer
203 maUsedBuffers.push_back(pRetval);
205 return pRetval;
208 void VDevBuffer::free(VirtualDevice& rDevice)
210 ::osl::MutexGuard aGuard(m_aMutex);
211 const aBuffers::iterator aUsedFound(::std::find(maUsedBuffers.begin(), maUsedBuffers.end(), &rDevice));
212 OSL_ENSURE(aUsedFound != maUsedBuffers.end(), "OOps, non-registered buffer freed (!)");
214 maUsedBuffers.erase(aUsedFound);
215 maFreeBuffers.push_back(&rDevice);
216 SAL_WARN_IF(maFreeBuffers.size() > 1000, "drawinglayer", "excessive cached buffers, "
217 << maFreeBuffers.size() << " entries!");
218 Start();
221 void VDevBuffer::Invoke()
223 ::osl::MutexGuard aGuard(m_aMutex);
225 while(!maFreeBuffers.empty())
227 aBuffers::iterator aLastOne(maFreeBuffers.end() - 1);
228 maDeviceTemplates.erase(*aLastOne);
229 aLastOne->disposeAndClear();
230 maFreeBuffers.pop_back();
236 // support for rendering Bitmap and BitmapEx contents
238 namespace drawinglayer
240 // static global VDev buffer for the VclProcessor2D's (VclMetafileProcessor2D and VclPixelProcessor2D)
241 VDevBuffer& getVDevBuffer()
243 // secure global instance with Vcl's safe destroyer of external (seen by
244 // library base) stuff, the remembered VDevs need to be deleted before
245 // Vcl's deinit
246 static vcl::DeleteOnDeinit< VDevBuffer > aVDevBuffer(new VDevBuffer());
247 return *aVDevBuffer.get();
250 impBufferDevice::impBufferDevice(
251 OutputDevice& rOutDev,
252 const basegfx::B2DRange& rRange)
253 : mrOutDev(rOutDev),
254 mpContent(nullptr),
255 mpMask(nullptr),
256 mpAlpha(nullptr)
258 basegfx::B2DRange aRangePixel(rRange);
259 aRangePixel.transform(mrOutDev.GetViewTransformation());
260 const Rectangle aRectPixel(
261 (sal_Int32)floor(aRangePixel.getMinX()), (sal_Int32)floor(aRangePixel.getMinY()),
262 (sal_Int32)ceil(aRangePixel.getMaxX()), (sal_Int32)ceil(aRangePixel.getMaxY()));
263 const Point aEmptyPoint;
264 maDestPixel = Rectangle(aEmptyPoint, mrOutDev.GetOutputSizePixel());
265 maDestPixel.Intersection(aRectPixel);
267 if(isVisible())
269 #ifdef IOS
270 // Exact mechanism unknown, but for some reason SmartArt
271 // rendering, especially shadows, is broken on iOS unless
272 // we pass 'true' here. Are virtual devices always de
273 // facto cleared when created on other platforms?
274 mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, false);
275 #else
276 mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), false, false);
277 #endif
279 // #i93485# assert when copying from window to VDev is used
280 OSL_ENSURE(mrOutDev.GetOutDevType() != OUTDEV_WINDOW,
281 "impBufferDevice render helper: Copying from Window to VDev, this should be avoided (!)");
283 const bool bWasEnabledSrc(mrOutDev.IsMapModeEnabled());
284 mrOutDev.EnableMapMode(false);
285 mpContent->DrawOutDev(aEmptyPoint, maDestPixel.GetSize(), maDestPixel.TopLeft(), maDestPixel.GetSize(), mrOutDev);
286 mrOutDev.EnableMapMode(bWasEnabledSrc);
288 MapMode aNewMapMode(mrOutDev.GetMapMode());
290 const Point aLogicTopLeft(mrOutDev.PixelToLogic(maDestPixel.TopLeft()));
291 aNewMapMode.SetOrigin(Point(-aLogicTopLeft.X(), -aLogicTopLeft.Y()));
293 mpContent->SetMapMode(aNewMapMode);
295 // copy AA flag for new target
296 mpContent->SetAntialiasing(mrOutDev.GetAntialiasing());
298 // copy RasterOp (e.g. may be ROP_XOR on destination)
299 mpContent->SetRasterOp(mrOutDev.GetRasterOp());
303 impBufferDevice::~impBufferDevice()
305 if(mpContent)
307 getVDevBuffer().free(*mpContent);
310 if(mpMask)
312 getVDevBuffer().free(*mpMask);
315 if(mpAlpha)
317 getVDevBuffer().free(*mpAlpha);
321 void impBufferDevice::paint(double fTrans)
323 if(isVisible())
325 const Point aEmptyPoint;
326 const Size aSizePixel(maDestPixel.GetSize());
327 const bool bWasEnabledDst(mrOutDev.IsMapModeEnabled());
328 #ifdef DBG_UTIL
329 static bool bDoSaveForVisualControl(false);
330 #endif
332 mrOutDev.EnableMapMode(false);
333 mpContent->EnableMapMode(false);
334 Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
336 #ifdef DBG_UTIL
337 if(bDoSaveForVisualControl)
339 SvFileStream aNew(
340 #ifdef _WIN32
341 "c:\\content.bmp",
342 #else
343 "~/content.bmp",
344 #endif
345 StreamMode::WRITE|StreamMode::TRUNC);
346 WriteDIB(aContent, aNew, false, true);
348 #endif
350 // during painting the buffer, disable evtl. set RasterOp (may be ROP_XOR)
351 const RasterOp aOrigRasterOp(mrOutDev.GetRasterOp());
352 mrOutDev.SetRasterOp(ROP_OVERPAINT);
354 if(mpAlpha)
356 mpAlpha->EnableMapMode(false);
357 const AlphaMask aAlphaMask(mpAlpha->GetBitmap(aEmptyPoint, aSizePixel));
359 #ifdef DBG_UTIL
360 if(bDoSaveForVisualControl)
362 SvFileStream aNew(
363 #ifdef _WIN32
364 "c:\\transparence.bmp",
365 #else
366 "~/transparence.bmp",
367 #endif
368 StreamMode::WRITE|StreamMode::TRUNC);
369 WriteDIB(aAlphaMask.GetBitmap(), aNew, false, true);
371 #endif
373 mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
375 else if(mpMask)
377 mpMask->EnableMapMode(false);
378 const Bitmap aMask(mpMask->GetBitmap(aEmptyPoint, aSizePixel));
380 #ifdef DBG_UTIL
381 if(bDoSaveForVisualControl)
383 SvFileStream aNew(
384 #ifdef _WIN32
385 "c:\\mask.bmp",
386 #else
387 "~/mask.bmp",
388 #endif
389 StreamMode::WRITE|StreamMode::TRUNC);
390 WriteDIB(aMask, aNew, false, true);
392 #endif
394 mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aMask));
396 else if(0.0 != fTrans)
398 sal_uInt8 nMaskValue((sal_uInt8)basegfx::fround(fTrans * 255.0));
399 const AlphaMask aAlphaMask(aSizePixel, &nMaskValue);
400 mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
402 else
404 mrOutDev.DrawBitmap(maDestPixel.TopLeft(), aContent);
407 mrOutDev.SetRasterOp(aOrigRasterOp);
408 mrOutDev.EnableMapMode(bWasEnabledDst);
412 VirtualDevice& impBufferDevice::getContent()
414 assert(mpContent && "impBufferDevice: No content, check isVisible() before accessing (!)");
415 return *mpContent;
418 VirtualDevice& impBufferDevice::getMask()
420 assert(mpContent && "impBufferDevice: No content, check isVisible() before accessing (!)");
421 if (!mpMask)
423 mpMask = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, true);
424 mpMask->SetMapMode(mpContent->GetMapMode());
426 // do NOT copy AA flag for mask!
429 return *mpMask;
432 VirtualDevice& impBufferDevice::getTransparence()
434 OSL_ENSURE(mpContent, "impBufferDevice: No content, check isVisible() before accessing (!)");
435 if(!mpAlpha)
437 mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, false);
438 mpAlpha->SetMapMode(mpContent->GetMapMode());
440 // copy AA flag for new target; masking needs to be smooth
441 mpAlpha->SetAntialiasing(mpContent->GetAntialiasing());
444 return *mpAlpha;
446 } // end of namespace drawinglayer
448 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */