Bump version to 5.0-14
[LibreOffice.git] / svtools / source / graphic / grfmgr2.cxx
blob03867e54cd22e2d4f426d52c588c31aacf866ee0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <cstdlib>
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>
38 // - defines -
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))
46 // - GraphicManager -
49 GraphicManager::GraphicManager( sal_uLong nCacheSize, sal_uLong nMaxObjCacheSize ) :
50 mnUsedSize(0),
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();
60 delete mpCache;
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 )
94 Point aPt( rPt );
95 Size aSz( rSz );
96 bool bRet = false;
98 rCached = false;
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;
117 if( nRot10 )
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 );
130 bRet = true;
133 if( !bRet )
135 // cached/direct drawing
136 if( !mpCache->DrawDisplayCacheObj( pOut, aPt, aSz, rObj, rAttr ) )
137 bRet = ImplDraw( pOut, aPt, aSz, rObj, rAttr, nFlags, rCached );
138 else
139 bRet = rCached = true;
143 return bRet;
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 );
170 return;
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 );
188 namespace
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
202 // to 200MB).
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
216 // is reached
217 GraphicObject* pObj = aCandidates[a];
218 if( pObj == pGraphicToIgnore )
220 continue;
222 if (std::find(maObjList.begin(), maObjList.end(), pObj) == maObjList.end())
224 // object has been deleted when swapping out another one
225 continue;
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();
249 bool bRet = false;
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 ) )
262 BitmapEx aDstBmpEx;
264 if( ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr, nFlags, &aDstBmpEx ) )
266 rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx );
267 bRet = true;
271 if( !bRet )
272 bRet = ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr, nFlags );
274 else
276 const GDIMetaFile& rSrcMtf = rGraphic.GetGDIMetaFile();
278 if( mpCache->IsDisplayCacheable( pOut, rPt, rSz, rObj, rAttr ) )
280 GDIMetaFile aDstMtf;
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).
289 BitmapEx aDstBmpEx;
291 if( ImplCreateOutput( pOut, rPt, rSz, aContainedBmpEx, rAttr, nFlags, &aDstBmpEx ) )
293 rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx );
294 bRet = true;
297 else
299 rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstMtf );
300 bRet = true;
305 if( !bRet )
307 const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) );
309 if( aGraphic.IsSupportedGraphic() )
311 aGraphic.Draw( pOut, rPt, rSz );
312 bRet = true;
318 return bRet;
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;
332 double fTmp;
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 ]);
342 double fRevScaleX;
343 double fRevScaleY;
345 bool scaleByAveraging = false;
346 int x,y;
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;
356 if( bHMirr )
357 fTmp = nTmpX - fTmp;
359 pMapIX[ x ] = MinMax( fTmp, 0, nTmp );
360 pMapFX[ x ] = (long) ( ( fTmp - pMapIX[ x ] ) * 1048576.0 );
362 scaleByAveraging |= fRevScaleX > 5.0/3.0;
364 else
366 if(aBitmapWidth == 1)
368 fRevScaleX = 1.0 / (double)( aUnrotatedWidth );
369 for ( x = 0; x < aUnrotatedWidth ; x++)
371 pMapIX[x] = 0;
372 pMapFX[x] = 0;
374 scaleByAveraging = true;
376 else
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;
394 if( bVMirr )
395 fTmp = nTmpY - fTmp;
397 pMapIY[ y ] = MinMax( fTmp, 0, nTmp );
398 pMapFY[ y ] = (long) ( ( fTmp - pMapIY[ y ] ) * 1048576.0 );
400 scaleByAveraging |= fRevScaleY > 5.0/3.0;
402 else
404 if(aBitmapHeight == 1)
406 fRevScaleY = 1.0 / (double)( aUnrotatedHeight);
407 for (y = 0; y < aUnrotatedHeight; ++y)
409 pMapIY[y] = 0;
410 pMapFY[y] = 0;
412 scaleByAveraging = true;
414 else
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() );
426 Bitmap aOutBmp;
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;
440 bool bRet = false;
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 );
460 if( pReadAccess )
462 aOutBmp = Bitmap( Size( aTargetWidth, aTargetHeight ), 24 );
463 pWriteAccess = aOutBmp.AcquireWriteAccess();
465 if( pWriteAccess )
467 BitmapColor aColRes;
469 if ( !scaleByAveraging )
471 if( pReadAccess->HasPalette() )
473 for( y = 0; y < aTargetHeight; y++ )
475 nSinY = pSinY[ y ];
476 nCosY = pCosY[ 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 );
509 else
511 BitmapColor aCol0, aCol1;
513 for( y = 0; y < aTargetHeight; y++ )
515 nSinY = pSinY[ y ];
516 nCosY = pCosY[ 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;
553 BitmapColor aColor;
554 BitmapColor aResultColor;
556 for( y = 0; y < aTargetHeight; y++ )
558 nSinY = pSinY[ y ];
559 nCosY = pCosY[ y ];
561 for( x = 0; x < aTargetWidth; x++ )
563 double aUnrotatedX = ( pCosX[ x ] - nSinY ) / 256.0;
564 double aUnrotatedY = ( pSinX[ x ] + nCosY ) / 256.0;
566 if ( bHMirr )
567 aUnrotatedX = aUnrotatedWidth - aUnrotatedX - 1;
568 if ( bVMirr )
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;
587 aCount = 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 ) );
595 else
596 aColor = pReadAccess->GetPixel( yIn, xIn );
598 aSumRed += aColor.GetRed();
599 aSumGreen += aColor.GetGreen();
600 aSumBlue += aColor.GetBlue();
602 aCount++;
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 );
617 bRet = true;
620 Bitmap::ReleaseAccess( pReadAccess );
623 // mask processing
624 if( bRet && ( rBmpEx.IsTransparent() || ( nRot10 != 0 && nRot10 != 900 && nRot10 != 1800 && nRot10 != 2700 ) ) )
626 bRet = false;
628 if( rBmpEx.IsAlpha() )
630 AlphaMask aAlpha( rBmpEx.GetAlpha() );
631 AlphaMask aOutAlpha;
633 pReadAccess = aAlpha.AcquireReadAccess();
635 if( pReadAccess )
637 aOutAlpha = AlphaMask( Size( aTargetWidth, aTargetHeight ) );
638 pWriteAccess = aOutAlpha.AcquireWriteAccess();
640 if( pWriteAccess )
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 );
677 else
678 *pLineW++ = 255;
682 else // scaleByAveraging
684 const BitmapColor aTrans( pWriteAccess->GetBestMatchingColor( Color( COL_WHITE ) ) );
685 BitmapColor aResultColor( 0 );
686 double aSum, aCount;
688 for( y = 0; y < aTargetHeight; y++ )
690 nSinY = pSinY[ y ];
691 nCosY = pCosY[ y ];
693 for( x = 0; x < aTargetWidth; x++ )
696 double aUnrotatedX = ( pCosX[ x ] - nSinY ) / 256.0;
697 double aUnrotatedY = ( pSinX[ x ] + nCosY ) / 256.0;
699 if ( bHMirr )
700 aUnrotatedX = aUnrotatedWidth - aUnrotatedX - 1;
701 if ( bVMirr )
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);
719 aSum = 0.0;
720 aCount = 0;
722 for (int yIn = yStart; yIn <= yEnd; yIn++)
724 for (int xIn = xStart; xIn <= xEnd; xIn++)
726 aSum += pReadAccess->GetPixel( yIn, xIn ).GetIndex();
727 aCount++;
730 aResultColor.SetIndex( MinMax( aSum / aCount, 0, 255) );
731 pWriteAccess->SetPixel( y, x, aResultColor );
733 else
735 pWriteAccess->SetPixel( y, x, aTrans );
741 else
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 );
771 else
772 pWriteAccess->SetPixel( nY, nX, aTrans );
777 aOutAlpha.ReleaseAccess( pWriteAccess );
778 bRet = true;
781 aAlpha.ReleaseAccess( pReadAccess );
784 if( bRet )
785 rOutBmpEx = BitmapEx( aOutBmp, aOutAlpha );
787 else
789 Bitmap aOutMsk( Size( aTargetWidth, aTargetHeight ), 1 );
790 pWriteAccess = aOutMsk.AcquireWriteAccess();
792 if( pWriteAccess )
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 ]);
803 BitmapColor aTestB;
805 if( pMAcc )
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 );
816 // do mask rotation
817 for( nY = 0; nY < aTargetHeight; nY++ )
819 nSinY = pSinY[ nY ];
820 nCosY = pCosY[ 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 ) )
830 if( pMAcc )
832 if( pMAcc->GetPixel( pMapLY[ nUnRotY ], pMapLX[ nUnRotX ] ) == aTestB )
833 pWriteAccess->SetPixel( nY, nX, aB );
834 else
835 pWriteAccess->SetPixel( nY, nX, aW );
837 else
838 pWriteAccess->SetPixel( nY, nX, aB );
840 else
841 pWriteAccess->SetPixel( nY, nX, aW );
845 pMapLX.reset();
846 pMapLY.reset();
848 if( pMAcc )
849 Bitmap::ReleaseAccess( pMAcc );
851 bRet = true;
854 Bitmap::ReleaseAccess( pWriteAccess );
857 if( bRet )
858 rOutBmpEx = BitmapEx( aOutBmp, aOutMsk );
861 if( !bRet )
862 rOutBmpEx = aOutBmp;
864 else
865 rOutBmpEx = aOutBmp;
867 return bRet;
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;
878 Size aOutputSizePix;
879 Point aUnrotatedPointPix( pOutputDevice->LogicToPixel( rPoint ) );
880 Size aUnrotatedSizePix( pOutputDevice->LogicToPixel( rSize ) );
882 bool bRet = false;
884 if( nRot10 )
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() );
892 else
894 aOutputPointPix = aUnrotatedPointPix;
895 aOutputSizePix = aUnrotatedSizePix;
898 if( aUnrotatedSizePix.Width() && aUnrotatedSizePix.Height() )
900 BitmapEx aBmpEx( rBitmapEx );
901 BitmapEx aOutBmpEx;
902 Point aOutPoint;
903 Size aOutSize;
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
912 if( !pBmpEx )
914 Point aPt;
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();
936 else
938 nStartX = -1L; // invalid
941 else
943 aOutPoint = pOutputDevice->PixelToLogic( aOutputPointPix );
944 aOutSize = pOutputDevice->PixelToLogic( aOutputSizePix );
945 nStartX = nStartY = 0;
946 nEndX = aOutputSizePix.Width() - 1L;
947 nEndY = aOutputSizePix.Height() - 1L;
950 // do transformation
951 if( nStartX >= 0L )
953 const bool bSimple = ( 1 == nW || 1 == nH );
955 if( nRot10 )
957 if( bSimple )
959 bRet = ( aOutBmpEx = aBmpEx ).Scale( aUnrotatedSizePix );
961 if( bRet )
962 aOutBmpEx.Rotate( nRot10, COL_TRANSPARENT );
964 else
966 bRet = ImplCreateRotatedScaled( aBmpEx, rAttributes,
967 nRot10, aUnrotatedSizePix,
968 nStartX, nEndX, nStartY, nEndY,
969 aOutBmpEx );
972 else
974 if( !bHMirr && !bVMirr && aOutputSizePix == rBmpSzPix )
976 aOutPoint = pOutputDevice->PixelToLogic( aOutputPointPix );
977 aOutSize = pOutputDevice->PixelToLogic( aOutputSizePix );
978 aOutBmpEx = aBmpEx;
979 bRet = true;
981 else
983 if( bSimple )
985 bRet = ( aOutBmpEx = aBmpEx ).Scale( Size( nEndX - nStartX + 1, nEndY - nStartY + 1 ) );
987 else
989 bRet = ImplCreateRotatedScaled( aBmpEx, rAttributes,
990 nRot10, aUnrotatedSizePix,
991 nStartX, nEndX, nStartY, nEndY,
992 aOutBmpEx );
997 if( bRet )
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 );
1009 // Create output
1010 if( bRet )
1012 if( !pBmpEx )
1013 pOutputDevice->DrawBitmapEx( aOutPoint, aOutSize, aOutBmpEx );
1014 else
1016 if( !rAttributes.IsTransparent() && !aOutBmpEx.IsAlpha() )
1017 aOutBmpEx = BitmapEx( aOutBmpEx.GetBitmap().CreateDisplayBitmap( pOutputDevice ), aOutBmpEx.GetMask() );
1019 pOutputDevice->DrawBitmapEx( aOutPoint, aOutSize, *pBmpEx = aOutBmpEx );
1024 return bRet;
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,
1031 Point rSrcPoint,
1032 Size rSrcSize,
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.
1039 BitmapEx aBmpEx;
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;
1047 return aBmpEx;
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
1066 else
1067 { // fall back to mtf rendering
1068 o_rbNonBitmapActionEncountered = true;
1069 return aBmpEx;
1073 aBmpEx = rBmpEx;
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,
1081 rSrcSize );
1082 aBmpEx.Crop( aCropRect );
1085 return aBmpEx;
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() );
1095 rOutMtf = rMtf;
1097 // Count bitmap actions, and flag actions that paint, but
1098 // are no bitmaps.
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.
1124 sal_uInt32 nCurPos;
1125 MetaAction* pAct;
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
1149 // output)
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:
1173 break;
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()),
1183 Point(), Size(),
1184 pOut->LogicToPixel( pAction->GetPoint(),
1185 rPrefMapMode ),
1186 pAction->GetBitmap().GetSizePixel(),
1187 rSizePix,
1188 bNonBitmapActionEncountered );
1190 ++nNumBitmaps;
1191 break;
1193 case MetaActionType::BMPSCALE:
1194 if( !nNumBitmaps && !bNonBitmapActionEncountered )
1196 MetaBmpScaleAction* pAction = static_cast<MetaBmpScaleAction*>(pAct);
1198 rOutBmpEx = checkMetadataBitmap(
1199 BitmapEx( pAction->GetBitmap()),
1200 Point(), Size(),
1201 pOut->LogicToPixel( pAction->GetPoint(),
1202 rPrefMapMode ),
1203 pOut->LogicToPixel( pAction->GetSize(),
1204 rPrefMapMode ),
1205 rSizePix,
1206 bNonBitmapActionEncountered );
1208 ++nNumBitmaps;
1209 break;
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(),
1221 rPrefMapMode ),
1222 pOut->LogicToPixel( pAction->GetDestSize(),
1223 rPrefMapMode ),
1224 rSizePix,
1225 bNonBitmapActionEncountered );
1227 ++nNumBitmaps;
1228 break;
1230 case MetaActionType::BMPEX:
1231 if( !nNumBitmaps && !bNonBitmapActionEncountered )
1233 MetaBmpExAction* pAction = static_cast<MetaBmpExAction*>(pAct);
1235 rOutBmpEx = checkMetadataBitmap(
1236 pAction->GetBitmapEx(),
1237 Point(), Size(),
1238 pOut->LogicToPixel( pAction->GetPoint(),
1239 rPrefMapMode ),
1240 pAction->GetBitmapEx().GetSizePixel(),
1241 rSizePix,
1242 bNonBitmapActionEncountered );
1244 ++nNumBitmaps;
1245 break;
1247 case MetaActionType::BMPEXSCALE:
1248 if( !nNumBitmaps && !bNonBitmapActionEncountered )
1250 MetaBmpExScaleAction* pAction = static_cast<MetaBmpExScaleAction*>(pAct);
1252 rOutBmpEx = checkMetadataBitmap(
1253 pAction->GetBitmapEx(),
1254 Point(), Size(),
1255 pOut->LogicToPixel( pAction->GetPoint(),
1256 rPrefMapMode ),
1257 pOut->LogicToPixel( pAction->GetSize(),
1258 rPrefMapMode ),
1259 rSizePix,
1260 bNonBitmapActionEncountered );
1262 ++nNumBitmaps;
1263 break;
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(),
1274 rPrefMapMode ),
1275 pOut->LogicToPixel( pAction->GetDestSize(),
1276 rPrefMapMode ),
1277 rSizePix,
1278 bNonBitmapActionEncountered );
1280 ++nNumBitmaps;
1281 break;
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 )
1287 break;
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
1347 // output
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
1361 default:
1362 bNonBitmapActionEncountered = true;
1363 break;
1365 if ( pModAct )
1367 MetaAction* pDeleteAction = rOutMtf.ReplaceAction( pModAct, nCurPos );
1368 assert(pDeleteAction);
1369 pDeleteAction->Delete();
1371 else
1373 if( pAct->GetRefCount() > 1 )
1375 MetaAction* pDeleteAction = rOutMtf.ReplaceAction( pModAct = pAct->Clone(), nCurPos );
1376 assert(pDeleteAction);
1377 pDeleteAction->Delete();
1379 else
1380 pModAct = pAct;
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();
1397 return true;
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 );
1410 break;
1412 case( GRAPHICDRAWMODE_GREYS ):
1413 rBmpEx.Convert( BMP_CONVERSION_8BIT_GREYS );
1414 break;
1416 case( GRAPHICDRAWMODE_WATERMARK ):
1418 aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
1419 aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
1421 break;
1423 default:
1424 break;
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() )
1447 AlphaMask aAlpha;
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 );
1457 else
1459 aAlpha = rBmpEx.GetAlpha();
1460 BitmapWriteAccess* pA = aAlpha.AcquireWriteAccess();
1462 if( pA )
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 );
1480 else
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 );
1513 break;
1515 case( GRAPHICDRAWMODE_GREYS ):
1516 rMtf.Convert( MTF_CONVERSION_8BIT_GREYS );
1517 break;
1519 case( GRAPHICDRAWMODE_WATERMARK ):
1521 aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
1522 aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
1524 break;
1526 default:
1527 break;
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 );
1564 break;
1566 case( GRAPHICDRAWMODE_GREYS ):
1567 rAnimation.Convert( BMP_CONVERSION_8BIT_GREYS );
1568 break;
1570 case( GRAPHICDRAWMODE_WATERMARK ):
1572 aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
1573 aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
1575 break;
1577 default:
1578 break;
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 );
1610 Size aOutSz( rSz );
1612 if( nRot10 )
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();
1629 pOut->Pop();
1632 struct ImplTileInfo
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
1638 // level's tile
1639 Size aTileSizePixel; // size of the generated tile (might
1640 // differ from
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 )
1667 return false;
1669 // determine MSB factor
1670 int nMSBFactor( 1 );
1671 while( nNumTilesX / nMSBFactor != 0 ||
1672 nNumTilesY / nMSBFactor != 0 )
1674 nMSBFactor *= nExponent;
1677 // one less
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 );
1692 return bRet;
1695 // define for debug drawings
1696 //#define DBG_TEST
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
1701 // representation.
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
1715 // there.
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
1723 // been generated
1724 ImplTileInfo aTileInfo;
1726 // current output position while drawing
1727 Point aCurrPos;
1728 int nX, nY;
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:
1752 // x0000
1753 // 0
1754 // 0
1755 // 0
1756 // 0
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 ) )
1776 return false;
1778 aCurrPos.X() += aTileInfo.aTileSizePixel.Width();
1781 #ifdef DBG_TEST
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) );
1788 #endif
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 ) )
1797 return false;
1799 aCurrPos.Y() += aTileInfo.aTileSizePixel.Height();
1802 #ifdef DBG_TEST
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()) );
1806 #endif
1808 else
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
1817 // only here.
1818 bNoFirstTileDraw = true;
1821 else
1823 return false;
1826 // calc number of original tiles in our drawing area without
1827 // remainder
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 ) )
1857 return false;
1859 aCurrPos.X() += aTileInfo.aTileSizePixel.Width();
1862 aCurrPos.Y() += aTileInfo.aTileSizePixel.Height();
1865 #ifdef DBG_TEST
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) );
1873 #endif
1875 return true;
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() );
1886 bool bRet( false );
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() );
1918 else
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() ) ) );
1928 else
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 );
1941 else
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();
1953 else
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();
1959 else
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 );
1972 // Paint all tiles
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 );
1980 pOut->Pop();
1983 return bRet;
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 ) );
1992 int nX, nY;
1994 // #107607# Use logical coordinates for metafile playing, too
1995 bool bDrawInPixel( rOut.GetConnectMetaFile() == NULL && GRAPHIC_BITMAP == GetType() );
1996 bool bRet = false;
1998 // #105229# Switch off mapping (converting to logic and back to
1999 // pixel might cause roundoff errors)
2000 bool bOldMap( rOut.IsMapModeEnabled() );
2002 if( bDrawInPixel )
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,
2020 pAttr, nFlags );
2022 aCurrPos.X() += rTileSizePixel.Width();
2025 aCurrPos.Y() += rTileSizePixel.Height();
2028 if( bDrawInPixel )
2029 rOut.EnableMapMode( bOldMap );
2031 return bRet;
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
2050 if( bEnlarge && (
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) );
2062 BitmapEx aBmpEx2;
2064 if( rBmpEx.IsTransparent() )
2066 if( rBmpEx.IsAlpha() )
2067 aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetAlpha() );
2068 else
2069 aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetMask() );
2071 else
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
2081 rBmpEx = aBmpEx2;
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 );
2087 rBmpEx = aBmpEx2;
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() );
2104 else
2105 fScaleX = fDstWH * aSizePixel.Height() / aSizePixel.Width();
2107 rBmpEx.Scale( fScaleX, fScaleY );
2112 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */