Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / drawinglayer / source / processor2d / vclhelperbufferdevice.cxx
blobc377f4ee6526e18db433119c0611ef3cd98bf095
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>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
24 #include <algorithm>
25 #include <map>
26 #include <vector>
28 #include "vclhelperbufferdevice.hxx"
29 #include <basegfx/range/b2drange.hxx>
30 #include <vcl/bitmapex.hxx>
31 #include <basegfx/matrix/b2dhommatrix.hxx>
32 #include <tools/stream.hxx>
33 #include <vcl/timer.hxx>
34 #include <cppuhelper/basemutex.hxx>
35 #include <vcl/lazydelete.hxx>
36 #include <vcl/dibtools.hxx>
39 // buffered VDev usage
41 namespace
43 typedef std::vector< VclPtr<VirtualDevice> > aBuffers;
45 class VDevBuffer : public Timer, protected cppu::BaseMutex
47 private:
48 // available buffers
49 aBuffers maFreeBuffers;
51 // allocated/used buffers (remembered to allow deleting them in destructor)
52 aBuffers maUsedBuffers;
54 // remember what outputdevice was the template passed to VirtualDevice::Create
55 // so we can test if that OutputDevice was disposed before reusing a
56 // virtualdevice because that isn't safe to do at least for Gtk2
57 std::map< VclPtr<VirtualDevice>, VclPtr<OutputDevice> > maDeviceTemplates;
59 public:
60 VDevBuffer();
61 virtual ~VDevBuffer() override;
63 VclPtr<VirtualDevice> alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, bool bMonoChrome);
64 void free(VirtualDevice& rDevice);
66 // Timer virtuals
67 virtual void Invoke() override;
70 VDevBuffer::VDevBuffer()
71 : Timer("VDevBuffer timer"),
72 maFreeBuffers(),
73 maUsedBuffers()
75 SetTimeout(10L * 1000L); // ten seconds
76 SetDebugName("drawinglayer::VDevBuffer via Invoke()");
79 VDevBuffer::~VDevBuffer()
81 ::osl::MutexGuard aGuard(m_aMutex);
82 Stop();
84 while(!maFreeBuffers.empty())
86 (*(maFreeBuffers.end() - 1)).disposeAndClear();
87 maFreeBuffers.pop_back();
90 while(!maUsedBuffers.empty())
92 (*(maUsedBuffers.end() - 1)).disposeAndClear();
93 maUsedBuffers.pop_back();
97 VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, bool bMonoChrome)
99 ::osl::MutexGuard aGuard(m_aMutex);
100 VclPtr<VirtualDevice> pRetval;
102 sal_Int32 nBits = bMonoChrome ? 1 : rOutDev.GetBitCount();
104 bool bOkay(false);
105 if(!maFreeBuffers.empty())
107 aBuffers::iterator aFound(maFreeBuffers.end());
109 for(aBuffers::iterator a(maFreeBuffers.begin()); a != maFreeBuffers.end(); ++a)
111 assert(*a && "Empty pointer in VDevBuffer (!)");
113 if (nBits == (*a)->GetBitCount())
115 // candidate is valid due to bit depth
116 if(aFound != maFreeBuffers.end())
118 // already found
119 if(bOkay)
121 // found is valid
122 const bool bCandidateOkay((*a)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*a)->GetOutputHeightPixel() >= rSizePixel.getHeight());
124 if(bCandidateOkay)
126 // found and candidate are valid
127 const sal_uLong aSquare((*aFound)->GetOutputWidthPixel() * (*aFound)->GetOutputHeightPixel());
128 const sal_uLong aCandidateSquare((*a)->GetOutputWidthPixel() * (*a)->GetOutputHeightPixel());
130 if(aCandidateSquare < aSquare)
132 // candidate is valid and smaller, use it
133 aFound = a;
136 else
138 // found is valid, candidate is not. Keep found
141 else
143 // found is invalid, use candidate
144 aFound = a;
145 bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight();
148 else
150 // none yet, use candidate
151 aFound = a;
152 bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight();
157 if(aFound != maFreeBuffers.end())
159 pRetval = *aFound;
160 maFreeBuffers.erase(aFound);
164 if (pRetval)
166 // found a suitable cached virtual device, but the
167 // outputdevice it was based on has been disposed,
168 // drop it and create a new one instead as reusing
169 // such devices is unsafe under at least Gtk2
170 if (maDeviceTemplates[pRetval]->isDisposed())
172 maDeviceTemplates.erase(pRetval);
173 pRetval = nullptr;
175 else
177 if (bOkay)
179 if (bClear)
181 pRetval->Erase(::tools::Rectangle(0, 0, rSizePixel.getWidth(), rSizePixel.getHeight()));
184 else
186 pRetval->SetOutputSizePixel(rSizePixel, bClear);
191 // no success yet, create new buffer
192 if(!pRetval)
194 pRetval = VclPtr<VirtualDevice>::Create(rOutDev, bMonoChrome ? DeviceFormat::BITMASK : DeviceFormat::DEFAULT);
195 maDeviceTemplates[pRetval] = &rOutDev;
196 pRetval->SetOutputSizePixel(rSizePixel, bClear);
198 else
200 // reused, reset some values
201 pRetval->SetMapMode();
202 pRetval->SetRasterOp(RasterOp::OverPaint);
205 // remember allocated buffer
206 maUsedBuffers.push_back(pRetval);
208 return pRetval;
211 void VDevBuffer::free(VirtualDevice& rDevice)
213 ::osl::MutexGuard aGuard(m_aMutex);
214 const aBuffers::iterator aUsedFound(std::find(maUsedBuffers.begin(), maUsedBuffers.end(), &rDevice));
215 OSL_ENSURE(aUsedFound != maUsedBuffers.end(), "OOps, non-registered buffer freed (!)");
217 maUsedBuffers.erase(aUsedFound);
218 maFreeBuffers.emplace_back(&rDevice);
219 SAL_WARN_IF(maFreeBuffers.size() > 1000, "drawinglayer", "excessive cached buffers, "
220 << maFreeBuffers.size() << " entries!");
221 Start();
224 void VDevBuffer::Invoke()
226 ::osl::MutexGuard aGuard(m_aMutex);
228 while(!maFreeBuffers.empty())
230 aBuffers::iterator aLastOne(maFreeBuffers.end() - 1);
231 maDeviceTemplates.erase(*aLastOne);
232 aLastOne->disposeAndClear();
233 maFreeBuffers.pop_back();
239 // support for rendering Bitmap and BitmapEx contents
241 namespace drawinglayer
243 // static global VDev buffer for the VclProcessor2D's (VclMetafileProcessor2D and VclPixelProcessor2D)
244 VDevBuffer& getVDevBuffer()
246 // secure global instance with Vcl's safe destroyer of external (seen by
247 // library base) stuff, the remembered VDevs need to be deleted before
248 // Vcl's deinit
249 static vcl::DeleteOnDeinit< VDevBuffer > aVDevBuffer(new VDevBuffer());
250 return *aVDevBuffer.get();
253 impBufferDevice::impBufferDevice(
254 OutputDevice& rOutDev,
255 const basegfx::B2DRange& rRange)
256 : mrOutDev(rOutDev),
257 mpContent(nullptr),
258 mpMask(nullptr),
259 mpAlpha(nullptr)
261 basegfx::B2DRange aRangePixel(rRange);
262 aRangePixel.transform(mrOutDev.GetViewTransformation());
263 const ::tools::Rectangle aRectPixel(
264 static_cast<sal_Int32>(floor(aRangePixel.getMinX())), static_cast<sal_Int32>(floor(aRangePixel.getMinY())),
265 static_cast<sal_Int32>(ceil(aRangePixel.getMaxX())), static_cast<sal_Int32>(ceil(aRangePixel.getMaxY())));
266 const Point aEmptyPoint;
267 maDestPixel = ::tools::Rectangle(aEmptyPoint, mrOutDev.GetOutputSizePixel());
268 maDestPixel.Intersection(aRectPixel);
270 if(isVisible())
272 #ifdef IOS
273 // Exact mechanism unknown, but for some reason SmartArt
274 // rendering, especially shadows, is broken on iOS unless
275 // we pass 'true' here. Are virtual devices always de
276 // facto cleared when created on other platforms?
277 mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, false);
278 #else
279 mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), false, false);
280 #endif
282 // #i93485# assert when copying from window to VDev is used
283 OSL_ENSURE(mrOutDev.GetOutDevType() != OUTDEV_WINDOW,
284 "impBufferDevice render helper: Copying from Window to VDev, this should be avoided (!)");
286 const bool bWasEnabledSrc(mrOutDev.IsMapModeEnabled());
287 mrOutDev.EnableMapMode(false);
288 mpContent->DrawOutDev(aEmptyPoint, maDestPixel.GetSize(), maDestPixel.TopLeft(), maDestPixel.GetSize(), mrOutDev);
289 mrOutDev.EnableMapMode(bWasEnabledSrc);
291 MapMode aNewMapMode(mrOutDev.GetMapMode());
293 const Point aLogicTopLeft(mrOutDev.PixelToLogic(maDestPixel.TopLeft()));
294 aNewMapMode.SetOrigin(Point(-aLogicTopLeft.X(), -aLogicTopLeft.Y()));
296 mpContent->SetMapMode(aNewMapMode);
298 // copy AA flag for new target
299 mpContent->SetAntialiasing(mrOutDev.GetAntialiasing());
301 // copy RasterOp (e.g. may be RasterOp::Xor on destination)
302 mpContent->SetRasterOp(mrOutDev.GetRasterOp());
306 impBufferDevice::~impBufferDevice()
308 if(mpContent)
310 getVDevBuffer().free(*mpContent);
313 if(mpMask)
315 getVDevBuffer().free(*mpMask);
318 if(mpAlpha)
320 getVDevBuffer().free(*mpAlpha);
324 void impBufferDevice::paint(double fTrans)
326 if(isVisible())
328 const Point aEmptyPoint;
329 const Size aSizePixel(maDestPixel.GetSize());
330 const bool bWasEnabledDst(mrOutDev.IsMapModeEnabled());
331 #ifdef DBG_UTIL
332 static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
333 #endif
335 mrOutDev.EnableMapMode(false);
336 mpContent->EnableMapMode(false);
338 #ifdef DBG_UTIL
339 if(bDoSaveForVisualControl)
341 SvFileStream aNew(
342 #ifdef _WIN32
343 "c:\\content.bmp",
344 #else
345 "~/content.bmp",
346 #endif
347 StreamMode::WRITE|StreamMode::TRUNC);
348 Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
349 WriteDIB(aContent, aNew, false, true);
351 #endif
353 // during painting the buffer, disable evtl. set RasterOp (may be RasterOp::Xor)
354 const RasterOp aOrigRasterOp(mrOutDev.GetRasterOp());
355 mrOutDev.SetRasterOp(RasterOp::OverPaint);
357 if(mpAlpha)
359 mpAlpha->EnableMapMode(false);
360 const AlphaMask aAlphaMask(mpAlpha->GetBitmap(aEmptyPoint, aSizePixel));
362 #ifdef DBG_UTIL
363 if(bDoSaveForVisualControl)
365 SvFileStream aNew(
366 #ifdef _WIN32
367 "c:\\transparence.bmp",
368 #else
369 "~/transparence.bmp",
370 #endif
371 StreamMode::WRITE|StreamMode::TRUNC);
372 WriteDIB(aAlphaMask.GetBitmap(), aNew, false, true);
374 #endif
376 Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
377 mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
379 else if(mpMask)
381 mpMask->EnableMapMode(false);
382 const Bitmap aMask(mpMask->GetBitmap(aEmptyPoint, aSizePixel));
384 #ifdef DBG_UTIL
385 if(bDoSaveForVisualControl)
387 SvFileStream aNew(
388 #ifdef _WIN32
389 "c:\\mask.bmp",
390 #else
391 "~/mask.bmp",
392 #endif
393 StreamMode::WRITE|StreamMode::TRUNC);
394 WriteDIB(aMask, aNew, false, true);
396 #endif
398 Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
399 mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aMask));
401 else if(0.0 != fTrans)
403 sal_uInt8 nMaskValue(static_cast<sal_uInt8>(basegfx::fround(fTrans * 255.0)));
404 const AlphaMask aAlphaMask(aSizePixel, &nMaskValue);
405 Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
406 mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
408 else
410 mrOutDev.DrawOutDev(maDestPixel.TopLeft(), aSizePixel,
411 aEmptyPoint, aSizePixel,
412 *mpContent);
415 mrOutDev.SetRasterOp(aOrigRasterOp);
416 mrOutDev.EnableMapMode(bWasEnabledDst);
420 VirtualDevice& impBufferDevice::getContent()
422 assert(mpContent && "impBufferDevice: No content, check isVisible() before accessing (!)");
423 return *mpContent;
426 VirtualDevice& impBufferDevice::getMask()
428 assert(mpContent && "impBufferDevice: No content, check isVisible() before accessing (!)");
429 if (!mpMask)
431 mpMask = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, true);
432 mpMask->SetMapMode(mpContent->GetMapMode());
434 // do NOT copy AA flag for mask!
437 return *mpMask;
440 VirtualDevice& impBufferDevice::getTransparence()
442 OSL_ENSURE(mpContent, "impBufferDevice: No content, check isVisible() before accessing (!)");
443 if(!mpAlpha)
445 mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, false);
446 mpAlpha->SetMapMode(mpContent->GetMapMode());
448 // copy AA flag for new target; masking needs to be smooth
449 mpAlpha->SetAntialiasing(mpContent->GetAntialiasing());
452 return *mpAlpha;
454 } // end of namespace drawinglayer
456 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */