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