1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <cassert>
21
22 #include <sal/types.h>
23 #include <tools/helpers.hxx>
24 #include <rtl/math.hxx>
25
26 #include <memory>
27
28 #include <vcl/gdimtf.hxx>
29 #include <vcl/metaact.hxx>
30 #include <vcl/outdev.hxx>
31 #include <vcl/settings.hxx>
32 #include <vcl/virdev.hxx>
33
34 #include <salgdi.hxx>
35 #include <bitmap/BitmapWriteAccess.hxx>
36
37 namespace
38 {
39 /**
40 * Perform a safe approximation of a polygon from double-precision
41 * coordinates to integer coordinates, to ensure that it has at least 2
42 * pixels in both X and Y directions.
43 */
toPolygon(const basegfx::B2DPolygon & rPoly)44 tools::Polygon toPolygon( const basegfx::B2DPolygon& rPoly )
45 {
46 basegfx::B2DRange aRange = rPoly.getB2DRange();
47 double fW = aRange.getWidth(), fH = aRange.getHeight();
48 if (0.0 < fW && 0.0 < fH && (fW <= 1.0 || fH <= 1.0))
49 {
50 // This polygon not empty but is too small to display. Approximate it
51 // with a rectangle large enough to be displayed.
52 double nX = aRange.getMinX(), nY = aRange.getMinY();
53 double nW = std::max<double>(1.0, rtl::math::round(fW));
54 double nH = std::max<double>(1.0, rtl::math::round(fH));
55
56 tools::Polygon aTarget;
57 aTarget.Insert(0, Point(nX, nY));
58 aTarget.Insert(1, Point(nX+nW, nY));
59 aTarget.Insert(2, Point(nX+nW, nY+nH));
60 aTarget.Insert(3, Point(nX, nY+nH));
61 aTarget.Insert(4, Point(nX, nY));
62 return aTarget;
63 }
64 return tools::Polygon(rPoly);
65 }
66
toPolyPolygon(const basegfx::B2DPolyPolygon & rPolyPoly)67 tools::PolyPolygon toPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly )
68 {
69 tools::PolyPolygon aTarget;
70 for (auto const& rB2DPolygon : rPolyPoly)
71 aTarget.Insert(toPolygon(rB2DPolygon));
72
73 return aTarget;
74 }
75 }
76
ImplDrawModeToColor(const Color & rColor) const77 Color OutputDevice::ImplDrawModeToColor( const Color& rColor ) const
78 {
79 Color aColor( rColor );
80 DrawModeFlags nDrawMode = GetDrawMode();
81
82 if( nDrawMode & ( DrawModeFlags::BlackLine | DrawModeFlags::WhiteLine |
83 DrawModeFlags::GrayLine |
84 DrawModeFlags::SettingsLine ) )
85 {
86 if( !aColor.IsTransparent() )
87 {
88 if( nDrawMode & DrawModeFlags::BlackLine )
89 {
90 aColor = COL_BLACK;
91 }
92 else if( nDrawMode & DrawModeFlags::WhiteLine )
93 {
94 aColor = COL_WHITE;
95 }
96 else if( nDrawMode & DrawModeFlags::GrayLine )
97 {
98 const sal_uInt8 cLum = aColor.GetLuminance();
99 aColor = Color( cLum, cLum, cLum );
100 }
101 else if( nDrawMode & DrawModeFlags::SettingsLine )
102 {
103 aColor = GetSettings().GetStyleSettings().GetFontColor();
104 }
105 }
106 }
107 return aColor;
108 }
109
110 // Caution: This method is nearly the same as
111 // void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rB2DPolyPoly )
112 // so when changes are made here do not forget to make changes there, too
113
DrawTransparent(const basegfx::B2DHomMatrix & rObjectTransform,const basegfx::B2DPolyPolygon & rB2DPolyPoly,double fTransparency)114 void OutputDevice::DrawTransparent(
115 const basegfx::B2DHomMatrix& rObjectTransform,
116 const basegfx::B2DPolyPolygon& rB2DPolyPoly,
117 double fTransparency)
118 {
119 assert(!is_double_buffered_window());
120
121 // AW: Do NOT paint empty PolyPolygons
122 if(!rB2DPolyPoly.count())
123 return;
124
125 // we need a graphics
126 if( !mpGraphics && !AcquireGraphics() )
127 return;
128 assert(mpGraphics);
129
130 if( mbInitClipRegion )
131 InitClipRegion();
132
133 if( mbOutputClipped )
134 return;
135
136 if( mbInitLineColor )
137 InitLineColor();
138
139 if( mbInitFillColor )
140 InitFillColor();
141
142 if(mpGraphics->supportsOperation(OutDevSupportType::B2DDraw) &&
143 (RasterOp::OverPaint == GetRasterOp()) )
144 {
145 // b2dpolygon support not implemented yet on non-UNX platforms
146 basegfx::B2DPolyPolygon aB2DPolyPolygon(rB2DPolyPoly);
147
148 // ensure it is closed
149 if(!aB2DPolyPolygon.isClosed())
150 {
151 // maybe assert, prevents buffering due to making a copy
152 aB2DPolyPolygon.setClosed( true );
153 }
154
155 // create ObjectToDevice transformation
156 const basegfx::B2DHomMatrix aFullTransform(ImplGetDeviceTransformation() * rObjectTransform);
157 // TODO: this must not drop transparency for mpAlphaVDev case, but instead use premultiplied
158 // alpha... but that requires using premultiplied alpha also for already drawn data
159 const double fAdjustedTransparency = mpAlphaVDev ? 0 : fTransparency;
160 bool bDrawnOk(true);
161
162 if( IsFillColor() )
163 {
164 bDrawnOk = mpGraphics->DrawPolyPolygon(
165 aFullTransform,
166 aB2DPolyPolygon,
167 fAdjustedTransparency,
168 *this);
169 }
170
171 if( bDrawnOk && IsLineColor() )
172 {
173 const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
174
175 for(auto const& rPolygon : aB2DPolyPolygon)
176 {
177 mpGraphics->DrawPolyLine(
178 aFullTransform,
179 rPolygon,
180 fAdjustedTransparency,
181 0.0, // tdf#124848 hairline
182 nullptr, // MM01
183 basegfx::B2DLineJoin::NONE,
184 css::drawing::LineCap_BUTT,
185 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
186 bPixelSnapHairline,
187 *this );
188 }
189 }
190
191 if( bDrawnOk )
192 {
193 if( mpMetaFile )
194 {
195 // tdf#119843 need transformed Polygon here
196 basegfx::B2DPolyPolygon aB2DPolyPoly(rB2DPolyPoly);
197 aB2DPolyPoly.transform(rObjectTransform);
198 mpMetaFile->AddAction(
199 new MetaTransparentAction(
200 tools::PolyPolygon(aB2DPolyPoly),
201 static_cast< sal_uInt16 >(fTransparency * 100.0)));
202 }
203
204 if (mpAlphaVDev)
205 mpAlphaVDev->DrawTransparent(rObjectTransform, rB2DPolyPoly, fTransparency);
206
207 return;
208 }
209 }
210
211 // fallback to old polygon drawing if needed
212 // tdf#119843 need transformed Polygon here
213 basegfx::B2DPolyPolygon aB2DPolyPoly(rB2DPolyPoly);
214 aB2DPolyPoly.transform(rObjectTransform);
215 DrawTransparent(
216 toPolyPolygon(aB2DPolyPoly),
217 static_cast<sal_uInt16>(fTransparency * 100.0));
218 }
219
DrawInvisiblePolygon(const tools::PolyPolygon & rPolyPoly)220 void OutputDevice::DrawInvisiblePolygon( const tools::PolyPolygon& rPolyPoly )
221 {
222 assert(!is_double_buffered_window());
223
224 // short circuit if the polygon border is invisible too
225 if( !mbLineColor )
226 return;
227
228 // we assume that the border is NOT to be drawn transparently???
229 Push( PushFlags::FILLCOLOR );
230 SetFillColor();
231 DrawPolyPolygon( rPolyPoly );
232 Pop();
233 }
234
DrawTransparentNatively(const tools::PolyPolygon & rPolyPoly,sal_uInt16 nTransparencePercent)235 bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly,
236 sal_uInt16 nTransparencePercent )
237 {
238 assert(!is_double_buffered_window());
239
240 bool bDrawn = false;
241
242 if (mpGraphics->supportsOperation(OutDevSupportType::B2DDraw)
243 #if defined UNX && ! defined MACOSX && ! defined IOS
244 && GetBitCount() > 8
245 #endif
246 #ifdef _WIN32
247 // workaround bad dithering on remote displaying when using GDI+ with toolbar button highlighting
248 && !rPolyPoly.IsRect()
249 #endif
250 )
251 {
252 // prepare the graphics device
253 if( mbInitClipRegion )
254 InitClipRegion();
255
256 if( mbOutputClipped )
257 return false;
258
259 if( mbInitLineColor )
260 InitLineColor();
261
262 if( mbInitFillColor )
263 InitFillColor();
264
265 // get the polygon in device coordinates
266 basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPoly.getB2DPolyPolygon());
267 const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
268
269 const double fTransparency = 0.01 * nTransparencePercent;
270 if( mbFillColor )
271 {
272 // #i121591#
273 // CAUTION: Only non printing (pixel-renderer) VCL commands from OutputDevices
274 // should be used when printing. Normally this is avoided by the printer being
275 // non-AAed and thus e.g. on WIN GdiPlus calls are not used. It may be necessary
276 // to figure out a way of moving this code to its own function that is
277 // overridden by the Print class, which will mean we deliberately override the
278 // functionality and we use the fallback some lines below (which is not very good,
279 // though. For now, WinSalGraphics::drawPolyPolygon will detect printer usage and
280 // correct the wrong mapping (see there for details)
281 bDrawn = mpGraphics->DrawPolyPolygon(
282 aTransform,
283 aB2DPolyPolygon,
284 fTransparency,
285 *this);
286 }
287
288 if( mbLineColor )
289 {
290 // disable the fill color for now
291 mpGraphics->SetFillColor();
292
293 // draw the border line
294 const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
295
296 for(auto const& rPolygon : aB2DPolyPolygon)
297 {
298 bDrawn = mpGraphics->DrawPolyLine(
299 aTransform,
300 rPolygon,
301 fTransparency,
302 0.0, // tdf#124848 hairline
303 nullptr, // MM01
304 basegfx::B2DLineJoin::NONE,
305 css::drawing::LineCap_BUTT,
306 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
307 bPixelSnapHairline,
308 *this );
309 }
310
311 // prepare to restore the fill color
312 mbInitFillColor = mbFillColor;
313 }
314 }
315
316 return bDrawn;
317 }
318
EmulateDrawTransparent(const tools::PolyPolygon & rPolyPoly,sal_uInt16 nTransparencePercent)319 void OutputDevice::EmulateDrawTransparent ( const tools::PolyPolygon& rPolyPoly,
320 sal_uInt16 nTransparencePercent )
321 {
322 // #110958# Disable alpha VDev, we perform the necessary
323 VirtualDevice* pOldAlphaVDev = mpAlphaVDev;
324
325 // operation explicitly further below.
326 if( mpAlphaVDev )
327 mpAlphaVDev = nullptr;
328
329 GDIMetaFile* pOldMetaFile = mpMetaFile;
330 mpMetaFile = nullptr;
331
332 tools::PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
333 tools::Rectangle aPolyRect( aPolyPoly.GetBoundRect() );
334 tools::Rectangle aDstRect( Point(), GetOutputSizePixel() );
335
336 aDstRect.Intersection( aPolyRect );
337
338 ClipToPaintRegion( aDstRect );
339
340 if( !aDstRect.IsEmpty() )
341 {
342 bool bDrawn = false;
343
344 // #i66849# Added fast path for exactly rectangular
345 // polygons
346 // #i83087# Naturally, system alpha blending cannot
347 // work with separate alpha VDev
348 if( !mpAlphaVDev && aPolyPoly.IsRect() )
349 {
350 // setup Graphics only here (other cases delegate
351 // to basic OutDev methods)
352 if ( mbInitClipRegion )
353 InitClipRegion();
354
355 if ( mbInitLineColor )
356 InitLineColor();
357
358 if ( mbInitFillColor )
359 InitFillColor();
360
361 tools::Rectangle aLogicPolyRect( rPolyPoly.GetBoundRect() );
362 tools::Rectangle aPixelRect( ImplLogicToDevicePixel( aLogicPolyRect ) );
363
364 if( !mbOutputClipped )
365 {
366 bDrawn = mpGraphics->DrawAlphaRect( aPixelRect.Left(), aPixelRect.Top(),
367 // #i98405# use methods with small g, else one pixel too much will be painted.
368 // This is because the source is a polygon which when painted would not paint
369 // the rightmost and lowest pixel line(s), so use one pixel less for the
370 // rectangle, too.
371 aPixelRect.getWidth(), aPixelRect.getHeight(),
372 sal::static_int_cast<sal_uInt8>(nTransparencePercent),
373 *this );
374 }
375 else
376 {
377 bDrawn = true;
378 }
379 }
380
381 if( !bDrawn )
382 {
383 ScopedVclPtrInstance< VirtualDevice > aVDev(*this);
384 const Size aDstSz( aDstRect.GetSize() );
385 const sal_uInt8 cTrans = static_cast<sal_uInt8>(MinMax( FRound( nTransparencePercent * 2.55 ), 0, 255 ));
386
387 if( aDstRect.Left() || aDstRect.Top() )
388 aPolyPoly.Move( -aDstRect.Left(), -aDstRect.Top() );
389
390 if( aVDev->SetOutputSizePixel( aDstSz ) )
391 {
392 const bool bOldMap = mbMap;
393
394 EnableMapMode( false );
395
396 aVDev->SetLineColor( COL_BLACK );
397 aVDev->SetFillColor( COL_BLACK );
398 aVDev->DrawPolyPolygon( aPolyPoly );
399
400 Bitmap aPaint( GetBitmap( aDstRect.TopLeft(), aDstSz ) );
401 Bitmap aPolyMask( aVDev->GetBitmap( Point(), aDstSz ) );
402
403 // #107766# check for non-empty bitmaps before accessing them
404 if( !aPaint.IsEmpty() && !aPolyMask.IsEmpty() )
405 {
406 BitmapScopedWriteAccess pW(aPaint);
407 Bitmap::ScopedReadAccess pR(aPolyMask);
408
409 if( pW && pR )
410 {
411 BitmapColor aPixCol;
412 const BitmapColor aFillCol( GetFillColor() );
413 const BitmapColor aBlack( pR->GetBestMatchingColor( COL_BLACK ) );
414 const tools::Long nWidth = pW->Width();
415 const tools::Long nHeight = pW->Height();
416 const tools::Long nR = aFillCol.GetRed();
417 const tools::Long nG = aFillCol.GetGreen();
418 const tools::Long nB = aFillCol.GetBlue();
419 tools::Long nX, nY;
420
421 if (vcl::isPalettePixelFormat(aPaint.getPixelFormat()))
422 {
423 const BitmapPalette& rPal = pW->GetPalette();
424 const sal_uInt16 nCount = rPal.GetEntryCount();
425 std::unique_ptr<sal_uInt8[]> xMap(new sal_uInt8[ nCount * sizeof( BitmapColor )]);
426 BitmapColor* pMap = reinterpret_cast<BitmapColor*>(xMap.get());
427
428 for( sal_uInt16 i = 0; i < nCount; i++ )
429 {
430 BitmapColor aCol( rPal[ i ] );
431 aCol.Merge( aFillCol, cTrans );
432 pMap[ i ] = BitmapColor( static_cast<sal_uInt8>(rPal.GetBestIndex( aCol )) );
433 }
434
435 if( pR->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal &&
436 pW->GetScanlineFormat() == ScanlineFormat::N8BitPal )
437 {
438 const sal_uInt8 cBlack = aBlack.GetIndex();
439
440 for( nY = 0; nY < nHeight; nY++ )
441 {
442 Scanline pWScan = pW->GetScanline( nY );
443 Scanline pRScan = pR->GetScanline( nY );
444 sal_uInt8 cBit = 128;
445
446 for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan++ )
447 {
448 if( !cBit )
449 {
450 cBit = 128;
451 pRScan += 1;
452 }
453 if( ( *pRScan & cBit ) == cBlack )
454 {
455 *pWScan = pMap[ *pWScan ].GetIndex();
456 }
457 }
458 }
459 }
460 else
461 {
462 for( nY = 0; nY < nHeight; nY++ )
463 {
464 Scanline pScanline = pW->GetScanline(nY);
465 Scanline pScanlineRead = pR->GetScanline(nY);
466 for( nX = 0; nX < nWidth; nX++ )
467 {
468 if( pR->GetPixelFromData( pScanlineRead, nX ) == aBlack )
469 {
470 pW->SetPixelOnData( pScanline, nX, pMap[ pW->GetIndexFromData( pScanline, nX ) ] );
471 }
472 }
473 }
474 }
475 }
476 else
477 {
478 if( pR->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal &&
479 pW->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr )
480 {
481 const sal_uInt8 cBlack = aBlack.GetIndex();
482
483 for( nY = 0; nY < nHeight; nY++ )
484 {
485 Scanline pWScan = pW->GetScanline( nY );
486 Scanline pRScan = pR->GetScanline( nY );
487 sal_uInt8 cBit = 128;
488
489 for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan += 3 )
490 {
491 if( !cBit )
492 {
493 cBit = 128;
494 pRScan += 1;
495 }
496 if( ( *pRScan & cBit ) == cBlack )
497 {
498 pWScan[ 0 ] = color::ColorChannelMerge( pWScan[ 0 ], nB, cTrans );
499 pWScan[ 1 ] = color::ColorChannelMerge( pWScan[ 1 ], nG, cTrans );
500 pWScan[ 2 ] = color::ColorChannelMerge( pWScan[ 2 ], nR, cTrans );
501 }
502 }
503 }
504 }
505 else
506 {
507 for( nY = 0; nY < nHeight; nY++ )
508 {
509 Scanline pScanline = pW->GetScanline(nY);
510 Scanline pScanlineRead = pR->GetScanline(nY);
511 for( nX = 0; nX < nWidth; nX++ )
512 {
513 if( pR->GetPixelFromData( pScanlineRead, nX ) == aBlack )
514 {
515 aPixCol = pW->GetColor( nY, nX );
516 aPixCol.Merge(aFillCol, cTrans);
517 pW->SetPixelOnData(pScanline, nX, aPixCol);
518 }
519 }
520 }
521 }
522 }
523 }
524
525 pR.reset();
526 pW.reset();
527
528 DrawBitmap( aDstRect.TopLeft(), aPaint );
529
530 EnableMapMode( bOldMap );
531
532 if( mbLineColor )
533 {
534 Push( PushFlags::FILLCOLOR );
535 SetFillColor();
536 DrawPolyPolygon( rPolyPoly );
537 Pop();
538 }
539 }
540 }
541 else
542 {
543 DrawPolyPolygon( rPolyPoly );
544 }
545 }
546 }
547
548 mpMetaFile = pOldMetaFile;
549
550 // #110958# Restore disabled alpha VDev
551 mpAlphaVDev = pOldAlphaVDev;
552 }
553
DrawTransparent(const tools::PolyPolygon & rPolyPoly,sal_uInt16 nTransparencePercent)554 void OutputDevice::DrawTransparent( const tools::PolyPolygon& rPolyPoly,
555 sal_uInt16 nTransparencePercent )
556 {
557 assert(!is_double_buffered_window());
558
559 // short circuit for drawing an opaque polygon
560 if( (nTransparencePercent < 1) || (mnDrawMode & DrawModeFlags::NoTransparency) )
561 {
562 DrawPolyPolygon( rPolyPoly );
563 return;
564 }
565
566 // short circuit for drawing an invisible polygon
567 if( !mbFillColor || (nTransparencePercent >= 100) )
568 {
569 DrawInvisiblePolygon( rPolyPoly );
570 return; // tdf#84294: do not record it in metafile
571 }
572
573 // handle metafile recording
574 if( mpMetaFile )
575 mpMetaFile->AddAction( new MetaTransparentAction( rPolyPoly, nTransparencePercent ) );
576
577 bool bDrawn = !IsDeviceOutputNecessary() || ImplIsRecordLayout();
578 if( bDrawn )
579 return;
580
581 // get the device graphics as drawing target
582 if( !mpGraphics && !AcquireGraphics() )
583 return;
584 assert(mpGraphics);
585
586 // try hard to draw it directly, because the emulation layers are slower
587 bDrawn = DrawTransparentNatively( rPolyPoly, nTransparencePercent );
588
589 if (!bDrawn)
590 EmulateDrawTransparent( rPolyPoly, nTransparencePercent );
591
592 // #110958# Apply alpha value also to VDev alpha channel
593 if( mpAlphaVDev )
594 {
595 const Color aFillCol( mpAlphaVDev->GetFillColor() );
596 mpAlphaVDev->SetFillColor( Color(sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100),
597 sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100),
598 sal::static_int_cast<sal_uInt8>(255*nTransparencePercent/100)) );
599
600 mpAlphaVDev->DrawTransparent( rPolyPoly, nTransparencePercent );
601
602 mpAlphaVDev->SetFillColor( aFillCol );
603 }
604 }
605
DrawTransparent(const GDIMetaFile & rMtf,const Point & rPos,const Size & rSize,const Gradient & rTransparenceGradient)606 void OutputDevice::DrawTransparent( const GDIMetaFile& rMtf, const Point& rPos,
607 const Size& rSize, const Gradient& rTransparenceGradient )
608 {
609 assert(!is_double_buffered_window());
610
611 const Color aBlack( COL_BLACK );
612
613 if( mpMetaFile )
614 {
615 // missing here is to map the data using the DeviceTransformation
616 mpMetaFile->AddAction( new MetaFloatTransparentAction( rMtf, rPos, rSize, rTransparenceGradient ) );
617 }
618
619 if ( !IsDeviceOutputNecessary() )
620 return;
621
622 if( ( rTransparenceGradient.GetStartColor() == aBlack && rTransparenceGradient.GetEndColor() == aBlack ) ||
623 ( mnDrawMode & DrawModeFlags::NoTransparency ) )
624 {
625 const_cast<GDIMetaFile&>(rMtf).WindStart();
626 const_cast<GDIMetaFile&>(rMtf).Play(*this, rPos, rSize);
627 const_cast<GDIMetaFile&>(rMtf).WindStart();
628 }
629 else
630 {
631 GDIMetaFile* pOldMetaFile = mpMetaFile;
632 tools::Rectangle aOutRect( LogicToPixel( rPos ), LogicToPixel( rSize ) );
633 Point aPoint;
634 tools::Rectangle aDstRect( aPoint, GetOutputSizePixel() );
635
636 mpMetaFile = nullptr;
637 aDstRect.Intersection( aOutRect );
638
639 ClipToPaintRegion( aDstRect );
640
641 if( !aDstRect.IsEmpty() )
642 {
643 // Create transparent buffer
644 ScopedVclPtrInstance<VirtualDevice> xVDev(DeviceFormat::DEFAULT, DeviceFormat::DEFAULT);
645
646 xVDev->mnDPIX = mnDPIX;
647 xVDev->mnDPIY = mnDPIY;
648
649 if( xVDev->SetOutputSizePixel( aDstRect.GetSize() ) )
650 {
651 if(GetAntialiasing() != AntialiasingFlags::NONE)
652 {
653 // #i102109#
654 // For MetaFile replay (see task) it may now be necessary to take
655 // into account that the content is AntiAlialiased and needs to be masked
656 // like that. Instead of masking, i will use a copy-modify-paste cycle
657 // here (as i already use in the VclPrimiziveRenderer with success)
658 xVDev->SetAntialiasing(GetAntialiasing());
659
660 // create MapMode for buffer (offset needed) and set
661 MapMode aMap(GetMapMode());
662 const Point aOutPos(PixelToLogic(aDstRect.TopLeft()));
663 aMap.SetOrigin(Point(-aOutPos.X(), -aOutPos.Y()));
664 xVDev->SetMapMode(aMap);
665
666 // copy MapMode state and disable for target
667 const bool bOrigMapModeEnabled(IsMapModeEnabled());
668 EnableMapMode(false);
669
670 // copy MapMode state and disable for buffer
671 const bool bBufferMapModeEnabled(xVDev->IsMapModeEnabled());
672 xVDev->EnableMapMode(false);
673
674 // copy content from original to buffer
675 xVDev->DrawOutDev( aPoint, xVDev->GetOutputSizePixel(), // dest
676 aDstRect.TopLeft(), xVDev->GetOutputSizePixel(), // source
677 *this);
678
679 // draw MetaFile to buffer
680 xVDev->EnableMapMode(bBufferMapModeEnabled);
681 const_cast<GDIMetaFile&>(rMtf).WindStart();
682 const_cast<GDIMetaFile&>(rMtf).Play(*xVDev, rPos, rSize);
683 const_cast<GDIMetaFile&>(rMtf).WindStart();
684
685 // get content bitmap from buffer
686 xVDev->EnableMapMode(false);
687
688 const Bitmap aPaint(xVDev->GetBitmap(aPoint, xVDev->GetOutputSizePixel()));
689
690 // create alpha mask from gradient and get as Bitmap
691 xVDev->EnableMapMode(bBufferMapModeEnabled);
692 xVDev->SetDrawMode(DrawModeFlags::GrayGradient);
693 xVDev->DrawGradient(tools::Rectangle(rPos, rSize), rTransparenceGradient);
694 xVDev->SetDrawMode(DrawModeFlags::Default);
695 xVDev->EnableMapMode(false);
696
697 const AlphaMask aAlpha(xVDev->GetBitmap(aPoint, xVDev->GetOutputSizePixel()));
698
699 xVDev.disposeAndClear();
700
701 // draw masked content to target and restore MapMode
702 DrawBitmapEx(aDstRect.TopLeft(), BitmapEx(aPaint, aAlpha));
703 EnableMapMode(bOrigMapModeEnabled);
704 }
705 else
706 {
707 MapMode aMap( GetMapMode() );
708 Point aOutPos( PixelToLogic( aDstRect.TopLeft() ) );
709 const bool bOldMap = mbMap;
710
711 aMap.SetOrigin( Point( -aOutPos.X(), -aOutPos.Y() ) );
712 xVDev->SetMapMode( aMap );
713 const bool bVDevOldMap = xVDev->IsMapModeEnabled();
714
715 // create paint bitmap
716 const_cast<GDIMetaFile&>(rMtf).WindStart();
717 const_cast<GDIMetaFile&>(rMtf).Play(*xVDev, rPos, rSize);
718 const_cast<GDIMetaFile&>(rMtf).WindStart();
719 xVDev->EnableMapMode( false );
720 BitmapEx aPaint = xVDev->GetBitmapEx(Point(), xVDev->GetOutputSizePixel());
721 xVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( sal_True ) here!
722
723 // create alpha mask from gradient
724 xVDev->SetDrawMode( DrawModeFlags::GrayGradient );
725 xVDev->DrawGradient( tools::Rectangle( rPos, rSize ), rTransparenceGradient );
726 xVDev->SetDrawMode( DrawModeFlags::Default );
727 xVDev->EnableMapMode( false );
728
729 AlphaMask aAlpha(xVDev->GetBitmap(Point(), xVDev->GetOutputSizePixel()));
730 aAlpha.BlendWith(aPaint.GetAlpha());
731
732 xVDev.disposeAndClear();
733
734 EnableMapMode( false );
735 DrawBitmapEx(aDstRect.TopLeft(), BitmapEx(aPaint.GetBitmap(), aAlpha));
736 EnableMapMode( bOldMap );
737 }
738 }
739 }
740
741 mpMetaFile = pOldMetaFile;
742 }
743 }
744
745 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
746