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< 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 deleteing 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 Timeout() SAL_OVERRIDE;
55 : };
56 :
57 22 : VDevBuffer::VDevBuffer()
58 : : Timer(),
59 : maFreeBuffers(),
60 22 : maUsedBuffers()
61 : {
62 22 : SetTimeout(10L * 1000L); // ten seconds
63 22 : }
64 :
65 66 : VDevBuffer::~VDevBuffer()
66 : {
67 22 : ::osl::MutexGuard aGuard(m_aMutex);
68 22 : Stop();
69 :
70 93 : while(!maFreeBuffers.empty())
71 : {
72 49 : delete *(maFreeBuffers.end() - 1);
73 49 : maFreeBuffers.pop_back();
74 : }
75 :
76 44 : while(!maUsedBuffers.empty())
77 : {
78 0 : delete *(maUsedBuffers.end() - 1);
79 0 : maUsedBuffers.pop_back();
80 22 : }
81 44 : }
82 :
83 9635 : VirtualDevice* VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, sal_Int32 nBits)
84 : {
85 9635 : ::osl::MutexGuard aGuard(m_aMutex);
86 9635 : VirtualDevice* pRetval = 0;
87 :
88 9635 : if (nBits == 0)
89 5868 : nBits = rOutDev.GetBitCount();
90 :
91 9635 : if(!maFreeBuffers.empty())
92 : {
93 9587 : bool bOkay(false);
94 9587 : aBuffers::iterator aFound(maFreeBuffers.end());
95 :
96 50622 : for(aBuffers::iterator a(maFreeBuffers.begin()); a != maFreeBuffers.end(); ++a)
97 : {
98 : OSL_ENSURE(*a, "Empty pointer in VDevBuffer (!)");
99 :
100 41035 : if(nBits == (*a)->GetBitCount())
101 : {
102 : // candidate is valid due to bit depth
103 24233 : if(aFound != maFreeBuffers.end())
104 : {
105 : // already found
106 14667 : if(bOkay)
107 : {
108 : // found is valid
109 12381 : const bool bCandidateOkay((*a)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*a)->GetOutputHeightPixel() >= rSizePixel.getHeight());
110 :
111 12381 : if(bCandidateOkay)
112 : {
113 : // found and candidate are valid
114 10804 : const sal_uLong aSquare((*aFound)->GetOutputWidthPixel() * (*aFound)->GetOutputHeightPixel());
115 10804 : const sal_uLong aCandidateSquare((*a)->GetOutputWidthPixel() * (*a)->GetOutputHeightPixel());
116 :
117 10804 : if(aCandidateSquare < aSquare)
118 : {
119 : // candidate is valid and smaller, use it
120 5318 : 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 2286 : aFound = a;
132 2286 : bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight();
133 : }
134 : }
135 : else
136 : {
137 : // none yet, use candidate
138 9566 : aFound = a;
139 9566 : bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight();
140 : }
141 : }
142 : }
143 :
144 9587 : if(aFound != maFreeBuffers.end())
145 : {
146 9566 : pRetval = *aFound;
147 9566 : maFreeBuffers.erase(aFound);
148 :
149 9566 : if(bOkay)
150 : {
151 8775 : if(bClear)
152 : {
153 4124 : pRetval->Erase(Rectangle(0, 0, rSizePixel.getWidth(), rSizePixel.getHeight()));
154 : }
155 : }
156 : else
157 : {
158 791 : pRetval->SetOutputSizePixel(rSizePixel, bClear);
159 : }
160 : }
161 : }
162 :
163 : // no success yet, create new buffer
164 9635 : if(!pRetval)
165 : {
166 69 : pRetval = new VirtualDevice(rOutDev, nBits);
167 69 : pRetval->SetOutputSizePixel(rSizePixel, bClear);
168 : }
169 : else
170 : {
171 : // reused, reset some values
172 9566 : pRetval->SetMapMode();
173 : }
174 :
175 : // remember allocated buffer
176 9635 : maUsedBuffers.push_back(pRetval);
177 :
178 9635 : return pRetval;
179 : }
180 :
181 9635 : void VDevBuffer::free(VirtualDevice& rDevice)
182 : {
183 9635 : ::osl::MutexGuard aGuard(m_aMutex);
184 9635 : const aBuffers::iterator aUsedFound(::std::find(maUsedBuffers.begin(), maUsedBuffers.end(), &rDevice));
185 : OSL_ENSURE(aUsedFound != maUsedBuffers.end(), "OOps, non-registered buffer freed (!)");
186 :
187 9635 : maUsedBuffers.erase(aUsedFound);
188 9635 : maFreeBuffers.push_back(&rDevice);
189 : SAL_WARN_IF(maFreeBuffers.size() > 1000, "drawinglayer", "excessive cached buffers, "
190 : << maFreeBuffers.size() << " entries!");
191 9635 : Start();
192 9635 : }
193 :
194 9 : void VDevBuffer::Timeout()
195 : {
196 9 : ::osl::MutexGuard aGuard(m_aMutex);
197 :
198 38 : while(!maFreeBuffers.empty())
199 : {
200 20 : delete *(maFreeBuffers.end() - 1);
201 20 : 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 19270 : 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 19270 : static vcl::DeleteOnDeinit< VDevBuffer > aVDevBuffer(new VDevBuffer());
218 19270 : return *aVDevBuffer.get();
219 : }
220 :
221 5226 : impBufferDevice::impBufferDevice(
222 : OutputDevice& rOutDev,
223 : const basegfx::B2DRange& rRange,
224 : bool bAddOffsetToMapping)
225 : : mrOutDev(rOutDev),
226 : mpContent(0),
227 : mpMask(0),
228 5226 : mpAlpha(0)
229 : {
230 5226 : basegfx::B2DRange aRangePixel(rRange);
231 5226 : aRangePixel.transform(mrOutDev.GetViewTransformation());
232 : const Rectangle aRectPixel(
233 10452 : (sal_Int32)floor(aRangePixel.getMinX()), (sal_Int32)floor(aRangePixel.getMinY()),
234 15678 : (sal_Int32)ceil(aRangePixel.getMaxX()), (sal_Int32)ceil(aRangePixel.getMaxY()));
235 5226 : const Point aEmptyPoint;
236 5226 : maDestPixel = Rectangle(aEmptyPoint, mrOutDev.GetOutputSizePixel());
237 5226 : maDestPixel.Intersection(aRectPixel);
238 :
239 5226 : 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 5120 : 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 5120 : const bool bWasEnabledSrc(mrOutDev.IsMapModeEnabled());
256 5120 : mrOutDev.EnableMapMode(false);
257 5120 : mpContent->DrawOutDev(aEmptyPoint, maDestPixel.GetSize(), maDestPixel.TopLeft(), maDestPixel.GetSize(), mrOutDev);
258 5120 : mrOutDev.EnableMapMode(bWasEnabledSrc);
259 :
260 5120 : MapMode aNewMapMode(mrOutDev.GetMapMode());
261 :
262 5120 : if(bAddOffsetToMapping)
263 : {
264 5120 : const Point aLogicTopLeft(mrOutDev.PixelToLogic(maDestPixel.TopLeft()));
265 5120 : aNewMapMode.SetOrigin(Point(-aLogicTopLeft.X(), -aLogicTopLeft.Y()));
266 : }
267 :
268 5120 : mpContent->SetMapMode(aNewMapMode);
269 :
270 : // copy AA flag for new target
271 5120 : mpContent->SetAntialiasing(mrOutDev.GetAntialiasing());
272 : }
273 5226 : }
274 :
275 5226 : impBufferDevice::~impBufferDevice()
276 : {
277 5226 : if(mpContent)
278 : {
279 5120 : getVDevBuffer().free(*mpContent);
280 : }
281 :
282 5226 : if(mpMask)
283 : {
284 3767 : getVDevBuffer().free(*mpMask);
285 : }
286 :
287 5226 : if(mpAlpha)
288 : {
289 748 : getVDevBuffer().free(*mpAlpha);
290 : }
291 5226 : }
292 :
293 5120 : void impBufferDevice::paint(double fTrans)
294 : {
295 5120 : if(isVisible())
296 : {
297 5120 : const Point aEmptyPoint;
298 5120 : const Size aSizePixel(maDestPixel.GetSize());
299 5120 : const bool bWasEnabledDst(mrOutDev.IsMapModeEnabled());
300 : static bool bDoSaveForVisualControl(false);
301 :
302 5120 : mrOutDev.EnableMapMode(false);
303 5120 : mpContent->EnableMapMode(false);
304 5120 : Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
305 :
306 5120 : if(bDoSaveForVisualControl)
307 : {
308 0 : SvFileStream aNew( "c:\\content.bmp", STREAM_WRITE|STREAM_TRUNC);
309 0 : WriteDIB(aContent, aNew, false, true);
310 : }
311 :
312 5120 : if(mpAlpha)
313 : {
314 748 : mpAlpha->EnableMapMode(false);
315 748 : const AlphaMask aAlphaMask(mpAlpha->GetBitmap(aEmptyPoint, aSizePixel));
316 :
317 748 : if(bDoSaveForVisualControl)
318 : {
319 0 : SvFileStream aNew( "c:\\transparence.bmp", STREAM_WRITE|STREAM_TRUNC);
320 0 : WriteDIB(aAlphaMask.GetBitmap(), aNew, false, true);
321 : }
322 :
323 748 : mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
324 : }
325 4372 : else if(mpMask)
326 : {
327 3767 : mpMask->EnableMapMode(false);
328 3767 : const Bitmap aMask(mpMask->GetBitmap(aEmptyPoint, aSizePixel));
329 :
330 3767 : if(bDoSaveForVisualControl)
331 : {
332 0 : SvFileStream aNew( "c:\\mask.bmp", STREAM_WRITE|STREAM_TRUNC);
333 0 : WriteDIB(aMask, aNew, false, true);
334 : }
335 :
336 3767 : mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aMask));
337 : }
338 605 : else if(0.0 != fTrans)
339 : {
340 605 : sal_uInt8 nMaskValue((sal_uInt8)basegfx::fround(fTrans * 255.0));
341 605 : const AlphaMask aAlphaMask(aSizePixel, &nMaskValue);
342 605 : mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
343 : }
344 : else
345 : {
346 0 : mrOutDev.DrawBitmap(maDestPixel.TopLeft(), aContent);
347 : }
348 :
349 5120 : mrOutDev.EnableMapMode(bWasEnabledDst);
350 : }
351 5120 : }
352 :
353 5120 : VirtualDevice& impBufferDevice::getContent()
354 : {
355 : assert(mpContent && "impBufferDevice: No content, check isVisible() before accessing (!)");
356 5120 : return *mpContent;
357 : }
358 :
359 3767 : VirtualDevice& impBufferDevice::getMask()
360 : {
361 : assert(mpContent && "impBufferDevice: No content, check isVisible() before accessing (!)");
362 3767 : if (!mpMask)
363 : {
364 3767 : mpMask = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, 1);
365 3767 : mpMask->SetMapMode(mpContent->GetMapMode());
366 :
367 : // do NOT copy AA flag for mask!
368 : }
369 :
370 3767 : return *mpMask;
371 : }
372 :
373 748 : VirtualDevice& impBufferDevice::getTransparence()
374 : {
375 : OSL_ENSURE(mpContent, "impBufferDevice: No content, check isVisible() before accessing (!)");
376 748 : if(!mpAlpha)
377 : {
378 748 : mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, 0);
379 748 : mpAlpha->SetMapMode(mpContent->GetMapMode());
380 :
381 : // copy AA flag for new target; masking needs to be smooth
382 748 : mpAlpha->SetAntialiasing(mpContent->GetAntialiasing());
383 : }
384 :
385 748 : return *mpAlpha;
386 : }
387 1143 : } // end of namespace drawinglayer
388 :
389 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|