Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <vclhelperbufferdevice.hxx>
21 : #include <basegfx/range/b2drange.hxx>
22 : #include <vcl/bitmapex.hxx>
23 : #include <basegfx/matrix/b2dhommatrix.hxx>
24 : #include <tools/stream.hxx>
25 : #include <vcl/timer.hxx>
26 : #include <comphelper/broadcasthelper.hxx>
27 : #include <vcl/lazydelete.hxx>
28 : #include <vcl/dibtools.hxx>
29 :
30 :
31 : // buffered VDev usage
32 :
33 : namespace
34 : {
35 : typedef ::std::vector< VclPtr<VirtualDevice> > aBuffers;
36 :
37 : class VDevBuffer : public Timer, protected comphelper::OBaseMutex
38 : {
39 : private:
40 : // available buffers
41 : aBuffers maFreeBuffers;
42 :
43 : // allocated/used buffers (remembered to allow deleting them in destructor)
44 : aBuffers maUsedBuffers;
45 :
46 : public:
47 : VDevBuffer();
48 : virtual ~VDevBuffer();
49 :
50 : VirtualDevice* alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, sal_Int32 nBits);
51 : void free(VirtualDevice& rDevice);
52 :
53 : // Timer virtuals
54 : virtual void Invoke() SAL_OVERRIDE;
55 : };
56 :
57 15 : VDevBuffer::VDevBuffer()
58 : : Timer(),
59 : maFreeBuffers(),
60 15 : maUsedBuffers()
61 : {
62 15 : SetTimeout(10L * 1000L); // ten seconds
63 15 : }
64 :
65 45 : VDevBuffer::~VDevBuffer()
66 : {
67 15 : ::osl::MutexGuard aGuard(m_aMutex);
68 15 : Stop();
69 :
70 63 : while(!maFreeBuffers.empty())
71 : {
72 33 : (*(maFreeBuffers.end() - 1)).disposeAndClear();
73 33 : maFreeBuffers.pop_back();
74 : }
75 :
76 30 : while(!maUsedBuffers.empty())
77 : {
78 0 : (*(maUsedBuffers.end() - 1)).disposeAndClear();
79 0 : maUsedBuffers.pop_back();
80 15 : }
81 30 : }
82 :
83 13496 : VirtualDevice* VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, sal_Int32 nBits)
84 : {
85 13496 : ::osl::MutexGuard aGuard(m_aMutex);
86 13496 : VirtualDevice* pRetval = 0;
87 :
88 13496 : if (nBits == 0)
89 8996 : nBits = rOutDev.GetBitCount();
90 :
91 13496 : if(!maFreeBuffers.empty())
92 : {
93 13462 : bool bOkay(false);
94 13462 : aBuffers::iterator aFound(maFreeBuffers.end());
95 :
96 64041 : for(aBuffers::iterator a(maFreeBuffers.begin()); a != maFreeBuffers.end(); ++a)
97 : {
98 : OSL_ENSURE(*a, "Empty pointer in VDevBuffer (!)");
99 :
100 50579 : if(nBits == (*a)->GetBitCount())
101 : {
102 : // candidate is valid due to bit depth
103 30052 : if(aFound != maFreeBuffers.end())
104 : {
105 : // already found
106 16608 : if(bOkay)
107 : {
108 : // found is valid
109 13431 : const bool bCandidateOkay((*a)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*a)->GetOutputHeightPixel() >= rSizePixel.getHeight());
110 :
111 13431 : if(bCandidateOkay)
112 : {
113 : // found and candidate are valid
114 11707 : const sal_uLong aSquare((*aFound)->GetOutputWidthPixel() * (*aFound)->GetOutputHeightPixel());
115 11707 : const sal_uLong aCandidateSquare((*a)->GetOutputWidthPixel() * (*a)->GetOutputHeightPixel());
116 :
117 11707 : if(aCandidateSquare < aSquare)
118 : {
119 : // candidate is valid and smaller, use it
120 5340 : aFound = a;
121 : }
122 : }
123 : else
124 : {
125 : // found is valid, candidate is not. Keep found
126 : }
127 : }
128 : else
129 : {
130 : // found is invalid, use candidate
131 3177 : aFound = a;
132 3177 : bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight();
133 : }
134 : }
135 : else
136 : {
137 : // none yet, use candidate
138 13444 : aFound = a;
139 13444 : bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight();
140 : }
141 : }
142 : }
143 :
144 13462 : if(aFound != maFreeBuffers.end())
145 : {
146 13444 : pRetval = *aFound;
147 13444 : maFreeBuffers.erase(aFound);
148 :
149 13444 : if(bOkay)
150 : {
151 11045 : if(bClear)
152 : {
153 5078 : pRetval->Erase(Rectangle(0, 0, rSizePixel.getWidth(), rSizePixel.getHeight()));
154 : }
155 : }
156 : else
157 : {
158 2399 : pRetval->SetOutputSizePixel(rSizePixel, bClear);
159 : }
160 : }
161 : }
162 :
163 : // no success yet, create new buffer
164 13496 : if(!pRetval)
165 : {
166 52 : pRetval = VclPtr<VirtualDevice>::Create(rOutDev, nBits);
167 52 : pRetval->SetOutputSizePixel(rSizePixel, bClear);
168 : }
169 : else
170 : {
171 : // reused, reset some values
172 13444 : pRetval->SetMapMode();
173 : }
174 :
175 : // remember allocated buffer
176 13496 : maUsedBuffers.push_back(pRetval);
177 :
178 13496 : return pRetval;
179 : }
180 :
181 13496 : void VDevBuffer::free(VirtualDevice& rDevice)
182 : {
183 13496 : ::osl::MutexGuard aGuard(m_aMutex);
184 13496 : const aBuffers::iterator aUsedFound(::std::find(maUsedBuffers.begin(), maUsedBuffers.end(), &rDevice));
185 : OSL_ENSURE(aUsedFound != maUsedBuffers.end(), "OOps, non-registered buffer freed (!)");
186 :
187 13496 : maUsedBuffers.erase(aUsedFound);
188 13496 : maFreeBuffers.push_back(&rDevice);
189 : SAL_WARN_IF(maFreeBuffers.size() > 1000, "drawinglayer", "excessive cached buffers, "
190 : << maFreeBuffers.size() << " entries!");
191 13496 : Start();
192 13496 : }
193 :
194 9 : void VDevBuffer::Invoke()
195 : {
196 9 : ::osl::MutexGuard aGuard(m_aMutex);
197 :
198 37 : while(!maFreeBuffers.empty())
199 : {
200 19 : (*(maFreeBuffers.end() - 1)).disposeAndClear();
201 19 : maFreeBuffers.pop_back();
202 9 : }
203 9 : }
204 : }
205 :
206 :
207 : // support for rendering Bitmap and BitmapEx contents
208 :
209 : namespace drawinglayer
210 : {
211 : // static global VDev buffer for the VclProcessor2D's (VclMetafileProcessor2D and VclPixelProcessor2D)
212 26992 : VDevBuffer& getVDevBuffer()
213 : {
214 : // secure global instance with Vcl's safe desroyer of external (seen by
215 : // library base) stuff, the remembered VDevs need to be deleted before
216 : // Vcl's deinit
217 26992 : static vcl::DeleteOnDeinit< VDevBuffer > aVDevBuffer(new VDevBuffer());
218 26992 : return *aVDevBuffer.get();
219 : }
220 :
221 7296 : impBufferDevice::impBufferDevice(
222 : OutputDevice& rOutDev,
223 : const basegfx::B2DRange& rRange,
224 : bool bAddOffsetToMapping)
225 : : mrOutDev(rOutDev),
226 : mpContent(0),
227 : mpMask(0),
228 7296 : mpAlpha(0)
229 : {
230 7296 : basegfx::B2DRange aRangePixel(rRange);
231 7296 : aRangePixel.transform(mrOutDev.GetViewTransformation());
232 : const Rectangle aRectPixel(
233 14592 : (sal_Int32)floor(aRangePixel.getMinX()), (sal_Int32)floor(aRangePixel.getMinY()),
234 21888 : (sal_Int32)ceil(aRangePixel.getMaxX()), (sal_Int32)ceil(aRangePixel.getMaxY()));
235 7296 : const Point aEmptyPoint;
236 7296 : maDestPixel = Rectangle(aEmptyPoint, mrOutDev.GetOutputSizePixel());
237 7296 : maDestPixel.Intersection(aRectPixel);
238 :
239 7296 : if(isVisible())
240 : {
241 : #ifdef IOS
242 : // Exact mechanism unknown, but for some reason SmartArt
243 : // rendering, especially shadows, is broken on iOS unless
244 : // we pass 'true' here. Are virtual devices always de
245 : // facto cleared when created on other platforms?
246 : mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, 0);
247 : #else
248 7198 : mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), false, 0);
249 : #endif
250 :
251 : // #i93485# assert when copying from window to VDev is used
252 : OSL_ENSURE(mrOutDev.GetOutDevType() != OUTDEV_WINDOW,
253 : "impBufferDevice render helper: Copying from Window to VDev, this should be avoided (!)");
254 :
255 7198 : const bool bWasEnabledSrc(mrOutDev.IsMapModeEnabled());
256 7198 : mrOutDev.EnableMapMode(false);
257 7198 : mpContent->DrawOutDev(aEmptyPoint, maDestPixel.GetSize(), maDestPixel.TopLeft(), maDestPixel.GetSize(), mrOutDev);
258 7198 : mrOutDev.EnableMapMode(bWasEnabledSrc);
259 :
260 7198 : MapMode aNewMapMode(mrOutDev.GetMapMode());
261 :
262 7198 : if(bAddOffsetToMapping)
263 : {
264 7198 : const Point aLogicTopLeft(mrOutDev.PixelToLogic(maDestPixel.TopLeft()));
265 7198 : aNewMapMode.SetOrigin(Point(-aLogicTopLeft.X(), -aLogicTopLeft.Y()));
266 : }
267 :
268 7198 : mpContent->SetMapMode(aNewMapMode);
269 :
270 : // copy AA flag for new target
271 7198 : mpContent->SetAntialiasing(mrOutDev.GetAntialiasing());
272 : }
273 7296 : }
274 :
275 14592 : impBufferDevice::~impBufferDevice()
276 : {
277 7296 : if(mpContent)
278 : {
279 7198 : getVDevBuffer().free(*mpContent);
280 : }
281 :
282 7296 : if(mpMask)
283 : {
284 4500 : getVDevBuffer().free(*mpMask);
285 : }
286 :
287 7296 : if(mpAlpha)
288 : {
289 1798 : getVDevBuffer().free(*mpAlpha);
290 : }
291 7296 : }
292 :
293 7198 : void impBufferDevice::paint(double fTrans)
294 : {
295 7198 : if(isVisible())
296 : {
297 7198 : const Point aEmptyPoint;
298 7198 : const Size aSizePixel(maDestPixel.GetSize());
299 7198 : const bool bWasEnabledDst(mrOutDev.IsMapModeEnabled());
300 : static bool bDoSaveForVisualControl(false);
301 :
302 7198 : mrOutDev.EnableMapMode(false);
303 7198 : mpContent->EnableMapMode(false);
304 7198 : Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
305 :
306 7198 : if(bDoSaveForVisualControl)
307 : {
308 0 : SvFileStream aNew( "c:\\content.bmp", StreamMode::WRITE|StreamMode::TRUNC);
309 0 : WriteDIB(aContent, aNew, false, true);
310 : }
311 :
312 7198 : if(mpAlpha)
313 : {
314 1798 : mpAlpha->EnableMapMode(false);
315 1798 : const AlphaMask aAlphaMask(mpAlpha->GetBitmap(aEmptyPoint, aSizePixel));
316 :
317 1798 : if(bDoSaveForVisualControl)
318 : {
319 0 : SvFileStream aNew( "c:\\transparence.bmp", StreamMode::WRITE|StreamMode::TRUNC);
320 0 : WriteDIB(aAlphaMask.GetBitmap(), aNew, false, true);
321 : }
322 :
323 1798 : mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
324 : }
325 5400 : else if(mpMask)
326 : {
327 4500 : mpMask->EnableMapMode(false);
328 4500 : const Bitmap aMask(mpMask->GetBitmap(aEmptyPoint, aSizePixel));
329 :
330 4500 : if(bDoSaveForVisualControl)
331 : {
332 0 : SvFileStream aNew( "c:\\mask.bmp", StreamMode::WRITE|StreamMode::TRUNC);
333 0 : WriteDIB(aMask, aNew, false, true);
334 : }
335 :
336 4500 : mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aMask));
337 : }
338 900 : else if(0.0 != fTrans)
339 : {
340 900 : sal_uInt8 nMaskValue((sal_uInt8)basegfx::fround(fTrans * 255.0));
341 900 : const AlphaMask aAlphaMask(aSizePixel, &nMaskValue);
342 900 : mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
343 : }
344 : else
345 : {
346 0 : mrOutDev.DrawBitmap(maDestPixel.TopLeft(), aContent);
347 : }
348 :
349 7198 : mrOutDev.EnableMapMode(bWasEnabledDst);
350 : }
351 7198 : }
352 :
353 7198 : VirtualDevice& impBufferDevice::getContent()
354 : {
355 : assert(mpContent && "impBufferDevice: No content, check isVisible() before accessing (!)");
356 7198 : return *mpContent;
357 : }
358 :
359 4500 : VirtualDevice& impBufferDevice::getMask()
360 : {
361 : assert(mpContent && "impBufferDevice: No content, check isVisible() before accessing (!)");
362 4500 : if (!mpMask)
363 : {
364 4500 : mpMask = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, 1);
365 4500 : mpMask->SetMapMode(mpContent->GetMapMode());
366 :
367 : // do NOT copy AA flag for mask!
368 : }
369 :
370 4500 : return *mpMask;
371 : }
372 :
373 1798 : VirtualDevice& impBufferDevice::getTransparence()
374 : {
375 : OSL_ENSURE(mpContent, "impBufferDevice: No content, check isVisible() before accessing (!)");
376 1798 : if(!mpAlpha)
377 : {
378 1798 : mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, 0);
379 1798 : mpAlpha->SetMapMode(mpContent->GetMapMode());
380 :
381 : // copy AA flag for new target; masking needs to be smooth
382 1798 : mpAlpha->SetAntialiasing(mpContent->GetAntialiasing());
383 : }
384 :
385 1798 : return *mpAlpha;
386 : }
387 : } // end of namespace drawinglayer
388 :
389 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|