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