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