1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_drawinglayer.hxx"
26 
27 #include <vclhelperbufferdevice.hxx>
28 #include <basegfx/range/b2drange.hxx>
29 #include <vcl/bitmapex.hxx>
30 #include <basegfx/matrix/b2dhommatrix.hxx>
31 #include <tools/stream.hxx>
32 #include <vcl/timer.hxx>
33 #include <comphelper/broadcasthelper.hxx>
34 #include <vcl/lazydelete.hxx>
35 
36 //////////////////////////////////////////////////////////////////////////////
37 // buffered VDev usage
38 
39 namespace
40 {
41     typedef ::std::vector< VirtualDevice* > aBuffers;
42 
43     class VDevBuffer : public Timer, protected comphelper::OBaseMutex
44     {
45     private:
46         // available buffers
47         aBuffers            maFreeBuffers;
48 
49         // allocated/used buffers (remembered to allow deleteing them in destructor)
50         aBuffers            maUsedBuffers;
51 
52     public:
53         VDevBuffer();
54         virtual ~VDevBuffer();
55 
56         VirtualDevice* alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, bool bMono);
57         void free(VirtualDevice& rDevice);
58 
59         // Timer virtuals
60         virtual void Timeout();
61     };
62 
63     VDevBuffer::VDevBuffer()
64     :   Timer(),
65         maFreeBuffers(),
66         maUsedBuffers()
67     {
68         SetTimeout(10L * 1000L); // ten seconds
69     }
70 
71     VDevBuffer::~VDevBuffer()
72     {
73         ::osl::MutexGuard aGuard(m_aMutex);
74         Stop();
75 
76         while(!maFreeBuffers.empty())
77         {
78             delete *(maFreeBuffers.end() - 1);
79             maFreeBuffers.pop_back();
80         }
81 
82         while(!maUsedBuffers.empty())
83         {
84             delete *(maUsedBuffers.end() - 1);
85             maUsedBuffers.pop_back();
86         }
87     }
88 
89     VirtualDevice* VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, bool bMono)
90     {
91         ::osl::MutexGuard aGuard(m_aMutex);
92         VirtualDevice* pRetval = 0;
93 
94         if(!maFreeBuffers.empty())
95         {
96             bool bOkay(false);
97             aBuffers::iterator aFound(maFreeBuffers.end());
98 
99             for(aBuffers::iterator a(maFreeBuffers.begin()); a != maFreeBuffers.end(); a++)
100             {
101                 OSL_ENSURE(*a, "Empty pointer in VDevBuffer (!)");
102 
103                 if((bMono && 1 == (*a)->GetBitCount()) || (!bMono && (*a)->GetBitCount() > 1))
104                 {
105                     // candidate is valid due to bit depth
106                     if(aFound != maFreeBuffers.end())
107                     {
108                         // already found
109                         if(bOkay)
110                         {
111                             // found is valid
112                             const bool bCandidateOkay((*a)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*a)->GetOutputHeightPixel() >= rSizePixel.getHeight());
113 
114                             if(bCandidateOkay)
115                             {
116                                 // found and candidate are valid
117                                 const sal_uLong aSquare((*aFound)->GetOutputWidthPixel() * (*aFound)->GetOutputHeightPixel());
118                                 const sal_uLong aCandidateSquare((*a)->GetOutputWidthPixel() * (*a)->GetOutputHeightPixel());
119 
120                                 if(aCandidateSquare < aSquare)
121                                 {
122                                     // candidate is valid and smaller, use it
123                                     aFound = a;
124                                 }
125                             }
126                             else
127                             {
128                                 // found is valid, candidate is not. Keep found
129                             }
130                         }
131                         else
132                         {
133                             // found is invalid, use candidate
134                             aFound = a;
135                             bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight();
136                         }
137                     }
138                     else
139                     {
140                         // none yet, use candidate
141                         aFound = a;
142                         bOkay = (*aFound)->GetOutputWidthPixel() >= rSizePixel.getWidth() && (*aFound)->GetOutputHeightPixel() >= rSizePixel.getHeight();
143                     }
144                 }
145             }
146 
147             if(aFound != maFreeBuffers.end())
148             {
149                 pRetval = *aFound;
150                 maFreeBuffers.erase(aFound);
151 
152                 if(bOkay)
153                 {
154                     if(bClear)
155                     {
156                         pRetval->Erase(Rectangle(0, 0, rSizePixel.getWidth(), rSizePixel.getHeight()));
157                     }
158                 }
159                 else
160                 {
161                     pRetval->SetOutputSizePixel(rSizePixel, bClear);
162                 }
163             }
164         }
165 
166         // no success yet, create new buffer
167         if(!pRetval)
168         {
169             pRetval = (bMono) ? new VirtualDevice(rOutDev, 1) : new VirtualDevice(rOutDev);
170             pRetval->SetOutputSizePixel(rSizePixel, bClear);
171         }
172         else
173         {
174             // reused, reset some values
175             pRetval->SetMapMode();
176         }
177 
178         // remember allocated buffer
179         maUsedBuffers.push_back(pRetval);
180 
181         return pRetval;
182     }
183 
184     void VDevBuffer::free(VirtualDevice& rDevice)
185     {
186         ::osl::MutexGuard aGuard(m_aMutex);
187         const aBuffers::iterator aUsedFound(::std::find(maUsedBuffers.begin(), maUsedBuffers.end(), &rDevice));
188         OSL_ENSURE(aUsedFound != maUsedBuffers.end(), "OOps, non-registered buffer freed (!)");
189 
190         maUsedBuffers.erase(aUsedFound);
191         maFreeBuffers.push_back(&rDevice);
192         Start();
193     }
194 
195     void VDevBuffer::Timeout()
196     {
197         ::osl::MutexGuard aGuard(m_aMutex);
198 
199         while(!maFreeBuffers.empty())
200         {
201             delete *(maFreeBuffers.end() - 1);
202             maFreeBuffers.pop_back();
203         }
204     }
205 }
206 
207 //////////////////////////////////////////////////////////////////////////////
208 // support for rendering Bitmap and BitmapEx contents
209 
210 namespace drawinglayer
211 {
212     // static global VDev buffer for the VclProcessor2D's (VclMetafileProcessor2D and VclPixelProcessor2D)
213     VDevBuffer& getVDevBuffer()
214     {
215         // secure global instance with Vcl's safe desroyer of external (seen by
216         // library base) stuff, the remembered VDevs need to be deleted before
217         // Vcl's deinit
218         static vcl::DeleteOnDeinit< VDevBuffer > aVDevBuffer(new VDevBuffer());
219         return *aVDevBuffer.get();
220     }
221 
222     impBufferDevice::impBufferDevice(
223         OutputDevice& rOutDev,
224         const basegfx::B2DRange& rRange,
225         bool bAddOffsetToMapping)
226     :   mrOutDev(rOutDev),
227         mpContent(0),
228         mpMask(0),
229         mpAlpha(0)
230     {
231         basegfx::B2DRange aRangePixel(rRange);
232         aRangePixel.transform(mrOutDev.GetViewTransformation());
233         const Rectangle aRectPixel(
234             (sal_Int32)floor(aRangePixel.getMinX()), (sal_Int32)floor(aRangePixel.getMinY()),
235             (sal_Int32)ceil(aRangePixel.getMaxX()), (sal_Int32)ceil(aRangePixel.getMaxY()));
236         const Point aEmptyPoint;
237         maDestPixel = Rectangle(aEmptyPoint, mrOutDev.GetOutputSizePixel());
238         maDestPixel.Intersection(aRectPixel);
239 
240         if(isVisible())
241         {
242             mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), false, false);
243 
244             // #i93485# assert when copying from window to VDev is used
245             OSL_ENSURE(mrOutDev.GetOutDevType() != OUTDEV_WINDOW,
246                 "impBufferDevice render helper: Copying from Window to VDev, this should be avoided (!)");
247 
248             const bool bWasEnabledSrc(mrOutDev.IsMapModeEnabled());
249             mrOutDev.EnableMapMode(false);
250             mpContent->DrawOutDev(aEmptyPoint, maDestPixel.GetSize(), maDestPixel.TopLeft(), maDestPixel.GetSize(), mrOutDev);
251             mrOutDev.EnableMapMode(bWasEnabledSrc);
252 
253             MapMode aNewMapMode(mrOutDev.GetMapMode());
254 
255             if(bAddOffsetToMapping)
256             {
257                 const Point aLogicTopLeft(mrOutDev.PixelToLogic(maDestPixel.TopLeft()));
258                 aNewMapMode.SetOrigin(Point(-aLogicTopLeft.X(), -aLogicTopLeft.Y()));
259             }
260 
261             mpContent->SetMapMode(aNewMapMode);
262 
263             // copy AA flag for new target
264             mpContent->SetAntialiasing(mrOutDev.GetAntialiasing());
265         }
266     }
267 
268     impBufferDevice::~impBufferDevice()
269     {
270         if(mpContent)
271         {
272             getVDevBuffer().free(*mpContent);
273         }
274 
275         if(mpMask)
276         {
277             getVDevBuffer().free(*mpMask);
278         }
279 
280         if(mpAlpha)
281         {
282             getVDevBuffer().free(*mpAlpha);
283         }
284     }
285 
286     void impBufferDevice::paint(double fTrans)
287     {
288         if(isVisible())
289         {
290             const Point aEmptyPoint;
291             const Size aSizePixel(maDestPixel.GetSize());
292             const bool bWasEnabledDst(mrOutDev.IsMapModeEnabled());
293             static bool bDoSaveForVisualControl(false);
294 
295             mrOutDev.EnableMapMode(false);
296             mpContent->EnableMapMode(false);
297             Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
298 
299             if(bDoSaveForVisualControl)
300             {
301                 SvFileStream aNew((const String&)String(ByteString( "c:\\content.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC);
302                 aNew << aContent;
303             }
304 
305             if(mpAlpha)
306             {
307                 mpAlpha->EnableMapMode(false);
308                 const AlphaMask aAlphaMask(mpAlpha->GetBitmap(aEmptyPoint, aSizePixel));
309 
310                 if(bDoSaveForVisualControl)
311                 {
312                     SvFileStream aNew((const String&)String(ByteString( "c:\\transparence.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC);
313                     aNew << aAlphaMask.GetBitmap();
314                 }
315 
316                 mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
317             }
318             else if(mpMask)
319             {
320                 mpMask->EnableMapMode(false);
321                 const Bitmap aMask(mpMask->GetBitmap(aEmptyPoint, aSizePixel));
322 
323                 if(bDoSaveForVisualControl)
324                 {
325                     SvFileStream aNew((const String&)String(ByteString( "c:\\mask.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC);
326                     aNew << aMask;
327                 }
328 
329                 mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aMask));
330             }
331             else if(0.0 != fTrans)
332             {
333                 sal_uInt8 nMaskValue((sal_uInt8)basegfx::fround(fTrans * 255.0));
334                 const AlphaMask aAlphaMask(aSizePixel, &nMaskValue);
335                 mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
336             }
337             else
338             {
339                 mrOutDev.DrawBitmap(maDestPixel.TopLeft(), aContent);
340             }
341 
342             mrOutDev.EnableMapMode(bWasEnabledDst);
343         }
344     }
345 
346     VirtualDevice& impBufferDevice::getContent()
347     {
348         OSL_ENSURE(mpContent, "impBufferDevice: No content, check isVisible() before accessing (!)");
349         return *mpContent;
350     }
351 
352     VirtualDevice& impBufferDevice::getMask()
353     {
354         OSL_ENSURE(mpContent, "impBufferDevice: No content, check isVisible() before accessing (!)");
355         if(!mpMask)
356         {
357             mpMask = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, true);
358             mpMask->SetMapMode(mpContent->GetMapMode());
359 
360             // do NOT copy AA flag for mask!
361         }
362 
363         return *mpMask;
364     }
365 
366     VirtualDevice& impBufferDevice::getTransparence()
367     {
368         OSL_ENSURE(mpContent, "impBufferDevice: No content, check isVisible() before accessing (!)");
369         if(!mpAlpha)
370         {
371             mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, false);
372             mpAlpha->SetMapMode(mpContent->GetMapMode());
373 
374             // copy AA flag for new target; masking needs to be smooth
375             mpAlpha->SetAntialiasing(mpContent->GetAntialiasing());
376         }
377 
378         return *mpAlpha;
379     }
380 } // end of namespace drawinglayer
381 
382 //////////////////////////////////////////////////////////////////////////////
383 // eof
384