1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
24 #include <vcl/bmpacc.hxx>
25 #include <tools/poly.hxx>
26 #include <vcl/outdev.hxx>
27 #include <vcl/window.hxx>
28 #include <vcl/gdimtf.hxx>
29 #include <vcl/metaact.hxx>
30 #include <vcl/metric.hxx>
31 #include <vcl/animate.hxx>
32 #include <vcl/alpha.hxx>
33 #include <vcl/virdev.hxx>
34 #include "grfcache.hxx"
35 #include <svtools/grfmgr.hxx>
36 #include <boost/scoped_array.hpp>
41 #define WATERMARK_LUM_OFFSET 50
42 #define WATERMARK_CON_OFFSET -70
43 #define MAP( cVal0, cVal1, nFrac ) ((sal_uInt8)((((long)(cVal0)<<20L)+nFrac*((long)(cVal1)-(cVal0)))>>20L))
49 GraphicManager::GraphicManager( sal_uLong nCacheSize
, sal_uLong nMaxObjCacheSize
) :
51 mpCache( new GraphicCache( nCacheSize
, nMaxObjCacheSize
) )
55 GraphicManager::~GraphicManager()
57 for( size_t i
= 0, n
= maObjList
.size(); i
< n
; ++i
)
58 maObjList
[ i
]->GraphicManagerDestroyed();
63 void GraphicManager::SetMaxCacheSize( sal_uLong nNewCacheSize
)
65 mpCache
->SetMaxDisplayCacheSize( nNewCacheSize
);
68 sal_uLong
GraphicManager::GetMaxCacheSize() const
70 return mpCache
->GetMaxDisplayCacheSize();
73 void GraphicManager::SetMaxObjCacheSize( sal_uLong nNewMaxObjSize
, bool bDestroyGreaterCached
)
75 mpCache
->SetMaxObjDisplayCacheSize( nNewMaxObjSize
, bDestroyGreaterCached
);
78 void GraphicManager::SetCacheTimeout( sal_uLong nTimeoutSeconds
)
80 mpCache
->SetCacheTimeout( nTimeoutSeconds
);
83 bool GraphicManager::IsInCache( OutputDevice
* pOut
, const Point
& rPt
,
84 const Size
& rSz
, const GraphicObject
& rObj
,
85 const GraphicAttr
& rAttr
) const
87 return mpCache
->IsInDisplayCache( pOut
, rPt
, rSz
, rObj
, rAttr
);
90 bool GraphicManager::DrawObj( OutputDevice
* pOut
, const Point
& rPt
, const Size
& rSz
,
91 GraphicObject
& rObj
, const GraphicAttr
& rAttr
,
92 const GraphicManagerDrawFlags nFlags
, bool& rCached
)
100 if( ( rObj
.GetType() == GRAPHIC_BITMAP
) || ( rObj
.GetType() == GRAPHIC_GDIMETAFILE
) )
102 // create output and fill cache
104 if( rObj
.IsAnimated() || ( pOut
->GetOutDevType() == OUTDEV_PRINTER
) ||
105 ( !( nFlags
& GraphicManagerDrawFlags::NO_SUBSTITUTE
) &&
106 ( ( nFlags
& GraphicManagerDrawFlags::SUBSTITUTE
) ||
107 !( nFlags
& GraphicManagerDrawFlags::CACHED
) ||
108 ( pOut
->GetConnectMetaFile() && !pOut
->IsOutputEnabled() ) ) ) )
110 // simple output of transformed graphic
111 const Graphic
aGraphic( rObj
.GetTransformedGraphic( &rAttr
) );
113 if( aGraphic
.IsSupportedGraphic() )
115 const sal_uInt16 nRot10
= rAttr
.GetRotation() % 3600;
119 Polygon
aPoly( Rectangle( aPt
, aSz
) );
121 aPoly
.Rotate( aPt
, nRot10
);
122 const Rectangle
aRotBoundRect( aPoly
.GetBoundRect() );
123 aPt
= aRotBoundRect
.TopLeft();
124 aSz
= aRotBoundRect
.GetSize();
127 aGraphic
.Draw( pOut
, aPt
, aSz
);
135 // cached/direct drawing
136 if( !mpCache
->DrawDisplayCacheObj( pOut
, aPt
, aSz
, rObj
, rAttr
) )
137 bRet
= ImplDraw( pOut
, aPt
, aSz
, rObj
, rAttr
, nFlags
, rCached
);
139 bRet
= rCached
= true;
146 void GraphicManager::ImplRegisterObj( const GraphicObject
& rObj
, Graphic
& rSubstitute
,
147 const OString
* pID
, const GraphicObject
* pCopyObj
)
149 assert(std::find(maObjList
.begin(), maObjList
.end(),
150 const_cast<GraphicObject
*>(&rObj
)) == maObjList
.end());
152 maObjList
.push_back( const_cast<GraphicObject
*>(&rObj
) );
153 mpCache
->AddGraphicObject( rObj
, rSubstitute
, pID
, pCopyObj
);
154 if( !rObj
.IsSwappedOut() )
155 mnUsedSize
+= rObj
.maGraphic
.GetSizeBytes();
158 void GraphicManager::ImplUnregisterObj( const GraphicObject
& rObj
)
160 mpCache
->ReleaseGraphicObject( rObj
);
161 if( !rObj
.IsSwappedOut() )
163 assert(mnUsedSize
>= rObj
.maGraphic
.GetSizeBytes());
164 mnUsedSize
-= rObj
.maGraphic
.GetSizeBytes();
166 for( GraphicObjectList_impl::iterator it
= maObjList
.begin(); it
!= maObjList
.end(); ++it
)
168 if ( *it
== &rObj
) {
169 maObjList
.erase( it
);
173 assert(false); // surely it should have been registered?
176 void GraphicManager::ImplGraphicObjectWasSwappedOut( const GraphicObject
& rObj
)
178 mpCache
->GraphicObjectWasSwappedOut( rObj
);
179 assert(mnUsedSize
>= rObj
.GetSizeBytes());
180 mnUsedSize
-= rObj
.GetSizeBytes();
183 OString
GraphicManager::ImplGetUniqueID( const GraphicObject
& rObj
) const
185 return mpCache
->GetUniqueID( rObj
);
190 struct simpleSortByDataChangeTimeStamp
192 bool operator() (GraphicObject
* p1
, GraphicObject
* p2
) const
194 return p1
->GetDataChangeTimeStamp() < p2
->GetDataChangeTimeStamp();
197 } // end of anonymous namespace
199 void GraphicManager::ImplCheckSizeOfSwappedInGraphics(const GraphicObject
* pGraphicToIgnore
)
201 // detect maximum allowed memory footprint. Use the user-settings of MaxCacheSize (defaulted
203 const sal_uLong
nMaxCacheSize(GetMaxCacheSize());
205 if(mnUsedSize
> nMaxCacheSize
)
207 // Copy the object list for now, because maObjList can change in the meantime unexpectedly.
208 std::vector
< GraphicObject
* > aCandidates(maObjList
.begin(), maObjList
.end());
209 // if we use more currently, sort by last DataChangeTimeStamp
210 // sort by DataChangeTimeStamp so that the oldest get removed first
211 ::std::sort(aCandidates
.begin(), aCandidates
.end(), simpleSortByDataChangeTimeStamp());
213 for(sal_uInt32
a(0); mnUsedSize
>= nMaxCacheSize
&& a
< aCandidates
.size(); a
++)
215 // swap out until we have no more or the goal to use less than nMaxCacheSize
217 GraphicObject
* pObj
= aCandidates
[a
];
218 if( pObj
== pGraphicToIgnore
)
222 if (std::find(maObjList
.begin(), maObjList
.end(), pObj
) == maObjList
.end())
224 // object has been deleted when swapping out another one
228 // do not swap out when we have less than 16KB data objects
229 if(pObj
->GetSizeBytes() >= (16 * 1024))
231 pObj
->FireSwapOutRequest();
237 void GraphicManager::ImplGraphicObjectWasSwappedIn( const GraphicObject
& rObj
)
239 mpCache
->GraphicObjectWasSwappedIn( rObj
);
240 mnUsedSize
+= rObj
.maGraphic
.GetSizeBytes();
243 bool GraphicManager::ImplDraw( OutputDevice
* pOut
, const Point
& rPt
,
244 const Size
& rSz
, GraphicObject
& rObj
,
245 const GraphicAttr
& rAttr
,
246 const GraphicManagerDrawFlags nFlags
, bool& rCached
)
248 const Graphic
& rGraphic
= rObj
.GetGraphic();
251 if( rGraphic
.IsSupportedGraphic() && !rObj
.IsSwappedOut() )
253 if( GRAPHIC_BITMAP
== rGraphic
.GetType() )
255 const BitmapEx
aSrcBmpEx( rGraphic
.GetBitmapEx() );
257 // #i46805# No point in caching a bitmap that is rendered
258 // via RectFill on the OutDev
259 if( !(pOut
->GetDrawMode() & ( DrawModeFlags::BlackBitmap
| DrawModeFlags::WhiteBitmap
)) &&
260 mpCache
->IsDisplayCacheable( pOut
, rPt
, rSz
, rObj
, rAttr
) )
264 if( ImplCreateOutput( pOut
, rPt
, rSz
, aSrcBmpEx
, rAttr
, nFlags
, &aDstBmpEx
) )
266 rCached
= mpCache
->CreateDisplayCacheObj( pOut
, rPt
, rSz
, rObj
, rAttr
, aDstBmpEx
);
272 bRet
= ImplCreateOutput( pOut
, rPt
, rSz
, aSrcBmpEx
, rAttr
, nFlags
);
276 const GDIMetaFile
& rSrcMtf
= rGraphic
.GetGDIMetaFile();
278 if( mpCache
->IsDisplayCacheable( pOut
, rPt
, rSz
, rObj
, rAttr
) )
281 BitmapEx aContainedBmpEx
;
283 if( ImplCreateOutput( pOut
, rPt
, rSz
, rSrcMtf
, rAttr
, nFlags
, aDstMtf
, aContainedBmpEx
) )
285 if( !!aContainedBmpEx
)
287 // Use bitmap output method, if metafile basically contains only a single
288 // bitmap (allows caching the resulting pixmap).
291 if( ImplCreateOutput( pOut
, rPt
, rSz
, aContainedBmpEx
, rAttr
, nFlags
, &aDstBmpEx
) )
293 rCached
= mpCache
->CreateDisplayCacheObj( pOut
, rPt
, rSz
, rObj
, rAttr
, aDstBmpEx
);
299 rCached
= mpCache
->CreateDisplayCacheObj( pOut
, rPt
, rSz
, rObj
, rAttr
, aDstMtf
);
307 const Graphic
aGraphic( rObj
.GetTransformedGraphic( &rAttr
) );
309 if( aGraphic
.IsSupportedGraphic() )
311 aGraphic
.Draw( pOut
, rPt
, rSz
);
321 bool ImplCreateRotatedScaled( const BitmapEx
& rBmpEx
, const GraphicAttr
& rAttributes
,
322 sal_uInt16 nRot10
, const Size
& rUnrotatedSzPix
,
323 long nStartX
, long nEndX
, long nStartY
, long nEndY
,
324 BitmapEx
& rOutBmpEx
)
326 const long aUnrotatedWidth
= rUnrotatedSzPix
.Width();
327 const long aUnrotatedHeight
= rUnrotatedSzPix
.Height();
328 const long aBitmapWidth
= rBmpEx
.GetSizePixel().Width();
329 const long aBitmapHeight
= rBmpEx
.GetSizePixel().Height();
331 long nX
, nY
, nTmpX
, nTmpY
, nTmpFX
, nTmpFY
, nTmp
;
334 bool bHMirr( rAttributes
.GetMirrorFlags() & BmpMirrorFlags::Horizontal
);
335 bool bVMirr( rAttributes
.GetMirrorFlags() & BmpMirrorFlags::Vertical
);
337 boost::scoped_array
<long> pMapIX(new long[ aUnrotatedWidth
]);
338 boost::scoped_array
<long> pMapFX(new long[ aUnrotatedWidth
]);
339 boost::scoped_array
<long> pMapIY(new long[ aUnrotatedHeight
]);
340 boost::scoped_array
<long> pMapFY(new long[ aUnrotatedHeight
]);
345 bool scaleByAveraging
= false;
348 if(aBitmapWidth
> 1 && aUnrotatedWidth
> 1)
350 fRevScaleX
= (double) ( aBitmapWidth
- 1 ) / (double)( aUnrotatedWidth
- 1 );
351 // create horizontal mapping table
352 for( x
= 0, nTmpX
= aBitmapWidth
- 1L, nTmp
= aBitmapWidth
- 2L >= 0 ? aBitmapWidth
-2L : 0L; x
< aUnrotatedWidth
; x
++ )
354 fTmp
= x
* fRevScaleX
;
359 pMapIX
[ x
] = MinMax( fTmp
, 0, nTmp
);
360 pMapFX
[ x
] = (long) ( ( fTmp
- pMapIX
[ x
] ) * 1048576.0 );
362 scaleByAveraging
|= fRevScaleX
> 5.0/3.0;
366 if(aBitmapWidth
== 1)
368 fRevScaleX
= 1.0 / (double)( aUnrotatedWidth
);
369 for ( x
= 0; x
< aUnrotatedWidth
; x
++)
374 scaleByAveraging
= true;
378 fRevScaleX
= (double) aBitmapWidth
/ (double)( aUnrotatedWidth
);
379 fTmp
= (double)aBitmapWidth
/ 2.0;
381 pMapIX
[ 0 ] = (long)fTmp
;
382 pMapFX
[ 0 ] = (long)( ( fTmp
- pMapIX
[ 0 ] ) * 1048576.0 );
383 scaleByAveraging
= true;
386 if(aBitmapHeight
> 1 && aUnrotatedHeight
> 1)
388 fRevScaleY
= (double) ( aBitmapHeight
- 1 ) / (double)( aUnrotatedHeight
- 1 );
389 // create vertical mapping table
390 for( y
= 0, nTmpY
= aBitmapHeight
- 1L, nTmp
= aBitmapHeight
- 2L >= 0 ? aBitmapHeight
- 2L : 0L; y
< aUnrotatedHeight
; y
++ )
392 fTmp
= y
* fRevScaleY
;
397 pMapIY
[ y
] = MinMax( fTmp
, 0, nTmp
);
398 pMapFY
[ y
] = (long) ( ( fTmp
- pMapIY
[ y
] ) * 1048576.0 );
400 scaleByAveraging
|= fRevScaleY
> 5.0/3.0;
404 if(aBitmapHeight
== 1)
406 fRevScaleY
= 1.0 / (double)( aUnrotatedHeight
);
407 for (y
= 0; y
< aUnrotatedHeight
; ++y
)
412 scaleByAveraging
= true;
416 fRevScaleY
= (double) aBitmapHeight
/ (double)( aUnrotatedHeight
);
417 fTmp
= (double)aBitmapHeight
/ 2.0;
419 pMapIY
[ 0 ] = (long)fTmp
;
420 pMapFY
[ 0 ] = (long)( ( fTmp
- pMapIY
[ 0 ] ) * 1048576.0 );
421 scaleByAveraging
= true;
425 Bitmap
aBmp( rBmpEx
.GetBitmap() );
427 BitmapReadAccess
* pReadAccess
= aBmp
.AcquireReadAccess();
428 BitmapWriteAccess
* pWriteAccess
;
430 const double fCosAngle
= cos( nRot10
* F_PI1800
);
431 const double fSinAngle
= sin( nRot10
* F_PI1800
);
432 const long aTargetWidth
= nEndX
- nStartX
+ 1L;
433 const long aTargetHeight
= nEndY
- nStartY
+ 1L;
434 boost::scoped_array
<long> pCosX(new long[ aTargetWidth
]);
435 boost::scoped_array
<long> pSinX(new long[ aTargetWidth
]);
436 boost::scoped_array
<long> pCosY(new long[ aTargetHeight
]);
437 boost::scoped_array
<long> pSinY(new long[ aTargetHeight
]);
438 long nUnRotX
, nUnRotY
, nSinY
, nCosY
;
439 sal_uInt8 cR0
, cG0
, cB0
, cR1
, cG1
, cB1
;
442 Polygon
aPoly( Rectangle( Point(), rUnrotatedSzPix
) );
443 aPoly
.Rotate( Point(), nRot10
);
444 Rectangle
aNewBound( aPoly
.GetBoundRect() );
446 // create horizontal mapping table
447 for( x
= 0, nTmpX
= aNewBound
.Left() + nStartX
; x
< aTargetWidth
; x
++ )
449 pCosX
[ x
] = FRound( fCosAngle
* ( fTmp
= nTmpX
++ << 8 ) );
450 pSinX
[ x
] = FRound( fSinAngle
* fTmp
);
453 // create vertical mapping table
454 for( y
= 0, nTmpY
= aNewBound
.Top() + nStartY
; y
< aTargetHeight
; y
++ )
456 pCosY
[ y
] = FRound( fCosAngle
* ( fTmp
= nTmpY
++ << 8 ) );
457 pSinY
[ y
] = FRound( fSinAngle
* fTmp
);
462 aOutBmp
= Bitmap( Size( aTargetWidth
, aTargetHeight
), 24 );
463 pWriteAccess
= aOutBmp
.AcquireWriteAccess();
469 if ( !scaleByAveraging
)
471 if( pReadAccess
->HasPalette() )
473 for( y
= 0; y
< aTargetHeight
; y
++ )
478 for( x
= 0; x
< aTargetWidth
; x
++ )
480 nUnRotX
= ( pCosX
[ x
] - nSinY
) >> 8;
481 nUnRotY
= ( pSinX
[ x
] + nCosY
) >> 8;
483 if( ( nUnRotX
>= 0L ) && ( nUnRotX
< aUnrotatedWidth
) &&
484 ( nUnRotY
>= 0L ) && ( nUnRotY
< aUnrotatedHeight
) )
486 nTmpX
= pMapIX
[ nUnRotX
]; nTmpFX
= pMapFX
[ nUnRotX
];
487 nTmpY
= pMapIY
[ nUnRotY
], nTmpFY
= pMapFY
[ nUnRotY
];
489 const BitmapColor
& rCol0
= pReadAccess
->GetPaletteColor( pReadAccess
->GetPixelIndex( nTmpY
, nTmpX
) );
490 const BitmapColor
& rCol1
= pReadAccess
->GetPaletteColor( pReadAccess
->GetPixelIndex( nTmpY
, ++nTmpX
) );
491 cR0
= MAP( rCol0
.GetRed(), rCol1
.GetRed(), nTmpFX
);
492 cG0
= MAP( rCol0
.GetGreen(), rCol1
.GetGreen(), nTmpFX
);
493 cB0
= MAP( rCol0
.GetBlue(), rCol1
.GetBlue(), nTmpFX
);
495 const BitmapColor
& rCol3
= pReadAccess
->GetPaletteColor( pReadAccess
->GetPixelIndex( ++nTmpY
, nTmpX
) );
496 const BitmapColor
& rCol2
= pReadAccess
->GetPaletteColor( pReadAccess
->GetPixelIndex( nTmpY
, --nTmpX
) );
497 cR1
= MAP( rCol2
.GetRed(), rCol3
.GetRed(), nTmpFX
);
498 cG1
= MAP( rCol2
.GetGreen(), rCol3
.GetGreen(), nTmpFX
);
499 cB1
= MAP( rCol2
.GetBlue(), rCol3
.GetBlue(), nTmpFX
);
501 aColRes
.SetRed( MAP( cR0
, cR1
, nTmpFY
) );
502 aColRes
.SetGreen( MAP( cG0
, cG1
, nTmpFY
) );
503 aColRes
.SetBlue( MAP( cB0
, cB1
, nTmpFY
) );
504 pWriteAccess
->SetPixel( y
, x
, aColRes
);
511 BitmapColor aCol0
, aCol1
;
513 for( y
= 0; y
< aTargetHeight
; y
++ )
518 for( x
= 0; x
< aTargetWidth
; x
++ )
520 nUnRotX
= ( pCosX
[ x
] - nSinY
) >> 8;
521 nUnRotY
= ( pSinX
[ x
] + nCosY
) >> 8;
523 if( ( nUnRotX
>= 0L ) && ( nUnRotX
< aUnrotatedWidth
) &&
524 ( nUnRotY
>= 0L ) && ( nUnRotY
< aUnrotatedHeight
) )
526 nTmpX
= pMapIX
[ nUnRotX
]; nTmpFX
= pMapFX
[ nUnRotX
];
527 nTmpY
= pMapIY
[ nUnRotY
], nTmpFY
= pMapFY
[ nUnRotY
];
529 aCol0
= pReadAccess
->GetPixel( nTmpY
, nTmpX
);
530 aCol1
= pReadAccess
->GetPixel( nTmpY
, ++nTmpX
);
531 cR0
= MAP( aCol0
.GetRed(), aCol1
.GetRed(), nTmpFX
);
532 cG0
= MAP( aCol0
.GetGreen(), aCol1
.GetGreen(), nTmpFX
);
533 cB0
= MAP( aCol0
.GetBlue(), aCol1
.GetBlue(), nTmpFX
);
535 aCol1
= pReadAccess
->GetPixel( ++nTmpY
, nTmpX
);
536 aCol0
= pReadAccess
->GetPixel( nTmpY
, --nTmpX
);
537 cR1
= MAP( aCol0
.GetRed(), aCol1
.GetRed(), nTmpFX
);
538 cG1
= MAP( aCol0
.GetGreen(), aCol1
.GetGreen(), nTmpFX
);
539 cB1
= MAP( aCol0
.GetBlue(), aCol1
.GetBlue(), nTmpFX
);
541 aColRes
.SetRed( MAP( cR0
, cR1
, nTmpFY
) );
542 aColRes
.SetGreen( MAP( cG0
, cG1
, nTmpFY
) );
543 aColRes
.SetBlue( MAP( cB0
, cB1
, nTmpFY
) );
544 pWriteAccess
->SetPixel( y
, x
, aColRes
);
550 else // scaleByAveraging
552 double aSumRed
, aSumGreen
, aSumBlue
, aCount
;
554 BitmapColor aResultColor
;
556 for( y
= 0; y
< aTargetHeight
; y
++ )
561 for( x
= 0; x
< aTargetWidth
; x
++ )
563 double aUnrotatedX
= ( pCosX
[ x
] - nSinY
) / 256.0;
564 double aUnrotatedY
= ( pSinX
[ x
] + nCosY
) / 256.0;
567 aUnrotatedX
= aUnrotatedWidth
- aUnrotatedX
- 1;
569 aUnrotatedY
= aUnrotatedHeight
- aUnrotatedY
- 1;
571 if( ( aUnrotatedX
>= 0 ) && ( aUnrotatedX
< aUnrotatedWidth
) &&
572 ( aUnrotatedY
>= 0 ) && ( aUnrotatedY
< aUnrotatedHeight
) )
574 double dYStart
= ((aUnrotatedY
+ 0.5) * fRevScaleY
) - 0.5;
575 double dYEnd
= ((aUnrotatedY
+ 1.5) * fRevScaleY
) - 0.5;
577 int yStart
= MinMax( dYStart
, 0, aBitmapHeight
- 1);
578 int yEnd
= MinMax( dYEnd
, 0, aBitmapHeight
- 1);
580 double dXStart
= ((aUnrotatedX
+ 0.5) * fRevScaleX
) - 0.5;
581 double dXEnd
= ((aUnrotatedX
+ 1.5) * fRevScaleX
) - 0.5;
583 int xStart
= MinMax( dXStart
, 0, aBitmapWidth
- 1);
584 int xEnd
= MinMax( dXEnd
, 0, aBitmapWidth
- 1);
586 aSumRed
= aSumGreen
= aSumBlue
= 0.0;
589 for (int yIn
= yStart
; yIn
<= yEnd
; yIn
++)
591 for (int xIn
= xStart
; xIn
<= xEnd
; xIn
++)
593 if( pReadAccess
->HasPalette() )
594 aColor
= pReadAccess
->GetPaletteColor( pReadAccess
->GetPixelIndex( yIn
, xIn
) );
596 aColor
= pReadAccess
->GetPixel( yIn
, xIn
);
598 aSumRed
+= aColor
.GetRed();
599 aSumGreen
+= aColor
.GetGreen();
600 aSumBlue
+= aColor
.GetBlue();
606 aResultColor
.SetRed( MinMax( aSumRed
/ aCount
, 0, 255) );
607 aResultColor
.SetGreen( MinMax( aSumGreen
/ aCount
, 0, 255) );
608 aResultColor
.SetBlue( MinMax( aSumBlue
/ aCount
, 0, 255) );
610 pWriteAccess
->SetPixel( y
, x
, aResultColor
);
616 Bitmap::ReleaseAccess( pWriteAccess
);
620 Bitmap::ReleaseAccess( pReadAccess
);
624 if( bRet
&& ( rBmpEx
.IsTransparent() || ( nRot10
!= 0 && nRot10
!= 900 && nRot10
!= 1800 && nRot10
!= 2700 ) ) )
628 if( rBmpEx
.IsAlpha() )
630 AlphaMask
aAlpha( rBmpEx
.GetAlpha() );
633 pReadAccess
= aAlpha
.AcquireReadAccess();
637 aOutAlpha
= AlphaMask( Size( aTargetWidth
, aTargetHeight
) );
638 pWriteAccess
= aOutAlpha
.AcquireWriteAccess();
642 if( pReadAccess
->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL
&&
643 pWriteAccess
->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL
)
645 if ( !scaleByAveraging
)
647 Scanline pLine0
, pLine1
, pLineW
;
649 for( nY
= 0; nY
< aTargetHeight
; nY
++ )
651 nSinY
= pSinY
[ nY
], nCosY
= pCosY
[ nY
];
652 pLineW
= pWriteAccess
->GetScanline( nY
);
654 for( nX
= 0; nX
< aTargetWidth
; nX
++ )
656 nUnRotX
= ( pCosX
[ nX
] - nSinY
) >> 8;
657 nUnRotY
= ( pSinX
[ nX
] + nCosY
) >> 8;
659 if( ( nUnRotX
>= 0L ) && ( nUnRotX
< aUnrotatedWidth
) &&
660 ( nUnRotY
>= 0L ) && ( nUnRotY
< aUnrotatedHeight
) )
662 nTmpX
= pMapIX
[ nUnRotX
], nTmpFX
= pMapFX
[ nUnRotX
];
663 nTmpY
= pMapIY
[ nUnRotY
], nTmpFY
= pMapFY
[ nUnRotY
];
665 pLine0
= pReadAccess
->GetScanline( nTmpY
++ );
666 pLine1
= pReadAccess
->GetScanline( nTmpY
);
668 const long nAlpha0
= pLine0
[ nTmpX
];
669 const long nAlpha2
= pLine1
[ nTmpX
++ ];
670 const long nAlpha1
= pLine0
[ nTmpX
];
671 const long nAlpha3
= pLine1
[ nTmpX
];
672 const long n0
= MAP( nAlpha0
, nAlpha1
, nTmpFX
);
673 const long n1
= MAP( nAlpha2
, nAlpha3
, nTmpFX
);
675 *pLineW
++ = MAP( n0
, n1
, nTmpFY
);
682 else // scaleByAveraging
684 const BitmapColor
aTrans( pWriteAccess
->GetBestMatchingColor( Color( COL_WHITE
) ) );
685 BitmapColor
aResultColor( 0 );
688 for( y
= 0; y
< aTargetHeight
; y
++ )
693 for( x
= 0; x
< aTargetWidth
; x
++ )
696 double aUnrotatedX
= ( pCosX
[ x
] - nSinY
) / 256.0;
697 double aUnrotatedY
= ( pSinX
[ x
] + nCosY
) / 256.0;
700 aUnrotatedX
= aUnrotatedWidth
- aUnrotatedX
- 1;
702 aUnrotatedY
= aUnrotatedHeight
- aUnrotatedY
- 1;
704 if( ( aUnrotatedX
>= 0 ) && ( aUnrotatedX
< aUnrotatedWidth
) &&
705 ( aUnrotatedY
>= 0 ) && ( aUnrotatedY
< aUnrotatedHeight
) )
707 double dYStart
= ((aUnrotatedY
+ 0.5) * fRevScaleY
) - 0.5;
708 double dYEnd
= ((aUnrotatedY
+ 1.5) * fRevScaleY
) - 0.5;
710 int yStart
= MinMax( dYStart
, 0, aBitmapHeight
- 1);
711 int yEnd
= MinMax( dYEnd
, 0, aBitmapHeight
- 1);
713 double dXStart
= ((aUnrotatedX
+ 0.5) * fRevScaleX
) - 0.5;
714 double dXEnd
= ((aUnrotatedX
+ 1.5) * fRevScaleX
) - 0.5;
716 int xStart
= MinMax( dXStart
, 0, aBitmapWidth
- 1);
717 int xEnd
= MinMax( dXEnd
, 0, aBitmapWidth
- 1);
722 for (int yIn
= yStart
; yIn
<= yEnd
; yIn
++)
724 for (int xIn
= xStart
; xIn
<= xEnd
; xIn
++)
726 aSum
+= pReadAccess
->GetPixel( yIn
, xIn
).GetIndex();
730 aResultColor
.SetIndex( MinMax( aSum
/ aCount
, 0, 255) );
731 pWriteAccess
->SetPixel( y
, x
, aResultColor
);
735 pWriteAccess
->SetPixel( y
, x
, aTrans
);
743 const BitmapColor
aTrans( pWriteAccess
->GetBestMatchingColor( Color( COL_WHITE
) ) );
744 BitmapColor
aAlphaVal( 0 );
746 for( nY
= 0; nY
< aTargetHeight
; nY
++ )
748 nSinY
= pSinY
[ nY
], nCosY
= pCosY
[ nY
];
750 for( nX
= 0; nX
< aTargetWidth
; nX
++ )
752 nUnRotX
= ( pCosX
[ nX
] - nSinY
) >> 8;
753 nUnRotY
= ( pSinX
[ nX
] + nCosY
) >> 8;
755 if( ( nUnRotX
>= 0L ) && ( nUnRotX
< aUnrotatedWidth
) &&
756 ( nUnRotY
>= 0L ) && ( nUnRotY
< aUnrotatedHeight
) )
758 nTmpX
= pMapIX
[ nUnRotX
]; nTmpFX
= pMapFX
[ nUnRotX
];
759 nTmpY
= pMapIY
[ nUnRotY
], nTmpFY
= pMapFY
[ nUnRotY
];
761 const long nAlpha0
= pReadAccess
->GetPixel( nTmpY
, nTmpX
).GetIndex();
762 const long nAlpha1
= pReadAccess
->GetPixel( nTmpY
, ++nTmpX
).GetIndex();
763 const long nAlpha3
= pReadAccess
->GetPixel( ++nTmpY
, nTmpX
).GetIndex();
764 const long nAlpha2
= pReadAccess
->GetPixel( nTmpY
, --nTmpX
).GetIndex();
765 const long n0
= MAP( nAlpha0
, nAlpha1
, nTmpFX
);
766 const long n1
= MAP( nAlpha2
, nAlpha3
, nTmpFX
);
768 aAlphaVal
.SetIndex( MAP( n0
, n1
, nTmpFY
) );
769 pWriteAccess
->SetPixel( nY
, nX
, aAlphaVal
);
772 pWriteAccess
->SetPixel( nY
, nX
, aTrans
);
777 aOutAlpha
.ReleaseAccess( pWriteAccess
);
781 aAlpha
.ReleaseAccess( pReadAccess
);
785 rOutBmpEx
= BitmapEx( aOutBmp
, aOutAlpha
);
789 Bitmap
aOutMsk( Size( aTargetWidth
, aTargetHeight
), 1 );
790 pWriteAccess
= aOutMsk
.AcquireWriteAccess();
794 Bitmap
aMsk( rBmpEx
.GetMask() );
795 const BitmapColor
aB( pWriteAccess
->GetBestMatchingColor( Color( COL_BLACK
) ) );
796 const BitmapColor
aW( pWriteAccess
->GetBestMatchingColor( Color( COL_WHITE
) ) );
797 BitmapReadAccess
* pMAcc
= NULL
;
799 if( !aMsk
|| ( ( pMAcc
= aMsk
.AcquireReadAccess() ) != NULL
) )
801 boost::scoped_array
<long> pMapLX(new long[ aUnrotatedWidth
]);
802 boost::scoped_array
<long> pMapLY(new long[ aUnrotatedHeight
]);
806 aTestB
= pMAcc
->GetBestMatchingColor( Color( COL_BLACK
) );
808 // create new horizontal mapping table
809 for( nX
= 0UL; nX
< aUnrotatedWidth
; nX
++ )
810 pMapLX
[ nX
] = FRound( (double) pMapIX
[ nX
] + pMapFX
[ nX
] / 1048576.0 );
812 // create new vertical mapping table
813 for( nY
= 0UL; nY
< aUnrotatedHeight
; nY
++ )
814 pMapLY
[ nY
] = FRound( (double) pMapIY
[ nY
] + pMapFY
[ nY
] / 1048576.0 );
817 for( nY
= 0; nY
< aTargetHeight
; nY
++ )
822 for( nX
= 0; nX
< aTargetWidth
; nX
++ )
824 nUnRotX
= ( pCosX
[ nX
] - nSinY
) >> 8;
825 nUnRotY
= ( pSinX
[ nX
] + nCosY
) >> 8;
827 if( ( nUnRotX
>= 0L ) && ( nUnRotX
< aUnrotatedWidth
) &&
828 ( nUnRotY
>= 0L ) && ( nUnRotY
< aUnrotatedHeight
) )
832 if( pMAcc
->GetPixel( pMapLY
[ nUnRotY
], pMapLX
[ nUnRotX
] ) == aTestB
)
833 pWriteAccess
->SetPixel( nY
, nX
, aB
);
835 pWriteAccess
->SetPixel( nY
, nX
, aW
);
838 pWriteAccess
->SetPixel( nY
, nX
, aB
);
841 pWriteAccess
->SetPixel( nY
, nX
, aW
);
849 Bitmap::ReleaseAccess( pMAcc
);
854 Bitmap::ReleaseAccess( pWriteAccess
);
858 rOutBmpEx
= BitmapEx( aOutBmp
, aOutMsk
);
870 bool GraphicManager::ImplCreateOutput( OutputDevice
* pOutputDevice
,
871 const Point
& rPoint
, const Size
& rSize
,
872 const BitmapEx
& rBitmapEx
, const GraphicAttr
& rAttributes
,
873 const GraphicManagerDrawFlags
/*nFlags*/, BitmapEx
* pBmpEx
)
875 sal_uInt16 nRot10
= rAttributes
.GetRotation() % 3600;
877 Point aOutputPointPix
;
879 Point
aUnrotatedPointPix( pOutputDevice
->LogicToPixel( rPoint
) );
880 Size
aUnrotatedSizePix( pOutputDevice
->LogicToPixel( rSize
) );
886 Polygon
aPoly( Rectangle( rPoint
, rSize
) );
887 aPoly
.Rotate( rPoint
, nRot10
);
888 const Rectangle
aRotBoundRect( aPoly
.GetBoundRect() );
889 aOutputPointPix
= pOutputDevice
->LogicToPixel( aRotBoundRect
.TopLeft() );
890 aOutputSizePix
= pOutputDevice
->LogicToPixel( aRotBoundRect
.GetSize() );
894 aOutputPointPix
= aUnrotatedPointPix
;
895 aOutputSizePix
= aUnrotatedSizePix
;
898 if( aUnrotatedSizePix
.Width() && aUnrotatedSizePix
.Height() )
900 BitmapEx
aBmpEx( rBitmapEx
);
904 const Size
& rBmpSzPix
= rBitmapEx
.GetSizePixel();
905 const long nW
= rBmpSzPix
.Width();
906 const long nH
= rBmpSzPix
.Height();
907 long nStartX
= -1, nStartY
= -1, nEndX
= -1, nEndY
= -1;
908 bool bHMirr( rAttributes
.GetMirrorFlags() & BmpMirrorFlags::Horizontal
);
909 bool bVMirr( rAttributes
.GetMirrorFlags() & BmpMirrorFlags::Vertical
);
911 // calculate output sizes
915 Rectangle
aOutRect( aPt
, pOutputDevice
->GetOutputSizePixel() );
916 Rectangle
aBmpRect( aOutputPointPix
, aOutputSizePix
);
918 if( pOutputDevice
->GetOutDevType() == OUTDEV_WINDOW
)
920 const vcl::Region
aPaintRgn( static_cast<vcl::Window
*>( pOutputDevice
)->GetPaintRegion() );
921 if( !aPaintRgn
.IsNull() )
922 aOutRect
.Intersection( pOutputDevice
->LogicToPixel( aPaintRgn
.GetBoundRect() ) );
925 aOutRect
.Intersection( aBmpRect
);
927 if( !aOutRect
.IsEmpty() )
929 aOutPoint
= pOutputDevice
->PixelToLogic( aOutRect
.TopLeft() );
930 aOutSize
= pOutputDevice
->PixelToLogic( aOutRect
.GetSize() );
931 nStartX
= aOutRect
.Left() - aBmpRect
.Left();
932 nStartY
= aOutRect
.Top() - aBmpRect
.Top();
933 nEndX
= aOutRect
.Right() - aBmpRect
.Left();
934 nEndY
= aOutRect
.Bottom() - aBmpRect
.Top();
938 nStartX
= -1L; // invalid
943 aOutPoint
= pOutputDevice
->PixelToLogic( aOutputPointPix
);
944 aOutSize
= pOutputDevice
->PixelToLogic( aOutputSizePix
);
945 nStartX
= nStartY
= 0;
946 nEndX
= aOutputSizePix
.Width() - 1L;
947 nEndY
= aOutputSizePix
.Height() - 1L;
953 const bool bSimple
= ( 1 == nW
|| 1 == nH
);
959 bRet
= ( aOutBmpEx
= aBmpEx
).Scale( aUnrotatedSizePix
);
962 aOutBmpEx
.Rotate( nRot10
, COL_TRANSPARENT
);
966 bRet
= ImplCreateRotatedScaled( aBmpEx
, rAttributes
,
967 nRot10
, aUnrotatedSizePix
,
968 nStartX
, nEndX
, nStartY
, nEndY
,
974 if( !bHMirr
&& !bVMirr
&& aOutputSizePix
== rBmpSzPix
)
976 aOutPoint
= pOutputDevice
->PixelToLogic( aOutputPointPix
);
977 aOutSize
= pOutputDevice
->PixelToLogic( aOutputSizePix
);
985 bRet
= ( aOutBmpEx
= aBmpEx
).Scale( Size( nEndX
- nStartX
+ 1, nEndY
- nStartY
+ 1 ) );
989 bRet
= ImplCreateRotatedScaled( aBmpEx
, rAttributes
,
990 nRot10
, aUnrotatedSizePix
,
991 nStartX
, nEndX
, nStartY
, nEndY
,
999 // Attribute adjustment if necessary
1000 if( rAttributes
.IsSpecialDrawMode() || rAttributes
.IsAdjusted() || rAttributes
.IsTransparent() )
1001 ImplAdjust( aOutBmpEx
, rAttributes
, GraphicAdjustmentFlags::DRAWMODE
| GraphicAdjustmentFlags::COLORS
| GraphicAdjustmentFlags::TRANSPARENCY
);
1003 // OutDev adjustment if necessary
1004 if( pOutputDevice
->GetOutDevType() != OUTDEV_PRINTER
&& pOutputDevice
->GetBitCount() <= 8 && aOutBmpEx
.GetBitCount() >= 8 )
1005 aOutBmpEx
.Dither( BmpDitherFlags::Matrix
);
1013 pOutputDevice
->DrawBitmapEx( aOutPoint
, aOutSize
, aOutBmpEx
);
1016 if( !rAttributes
.IsTransparent() && !aOutBmpEx
.IsAlpha() )
1017 aOutBmpEx
= BitmapEx( aOutBmpEx
.GetBitmap().CreateDisplayBitmap( pOutputDevice
), aOutBmpEx
.GetMask() );
1019 pOutputDevice
->DrawBitmapEx( aOutPoint
, aOutSize
, *pBmpEx
= aOutBmpEx
);
1027 // This function checks whether the bitmap is usable for skipping
1028 // mtf rendering by using just this one bitmap (i.e. in case the metafile
1029 // contains just this one pixmap that covers the entire metafile area).
1030 static BitmapEx
checkMetadataBitmap( const BitmapEx
& rBmpEx
,
1033 const Point
& rDestPoint
,
1034 const Size
& rDestSize
,
1035 const Size
& rRefSize
,
1036 bool& o_rbNonBitmapActionEncountered
)
1038 // NOTE: If you do changes in this function, change checkMetadataBitmap() in grfcache.cxx too.
1040 if( rSrcSize
== Size())
1041 rSrcSize
= rBmpEx
.GetSizePixel();
1043 if( rDestPoint
!= Point( 0, 0 ))
1044 { // The pixmap in the metafile has an offset (and so would not cover)
1045 // the entire result -> fall back to mtf rendering.
1046 o_rbNonBitmapActionEncountered
= true;
1049 if( rDestSize
!= rRefSize
)
1050 { // The pixmap is not fullscale (does not cover the entire metafile area).
1051 // HACK: The code here should refuse to use the bitmap directly
1052 // and fall back to mtf rendering, but there seem to be metafiles
1053 // that do not specify exactly their area (the Windows API requires apps
1054 // the specify it manually, the rectangle is specified as topleft/bottomright
1055 // rather than topleft/size [which may be confusing], and the docs
1056 // on the exact meaning are somewhat confusing as well), so if it turns
1057 // out this metafile really contains just one bitmap and no other painting,
1058 // and if the sizes almost match, just use the pixmap (which will be scaled
1059 // to fit exactly the requested size, so there should not be any actual problem
1060 // caused by this small difference). This will allow caching of the resulting
1061 // (scaled) pixmap, which can make a noticeable performance difference.
1062 if( rBmpEx
.GetSizePixel().Width() > 100 && rBmpEx
.GetSizePixel().Height() > 100
1063 && std::abs( rDestSize
.Width() - rRefSize
.Width()) < 5
1064 && std::abs( rDestSize
.Height() - rRefSize
.Height()) < 5 )
1065 ; // ok, assume it's close enough
1067 { // fall back to mtf rendering
1068 o_rbNonBitmapActionEncountered
= true;
1075 if( (rSrcPoint
.X() != 0 && rSrcPoint
.Y() != 0) ||
1076 rSrcSize
!= rBmpEx
.GetSizePixel() )
1078 // crop bitmap to given source rectangle (no
1079 // need to copy and convert the whole bitmap)
1080 const Rectangle
aCropRect( rSrcPoint
,
1082 aBmpEx
.Crop( aCropRect
);
1088 bool GraphicManager::ImplCreateOutput( OutputDevice
* pOut
,
1089 const Point
& rPt
, const Size
& rSz
,
1090 const GDIMetaFile
& rMtf
, const GraphicAttr
& rAttr
,
1091 const GraphicManagerDrawFlags
/*nFlags*/, GDIMetaFile
& rOutMtf
, BitmapEx
& rOutBmpEx
)
1093 const Size
aNewSize( rMtf
.GetPrefSize() );
1097 // Count bitmap actions, and flag actions that paint, but
1099 sal_Int32
nNumBitmaps(0);
1100 bool bNonBitmapActionEncountered(false);
1101 if( aNewSize
.Width() && aNewSize
.Height() && rSz
.Width() && rSz
.Height() )
1103 const double fGrfWH
= (double) aNewSize
.Width() / aNewSize
.Height();
1104 const double fOutWH
= (double) rSz
.Width() / rSz
.Height();
1106 const double fScaleX
= fOutWH
/ fGrfWH
;
1107 const double fScaleY
= 1.0;
1109 const MapMode
rPrefMapMode( rMtf
.GetPrefMapMode() );
1110 const Size
rSizePix( pOut
->LogicToPixel( aNewSize
, rPrefMapMode
) );
1112 // NOTE: If you do changes in this function, check GraphicDisplayCacheEntry::IsCacheableAsBitmap
1113 // in grfcache.cxx too.
1115 // Determine whether the metafile basically displays
1116 // a single bitmap (in which case that bitmap is simply used directly
1117 // instead of playing the metafile). Note that
1118 // the solution, as implemented here, is quite suboptimal (the
1119 // cases where a mtf consisting basically of a single bitmap,
1120 // that fail to pass the test below, are probably frequent). A
1121 // better solution would involve FSAA, but that's currently
1122 // expensive, and might trigger bugs on display drivers, if
1123 // VDevs get bigger than the actual screen.
1126 for( nCurPos
= 0, pAct
= rOutMtf
.FirstAction(); pAct
;
1127 pAct
= rOutMtf
.NextAction(), nCurPos
++ )
1129 MetaAction
* pModAct
= NULL
;
1130 switch( pAct
->GetType() )
1132 case MetaActionType::FONT
:
1134 // taking care of font width default if scaling metafile.
1135 MetaFontAction
* pA
= static_cast<MetaFontAction
*>(pAct
);
1136 vcl::Font
aFont( pA
->GetFont() );
1137 if ( !aFont
.GetWidth() )
1139 FontMetric
aFontMetric( pOut
->GetFontMetric( aFont
) );
1140 aFont
.SetWidth( aFontMetric
.GetWidth() );
1141 pModAct
= new MetaFontAction( aFont
);
1144 // FALLTHROUGH intended
1145 case MetaActionType::NONE
:
1146 // FALLTHROUGH intended
1148 // OutDev state changes (which don't affect bitmap
1150 case MetaActionType::LINECOLOR
:
1151 // FALLTHROUGH intended
1152 case MetaActionType::FILLCOLOR
:
1153 // FALLTHROUGH intended
1154 case MetaActionType::TEXTCOLOR
:
1155 // FALLTHROUGH intended
1156 case MetaActionType::TEXTFILLCOLOR
:
1157 // FALLTHROUGH intended
1158 case MetaActionType::TEXTALIGN
:
1159 // FALLTHROUGH intended
1160 case MetaActionType::TEXTLINECOLOR
:
1161 // FALLTHROUGH intended
1162 case MetaActionType::TEXTLINE
:
1163 // FALLTHROUGH intended
1164 case MetaActionType::PUSH
:
1165 // FALLTHROUGH intended
1166 case MetaActionType::POP
:
1167 // FALLTHROUGH intended
1168 case MetaActionType::LAYOUTMODE
:
1169 // FALLTHROUGH intended
1170 case MetaActionType::TEXTLANGUAGE
:
1171 // FALLTHROUGH intended
1172 case MetaActionType::COMMENT
:
1175 // bitmap output methods
1176 case MetaActionType::BMP
:
1177 if( !nNumBitmaps
&& !bNonBitmapActionEncountered
)
1179 MetaBmpAction
* pAction
= static_cast<MetaBmpAction
*>(pAct
);
1181 rOutBmpEx
= checkMetadataBitmap(
1182 BitmapEx( pAction
->GetBitmap()),
1184 pOut
->LogicToPixel( pAction
->GetPoint(),
1186 pAction
->GetBitmap().GetSizePixel(),
1188 bNonBitmapActionEncountered
);
1193 case MetaActionType::BMPSCALE
:
1194 if( !nNumBitmaps
&& !bNonBitmapActionEncountered
)
1196 MetaBmpScaleAction
* pAction
= static_cast<MetaBmpScaleAction
*>(pAct
);
1198 rOutBmpEx
= checkMetadataBitmap(
1199 BitmapEx( pAction
->GetBitmap()),
1201 pOut
->LogicToPixel( pAction
->GetPoint(),
1203 pOut
->LogicToPixel( pAction
->GetSize(),
1206 bNonBitmapActionEncountered
);
1211 case MetaActionType::BMPSCALEPART
:
1212 if( !nNumBitmaps
&& !bNonBitmapActionEncountered
)
1214 MetaBmpScalePartAction
* pAction
= static_cast<MetaBmpScalePartAction
*>(pAct
);
1216 rOutBmpEx
= checkMetadataBitmap(
1217 BitmapEx( pAction
->GetBitmap() ),
1218 pAction
->GetSrcPoint(),
1219 pAction
->GetSrcSize(),
1220 pOut
->LogicToPixel( pAction
->GetDestPoint(),
1222 pOut
->LogicToPixel( pAction
->GetDestSize(),
1225 bNonBitmapActionEncountered
);
1230 case MetaActionType::BMPEX
:
1231 if( !nNumBitmaps
&& !bNonBitmapActionEncountered
)
1233 MetaBmpExAction
* pAction
= static_cast<MetaBmpExAction
*>(pAct
);
1235 rOutBmpEx
= checkMetadataBitmap(
1236 pAction
->GetBitmapEx(),
1238 pOut
->LogicToPixel( pAction
->GetPoint(),
1240 pAction
->GetBitmapEx().GetSizePixel(),
1242 bNonBitmapActionEncountered
);
1247 case MetaActionType::BMPEXSCALE
:
1248 if( !nNumBitmaps
&& !bNonBitmapActionEncountered
)
1250 MetaBmpExScaleAction
* pAction
= static_cast<MetaBmpExScaleAction
*>(pAct
);
1252 rOutBmpEx
= checkMetadataBitmap(
1253 pAction
->GetBitmapEx(),
1255 pOut
->LogicToPixel( pAction
->GetPoint(),
1257 pOut
->LogicToPixel( pAction
->GetSize(),
1260 bNonBitmapActionEncountered
);
1265 case MetaActionType::BMPEXSCALEPART
:
1266 if( !nNumBitmaps
&& !bNonBitmapActionEncountered
)
1268 MetaBmpExScalePartAction
* pAction
= static_cast<MetaBmpExScalePartAction
*>(pAct
);
1270 rOutBmpEx
= checkMetadataBitmap( pAction
->GetBitmapEx(),
1271 pAction
->GetSrcPoint(),
1272 pAction
->GetSrcSize(),
1273 pOut
->LogicToPixel( pAction
->GetDestPoint(),
1275 pOut
->LogicToPixel( pAction
->GetDestSize(),
1278 bNonBitmapActionEncountered
);
1283 // these actions actually output something (that's
1284 // different from a bitmap)
1285 case MetaActionType::RASTEROP
:
1286 if( static_cast<MetaRasterOpAction
*>(pAct
)->GetRasterOp() == ROP_OVERPAINT
)
1288 // FALLTHROUGH intended
1289 case MetaActionType::PIXEL
:
1290 // FALLTHROUGH intended
1291 case MetaActionType::POINT
:
1292 // FALLTHROUGH intended
1293 case MetaActionType::LINE
:
1294 // FALLTHROUGH intended
1295 case MetaActionType::RECT
:
1296 // FALLTHROUGH intended
1297 case MetaActionType::ROUNDRECT
:
1298 // FALLTHROUGH intended
1299 case MetaActionType::ELLIPSE
:
1300 // FALLTHROUGH intended
1301 case MetaActionType::ARC
:
1302 // FALLTHROUGH intended
1303 case MetaActionType::PIE
:
1304 // FALLTHROUGH intended
1305 case MetaActionType::CHORD
:
1306 // FALLTHROUGH intended
1307 case MetaActionType::POLYLINE
:
1308 // FALLTHROUGH intended
1309 case MetaActionType::POLYGON
:
1310 // FALLTHROUGH intended
1311 case MetaActionType::POLYPOLYGON
:
1312 // FALLTHROUGH intended
1314 case MetaActionType::TEXT
:
1315 // FALLTHROUGH intended
1316 case MetaActionType::TEXTARRAY
:
1317 // FALLTHROUGH intended
1318 case MetaActionType::STRETCHTEXT
:
1319 // FALLTHROUGH intended
1320 case MetaActionType::TEXTRECT
:
1321 // FALLTHROUGH intended
1323 case MetaActionType::MASK
:
1324 // FALLTHROUGH intended
1325 case MetaActionType::MASKSCALE
:
1326 // FALLTHROUGH intended
1327 case MetaActionType::MASKSCALEPART
:
1328 // FALLTHROUGH intended
1330 case MetaActionType::GRADIENT
:
1331 // FALLTHROUGH intended
1332 case MetaActionType::HATCH
:
1333 // FALLTHROUGH intended
1334 case MetaActionType::WALLPAPER
:
1335 // FALLTHROUGH intended
1337 case MetaActionType::Transparent
:
1338 // FALLTHROUGH intended
1339 case MetaActionType::EPS
:
1340 // FALLTHROUGH intended
1341 case MetaActionType::FLOATTRANSPARENT
:
1342 // FALLTHROUGH intended
1343 case MetaActionType::GRADIENTEX
:
1344 // FALLTHROUGH intended
1346 // OutDev state changes that _do_ affect bitmap
1348 case MetaActionType::CLIPREGION
:
1349 // FALLTHROUGH intended
1350 case MetaActionType::ISECTRECTCLIPREGION
:
1351 // FALLTHROUGH intended
1352 case MetaActionType::ISECTREGIONCLIPREGION
:
1353 // FALLTHROUGH intended
1354 case MetaActionType::MOVECLIPREGION
:
1355 // FALLTHROUGH intended
1357 case MetaActionType::MAPMODE
:
1358 // FALLTHROUGH intended
1359 case MetaActionType::REFPOINT
:
1360 // FALLTHROUGH intended
1362 bNonBitmapActionEncountered
= true;
1367 MetaAction
* pDeleteAction
= rOutMtf
.ReplaceAction( pModAct
, nCurPos
);
1368 assert(pDeleteAction
);
1369 pDeleteAction
->Delete();
1373 if( pAct
->GetRefCount() > 1 )
1375 MetaAction
* pDeleteAction
= rOutMtf
.ReplaceAction( pModAct
= pAct
->Clone(), nCurPos
);
1376 assert(pDeleteAction
);
1377 pDeleteAction
->Delete();
1382 pModAct
->Scale( fScaleX
, fScaleY
);
1384 rOutMtf
.SetPrefSize( Size( FRound( aNewSize
.Width() * fScaleX
),
1385 FRound( aNewSize
.Height() * fScaleY
) ) );
1388 if( nNumBitmaps
!= 1 || bNonBitmapActionEncountered
)
1390 if( rAttr
.IsSpecialDrawMode() || rAttr
.IsAdjusted() || rAttr
.IsMirrored() || rAttr
.IsRotated() || rAttr
.IsTransparent() )
1391 ImplAdjust( rOutMtf
, rAttr
, GraphicAdjustmentFlags::ALL
);
1393 ImplDraw( pOut
, rPt
, rSz
, rOutMtf
, rAttr
);
1394 rOutBmpEx
= BitmapEx();
1400 void GraphicManager::ImplAdjust( BitmapEx
& rBmpEx
, const GraphicAttr
& rAttr
, GraphicAdjustmentFlags nAdjustmentFlags
)
1402 GraphicAttr
aAttr( rAttr
);
1404 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::DRAWMODE
) && aAttr
.IsSpecialDrawMode() )
1406 switch( aAttr
.GetDrawMode() )
1408 case( GRAPHICDRAWMODE_MONO
):
1409 rBmpEx
.Convert( BMP_CONVERSION_1BIT_THRESHOLD
);
1412 case( GRAPHICDRAWMODE_GREYS
):
1413 rBmpEx
.Convert( BMP_CONVERSION_8BIT_GREYS
);
1416 case( GRAPHICDRAWMODE_WATERMARK
):
1418 aAttr
.SetLuminance( aAttr
.GetLuminance() + WATERMARK_LUM_OFFSET
);
1419 aAttr
.SetContrast( aAttr
.GetContrast() + WATERMARK_CON_OFFSET
);
1428 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::COLORS
) && aAttr
.IsAdjusted() )
1430 rBmpEx
.Adjust( aAttr
.GetLuminance(), aAttr
.GetContrast(),
1431 aAttr
.GetChannelR(), aAttr
.GetChannelG(), aAttr
.GetChannelB(),
1432 aAttr
.GetGamma(), aAttr
.IsInvert() );
1435 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::MIRROR
) && aAttr
.IsMirrored() )
1437 rBmpEx
.Mirror( aAttr
.GetMirrorFlags() );
1440 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::ROTATE
) && aAttr
.IsRotated() )
1442 rBmpEx
.Rotate( aAttr
.GetRotation(), Color( COL_TRANSPARENT
) );
1445 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::TRANSPARENCY
) && aAttr
.IsTransparent() )
1448 sal_uInt8 cTrans
= aAttr
.GetTransparency();
1450 if( !rBmpEx
.IsTransparent() )
1451 aAlpha
= AlphaMask( rBmpEx
.GetSizePixel(), &cTrans
);
1452 else if( !rBmpEx
.IsAlpha() )
1454 aAlpha
= rBmpEx
.GetMask();
1455 aAlpha
.Replace( 0, cTrans
);
1459 aAlpha
= rBmpEx
.GetAlpha();
1460 BitmapWriteAccess
* pA
= aAlpha
.AcquireWriteAccess();
1464 sal_uLong nTrans
= cTrans
, nNewTrans
;
1465 const long nWidth
= pA
->Width(), nHeight
= pA
->Height();
1467 if( pA
->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL
)
1469 for( long nY
= 0; nY
< nHeight
; nY
++ )
1471 Scanline pAScan
= pA
->GetScanline( nY
);
1473 for( long nX
= 0; nX
< nWidth
; nX
++ )
1475 nNewTrans
= nTrans
+ *pAScan
;
1476 *pAScan
++ = (sal_uInt8
) ( ( nNewTrans
& 0xffffff00 ) ? 255 : nNewTrans
);
1482 BitmapColor
aAlphaValue( 0 );
1484 for( long nY
= 0; nY
< nHeight
; nY
++ )
1486 for( long nX
= 0; nX
< nWidth
; nX
++ )
1488 nNewTrans
= nTrans
+ pA
->GetPixel( nY
, nX
).GetIndex();
1489 aAlphaValue
.SetIndex( (sal_uInt8
) ( ( nNewTrans
& 0xffffff00 ) ? 255 : nNewTrans
) );
1490 pA
->SetPixel( nY
, nX
, aAlphaValue
);
1495 aAlpha
.ReleaseAccess( pA
);
1499 rBmpEx
= BitmapEx( rBmpEx
.GetBitmap(), aAlpha
);
1503 void GraphicManager::ImplAdjust( GDIMetaFile
& rMtf
, const GraphicAttr
& rAttr
, GraphicAdjustmentFlags nAdjustmentFlags
)
1505 GraphicAttr
aAttr( rAttr
);
1507 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::DRAWMODE
) && aAttr
.IsSpecialDrawMode() )
1509 switch( aAttr
.GetDrawMode() )
1511 case( GRAPHICDRAWMODE_MONO
):
1512 rMtf
.Convert( MTF_CONVERSION_1BIT_THRESHOLD
);
1515 case( GRAPHICDRAWMODE_GREYS
):
1516 rMtf
.Convert( MTF_CONVERSION_8BIT_GREYS
);
1519 case( GRAPHICDRAWMODE_WATERMARK
):
1521 aAttr
.SetLuminance( aAttr
.GetLuminance() + WATERMARK_LUM_OFFSET
);
1522 aAttr
.SetContrast( aAttr
.GetContrast() + WATERMARK_CON_OFFSET
);
1531 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::COLORS
) && aAttr
.IsAdjusted() )
1533 rMtf
.Adjust( aAttr
.GetLuminance(), aAttr
.GetContrast(),
1534 aAttr
.GetChannelR(), aAttr
.GetChannelG(), aAttr
.GetChannelB(),
1535 aAttr
.GetGamma(), aAttr
.IsInvert() );
1538 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::MIRROR
) && aAttr
.IsMirrored() )
1540 rMtf
.Mirror( aAttr
.GetMirrorFlags() );
1543 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::ROTATE
) && aAttr
.IsRotated() )
1545 rMtf
.Rotate( aAttr
.GetRotation() );
1548 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::TRANSPARENCY
) && aAttr
.IsTransparent() )
1550 OSL_FAIL( "Missing implementation: Mtf-Transparency" );
1554 void GraphicManager::ImplAdjust( Animation
& rAnimation
, const GraphicAttr
& rAttr
, GraphicAdjustmentFlags nAdjustmentFlags
)
1556 GraphicAttr
aAttr( rAttr
);
1558 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::DRAWMODE
) && aAttr
.IsSpecialDrawMode() )
1560 switch( aAttr
.GetDrawMode() )
1562 case( GRAPHICDRAWMODE_MONO
):
1563 rAnimation
.Convert( BMP_CONVERSION_1BIT_THRESHOLD
);
1566 case( GRAPHICDRAWMODE_GREYS
):
1567 rAnimation
.Convert( BMP_CONVERSION_8BIT_GREYS
);
1570 case( GRAPHICDRAWMODE_WATERMARK
):
1572 aAttr
.SetLuminance( aAttr
.GetLuminance() + WATERMARK_LUM_OFFSET
);
1573 aAttr
.SetContrast( aAttr
.GetContrast() + WATERMARK_CON_OFFSET
);
1582 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::COLORS
) && aAttr
.IsAdjusted() )
1584 rAnimation
.Adjust( aAttr
.GetLuminance(), aAttr
.GetContrast(),
1585 aAttr
.GetChannelR(), aAttr
.GetChannelG(), aAttr
.GetChannelB(),
1586 aAttr
.GetGamma(), aAttr
.IsInvert() );
1589 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::MIRROR
) && aAttr
.IsMirrored() )
1591 rAnimation
.Mirror( aAttr
.GetMirrorFlags() );
1594 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::ROTATE
) && aAttr
.IsRotated() )
1596 OSL_FAIL( "Missing implementation: Animation-Rotation" );
1599 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::TRANSPARENCY
) && aAttr
.IsTransparent() )
1601 OSL_FAIL( "Missing implementation: Animation-Transparency" );
1605 void GraphicManager::ImplDraw( OutputDevice
* pOut
, const Point
& rPt
, const Size
& rSz
,
1606 const GDIMetaFile
& rMtf
, const GraphicAttr
& rAttr
)
1608 sal_uInt16 nRot10
= rAttr
.GetRotation() % 3600;
1609 Point
aOutPt( rPt
);
1614 Polygon
aPoly( Rectangle( aOutPt
, aOutSz
) );
1616 aPoly
.Rotate( aOutPt
, nRot10
);
1617 const Rectangle
aRotBoundRect( aPoly
.GetBoundRect() );
1618 aOutPt
= aRotBoundRect
.TopLeft();
1619 aOutSz
= aRotBoundRect
.GetSize();
1622 pOut
->Push( PushFlags::CLIPREGION
);
1623 pOut
->IntersectClipRegion( Rectangle( aOutPt
, aOutSz
) );
1625 ( (GDIMetaFile
&) rMtf
).WindStart();
1626 ( (GDIMetaFile
&) rMtf
).Play( pOut
, aOutPt
, aOutSz
);
1627 ( (GDIMetaFile
&) rMtf
).WindStart();
1634 ImplTileInfo() : aTileTopLeft(), aNextTileTopLeft(), aTileSizePixel(), nTilesEmptyX(0), nTilesEmptyY(0) {}
1636 Point aTileTopLeft
; // top, left position of the rendered tile
1637 Point aNextTileTopLeft
; // top, left position for next recursion
1639 Size aTileSizePixel
; // size of the generated tile (might
1641 // aNextTileTopLeft-aTileTopLeft, because
1642 // this is nExponent*prevTileSize. The
1643 // generated tile is always nExponent
1644 // times the previous tile, such that it
1645 // can be used in the next stage. The
1646 // required area coverage is often
1647 // less. The extraneous area covered is
1648 // later overwritten by the next stage)
1649 int nTilesEmptyX
; // number of original tiles empty right of
1650 // this tile. This counts from
1651 // aNextTileTopLeft, i.e. the additional
1652 // area covered by aTileSizePixel is not
1653 // considered here. This is for
1654 // unification purposes, as the iterative
1655 // calculation of the next level's empty
1656 // tiles has to be based on this value.
1657 int nTilesEmptyY
; // as above, for Y
1661 bool GraphicObject::ImplRenderTempTile( VirtualDevice
& rVDev
, int nExponent
,
1662 int nNumTilesX
, int nNumTilesY
,
1663 const Size
& rTileSizePixel
,
1664 const GraphicAttr
* pAttr
, GraphicManagerDrawFlags nFlags
)
1666 if( nExponent
<= 1 )
1669 // determine MSB factor
1670 int nMSBFactor( 1 );
1671 while( nNumTilesX
/ nMSBFactor
!= 0 ||
1672 nNumTilesY
/ nMSBFactor
!= 0 )
1674 nMSBFactor
*= nExponent
;
1678 nMSBFactor
/= nExponent
;
1680 ImplTileInfo aTileInfo
;
1682 // #105229# Switch off mapping (converting to logic and back to
1683 // pixel might cause roundoff errors)
1684 bool bOldMap( rVDev
.IsMapModeEnabled() );
1685 rVDev
.EnableMapMode( false );
1687 bool bRet( ImplRenderTileRecursive( rVDev
, nExponent
, nMSBFactor
, nNumTilesX
, nNumTilesY
,
1688 nNumTilesX
, nNumTilesY
, rTileSizePixel
, pAttr
, nFlags
, aTileInfo
) );
1690 rVDev
.EnableMapMode( bOldMap
);
1695 // define for debug drawings
1698 // see header comment. this works similar to base conversion of a
1699 // number, i.e. if the exponent is 10, then the number for every tile
1700 // size is given by the decimal place of the corresponding decimal
1702 bool GraphicObject::ImplRenderTileRecursive( VirtualDevice
& rVDev
, int nExponent
, int nMSBFactor
,
1703 int nNumOrigTilesX
, int nNumOrigTilesY
,
1704 int nRemainderTilesX
, int nRemainderTilesY
,
1705 const Size
& rTileSizePixel
, const GraphicAttr
* pAttr
,
1706 GraphicManagerDrawFlags nFlags
, ImplTileInfo
& rTileInfo
)
1708 // gets loaded with our tile bitmap
1709 GraphicObject aTmpGraphic
;
1711 // stores a flag that renders the zero'th tile position
1712 // (i.e. (0,0)+rCurrPos) only if we're at the bottom of the
1713 // recursion stack. All other position already have that tile
1714 // rendered, because the lower levels painted their generated tile
1716 bool bNoFirstTileDraw( false );
1718 // what's left when we're done with our tile size
1719 const int nNewRemainderX( nRemainderTilesX
% nMSBFactor
);
1720 const int nNewRemainderY( nRemainderTilesY
% nMSBFactor
);
1722 // gets filled out from the recursive call with info of what's
1724 ImplTileInfo aTileInfo
;
1726 // current output position while drawing
1730 // check for recursion's end condition: LSB place reached?
1731 if( nMSBFactor
== 1 )
1733 aTmpGraphic
= *this;
1735 // set initial tile size -> orig size
1736 aTileInfo
.aTileSizePixel
= rTileSizePixel
;
1737 aTileInfo
.nTilesEmptyX
= nNumOrigTilesX
;
1738 aTileInfo
.nTilesEmptyY
= nNumOrigTilesY
;
1740 else if( ImplRenderTileRecursive( rVDev
, nExponent
, nMSBFactor
/nExponent
,
1741 nNumOrigTilesX
, nNumOrigTilesY
,
1742 nNewRemainderX
, nNewRemainderY
,
1743 rTileSizePixel
, pAttr
, nFlags
, aTileInfo
) )
1745 // extract generated tile -> see comment on the first loop below
1746 BitmapEx
aTileBitmap( rVDev
.GetBitmap( aTileInfo
.aTileTopLeft
, aTileInfo
.aTileSizePixel
) );
1748 aTmpGraphic
= GraphicObject( aTileBitmap
);
1750 // fill stripes left over from upstream levels:
1758 // where x denotes the place filled by our recursive predecessors
1760 // check whether we have to fill stripes here. Although not
1761 // obvious, there is one case where we can skip this step: if
1762 // the previous recursion level (the one who filled our
1763 // aTileInfo) had zero area to fill, then there are no white
1764 // stripes left, naturally. This happens if the digit
1765 // associated to that level has a zero, and can be checked via
1766 // aTileTopLeft==aNextTileTopLeft.
1767 if( aTileInfo
.aTileTopLeft
!= aTileInfo
.aNextTileTopLeft
)
1769 // now fill one row from aTileInfo.aNextTileTopLeft.X() all
1770 // the way to the right
1771 aCurrPos
.X() = aTileInfo
.aNextTileTopLeft
.X();
1772 aCurrPos
.Y() = aTileInfo
.aTileTopLeft
.Y();
1773 for( nX
=0; nX
< aTileInfo
.nTilesEmptyX
; nX
+= nMSBFactor
)
1775 if( !aTmpGraphic
.Draw( &rVDev
, aCurrPos
, aTileInfo
.aTileSizePixel
, pAttr
, nFlags
) )
1778 aCurrPos
.X() += aTileInfo
.aTileSizePixel
.Width();
1782 // rVDev.SetFillColor( COL_WHITE );
1783 rVDev
.SetFillColor();
1784 rVDev
.SetLineColor( Color( 255 * nExponent
/ nMSBFactor
, 255 - 255 * nExponent
/ nMSBFactor
, 128 - 255 * nExponent
/ nMSBFactor
) );
1785 rVDev
.DrawEllipse( Rectangle(aTileInfo
.aNextTileTopLeft
.X(), aTileInfo
.aTileTopLeft
.Y(),
1786 aTileInfo
.aNextTileTopLeft
.X() - 1 + (aTileInfo
.nTilesEmptyX
/nMSBFactor
)*aTileInfo
.aTileSizePixel
.Width(),
1787 aTileInfo
.aTileTopLeft
.Y() + aTileInfo
.aTileSizePixel
.Height() - 1) );
1790 // now fill one column from aTileInfo.aNextTileTopLeft.Y() all
1791 // the way to the bottom
1792 aCurrPos
.X() = aTileInfo
.aTileTopLeft
.X();
1793 aCurrPos
.Y() = aTileInfo
.aNextTileTopLeft
.Y();
1794 for( nY
=0; nY
< aTileInfo
.nTilesEmptyY
; nY
+= nMSBFactor
)
1796 if( !aTmpGraphic
.Draw( &rVDev
, aCurrPos
, aTileInfo
.aTileSizePixel
, pAttr
, nFlags
) )
1799 aCurrPos
.Y() += aTileInfo
.aTileSizePixel
.Height();
1803 rVDev
.DrawEllipse( Rectangle(aTileInfo
.aTileTopLeft
.X(), aTileInfo
.aNextTileTopLeft
.Y(),
1804 aTileInfo
.aTileTopLeft
.X() + aTileInfo
.aTileSizePixel
.Width() - 1,
1805 aTileInfo
.aNextTileTopLeft
.Y() - 1 + (aTileInfo
.nTilesEmptyY
/nMSBFactor
)*aTileInfo
.aTileSizePixel
.Height()) );
1810 // Thought that aTileInfo.aNextTileTopLeft tile has always
1811 // been drawn already, but that's wrong: typically,
1812 // _parts_ of that tile have been drawn, since the
1813 // previous level generated the tile there. But when
1814 // aTileInfo.aNextTileTopLeft!=aTileInfo.aTileTopLeft, the
1815 // difference between these two values is missing in the
1816 // lower right corner of this first tile. So, can do that
1818 bNoFirstTileDraw
= true;
1826 // calc number of original tiles in our drawing area without
1828 nRemainderTilesX
-= nNewRemainderX
;
1829 nRemainderTilesY
-= nNewRemainderY
;
1831 // fill tile info for calling method
1832 rTileInfo
.aTileTopLeft
= aTileInfo
.aNextTileTopLeft
;
1833 rTileInfo
.aNextTileTopLeft
= Point( rTileInfo
.aTileTopLeft
.X() + rTileSizePixel
.Width()*nRemainderTilesX
,
1834 rTileInfo
.aTileTopLeft
.Y() + rTileSizePixel
.Height()*nRemainderTilesY
);
1835 rTileInfo
.aTileSizePixel
= Size( rTileSizePixel
.Width()*nMSBFactor
*nExponent
,
1836 rTileSizePixel
.Height()*nMSBFactor
*nExponent
);
1837 rTileInfo
.nTilesEmptyX
= aTileInfo
.nTilesEmptyX
- nRemainderTilesX
;
1838 rTileInfo
.nTilesEmptyY
= aTileInfo
.nTilesEmptyY
- nRemainderTilesY
;
1840 // init output position
1841 aCurrPos
= aTileInfo
.aNextTileTopLeft
;
1843 // fill our drawing area. Fill possibly more, to create the next
1844 // bigger tile size -> see bitmap extraction above. This does no
1845 // harm, since everything right or below our actual area is
1846 // overdrawn by our caller. Just in case we're in the last level,
1847 // we don't draw beyond the right or bottom border.
1848 for( nY
=0; nY
< aTileInfo
.nTilesEmptyY
&& nY
< nExponent
*nMSBFactor
; nY
+= nMSBFactor
)
1850 aCurrPos
.X() = aTileInfo
.aNextTileTopLeft
.X();
1852 for( nX
=0; nX
< aTileInfo
.nTilesEmptyX
&& nX
< nExponent
*nMSBFactor
; nX
+= nMSBFactor
)
1854 if( bNoFirstTileDraw
)
1855 bNoFirstTileDraw
= false; // don't draw first tile position
1856 else if( !aTmpGraphic
.Draw( &rVDev
, aCurrPos
, aTileInfo
.aTileSizePixel
, pAttr
, nFlags
) )
1859 aCurrPos
.X() += aTileInfo
.aTileSizePixel
.Width();
1862 aCurrPos
.Y() += aTileInfo
.aTileSizePixel
.Height();
1866 // rVDev.SetFillColor( COL_WHITE );
1867 rVDev
.SetFillColor();
1868 rVDev
.SetLineColor( Color( 255 * nExponent
/ nMSBFactor
, 255 - 255 * nExponent
/ nMSBFactor
, 128 - 255 * nExponent
/ nMSBFactor
) );
1869 rVDev
.DrawRect( Rectangle((rTileInfo
.aTileTopLeft
.X())*rTileSizePixel
.Width(),
1870 (rTileInfo
.aTileTopLeft
.Y())*rTileSizePixel
.Height(),
1871 (rTileInfo
.aNextTileTopLeft
.X())*rTileSizePixel
.Width()-1,
1872 (rTileInfo
.aNextTileTopLeft
.Y())*rTileSizePixel
.Height()-1) );
1878 bool GraphicObject::ImplDrawTiled( OutputDevice
* pOut
, const Rectangle
& rArea
, const Size
& rSizePixel
,
1879 const Size
& rOffset
, const GraphicAttr
* pAttr
, GraphicManagerDrawFlags nFlags
, int nTileCacheSize1D
)
1881 // how many tiles to generate per recursion step
1882 enum{ SubdivisionExponent
=2 };
1884 const MapMode
aOutMapMode( pOut
->GetMapMode() );
1885 const MapMode
aMapMode( aOutMapMode
.GetMapUnit(), Point(), aOutMapMode
.GetScaleX(), aOutMapMode
.GetScaleY() );
1888 // #i42643# Casting to Int64, to avoid integer overflow for
1889 // huge-DPI output devices
1890 if( GetGraphic().GetType() == GRAPHIC_BITMAP
&&
1891 static_cast<sal_Int64
>(rSizePixel
.Width()) * rSizePixel
.Height() <
1892 static_cast<sal_Int64
>(nTileCacheSize1D
)*nTileCacheSize1D
)
1894 // First combine very small bitmaps into a larger tile
1897 ScopedVclPtrInstance
< VirtualDevice
> aVDev
;
1898 const int nNumTilesInCacheX( (nTileCacheSize1D
+ rSizePixel
.Width()-1) / rSizePixel
.Width() );
1899 const int nNumTilesInCacheY( (nTileCacheSize1D
+ rSizePixel
.Height()-1) / rSizePixel
.Height() );
1901 aVDev
->SetOutputSizePixel( Size( nNumTilesInCacheX
*rSizePixel
.Width(),
1902 nNumTilesInCacheY
*rSizePixel
.Height() ) );
1903 aVDev
->SetMapMode( aMapMode
);
1905 // draw bitmap content
1906 if( ImplRenderTempTile( *aVDev
.get(), SubdivisionExponent
, nNumTilesInCacheX
,
1907 nNumTilesInCacheY
, rSizePixel
, pAttr
, nFlags
) )
1909 BitmapEx
aTileBitmap( aVDev
->GetBitmap( Point(0,0), aVDev
->GetOutputSize() ) );
1911 // draw alpha content, if any
1912 if( IsTransparent() )
1914 GraphicObject aAlphaGraphic
;
1916 if( GetGraphic().IsAlpha() )
1917 aAlphaGraphic
.SetGraphic( GetGraphic().GetBitmapEx().GetAlpha().GetBitmap() );
1919 aAlphaGraphic
.SetGraphic( GetGraphic().GetBitmapEx().GetMask() );
1921 if( aAlphaGraphic
.ImplRenderTempTile( *aVDev
.get(), SubdivisionExponent
, nNumTilesInCacheX
,
1922 nNumTilesInCacheY
, rSizePixel
, pAttr
, nFlags
) )
1924 // Combine bitmap and alpha/mask
1925 if( GetGraphic().IsAlpha() )
1926 aTileBitmap
= BitmapEx( aTileBitmap
.GetBitmap(),
1927 AlphaMask( aVDev
->GetBitmap( Point(0,0), aVDev
->GetOutputSize() ) ) );
1929 aTileBitmap
= BitmapEx( aTileBitmap
.GetBitmap(),
1930 aVDev
->GetBitmap( Point(0,0), aVDev
->GetOutputSize() ).CreateMask( Color(COL_WHITE
) ) );
1934 // paint generated tile
1935 GraphicObject
aTmpGraphic( aTileBitmap
);
1936 bRet
= aTmpGraphic
.ImplDrawTiled( pOut
, rArea
,
1937 aTileBitmap
.GetSizePixel(),
1938 rOffset
, pAttr
, nFlags
, nTileCacheSize1D
);
1943 const Size
aOutOffset( pOut
->LogicToPixel( rOffset
, aOutMapMode
) );
1944 const Rectangle
aOutArea( pOut
->LogicToPixel( rArea
, aOutMapMode
) );
1946 // number of invisible (because out-of-area) tiles
1947 int nInvisibleTilesX
;
1948 int nInvisibleTilesY
;
1950 // round towards -infty for negative offset
1951 if( aOutOffset
.Width() < 0 )
1952 nInvisibleTilesX
= (aOutOffset
.Width() - rSizePixel
.Width() + 1) / rSizePixel
.Width();
1954 nInvisibleTilesX
= aOutOffset
.Width() / rSizePixel
.Width();
1956 // round towards -infty for negative offset
1957 if( aOutOffset
.Height() < 0 )
1958 nInvisibleTilesY
= (aOutOffset
.Height() - rSizePixel
.Height() + 1) / rSizePixel
.Height();
1960 nInvisibleTilesY
= aOutOffset
.Height() / rSizePixel
.Height();
1962 // origin from where to 'virtually' start drawing in pixel
1963 const Point
aOutOrigin( pOut
->LogicToPixel( Point( rArea
.Left() - rOffset
.Width(),
1964 rArea
.Top() - rOffset
.Height() ) ) );
1965 // position in pixel from where to really start output
1966 const Point
aOutStart( aOutOrigin
.X() + nInvisibleTilesX
*rSizePixel
.Width(),
1967 aOutOrigin
.Y() + nInvisibleTilesY
*rSizePixel
.Height() );
1969 pOut
->Push( PushFlags::CLIPREGION
);
1970 pOut
->IntersectClipRegion( rArea
);
1975 bRet
= ImplDrawTiled( *pOut
, aOutStart
,
1976 (aOutArea
.GetWidth() + aOutArea
.Left() - aOutStart
.X() + rSizePixel
.Width() - 1) / rSizePixel
.Width(),
1977 (aOutArea
.GetHeight() + aOutArea
.Top() - aOutStart
.Y() + rSizePixel
.Height() - 1) / rSizePixel
.Height(),
1978 rSizePixel
, pAttr
, nFlags
);
1986 bool GraphicObject::ImplDrawTiled( OutputDevice
& rOut
, const Point
& rPosPixel
,
1987 int nNumTilesX
, int nNumTilesY
,
1988 const Size
& rTileSizePixel
, const GraphicAttr
* pAttr
, GraphicManagerDrawFlags nFlags
)
1990 Point
aCurrPos( rPosPixel
);
1991 Size
aTileSizeLogic( rOut
.PixelToLogic( rTileSizePixel
) );
1994 // #107607# Use logical coordinates for metafile playing, too
1995 bool bDrawInPixel( rOut
.GetConnectMetaFile() == NULL
&& GRAPHIC_BITMAP
== GetType() );
1998 // #105229# Switch off mapping (converting to logic and back to
1999 // pixel might cause roundoff errors)
2000 bool bOldMap( rOut
.IsMapModeEnabled() );
2003 rOut
.EnableMapMode( false );
2005 for( nY
=0; nY
< nNumTilesY
; ++nY
)
2007 aCurrPos
.X() = rPosPixel
.X();
2009 for( nX
=0; nX
< nNumTilesX
; ++nX
)
2011 // #105229# work with pixel coordinates here, mapping is disabled!
2012 // #104004# don't disable mapping for metafile recordings
2013 // #108412# don't quit the loop if one draw fails
2015 // update return value. This method should return true, if
2016 // at least one of the looped Draws succeeded.
2017 bRet
|= Draw( &rOut
,
2018 bDrawInPixel
? aCurrPos
: rOut
.PixelToLogic( aCurrPos
),
2019 bDrawInPixel
? rTileSizePixel
: aTileSizeLogic
,
2022 aCurrPos
.X() += rTileSizePixel
.Width();
2025 aCurrPos
.Y() += rTileSizePixel
.Height();
2029 rOut
.EnableMapMode( bOldMap
);
2034 void GraphicObject::ImplTransformBitmap( BitmapEx
& rBmpEx
,
2035 const GraphicAttr
& rAttr
,
2036 const Size
& rCropLeftTop
,
2037 const Size
& rCropRightBottom
,
2038 const Rectangle
& rCropRect
,
2039 const Size
& rDstSize
,
2040 bool bEnlarge
) const
2042 // #107947# Extracted from svdograf.cxx
2044 // #104115# Crop the bitmap
2045 if( rAttr
.IsCropped() )
2047 rBmpEx
.Crop( rCropRect
);
2049 // #104115# Negative crop sizes mean: enlarge bitmap and pad
2051 rCropLeftTop
.Width() < 0 ||
2052 rCropLeftTop
.Height() < 0 ||
2053 rCropRightBottom
.Width() < 0 ||
2054 rCropRightBottom
.Height() < 0 ) )
2056 Size
aBmpSize( rBmpEx
.GetSizePixel() );
2057 sal_Int32
nPadLeft( rCropLeftTop
.Width() < 0 ? -rCropLeftTop
.Width() : 0 );
2058 sal_Int32
nPadTop( rCropLeftTop
.Height() < 0 ? -rCropLeftTop
.Height() : 0 );
2059 sal_Int32
nPadTotalWidth( aBmpSize
.Width() + nPadLeft
+ (rCropRightBottom
.Width() < 0 ? -rCropRightBottom
.Width() : 0) );
2060 sal_Int32
nPadTotalHeight( aBmpSize
.Height() + nPadTop
+ (rCropRightBottom
.Height() < 0 ? -rCropRightBottom
.Height() : 0) );
2064 if( rBmpEx
.IsTransparent() )
2066 if( rBmpEx
.IsAlpha() )
2067 aBmpEx2
= BitmapEx( rBmpEx
.GetBitmap(), rBmpEx
.GetAlpha() );
2069 aBmpEx2
= BitmapEx( rBmpEx
.GetBitmap(), rBmpEx
.GetMask() );
2073 // #104115# Generate mask bitmap and init to zero
2074 Bitmap
aMask( aBmpSize
, 1 );
2075 aMask
.Erase( Color(0,0,0) );
2077 // #104115# Always generate transparent bitmap, we need the border transparent
2078 aBmpEx2
= BitmapEx( rBmpEx
.GetBitmap(), aMask
);
2080 // #104115# Add opaque mask to source bitmap, otherwise the destination remains transparent
2084 aBmpEx2
.SetSizePixel( Size(nPadTotalWidth
, nPadTotalHeight
) );
2085 aBmpEx2
.Erase( Color(0xFF,0,0,0) );
2086 aBmpEx2
.CopyPixel( Rectangle( Point(nPadLeft
, nPadTop
), aBmpSize
), Rectangle( Point(0, 0), aBmpSize
), &rBmpEx
);
2091 const Size
aSizePixel( rBmpEx
.GetSizePixel() );
2093 if( rAttr
.GetRotation() != 0 && !IsAnimated() )
2095 if( aSizePixel
.Width() && aSizePixel
.Height() && rDstSize
.Width() && rDstSize
.Height() )
2097 double fSrcWH
= (double) aSizePixel
.Width() / aSizePixel
.Height();
2098 double fDstWH
= (double) rDstSize
.Width() / rDstSize
.Height();
2099 double fScaleX
= 1.0, fScaleY
= 1.0;
2101 // always choose scaling to shrink bitmap
2102 if( fSrcWH
< fDstWH
)
2103 fScaleY
= aSizePixel
.Width() / ( fDstWH
* aSizePixel
.Height() );
2105 fScaleX
= fDstWH
* aSizePixel
.Height() / aSizePixel
.Width();
2107 rBmpEx
.Scale( fScaleX
, fScaleY
);
2112 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */