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