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