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