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