xref: /aoo42x/main/svtools/source/graphic/grfmgr2.cxx (revision cb24a8fa)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svtools.hxx"
26 
27 #include <vos/macros.hxx>
28 #include <vcl/bmpacc.hxx>
29 #include <tools/poly.hxx>
30 #include <vcl/outdev.hxx>
31 #include <vcl/window.hxx>
32 #include <vcl/gdimtf.hxx>
33 #include <vcl/metaact.hxx>
34 #include <vcl/metric.hxx>
35 #include <vcl/animate.hxx>
36 #include <vcl/alpha.hxx>
37 #include <vcl/virdev.hxx>
38 #include "grfcache.hxx"
39 #include <svtools/grfmgr.hxx>
40 
41 // -----------
42 // - defines -
43 // -----------
44 
45 #define MAX_PRINTER_EXT				1024
46 #define MAP( cVal0, cVal1, nFrac )	((sal_uInt8)((((long)(cVal0)<<20L)+nFrac*((long)(cVal1)-(cVal0)))>>20L))
47 #define WATERMARK_LUM_OFFSET		50
48 #define WATERMARK_CON_OFFSET		-70
49 
50 // -----------
51 // - helpers -
52 // -----------
53 
54 namespace {
55 
56 void muckWithBitmap( const Point&    rDestPoint,
57                      const Size&     rDestSize,
58                      const Size&     rRefSize,
59                      bool&           o_rbNonBitmapActionEncountered )
60 {
61     const Point aEmptyPoint;
62 
63     if( aEmptyPoint != rDestPoint ||
64         rDestSize != rRefSize )
65     {
66         // non-fullscale, or offsetted bmp -> fallback to mtf
67         // rendering
68         o_rbNonBitmapActionEncountered = true;
69     }
70 }
71 
72 BitmapEx muckWithBitmap( const BitmapEx& rBmpEx,
73                          const Point&    rSrcPoint,
74                          const Size&     rSrcSize,
75                          const Point&    rDestPoint,
76                          const Size&     rDestSize,
77                          const Size&     rRefSize,
78                          bool&           o_rbNonBitmapActionEncountered )
79 {
80     BitmapEx aBmpEx;
81 
82     muckWithBitmap(rDestPoint,
83                    rDestSize,
84                    rRefSize,
85                    o_rbNonBitmapActionEncountered);
86 
87     if( o_rbNonBitmapActionEncountered )
88         return aBmpEx;
89 
90     aBmpEx = rBmpEx;
91 
92     if( (rSrcPoint.X() != 0 && rSrcPoint.Y() != 0) ||
93         rSrcSize != rBmpEx.GetSizePixel() )
94     {
95         // crop bitmap to given source rectangle (no
96         // need to copy and convert the whole bitmap)
97         const Rectangle aCropRect( rSrcPoint,
98                                    rSrcSize );
99         aBmpEx.Crop( aCropRect );
100     }
101 
102     return aBmpEx;
103 }
104 
105 } // namespace {
106 
107 
108 // ------------------
109 // - GraphicManager -
110 // ------------------
111 
112 GraphicManager::GraphicManager( sal_uLong nCacheSize, sal_uLong nMaxObjCacheSize ) :
113 		mpCache( new GraphicCache( *this, nCacheSize, nMaxObjCacheSize ) )
114 {
115 }
116 
117 // -----------------------------------------------------------------------------
118 
119 GraphicManager::~GraphicManager()
120 {
121 	for( void* pObj = maObjList.First(); pObj; pObj = maObjList.Next() )
122 		( (GraphicObject*) pObj )->GraphicManagerDestroyed();
123 
124 	delete mpCache;
125 }
126 
127 // -----------------------------------------------------------------------------
128 
129 void GraphicManager::SetMaxCacheSize( sal_uLong nNewCacheSize )
130 {
131 	mpCache->SetMaxDisplayCacheSize( nNewCacheSize );
132 }
133 
134 // -----------------------------------------------------------------------------
135 
136 sal_uLong GraphicManager::GetMaxCacheSize() const
137 {
138 	return mpCache->GetMaxDisplayCacheSize();
139 }
140 
141 // -----------------------------------------------------------------------------
142 
143 void GraphicManager::SetMaxObjCacheSize( sal_uLong nNewMaxObjSize, sal_Bool bDestroyGreaterCached )
144 {
145 	mpCache->SetMaxObjDisplayCacheSize( nNewMaxObjSize, bDestroyGreaterCached );
146 }
147 
148 // -----------------------------------------------------------------------------
149 
150 sal_uLong GraphicManager::GetMaxObjCacheSize() const
151 {
152 	return mpCache->GetMaxObjDisplayCacheSize();
153 }
154 
155 // -----------------------------------------------------------------------------
156 
157 sal_uLong GraphicManager::GetUsedCacheSize() const
158 {
159 	return mpCache->GetUsedDisplayCacheSize();
160 }
161 
162 // -----------------------------------------------------------------------------
163 
164 sal_uLong GraphicManager::GetFreeCacheSize() const
165 {
166 	return mpCache->GetFreeDisplayCacheSize();
167 }
168 
169 // -----------------------------------------------------------------------------
170 
171 void GraphicManager::SetCacheTimeout( sal_uLong nTimeoutSeconds )
172 {
173 	mpCache->SetCacheTimeout( nTimeoutSeconds );
174 }
175 
176 // -----------------------------------------------------------------------------
177 
178 sal_uLong GraphicManager::GetCacheTimeout() const
179 {
180 	return mpCache->GetCacheTimeout();
181 }
182 
183 // -----------------------------------------------------------------------------
184 
185 void GraphicManager::ClearCache()
186 {
187 	mpCache->ClearDisplayCache();
188 }
189 
190 // -----------------------------------------------------------------------------
191 
192 void GraphicManager::ReleaseFromCache( const GraphicObject& /*rObj*/ )
193 {
194 	// !!!
195 }
196 
197 // -----------------------------------------------------------------------------
198 
199 sal_Bool GraphicManager::IsInCache( OutputDevice* pOut, const Point& rPt,
200 									const Size& rSz, const GraphicObject& rObj,
201 									const GraphicAttr& rAttr ) const
202 {
203 	return mpCache->IsInDisplayCache( pOut, rPt, rSz, rObj, rAttr );
204 }
205 
206 // -----------------------------------------------------------------------------
207 
208 sal_Bool GraphicManager::DrawObj( OutputDevice* pOut, const Point& rPt, const Size& rSz,
209 							  GraphicObject& rObj, const GraphicAttr& rAttr,
210 							  const sal_uLong nFlags, sal_Bool& rCached )
211 {
212 	Point   aPt( rPt );
213 	Size	aSz( rSz );
214 	sal_Bool	bRet = sal_False;
215 
216 	rCached = sal_False;
217 
218 	if( ( rObj.GetType() == GRAPHIC_BITMAP ) || ( rObj.GetType() == GRAPHIC_GDIMETAFILE ) )
219 	{
220 		// create output and fill cache
221 		const Size aOutSize( pOut->GetOutputSizePixel() );
222 
223 		if( rObj.IsAnimated() || ( pOut->GetOutDevType() == OUTDEV_PRINTER ) ||
224 		    ( !( nFlags & GRFMGR_DRAW_NO_SUBSTITUTE ) &&
225 		      ( ( nFlags & GRFMGR_DRAW_SUBSTITUTE ) ||
226 		        !( nFlags & GRFMGR_DRAW_CACHED ) ||
227 		        ( pOut->GetConnectMetaFile() && !pOut->IsOutputEnabled() ) ) ) )
228 		{
229 			// simple output of transformed graphic
230 			const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) );
231 
232 			if( aGraphic.IsSupportedGraphic() )
233 			{
234 				const sal_uInt16 nRot10 = rAttr.GetRotation() % 3600;
235 
236 				if( nRot10 )
237 				{
238 					Polygon aPoly( Rectangle( aPt, aSz ) );
239 
240 					aPoly.Rotate( aPt, nRot10 );
241 					const Rectangle aRotBoundRect( aPoly.GetBoundRect() );
242 					aPt = aRotBoundRect.TopLeft();
243 					aSz = aRotBoundRect.GetSize();
244 				}
245 
246 				aGraphic.Draw( pOut, aPt, aSz );
247 			}
248 
249 			bRet = sal_True;
250 		}
251 
252 		if( !bRet )
253 		{
254 			// cached/direct drawing
255 			if( !mpCache->DrawDisplayCacheObj( pOut, aPt, aSz, rObj, rAttr ) )
256 				bRet = ImplDraw( pOut, aPt, aSz, rObj, rAttr, nFlags, rCached );
257 			else
258 				bRet = rCached = sal_True;
259 		}
260 	}
261 
262 	return bRet;
263 }
264 
265 // -----------------------------------------------------------------------------
266 
267 void GraphicManager::ImplRegisterObj( const GraphicObject& rObj, Graphic& rSubstitute,
268                                       const ByteString* pID, const GraphicObject* pCopyObj )
269 {
270     maObjList.Insert( (void*) &rObj, LIST_APPEND );
271 	mpCache->AddGraphicObject( rObj, rSubstitute, pID, pCopyObj );
272 }
273 
274 // -----------------------------------------------------------------------------
275 
276 void GraphicManager::ImplUnregisterObj( const GraphicObject& rObj )
277 {
278 	mpCache->ReleaseGraphicObject( rObj );
279 	maObjList.Remove( (void*) &rObj );
280 }
281 
282 // -----------------------------------------------------------------------------
283 
284 void GraphicManager::ImplGraphicObjectWasSwappedOut( const GraphicObject& rObj )
285 {
286 	mpCache->GraphicObjectWasSwappedOut( rObj );
287 }
288 
289 // -----------------------------------------------------------------------------
290 
291 ByteString GraphicManager::ImplGetUniqueID( const GraphicObject& rObj ) const
292 {
293 	return mpCache->GetUniqueID( rObj );
294 }
295 
296 // -----------------------------------------------------------------------------
297 
298 namespace
299 {
300     struct simpleSortByDataChangeTimeStamp
301     {
302         bool operator() (GraphicObject* p1, GraphicObject* p2) const
303         {
304             return p1->GetDataChangeTimeStamp() < p2->GetDataChangeTimeStamp();
305         }
306     };
307 } // end of anonymous namespace
308 
309 void GraphicManager::ImplCheckSizeOfSwappedInGraphics()
310 {
311     // only necessary for 32bit systems
312     if(SAL_TYPES_SIZEOFPOINTER <= 4)
313     {
314         // get the currently used memory footprint of all swapped in bitmap graphics
315         // of this graphic manager. Remember candidates in a vector. The size in bytes is
316         // already available, thus this loop is not expensive to execute
317         sal_uLong nUsedSize(0);
318         GraphicObject* pObj = 0;
319         std::vector< GraphicObject* > aCandidates;
320 
321         for(pObj = (GraphicObject*)maObjList.First(); pObj; pObj = (GraphicObject*)maObjList.Next())
322         {
323             if(pObj->meType == GRAPHIC_BITMAP && !pObj->IsSwappedOut() && pObj->GetSizeBytes())
324             {
325                 aCandidates.push_back(pObj);
326                 nUsedSize += pObj->GetSizeBytes();
327             }
328         }
329 
330         // detect maximum allowed memory footprint. Use the user-settings of MaxCacheSize (defaulted
331         // to 20MB) and add a decent multiplicator (expecrimented to find one). Limit to
332         // a useful maximum for 32Bit address space
333 
334         // default is 20MB, so allow 200MB initially
335         static sal_uLong aMultiplicator(10);
336 
337         // max at 500MB; I experimented with 800 for debug and 750 for non-debug settings (pics start
338         // missing when AOO reaches a mem footprint of 1.5GB) but some secure left over space for
339         // app activity is needed
340         static sal_uLong aMaxSize32Bit(500 * 1024 * 1024);
341 
342         // calc max allowed cache size
343         const sal_uLong nMaxCacheSize(::std::min(GetMaxCacheSize() * aMultiplicator, aMaxSize32Bit));
344 
345         if(nUsedSize >= nMaxCacheSize && !aCandidates.empty())
346         {
347             // if we use more currently, sort by last DataChangeTimeStamp
348             // sort by DataChangeTimeStamp so that the oldest get removed first
349             ::std::sort(aCandidates.begin(), aCandidates.end(), simpleSortByDataChangeTimeStamp());
350 
351             for(sal_uInt32 a(0); nUsedSize >= nMaxCacheSize && a < aCandidates.size(); a++)
352             {
353                 // swap out until we have no more or the goal to use less than nMaxCacheSize
354                 // is reached
355                 pObj = aCandidates[a];
356                 const sal_uLong nSizeBytes(pObj->GetSizeBytes());
357 
358                 // do not swap out when we have less than 16KB data objects
359                 if(nSizeBytes >= (16 * 1024))
360                 {
361                     pObj->FireSwapOutRequest();
362                     nUsedSize = (nSizeBytes < nUsedSize) ? nUsedSize - nSizeBytes : 0;
363                 }
364             }
365         }
366     }
367 }
368 
369 // -----------------------------------------------------------------------------
370 
371 sal_Bool GraphicManager::ImplFillSwappedGraphicObject( const GraphicObject& rObj, Graphic& rSubstitute )
372 {
373     return mpCache->FillSwappedGraphicObject(rObj, rSubstitute);
374 }
375 
376 // -----------------------------------------------------------------------------
377 
378 void GraphicManager::ImplGraphicObjectWasSwappedIn( const GraphicObject& rObj )
379 {
380     mpCache->GraphicObjectWasSwappedIn( rObj );
381 }
382 
383 // -----------------------------------------------------------------------------
384 
385 sal_Bool GraphicManager::ImplDraw( OutputDevice* pOut, const Point& rPt,
386 							   const Size& rSz, GraphicObject& rObj,
387 							   const GraphicAttr& rAttr,
388                                const sal_uLong nFlags, sal_Bool& rCached )
389 {
390 	const Graphic&	rGraphic = rObj.GetGraphic();
391 	sal_Bool			bRet = sal_False;
392 
393 	if( rGraphic.IsSupportedGraphic() && !rGraphic.IsSwapOut() )
394 	{
395 		if( GRAPHIC_BITMAP == rGraphic.GetType() )
396 		{
397 			const BitmapEx aSrcBmpEx( rGraphic.GetBitmapEx() );
398 
399             // #i46805# No point in caching a bitmap that is rendered
400             // via RectFill on the OutDev
401 			if( !(pOut->GetDrawMode() & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP )) &&
402                 mpCache->IsDisplayCacheable( pOut, rPt, rSz, rObj, rAttr ) )
403 			{
404 				BitmapEx aDstBmpEx;
405 
406 				if( ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr, nFlags, &aDstBmpEx ) )
407 				{
408 					rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx );
409 					bRet = sal_True;
410 				}
411 			}
412 
413 			if( !bRet )
414 				bRet = ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr, nFlags );
415 		}
416 		else
417 		{
418 			const GDIMetaFile& rSrcMtf = rGraphic.GetGDIMetaFile();
419 
420 			if( mpCache->IsDisplayCacheable( pOut, rPt, rSz, rObj, rAttr ) )
421 			{
422 				GDIMetaFile aDstMtf;
423                 BitmapEx    aContainedBmpEx;
424 
425 				if( ImplCreateOutput( pOut, rPt, rSz, rSrcMtf, rAttr, nFlags, aDstMtf, aContainedBmpEx ) )
426 				{
427                     if( !!aContainedBmpEx )
428                     {
429                         // #117889# Use bitmap output method, if
430                         // metafile basically contains only a single
431                         // bitmap
432                         BitmapEx aDstBmpEx;
433 
434                         if( ImplCreateOutput( pOut, rPt, rSz, aContainedBmpEx, rAttr, nFlags, &aDstBmpEx ) )
435                         {
436                             rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx );
437                             bRet = sal_True;
438                         }
439                     }
440                     else
441                     {
442                         rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstMtf );
443                         bRet = sal_True;
444                     }
445 				}
446 			}
447 
448 			if( !bRet )
449 			{
450 				const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) );
451 
452 				if( aGraphic.IsSupportedGraphic() )
453 				{
454 					aGraphic.Draw( pOut, rPt, rSz );
455 					bRet = sal_True;
456 				}
457 			}
458 		}
459 	}
460 
461 	return bRet;
462 }
463 
464 // -----------------------------------------------------------------------------
465 
466 sal_Bool GraphicManager::ImplCreateOutput( OutputDevice* pOut,
467                                        const Point& rPt, const Size& rSz,
468 									   const BitmapEx& rBmpEx, const GraphicAttr& rAttr,
469 									   const sal_uLong nFlags, BitmapEx* pBmpEx )
470 {
471 	sal_uInt16	nRot10 = rAttr.GetRotation() % 3600;
472 	Point	aOutPtPix;
473 	Size	aOutSzPix;
474 	Size	aUnrotatedSzPix( pOut->LogicToPixel( rSz ) );
475 	sal_Bool	bRet = sal_False;
476 
477 	if( nRot10 )
478 	{
479 		Polygon aPoly( Rectangle( rPt, rSz ) );
480 
481 		aPoly.Rotate( rPt, nRot10 );
482 		const Rectangle aRotBoundRect( aPoly.GetBoundRect() );
483 		aOutPtPix = pOut->LogicToPixel( aRotBoundRect.TopLeft() );
484 		aOutSzPix = pOut->LogicToPixel( aRotBoundRect.GetSize() );
485 	}
486 	else
487 	{
488 		aOutPtPix = pOut->LogicToPixel( rPt );
489 		aOutSzPix = aUnrotatedSzPix;
490 	}
491 
492 	if( aUnrotatedSzPix.Width() && aUnrotatedSzPix.Height() )
493 	{
494 		BitmapEx		aBmpEx( rBmpEx );
495 		BitmapEx		aOutBmpEx;
496 		Point			aOutPt;
497 		Size			aOutSz;
498 		const Size&		rBmpSzPix = rBmpEx.GetSizePixel();
499 		const long		nW = rBmpSzPix.Width();
500 		const long		nH = rBmpSzPix.Height();
501 		const long		nNewW = aUnrotatedSzPix.Width();
502 		const long		nNewH = aUnrotatedSzPix.Height();
503 		double			fTmp;
504 		long*			pMapIX = new long[ nNewW ];
505 		long*			pMapFX = new long[ nNewW ];
506 		long*			pMapIY = new long[ nNewH ];
507 		long*			pMapFY = new long[ nNewH ];
508 		long			nStartX = -1, nStartY = -1, nEndX = -1, nEndY = -1;
509 		long			nX, nY, nTmp, nTmpX, nTmpY;
510 		sal_Bool			bHMirr = ( rAttr.GetMirrorFlags() & BMP_MIRROR_HORZ ) != 0;
511 		sal_Bool			bVMirr = ( rAttr.GetMirrorFlags() & BMP_MIRROR_VERT ) != 0;
512 
513         if( nFlags & GRFMGR_DRAW_BILINEAR )
514         {
515             const double	fRevScaleX = ( nNewW > 1L ) ? ( (double) ( nW - 1L ) / ( nNewW - 1L ) ) : 0.0;
516             const double	fRevScaleY = ( nNewH > 1L ) ? ( (double) ( nH - 1L ) / ( nNewH - 1L ) ) : 0.0;
517 
518             // create horizontal mapping table
519             for( nX = 0L, nTmpX = nW - 1L, nTmp = nW - 2L; nX < nNewW; nX++ )
520             {
521                 fTmp = nX * fRevScaleX;
522 
523                 if( bHMirr )
524                     fTmp = nTmpX - fTmp;
525 
526                 pMapFX[ nX ] = (long) ( ( fTmp - ( pMapIX[ nX ] = MinMax( (long) fTmp, 0, nTmp ) ) ) * 1048576. );
527             }
528 
529             // create vertical mapping table
530             for( nY = 0L, nTmpY = nH - 1L, nTmp = nH - 2L; nY < nNewH; nY++ )
531             {
532                 fTmp = nY * fRevScaleY;
533 
534                 if( bVMirr )
535                     fTmp = nTmpY - fTmp;
536 
537                 pMapFY[ nY ] = (long) ( ( fTmp - ( pMapIY[ nY ] = MinMax( (long) fTmp, 0, nTmp ) ) ) * 1048576. );
538             }
539         }
540         else
541         {
542             // #98290# Use a different mapping for non-interpolating mode, to avoid missing rows/columns
543             const double	fRevScaleX = ( nNewW > 1L ) ? ( (double) nW / nNewW ) : 0.0;
544             const double	fRevScaleY = ( nNewH > 1L ) ? ( (double) nH / nNewH ) : 0.0;
545 
546             // create horizontal mapping table
547             for( nX = 0L, nTmpX = nW - 1L, nTmp = nW - 2L; nX < nNewW; nX++ )
548             {
549                 fTmp = nX * fRevScaleX;
550 
551                 if( bHMirr )
552                     fTmp = nTmpX - fTmp;
553 
554                 // #98290# Do not use round to zero, otherwise last column will be missing
555                 pMapIX[ nX ] = MinMax( (long) fTmp, 0, nTmp );
556                 pMapFX[ nX ] = fTmp >= nTmp+1 ? 1048576 : 0;
557             }
558 
559             // create vertical mapping table
560             for( nY = 0L, nTmpY = nH - 1L, nTmp = nH - 2L; nY < nNewH; nY++ )
561             {
562                 fTmp = nY * fRevScaleY;
563 
564                 if( bVMirr )
565                     fTmp = nTmpY - fTmp;
566 
567                 // #98290# Do not use round to zero, otherwise last row will be missing
568                 pMapIY[ nY ] = MinMax( (long) fTmp, 0, nTmp );
569                 pMapFY[ nY ] = fTmp >= nTmp+1 ? 1048576 : 0;
570             }
571         }
572 
573         // calculate output sizes
574 		if( !pBmpEx )
575 		{
576 			Point		aPt;
577 			Rectangle	aOutRect( aPt, pOut->GetOutputSizePixel() );
578 			Rectangle	aBmpRect( aOutPtPix, aOutSzPix );
579 
580 			if( pOut->GetOutDevType() == OUTDEV_WINDOW )
581 			{
582 				const Region aPaintRgn( ( (Window*) pOut )->GetPaintRegion() );
583 				if( !aPaintRgn.IsNull() )
584 					aOutRect.Intersection( pOut->LogicToPixel( aPaintRgn.GetBoundRect() ) );
585 			}
586 
587 			aOutRect.Intersection( aBmpRect );
588 
589 			if( !aOutRect.IsEmpty() )
590 			{
591 				aOutPt = pOut->PixelToLogic( aOutRect.TopLeft() );
592 				aOutSz = pOut->PixelToLogic( aOutRect.GetSize() );
593 				nStartX = aOutRect.Left() - aBmpRect.Left();
594 				nStartY = aOutRect.Top() - aBmpRect.Top();
595 				nEndX = aOutRect.Right() - aBmpRect.Left();
596 				nEndY = aOutRect.Bottom() - aBmpRect.Top();
597 			}
598 			else
599 				nStartX = -1L; // invalid
600 		}
601 		else
602 		{
603 			aOutPt = pOut->PixelToLogic( aOutPtPix );
604 			aOutSz = pOut->PixelToLogic( aOutSzPix );
605 			nStartX = nStartY = 0;
606 			nEndX = aOutSzPix.Width() - 1L;
607 			nEndY = aOutSzPix.Height() - 1L;
608 		}
609 
610 		// do transformation
611 		if( nStartX >= 0L )
612 		{
613 			const sal_Bool bSimple = ( 1 == nW || 1 == nH );
614 
615 			if( nRot10 )
616 			{
617 				if( bSimple )
618 				{
619 					bRet = ( aOutBmpEx = aBmpEx ).Scale( aUnrotatedSzPix );
620 
621 					if( bRet )
622 						aOutBmpEx.Rotate( nRot10, COL_TRANSPARENT );
623 				}
624 				else
625 				{
626 					bRet = ImplCreateRotatedScaled( aBmpEx,
627 													nRot10, aOutSzPix, aUnrotatedSzPix,
628 													pMapIX, pMapFX, pMapIY, pMapFY, nStartX, nEndX, nStartY, nEndY,
629 													aOutBmpEx );
630 				}
631 			}
632 			else
633 			{
634                 // #105229# Don't scale if output size equals bitmap size
635                 // #107226# Copy through only if we're not mirroring
636                 if( !bHMirr && !bVMirr && aOutSzPix == rBmpSzPix )
637                 {
638                     // #107226# Use original dimensions when just copying through
639                     aOutPt = pOut->PixelToLogic( aOutPtPix );
640                     aOutSz = pOut->PixelToLogic( aOutSzPix );
641                     aOutBmpEx = aBmpEx;
642                     bRet = sal_True;
643                 }
644                 else
645                 {
646                     if( bSimple )
647                         bRet = ( aOutBmpEx = aBmpEx ).Scale( Size( nEndX - nStartX + 1, nEndY - nStartY + 1 ) );
648                     else
649                     {
650                         bRet = ImplCreateScaled( aBmpEx,
651                                                  pMapIX, pMapFX, pMapIY, pMapFY,
652                                                  nStartX, nEndX, nStartY, nEndY,
653                                                  aOutBmpEx );
654                     }
655                 }
656 			}
657 
658 			if( bRet )
659 			{
660 				// attribute adjustment if neccessary
661 				if( rAttr.IsSpecialDrawMode() || rAttr.IsAdjusted() || rAttr.IsTransparent() )
662 					ImplAdjust( aOutBmpEx, rAttr, ADJUSTMENT_DRAWMODE | ADJUSTMENT_COLORS | ADJUSTMENT_TRANSPARENCY );
663 
664 				// OutDev adjustment if neccessary
665 				if( pOut->GetOutDevType() != OUTDEV_PRINTER && pOut->GetBitCount() <= 8 && aOutBmpEx.GetBitCount() >= 8 )
666 					aOutBmpEx.Dither( BMP_DITHER_MATRIX );
667 			}
668 		}
669 
670 		// delete lookup tables
671 		delete[] pMapIX;
672 		delete[] pMapFX;
673 		delete[] pMapIY;
674 		delete[] pMapFY;
675 
676 		// create output
677 		if( bRet )
678 		{
679 			if( !pBmpEx )
680 				pOut->DrawBitmapEx( aOutPt, aOutSz, aOutBmpEx );
681 			else
682 			{
683 				if( !rAttr.IsTransparent() && !aOutBmpEx.IsAlpha() )
684 					aOutBmpEx = BitmapEx( aOutBmpEx.GetBitmap().CreateDisplayBitmap( pOut ), aOutBmpEx.GetMask() );
685 
686 				pOut->DrawBitmapEx( aOutPt, aOutSz, *pBmpEx = aOutBmpEx );
687 			}
688 		}
689 	}
690 
691 	return bRet;
692 }
693 
694 // -----------------------------------------------------------------------------
695 
696 sal_Bool GraphicManager::ImplCreateOutput( OutputDevice* pOut,
697 									   const Point& rPt, const Size& rSz,
698 									   const GDIMetaFile& rMtf, const GraphicAttr& rAttr,
699 									   const sal_uLong /*nFlags*/, GDIMetaFile& rOutMtf, BitmapEx& rOutBmpEx )
700 {
701     const Size aNewSize( rMtf.GetPrefSize() );
702 
703     rOutMtf = rMtf;
704 
705     // #117889# count bitmap actions, and flag actions that paint, but
706     // are no bitmaps.
707     sal_Int32   nNumBitmaps(0);
708     bool        bNonBitmapActionEncountered(false);
709     if( aNewSize.Width() && aNewSize.Height() && rSz.Width() && rSz.Height() )
710     {
711         const double fGrfWH = (double) aNewSize.Width() / aNewSize.Height();
712         const double fOutWH = (double) rSz.Width() / rSz.Height();
713 
714         const double fScaleX = fOutWH / fGrfWH;
715         const double fScaleY = 1.0;
716 
717         const MapMode& rPrefMapMode( rMtf.GetPrefMapMode() );
718         const Size&    rSizePix( pOut->LogicToPixel( aNewSize,
719                                                      rPrefMapMode ) );
720 
721         // taking care of font width default if scaling metafile.
722         // #117889# use existing metafile scan, to determine whether
723         // the metafile basically displays a single bitmap. Note that
724         // the solution, as implemented here, is quite suboptimal (the
725         // cases where a mtf consisting basically of a single bitmap,
726         // that fail to pass the test below, are probably frequent). A
727         // better solution would involve FSAA, but that's currently
728         // expensive, and might trigger bugs on display drivers, if
729         // VDevs get bigger than the actual screen.
730         sal_uInt32  nCurPos;
731         MetaAction* pAct;
732         for( nCurPos = 0, pAct = (MetaAction*)rOutMtf.FirstAction(); pAct;
733              pAct = (MetaAction*)rOutMtf.NextAction(), nCurPos++ )
734         {
735             MetaAction* pModAct = NULL;
736             switch( pAct->GetType() )
737             {
738                 case META_FONT_ACTION:
739                 {
740                     MetaFontAction* pA = (MetaFontAction*)pAct;
741                     Font aFont( pA->GetFont() );
742                     if ( !aFont.GetWidth() )
743                     {
744                         FontMetric aFontMetric( pOut->GetFontMetric( aFont ) );
745                         aFont.SetWidth( aFontMetric.GetWidth() );
746                         pModAct = new MetaFontAction( aFont );
747                     }
748                 }
749                     // FALLTHROUGH intended
750                 case META_NULL_ACTION:
751                     // FALLTHROUGH intended
752 
753                     // OutDev state changes (which don't affect bitmap
754                     // output)
755                 case META_LINECOLOR_ACTION:
756                     // FALLTHROUGH intended
757                 case META_FILLCOLOR_ACTION:
758                     // FALLTHROUGH intended
759                 case META_TEXTCOLOR_ACTION:
760                     // FALLTHROUGH intended
761                 case META_TEXTFILLCOLOR_ACTION:
762                     // FALLTHROUGH intended
763                 case META_TEXTALIGN_ACTION:
764                     // FALLTHROUGH intended
765                 case META_TEXTLINECOLOR_ACTION:
766                     // FALLTHROUGH intended
767                 case META_TEXTLINE_ACTION:
768                     // FALLTHROUGH intended
769                 case META_PUSH_ACTION:
770                     // FALLTHROUGH intended
771                 case META_POP_ACTION:
772                     // FALLTHROUGH intended
773                 case META_LAYOUTMODE_ACTION:
774                     // FALLTHROUGH intended
775                 case META_TEXTLANGUAGE_ACTION:
776                     // FALLTHROUGH intended
777                 case META_COMMENT_ACTION:
778                     break;
779 
780                     // bitmap output methods
781                 case META_BMP_ACTION:
782                     if( !nNumBitmaps && !bNonBitmapActionEncountered )
783                     {
784                         MetaBmpAction* pAction = (MetaBmpAction*)pAct;
785 
786                         rOutBmpEx = BitmapEx( pAction->GetBitmap() );
787                         muckWithBitmap( pOut->LogicToPixel( pAction->GetPoint(),
788                                                             rPrefMapMode ),
789                                         pAction->GetBitmap().GetSizePixel(),
790                                         rSizePix,
791                                         bNonBitmapActionEncountered );
792                         ++nNumBitmaps;
793                     }
794                     break;
795 
796                 case META_BMPSCALE_ACTION:
797                     if( !nNumBitmaps && !bNonBitmapActionEncountered )
798                     {
799                         MetaBmpScaleAction* pAction = (MetaBmpScaleAction*)pAct;
800 
801                         rOutBmpEx = BitmapEx( pAction->GetBitmap() );
802                         muckWithBitmap( pOut->LogicToPixel( pAction->GetPoint(),
803                                                             rPrefMapMode ),
804                                         pOut->LogicToPixel( pAction->GetSize(),
805                                                             rPrefMapMode ),
806                                         rSizePix,
807                                         bNonBitmapActionEncountered );
808                         ++nNumBitmaps;
809                     }
810                     break;
811 
812                 case META_BMPSCALEPART_ACTION:
813                     if( !nNumBitmaps && !bNonBitmapActionEncountered )
814                     {
815                         MetaBmpScalePartAction* pAction = (MetaBmpScalePartAction*)pAct;
816 
817                         rOutBmpEx = muckWithBitmap( BitmapEx( pAction->GetBitmap() ),
818                                                     pAction->GetSrcPoint(),
819                                                     pAction->GetSrcSize(),
820                                                     pOut->LogicToPixel( pAction->GetDestPoint(),
821                                                                         rPrefMapMode ),
822                                                     pOut->LogicToPixel( pAction->GetDestSize(),
823                                                                         rPrefMapMode ),
824                                                     rSizePix,
825                                                     bNonBitmapActionEncountered );
826                         ++nNumBitmaps;
827                     }
828                     break;
829 
830                 case META_BMPEX_ACTION:
831                     if( !nNumBitmaps && !bNonBitmapActionEncountered )
832                     {
833                         MetaBmpExAction* pAction = (MetaBmpExAction*)pAct;
834 
835                         rOutBmpEx = pAction->GetBitmapEx();
836                         muckWithBitmap( pOut->LogicToPixel( pAction->GetPoint(),
837                                                             rPrefMapMode ),
838                                         pAction->GetBitmapEx().GetSizePixel(),
839                                         rSizePix,
840                                         bNonBitmapActionEncountered );
841                         ++nNumBitmaps;
842                     }
843                     break;
844 
845                 case META_BMPEXSCALE_ACTION:
846                     if( !nNumBitmaps && !bNonBitmapActionEncountered )
847                     {
848                         MetaBmpExScaleAction* pAction = (MetaBmpExScaleAction*)pAct;
849 
850                         rOutBmpEx = pAction->GetBitmapEx();
851                         muckWithBitmap( pOut->LogicToPixel( pAction->GetPoint(),
852                                                             rPrefMapMode ),
853                                         pOut->LogicToPixel( pAction->GetSize(),
854                                                             rPrefMapMode ),
855                                         rSizePix,
856                                         bNonBitmapActionEncountered );
857                         ++nNumBitmaps;
858                     }
859                     break;
860 
861                 case META_BMPEXSCALEPART_ACTION:
862                     if( !nNumBitmaps && !bNonBitmapActionEncountered )
863                     {
864                         MetaBmpExScalePartAction* pAction = (MetaBmpExScalePartAction*)pAct;
865 
866                         rOutBmpEx = muckWithBitmap( pAction->GetBitmapEx(),
867                                                     pAction->GetSrcPoint(),
868                                                     pAction->GetSrcSize(),
869                                                     pOut->LogicToPixel( pAction->GetDestPoint(),
870                                                                         rPrefMapMode ),
871                                                     pOut->LogicToPixel( pAction->GetDestSize(),
872                                                                         rPrefMapMode ),
873                                                     rSizePix,
874                                                     bNonBitmapActionEncountered );
875                         ++nNumBitmaps;
876                     }
877                     break;
878 
879                     // these actions actually output something (that's
880                     // different from a bitmap)
881                 case META_RASTEROP_ACTION:
882                     if( ((MetaRasterOpAction*)pAct)->GetRasterOp() == ROP_OVERPAINT )
883                         break;
884                     // FALLTHROUGH intended
885                 case META_PIXEL_ACTION:
886                     // FALLTHROUGH intended
887                 case META_POINT_ACTION:
888                     // FALLTHROUGH intended
889                 case META_LINE_ACTION:
890                     // FALLTHROUGH intended
891                 case META_RECT_ACTION:
892                     // FALLTHROUGH intended
893                 case META_ROUNDRECT_ACTION:
894                     // FALLTHROUGH intended
895                 case META_ELLIPSE_ACTION:
896                     // FALLTHROUGH intended
897                 case META_ARC_ACTION:
898                     // FALLTHROUGH intended
899                 case META_PIE_ACTION:
900                     // FALLTHROUGH intended
901                 case META_CHORD_ACTION:
902                     // FALLTHROUGH intended
903                 case META_POLYLINE_ACTION:
904                     // FALLTHROUGH intended
905                 case META_POLYGON_ACTION:
906                     // FALLTHROUGH intended
907                 case META_POLYPOLYGON_ACTION:
908                     // FALLTHROUGH intended
909 
910                 case META_TEXT_ACTION:
911                     // FALLTHROUGH intended
912                 case META_TEXTARRAY_ACTION:
913                     // FALLTHROUGH intended
914                 case META_STRETCHTEXT_ACTION:
915                     // FALLTHROUGH intended
916                 case META_TEXTRECT_ACTION:
917                     // FALLTHROUGH intended
918 
919                 case META_MASK_ACTION:
920                     // FALLTHROUGH intended
921                 case META_MASKSCALE_ACTION:
922                     // FALLTHROUGH intended
923                 case META_MASKSCALEPART_ACTION:
924                     // FALLTHROUGH intended
925 
926                 case META_GRADIENT_ACTION:
927                     // FALLTHROUGH intended
928                 case META_HATCH_ACTION:
929                     // FALLTHROUGH intended
930                 case META_WALLPAPER_ACTION:
931                     // FALLTHROUGH intended
932 
933                 case META_TRANSPARENT_ACTION:
934                     // FALLTHROUGH intended
935                 case META_EPS_ACTION:
936                     // FALLTHROUGH intended
937                 case META_FLOATTRANSPARENT_ACTION:
938                     // FALLTHROUGH intended
939                 case META_GRADIENTEX_ACTION:
940                     // FALLTHROUGH intended
941 
942                     // OutDev state changes that _do_ affect bitmap
943                     // output
944                 case META_CLIPREGION_ACTION:
945                     // FALLTHROUGH intended
946                 case META_ISECTRECTCLIPREGION_ACTION:
947                     // FALLTHROUGH intended
948                 case META_ISECTREGIONCLIPREGION_ACTION:
949                     // FALLTHROUGH intended
950                 case META_MOVECLIPREGION_ACTION:
951                     // FALLTHROUGH intended
952 
953                 case META_MAPMODE_ACTION:
954                     // FALLTHROUGH intended
955                 case META_REFPOINT_ACTION:
956                     // FALLTHROUGH intended
957                 default:
958                     bNonBitmapActionEncountered = true;
959                     break;
960             }
961             if ( pModAct )
962             {
963                 rOutMtf.ReplaceAction( pModAct, nCurPos );
964                 pAct->Delete();
965             }
966             else
967             {
968                 if( pAct->GetRefCount() > 1 )
969                 {
970                     rOutMtf.ReplaceAction( pModAct = pAct->Clone(), nCurPos );
971                     pAct->Delete();
972                 }
973                 else
974                     pModAct = pAct;
975             }
976             pModAct->Scale( fScaleX, fScaleY );
977         }
978         rOutMtf.SetPrefSize( Size( FRound( aNewSize.Width() * fScaleX ),
979                                    FRound( aNewSize.Height() * fScaleY ) ) );
980     }
981 
982     if( nNumBitmaps != 1 || bNonBitmapActionEncountered )
983     {
984         if( rAttr.IsSpecialDrawMode() || rAttr.IsAdjusted() || rAttr.IsMirrored() || rAttr.IsRotated() || rAttr.IsTransparent() )
985             ImplAdjust( rOutMtf, rAttr, ADJUSTMENT_ALL );
986 
987         ImplDraw( pOut, rPt, rSz, rOutMtf, rAttr );
988         rOutBmpEx = BitmapEx();
989     }
990 
991     return sal_True;
992 }
993 
994 // -----------------------------------------------------------------------------
995 
996 sal_Bool GraphicManager::ImplCreateScaled( const BitmapEx& rBmpEx,
997 									   long* pMapIX, long* pMapFX, long* pMapIY, long* pMapFY,
998 									   long nStartX, long nEndX, long nStartY, long nEndY,
999 									   BitmapEx& rOutBmpEx )
1000 {
1001 	Bitmap				aBmp( rBmpEx.GetBitmap() );
1002 	Bitmap				aOutBmp;
1003 	BitmapReadAccess*	pAcc = aBmp.AcquireReadAccess();
1004 	BitmapWriteAccess*	pWAcc;
1005 	BitmapColor			aCol0, aCol1, aColRes;
1006 	const long			nDstW = nEndX - nStartX + 1L;
1007 	const long			nDstH = nEndY - nStartY + 1L;
1008 	long				nX, nY, nTmpX, nTmpY, nTmpFX, nTmpFY;
1009 	long				nXDst, nYDst;
1010 	sal_uInt8				cR0, cG0, cB0, cR1, cG1, cB1;
1011 	sal_Bool				bRet = sal_False;
1012 
1013     DBG_ASSERT( aBmp.GetSizePixel() == rBmpEx.GetSizePixel(),
1014                 "GraphicManager::ImplCreateScaled(): bmp size inconsistent" );
1015 
1016 	if( pAcc )
1017 	{
1018 		aOutBmp = Bitmap( Size( nDstW, nDstH ), 24 );
1019 		pWAcc = aOutBmp.AcquireWriteAccess();
1020 
1021 		if( pWAcc )
1022 		{
1023 			if( pAcc->HasPalette() )
1024 			{
1025 				if( pAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
1026 				{
1027 					Scanline pLine0, pLine1;
1028 
1029 					for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
1030 					{
1031 						nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ];
1032 						pLine0 = pAcc->GetScanline( nTmpY );
1033 						pLine1 = pAcc->GetScanline( ++nTmpY );
1034 
1035 						for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
1036 						{
1037 							nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ];
1038 
1039 							const BitmapColor& rCol0 = pAcc->GetPaletteColor( pLine0[ nTmpX ] );
1040 							const BitmapColor& rCol2 = pAcc->GetPaletteColor( pLine1[ nTmpX ] );
1041 							const BitmapColor& rCol1 = pAcc->GetPaletteColor( pLine0[ ++nTmpX ] );
1042 							const BitmapColor& rCol3 = pAcc->GetPaletteColor( pLine1[ nTmpX ] );
1043 
1044 							cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTmpFX );
1045 							cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTmpFX );
1046 							cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTmpFX );
1047 
1048 							cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTmpFX );
1049 							cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTmpFX );
1050 							cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTmpFX );
1051 
1052 							aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
1053 							aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
1054 							aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
1055 							pWAcc->SetPixel( nYDst, nXDst++, aColRes );
1056 						}
1057 					}
1058 				}
1059 				else
1060 				{
1061 					for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
1062 					{
1063 						nTmpY = pMapIY[ nY ], nTmpFY = pMapFY[ nY ];
1064 
1065 						for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
1066 						{
1067 							nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ];
1068 
1069 							aCol0 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY, nTmpX ) );
1070 							aCol1 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY, ++nTmpX ) );
1071 							cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
1072 							cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
1073 							cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
1074 
1075 							aCol1 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( ++nTmpY, nTmpX ) );
1076 							aCol0 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY--, --nTmpX ) );
1077 							cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
1078 							cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
1079 							cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
1080 
1081 							aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
1082 							aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
1083 							aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
1084 							pWAcc->SetPixel( nYDst, nXDst++, aColRes );
1085 						}
1086 					}
1087 				}
1088 			}
1089 			else
1090 			{
1091 				if( pAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_BGR )
1092 				{
1093 					Scanline	pLine0, pLine1, pTmp0, pTmp1;
1094 					long		nOff;
1095 
1096 					for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
1097 					{
1098 						nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ];
1099 						pLine0 = pAcc->GetScanline( nTmpY );
1100 						pLine1 = pAcc->GetScanline( ++nTmpY );
1101 
1102 						for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
1103 						{
1104 							nOff = 3L * ( nTmpX = pMapIX[ nX ] );
1105 							nTmpFX = pMapFX[ nX ];
1106 
1107 							pTmp1 = ( pTmp0 = pLine0 + nOff ) + 3L;
1108 							cB0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
1109 							cG0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
1110 							cR0 = MAP( *pTmp0, *pTmp1, nTmpFX );
1111 
1112 							pTmp1 = ( pTmp0 = pLine1 + nOff ) + 3L;
1113 							cB1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
1114 							cG1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
1115 							cR1 = MAP( *pTmp0, *pTmp1, nTmpFX );
1116 
1117 							aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
1118 							aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
1119 							aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
1120 							pWAcc->SetPixel( nYDst, nXDst++, aColRes );
1121 						}
1122 					}
1123 				}
1124 				else if( pAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
1125 				{
1126 					Scanline	pLine0, pLine1, pTmp0, pTmp1;
1127 					long		nOff;
1128 
1129 					for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
1130 					{
1131 						nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ];
1132 						pLine0 = pAcc->GetScanline( nTmpY );
1133 						pLine1 = pAcc->GetScanline( ++nTmpY );
1134 
1135 						for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
1136 						{
1137 							nOff = 3L * ( nTmpX = pMapIX[ nX ] );
1138 							nTmpFX = pMapFX[ nX ];
1139 
1140 							pTmp1 = ( pTmp0 = pLine0 + nOff ) + 3L;
1141 							cR0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
1142 							cG0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
1143 							cB0 = MAP( *pTmp0, *pTmp1, nTmpFX );
1144 
1145 							pTmp1 = ( pTmp0 = pLine1 + nOff ) + 3L;
1146 							cR1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
1147 							cG1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
1148 							cB1 = MAP( *pTmp0, *pTmp1, nTmpFX );
1149 
1150 							aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
1151 							aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
1152 							aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
1153 							pWAcc->SetPixel( nYDst, nXDst++, aColRes );
1154 						}
1155 					}
1156 				}
1157 				else
1158 				{
1159 					for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
1160 					{
1161 						nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ];
1162 
1163 						for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
1164 						{
1165 							nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ];
1166 
1167 							aCol0 = pAcc->GetPixel( nTmpY, nTmpX );
1168 							aCol1 = pAcc->GetPixel( nTmpY, ++nTmpX );
1169 							cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
1170 							cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
1171 							cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
1172 
1173 							aCol1 = pAcc->GetPixel( ++nTmpY, nTmpX );
1174 							aCol0 = pAcc->GetPixel( nTmpY--, --nTmpX );
1175 							cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
1176 							cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
1177 							cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
1178 
1179 							aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
1180 							aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
1181 							aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
1182 							pWAcc->SetPixel( nYDst, nXDst++, aColRes );
1183 						}
1184 					}
1185 				}
1186 			}
1187 
1188 			aOutBmp.ReleaseAccess( pWAcc );
1189 			bRet = sal_True;
1190 		}
1191 
1192 		aBmp.ReleaseAccess( pAcc );
1193 	}
1194 
1195 	if( bRet && rBmpEx.IsTransparent() )
1196 	{
1197 		bRet = sal_False;
1198 
1199 		if( rBmpEx.IsAlpha() )
1200 		{
1201             DBG_ASSERT( rBmpEx.GetAlpha().GetSizePixel() == rBmpEx.GetSizePixel(),
1202                         "GraphicManager::ImplCreateScaled(): alpha mask size inconsistent" );
1203 
1204 			AlphaMask	aAlpha( rBmpEx.GetAlpha() );
1205 			AlphaMask	aOutAlpha;
1206 
1207 			pAcc = aAlpha.AcquireReadAccess();
1208 
1209 			if( pAcc )
1210 			{
1211 				aOutAlpha = AlphaMask( Size( nDstW, nDstH ) );
1212 				pWAcc = aOutAlpha.AcquireWriteAccess();
1213 
1214 				if( pWAcc )
1215 				{
1216 					if( pAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL &&
1217 						pWAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
1218 					{
1219 						Scanline pLine0, pLine1, pLineW;
1220 
1221 						for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
1222 						{
1223 							nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ];
1224 							pLine0 = pAcc->GetScanline( nTmpY );
1225 							pLine1 = pAcc->GetScanline( ++nTmpY );
1226 							pLineW = pWAcc->GetScanline( nYDst );
1227 
1228 							for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++, nXDst++ )
1229 							{
1230 								nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ];
1231 
1232 								const long	nAlpha0 = pLine0[ nTmpX ];
1233 								const long	nAlpha2 = pLine1[ nTmpX ];
1234 								const long	nAlpha1 = pLine0[ ++nTmpX ];
1235 								const long	nAlpha3 = pLine1[ nTmpX ];
1236 								const long	n0 = MAP( nAlpha0, nAlpha1, nTmpFX );
1237 								const long	n1 = MAP( nAlpha2, nAlpha3, nTmpFX );
1238 
1239 								*pLineW++ = MAP( n0, n1, nTmpFY );
1240 							}
1241 						}
1242 					}
1243 					else
1244 					{
1245 						BitmapColor aAlphaValue( 0 );
1246 
1247 						for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
1248 						{
1249 							nTmpY = pMapIY[ nY ], nTmpFY = pMapFY[ nY ];
1250 
1251 							for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
1252 							{
1253 								nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ];
1254 
1255 								long		nAlpha0 = pAcc->GetPixel( nTmpY, nTmpX ).GetIndex();
1256 								long		nAlpha1 = pAcc->GetPixel( nTmpY, ++nTmpX ).GetIndex();
1257 								const long	n0 = MAP( nAlpha0, nAlpha1, nTmpFX );
1258 
1259 								nAlpha1 = pAcc->GetPixel( ++nTmpY, nTmpX ).GetIndex();
1260 								nAlpha0 = pAcc->GetPixel( nTmpY--, --nTmpX ).GetIndex();
1261 								const long	n1 = MAP( nAlpha0, nAlpha1, nTmpFX );
1262 
1263 								aAlphaValue.SetIndex( MAP( n0, n1, nTmpFY ) );
1264 								pWAcc->SetPixel( nYDst, nXDst++, aAlphaValue );
1265 							}
1266 						}
1267 					}
1268 
1269 					aOutAlpha.ReleaseAccess( pWAcc );
1270 					bRet = sal_True;
1271 				}
1272 
1273 				aAlpha.ReleaseAccess( pAcc );
1274 
1275 				if( bRet )
1276 					rOutBmpEx = BitmapEx( aOutBmp, aOutAlpha );
1277 			}
1278 		}
1279 		else
1280 		{
1281             DBG_ASSERT( rBmpEx.GetMask().GetSizePixel() == rBmpEx.GetSizePixel(),
1282                         "GraphicManager::ImplCreateScaled(): mask size inconsistent" );
1283 
1284 			Bitmap	aMsk( rBmpEx.GetMask() );
1285 			Bitmap	aOutMsk;
1286 
1287 			pAcc = aMsk.AcquireReadAccess();
1288 
1289 			if( pAcc )
1290 			{
1291                 // #i40115# Use the same palette for destination
1292                 // bitmap. Otherwise, we'd have to color-map even the
1293                 // case below, when both masks are one bit deep.
1294                 if( pAcc->HasPalette() )
1295                     aOutMsk = Bitmap( Size( nDstW, nDstH ),
1296                                       1,
1297                                       &pAcc->GetPalette() );
1298                 else
1299                     aOutMsk = Bitmap( Size( nDstW, nDstH ), 1 );
1300 
1301 				pWAcc = aOutMsk.AcquireWriteAccess();
1302 
1303 				if( pWAcc )
1304 				{
1305 					long* pMapLX = new long[ nDstW ];
1306 					long* pMapLY = new long[ nDstH ];
1307 
1308 					// create new horizontal mapping table
1309 					for( nX = 0UL, nTmpX = nStartX; nX < nDstW; nTmpX++ )
1310 						pMapLX[ nX++ ] = FRound( (double) pMapIX[ nTmpX ] + pMapFX[ nTmpX ] / 1048576. );
1311 
1312 					// create new vertical mapping table
1313 					for( nY = 0UL, nTmpY = nStartY; nY < nDstH; nTmpY++ )
1314 						pMapLY[ nY++ ] = FRound( (double) pMapIY[ nTmpY ] + pMapFY[ nTmpY ] / 1048576. );
1315 
1316 					// do normal scaling
1317 					if( pAcc->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL &&
1318 						pWAcc->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL )
1319 					{
1320 						// optimized
1321 						for( nY = 0; nY < nDstH; nY++ )
1322 						{
1323 							Scanline pSrc = pAcc->GetScanline( pMapLY[ nY ] );
1324 							Scanline pDst = pWAcc->GetScanline( nY );
1325 
1326 							for( nX = 0L; nX < nDstW; nX++ )
1327 							{
1328 								const long nSrcX = pMapLX[ nX ];
1329 
1330 								if( pSrc[ nSrcX >> 3 ] & ( 1 << ( 7 - ( nSrcX & 7 ) ) ) )
1331 									pDst[ nX >> 3 ] |= 1 << ( 7 - ( nX & 7 ) );
1332 								else
1333 									pDst[ nX >> 3 ] &= ~( 1 << ( 7 - ( nX & 7 ) ) );
1334 							}
1335 						}
1336 					}
1337 					else
1338 					{
1339 						const BitmapColor aB( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
1340 						const BitmapColor aWB( pWAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
1341 						const BitmapColor aWW( pWAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
1342 
1343 						if( pAcc->HasPalette() )
1344 						{
1345 							for( nY = 0L; nY < nDstH; nY++ )
1346 							{
1347 								for( nX = 0L; nX < nDstW; nX++ )
1348 								{
1349 									if( pAcc->GetPaletteColor( pAcc->GetPixelIndex( pMapLY[ nY ], pMapLX[ nX ] ) ) == aB )
1350 										pWAcc->SetPixel( nY, nX, aWB );
1351 									else
1352 										pWAcc->SetPixel( nY, nX, aWW );
1353 								}
1354 							}
1355 						}
1356 						else
1357 						{
1358 							for( nY = 0L; nY < nDstH; nY++ )
1359 							{
1360 								for( nX = 0L; nX < nDstW; nX++ )
1361 								{
1362 									if( pAcc->GetPixel( pMapLY[ nY ], pMapLX[ nX ] ) == aB )
1363 										pWAcc->SetPixel( nY, nX, aWB );
1364 									else
1365 										pWAcc->SetPixel( nY, nX, aWW );
1366 								}
1367 							}
1368 						}
1369 					}
1370 
1371 					delete[] pMapLX;
1372 					delete[] pMapLY;
1373 					aOutMsk.ReleaseAccess( pWAcc );
1374 					bRet = sal_True;
1375 				}
1376 
1377 				aMsk.ReleaseAccess( pAcc );
1378 
1379 				if( bRet )
1380 					rOutBmpEx = BitmapEx( aOutBmp, aOutMsk );
1381 			}
1382 		}
1383 
1384 		if( !bRet )
1385 			rOutBmpEx = aOutBmp;
1386 	}
1387 	else
1388 		rOutBmpEx = aOutBmp;
1389 
1390 	return bRet;
1391 }
1392 
1393 // -----------------------------------------------------------------------------
1394 
1395 sal_Bool GraphicManager::ImplCreateRotatedScaled( const BitmapEx& rBmpEx,
1396 											  sal_uInt16 nRot10, const Size& /*rOutSzPix*/, const Size& rUnrotatedSzPix,
1397 											  long* pMapIX, long* pMapFX, long* pMapIY, long* pMapFY,
1398 											  long nStartX, long nEndX, long nStartY, long nEndY,
1399 											  BitmapEx& rOutBmpEx )
1400 {
1401 	Point				aPt;
1402 	Bitmap				aBmp( rBmpEx.GetBitmap() );
1403 	Bitmap				aOutBmp;
1404 	BitmapReadAccess*	pAcc = aBmp.AcquireReadAccess();
1405 	BitmapWriteAccess*	pWAcc;
1406 	Polygon				aPoly( Rectangle( aPt, rUnrotatedSzPix ) ); aPoly.Rotate( Point(), nRot10 );
1407 	Rectangle			aNewBound( aPoly.GetBoundRect() );
1408 	const double		fCosAngle = cos( nRot10 * F_PI1800 ), fSinAngle = sin( nRot10 * F_PI1800 );
1409 	double				fTmp;
1410 	const long			nDstW = nEndX - nStartX + 1L;
1411 	const long			nDstH = nEndY - nStartY + 1L;
1412 	const long			nUnRotW = rUnrotatedSzPix.Width();
1413 	const long			nUnRotH = rUnrotatedSzPix.Height();
1414 	long*				pCosX = new long[ nDstW ];
1415 	long*				pSinX = new long[ nDstW ];
1416 	long*				pCosY = new long[ nDstH ];
1417 	long*				pSinY = new long[ nDstH ];
1418 	long				nX, nY, nTmpX, nTmpY, nTmpFX, nTmpFY, nUnRotX, nUnRotY, nSinY, nCosY;
1419 	sal_uInt8				cR0, cG0, cB0, cR1, cG1, cB1;
1420 	sal_Bool				bRet = sal_False;
1421 
1422 	// create horizontal mapping table
1423 	for( nX = 0L, nTmpX = aNewBound.Left() + nStartX; nX < nDstW; nX++ )
1424 	{
1425 		pCosX[ nX ] = FRound( fCosAngle * ( fTmp = nTmpX++ << 8 ) );
1426 		pSinX[ nX ] = FRound( fSinAngle * fTmp );
1427 	}
1428 
1429 	// create vertical mapping table
1430 	for( nY = 0L, nTmpY = aNewBound.Top() + nStartY; nY < nDstH; nY++ )
1431 	{
1432 		pCosY[ nY ] = FRound( fCosAngle * ( fTmp = nTmpY++ << 8 ) );
1433 		pSinY[ nY ] = FRound( fSinAngle * fTmp );
1434 	}
1435 
1436 	if( pAcc )
1437 	{
1438 		aOutBmp = Bitmap( Size( nDstW, nDstH ), 24 );
1439 		pWAcc = aOutBmp.AcquireWriteAccess();
1440 
1441 		if( pWAcc )
1442 		{
1443 			BitmapColor aColRes;
1444 
1445 			if( pAcc->HasPalette() )
1446 			{
1447 				for( nY = 0; nY < nDstH; nY++ )
1448 				{
1449 					nSinY = pSinY[ nY ];
1450 					nCosY = pCosY[ nY ];
1451 
1452 					for( nX = 0; nX < nDstW; nX++ )
1453 					{
1454 						nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
1455 						nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
1456 
1457 						if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) &&
1458 							( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) )
1459 						{
1460 							nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ];
1461 							nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
1462 
1463 							const BitmapColor& rCol0 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY, nTmpX ) );
1464 							const BitmapColor& rCol1 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY, ++nTmpX ) );
1465 							cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTmpFX );
1466 							cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTmpFX );
1467 							cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTmpFX );
1468 
1469 							const BitmapColor& rCol3 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( ++nTmpY, nTmpX ) );
1470 							const BitmapColor& rCol2 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY, --nTmpX ) );
1471 							cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTmpFX );
1472 							cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTmpFX );
1473 							cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTmpFX );
1474 
1475 							aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
1476 							aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
1477 							aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
1478 							pWAcc->SetPixel( nY, nX, aColRes );
1479 						}
1480 					}
1481 				}
1482 			}
1483 			else
1484 			{
1485 				BitmapColor	aCol0, aCol1;
1486 
1487 				for( nY = 0; nY < nDstH; nY++ )
1488 				{
1489 					nSinY = pSinY[ nY ];
1490 					nCosY = pCosY[ nY ];
1491 
1492 					for( nX = 0; nX < nDstW; nX++ )
1493 					{
1494 						nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
1495 						nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
1496 
1497 						if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) &&
1498 							( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) )
1499 						{
1500 							nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ];
1501 							nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
1502 
1503 							aCol0 = pAcc->GetPixel( nTmpY, nTmpX );
1504 							aCol1 = pAcc->GetPixel( nTmpY, ++nTmpX );
1505 							cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
1506 							cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
1507 							cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
1508 
1509 							aCol1 = pAcc->GetPixel( ++nTmpY, nTmpX );
1510 							aCol0 = pAcc->GetPixel( nTmpY, --nTmpX );
1511 							cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
1512 							cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
1513 							cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
1514 
1515 							aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
1516 							aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
1517 							aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
1518 							pWAcc->SetPixel( nY, nX, aColRes );
1519 						}
1520 					}
1521 				}
1522 			}
1523 
1524 			aOutBmp.ReleaseAccess( pWAcc );
1525 			bRet = sal_True;
1526 		}
1527 
1528 		aBmp.ReleaseAccess( pAcc );
1529 	}
1530 
1531 	// mask processing
1532 	if( bRet && ( rBmpEx.IsTransparent() || ( nRot10 != 900 && nRot10 != 1800 && nRot10 != 2700 ) ) )
1533 	{
1534 		bRet = sal_False;
1535 
1536 		if( rBmpEx.IsAlpha() )
1537 		{
1538 			AlphaMask	aAlpha( rBmpEx.GetAlpha() );
1539 			AlphaMask	aOutAlpha;
1540 
1541 			pAcc = aAlpha.AcquireReadAccess();
1542 
1543 			if( pAcc )
1544 			{
1545 				aOutAlpha = AlphaMask( Size( nDstW, nDstH ) );
1546 				pWAcc = aOutAlpha.AcquireWriteAccess();
1547 
1548 				if( pWAcc )
1549 				{
1550 					if( pAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL &&
1551 						pWAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
1552 					{
1553 						Scanline pLine0, pLine1, pLineW;
1554 
1555 						for( nY = 0; nY < nDstH; nY++ )
1556 						{
1557 							nSinY = pSinY[ nY ], nCosY = pCosY[ nY ];
1558 							pLineW = pWAcc->GetScanline( nY );
1559 
1560 							for( nX = 0; nX < nDstW; nX++ )
1561 							{
1562 								nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
1563 								nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
1564 
1565 								if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) &&
1566 									( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) )
1567 								{
1568 									nTmpX = pMapIX[ nUnRotX ], nTmpFX = pMapFX[ nUnRotX ];
1569 									nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
1570 
1571 									pLine0 = pAcc->GetScanline( nTmpY++ );
1572 									pLine1 = pAcc->GetScanline( nTmpY );
1573 
1574 									const long	nAlpha0 = pLine0[ nTmpX ];
1575 									const long	nAlpha2 = pLine1[ nTmpX++ ];
1576 									const long	nAlpha1 = pLine0[ nTmpX ];
1577 									const long	nAlpha3 = pLine1[ nTmpX ];
1578 									const long	n0 = MAP( nAlpha0, nAlpha1, nTmpFX );
1579 									const long	n1 = MAP( nAlpha2, nAlpha3, nTmpFX );
1580 
1581 									*pLineW++ = MAP( n0, n1, nTmpFY );
1582 								}
1583 								else
1584 									*pLineW++ = 255;
1585 							}
1586 						}
1587 					}
1588 					else
1589 					{
1590 						const BitmapColor	aTrans( pWAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
1591 						BitmapColor			aAlphaVal( 0 );
1592 
1593 						for( nY = 0; nY < nDstH; nY++ )
1594 						{
1595 							nSinY = pSinY[ nY ], nCosY = pCosY[ nY ];
1596 
1597 							for( nX = 0; nX < nDstW; nX++ )
1598 							{
1599 								nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
1600 								nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
1601 
1602 								if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) &&
1603 									( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) )
1604 								{
1605 									nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ];
1606 									nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
1607 
1608 									const long	nAlpha0 = pAcc->GetPixel( nTmpY, nTmpX ).GetIndex();
1609 									const long	nAlpha1 = pAcc->GetPixel( nTmpY, ++nTmpX ).GetIndex();
1610 									const long	nAlpha3 = pAcc->GetPixel( ++nTmpY, nTmpX ).GetIndex();
1611 									const long	nAlpha2 = pAcc->GetPixel( nTmpY, --nTmpX ).GetIndex();
1612 									const long 	n0 = MAP( nAlpha0, nAlpha1, nTmpFX );
1613 									const long 	n1 = MAP( nAlpha2, nAlpha3, nTmpFX );
1614 
1615 									aAlphaVal.SetIndex( MAP( n0, n1, nTmpFY ) );
1616 									pWAcc->SetPixel( nY, nX, aAlphaVal );
1617 								}
1618 								else
1619 									pWAcc->SetPixel( nY, nX, aTrans );
1620 							}
1621 						}
1622 					}
1623 
1624 					aOutAlpha.ReleaseAccess( pWAcc );
1625 					bRet = sal_True;
1626 				}
1627 
1628 				aAlpha.ReleaseAccess( pAcc );
1629 			}
1630 
1631 			if( bRet )
1632 				rOutBmpEx = BitmapEx( aOutBmp, aOutAlpha );
1633 		}
1634 		else
1635 		{
1636 			Bitmap aOutMsk( Size( nDstW, nDstH ), 1 );
1637 			pWAcc = aOutMsk.AcquireWriteAccess();
1638 
1639 			if( pWAcc )
1640 			{
1641 				Bitmap				aMsk( rBmpEx.GetMask() );
1642 				const BitmapColor	aB( pWAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
1643 				const BitmapColor	aW( pWAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
1644 				BitmapReadAccess*	pMAcc = NULL;
1645 
1646 				if( !aMsk || ( ( pMAcc = aMsk.AcquireReadAccess() ) != NULL ) )
1647 				{
1648 					long*		pMapLX = new long[ nUnRotW ];
1649 					long*		pMapLY = new long[ nUnRotH ];
1650 					BitmapColor	aTestB;
1651 
1652 					if( pMAcc )
1653 						aTestB = pMAcc->GetBestMatchingColor( Color( COL_BLACK ) );
1654 
1655 					// create new horizontal mapping table
1656 					for( nX = 0UL; nX < nUnRotW; nX++ )
1657 						pMapLX[ nX ] = FRound( (double) pMapIX[ nX ] + pMapFX[ nX ] / 1048576. );
1658 
1659 					// create new vertical mapping table
1660 					for( nY = 0UL; nY < nUnRotH; nY++ )
1661 						pMapLY[ nY ] = FRound( (double) pMapIY[ nY ] + pMapFY[ nY ] / 1048576. );
1662 
1663 					// do mask rotation
1664 					for( nY = 0; nY < nDstH; nY++ )
1665 					{
1666 						nSinY = pSinY[ nY ];
1667 						nCosY = pCosY[ nY ];
1668 
1669 						for( nX = 0; nX < nDstW; nX++ )
1670 						{
1671 							nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
1672 							nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
1673 
1674 							if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) &&
1675 								( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) )
1676 							{
1677 								if( pMAcc )
1678 								{
1679 									if( pMAcc->GetPixel( pMapLY[ nUnRotY ], pMapLX[ nUnRotX ] ) == aTestB )
1680 										pWAcc->SetPixel( nY, nX, aB );
1681 									else
1682 										pWAcc->SetPixel( nY, nX, aW );
1683 								}
1684 								else
1685 									pWAcc->SetPixel( nY, nX, aB );
1686 							}
1687 							else
1688 								pWAcc->SetPixel( nY, nX, aW );
1689 						}
1690 					}
1691 
1692 					delete[] pMapLX;
1693 					delete[] pMapLY;
1694 
1695 					if( pMAcc )
1696 						aMsk.ReleaseAccess( pMAcc );
1697 
1698 					bRet = sal_True;
1699 				}
1700 
1701 				aOutMsk.ReleaseAccess( pWAcc );
1702 			}
1703 
1704 			if( bRet )
1705 				rOutBmpEx = BitmapEx( aOutBmp, aOutMsk );
1706 		}
1707 
1708 		if( !bRet )
1709 			rOutBmpEx = aOutBmp;
1710 	}
1711 	else
1712 		rOutBmpEx = aOutBmp;
1713 
1714 	delete[] pSinX;
1715 	delete[] pCosX;
1716 	delete[] pSinY;
1717 	delete[] pCosY;
1718 
1719 	return bRet;
1720 }
1721 
1722 // -----------------------------------------------------------------------------
1723 
1724 void GraphicManager::ImplAdjust( BitmapEx& rBmpEx, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags )
1725 {
1726 	GraphicAttr aAttr( rAttr );
1727 
1728 	if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() )
1729 	{
1730 		switch( aAttr.GetDrawMode() )
1731 		{
1732 			case( GRAPHICDRAWMODE_MONO ):
1733 				rBmpEx.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
1734 			break;
1735 
1736 			case( GRAPHICDRAWMODE_GREYS ):
1737 				rBmpEx.Convert( BMP_CONVERSION_8BIT_GREYS );
1738 			break;
1739 
1740 			case( GRAPHICDRAWMODE_WATERMARK ):
1741 			{
1742 				aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
1743 				aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
1744 			}
1745 			break;
1746 
1747 			default:
1748 			break;
1749 		}
1750 	}
1751 
1752 	if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() )
1753 	{
1754 		rBmpEx.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
1755 					   aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
1756 					   aAttr.GetGamma(), aAttr.IsInvert() );
1757 	}
1758 
1759 	if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() )
1760 	{
1761 		rBmpEx.Mirror( aAttr.GetMirrorFlags() );
1762 	}
1763 
1764 	if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() )
1765 	{
1766 		rBmpEx.Rotate( aAttr.GetRotation(), Color( COL_TRANSPARENT ) );
1767 	}
1768 
1769 	if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() )
1770 	{
1771 		AlphaMask	aAlpha;
1772 		sal_uInt8		cTrans = aAttr.GetTransparency();
1773 
1774 		if( !rBmpEx.IsTransparent() )
1775 			aAlpha = AlphaMask( rBmpEx.GetSizePixel(), &cTrans );
1776 		else if( !rBmpEx.IsAlpha() )
1777 		{
1778 			aAlpha = rBmpEx.GetMask();
1779 			aAlpha.Replace( 0, cTrans );
1780 		}
1781 		else
1782 		{
1783 			aAlpha = rBmpEx.GetAlpha();
1784 			BitmapWriteAccess* pA = aAlpha.AcquireWriteAccess();
1785 
1786 			if( pA )
1787 			{
1788 				sal_uLong		nTrans = cTrans, nNewTrans;
1789 				const long	nWidth = pA->Width(), nHeight = pA->Height();
1790 
1791 				if( pA->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
1792 				{
1793 					for( long nY = 0; nY < nHeight; nY++ )
1794 					{
1795 						Scanline pAScan = pA->GetScanline( nY );
1796 
1797 						for( long nX = 0; nX < nWidth; nX++ )
1798 						{
1799 							nNewTrans = nTrans + *pAScan;
1800 							*pAScan++ = (sal_uInt8) ( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans );
1801 						}
1802 					}
1803 				}
1804 				else
1805 				{
1806 					BitmapColor aAlphaValue( 0 );
1807 
1808 					for( long nY = 0; nY < nHeight; nY++ )
1809 					{
1810 						for( long nX = 0; nX < nWidth; nX++ )
1811 						{
1812 							nNewTrans = nTrans + pA->GetPixel( nY, nX ).GetIndex();
1813 							aAlphaValue.SetIndex( (sal_uInt8) ( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans ) );
1814 							pA->SetPixel( nY, nX, aAlphaValue );
1815 						}
1816 					}
1817 				}
1818 
1819 				aAlpha.ReleaseAccess( pA );
1820 			}
1821 		}
1822 
1823 		rBmpEx = BitmapEx( rBmpEx.GetBitmap(), aAlpha );
1824 	}
1825 }
1826 
1827 // -----------------------------------------------------------------------------
1828 
1829 void GraphicManager::ImplAdjust( GDIMetaFile& rMtf, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags )
1830 {
1831 	GraphicAttr aAttr( rAttr );
1832 
1833 	if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() )
1834 	{
1835 		switch( aAttr.GetDrawMode() )
1836 		{
1837 			case( GRAPHICDRAWMODE_MONO ):
1838 				rMtf.Convert( MTF_CONVERSION_1BIT_THRESHOLD );
1839 			break;
1840 
1841 			case( GRAPHICDRAWMODE_GREYS ):
1842 				rMtf.Convert( MTF_CONVERSION_8BIT_GREYS );
1843 			break;
1844 
1845 			case( GRAPHICDRAWMODE_WATERMARK ):
1846 			{
1847 				aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
1848 				aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
1849 			}
1850 			break;
1851 
1852 			default:
1853 			break;
1854 		}
1855 	}
1856 
1857 	if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() )
1858 	{
1859 		rMtf.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
1860 					 aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
1861 					 aAttr.GetGamma(), aAttr.IsInvert() );
1862 	}
1863 
1864 	if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() )
1865 	{
1866         rMtf.Mirror( aAttr.GetMirrorFlags() );
1867 	}
1868 
1869 	if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() )
1870 	{
1871 		rMtf.Rotate( aAttr.GetRotation() );
1872 	}
1873 
1874 	if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() )
1875 	{
1876 		DBG_WARNING( "Missing implementation: Mtf-Transparency" );
1877 	}
1878 }
1879 
1880 // -----------------------------------------------------------------------------
1881 
1882 void GraphicManager::ImplAdjust( Animation& rAnimation, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags )
1883 {
1884 	GraphicAttr aAttr( rAttr );
1885 
1886 	if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() )
1887 	{
1888 		switch( aAttr.GetDrawMode() )
1889 		{
1890 			case( GRAPHICDRAWMODE_MONO ):
1891 				rAnimation.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
1892 			break;
1893 
1894 			case( GRAPHICDRAWMODE_GREYS ):
1895 				rAnimation.Convert( BMP_CONVERSION_8BIT_GREYS );
1896 			break;
1897 
1898 			case( GRAPHICDRAWMODE_WATERMARK ):
1899 			{
1900 				aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
1901 				aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
1902 			}
1903 			break;
1904 
1905 			default:
1906 			break;
1907 		}
1908 	}
1909 
1910 	if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() )
1911 	{
1912 		rAnimation.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
1913 						   aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
1914 						   aAttr.GetGamma(), aAttr.IsInvert() );
1915 	}
1916 
1917 	if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() )
1918     {
1919 		rAnimation.Mirror( aAttr.GetMirrorFlags() );
1920     }
1921 
1922 	if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() )
1923 	{
1924 		DBG_ERROR( "Missing implementation: Animation-Rotation" );
1925 	}
1926 
1927 	if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() )
1928 	{
1929 		DBG_ERROR( "Missing implementation: Animation-Transparency" );
1930 	}
1931 }
1932 
1933 // -----------------------------------------------------------------------------
1934 
1935 void GraphicManager::ImplDraw( OutputDevice* pOut, const Point& rPt, const Size& rSz,
1936 							   const GDIMetaFile& rMtf, const GraphicAttr& rAttr )
1937 {
1938    	sal_uInt16	nRot10 = rAttr.GetRotation() % 3600;
1939     Point	aOutPt( rPt );
1940     Size	aOutSz( rSz );
1941 
1942     if( nRot10 )
1943     {
1944 		Polygon aPoly( Rectangle( aOutPt, aOutSz ) );
1945 
1946 		aPoly.Rotate( aOutPt, nRot10 );
1947 		const Rectangle aRotBoundRect( aPoly.GetBoundRect() );
1948 		aOutPt = aRotBoundRect.TopLeft();
1949 		aOutSz = aRotBoundRect.GetSize();
1950 	}
1951 
1952 	pOut->Push( PUSH_CLIPREGION );
1953 	pOut->IntersectClipRegion( Rectangle( aOutPt, aOutSz ) );
1954 
1955 	( (GDIMetaFile&) rMtf ).WindStart();
1956 	( (GDIMetaFile&) rMtf ).Play( pOut, aOutPt, aOutSz );
1957 	( (GDIMetaFile&) rMtf ).WindStart();
1958 
1959 	pOut->Pop();
1960 }
1961 
1962 // -----------------------------------------------------------------------------
1963 
1964 struct ImplTileInfo
1965 {
1966     ImplTileInfo() : aTileTopLeft(), aNextTileTopLeft(), aTileSizePixel(), nTilesEmptyX(0), nTilesEmptyY(0) {}
1967 
1968     Point aTileTopLeft;   	// top, left position of the rendered tile
1969     Point aNextTileTopLeft; // top, left position for next recursion
1970                             // level's tile
1971     Size  aTileSizePixel;   // size of the generated tile (might
1972                             // differ from
1973                             // aNextTileTopLeft-aTileTopLeft, because
1974                             // this is nExponent*prevTileSize. The
1975                             // generated tile is always nExponent
1976                             // times the previous tile, such that it
1977                             // can be used in the next stage. The
1978                             // required area coverage is often
1979                             // less. The extraneous area covered is
1980                             // later overwritten by the next stage)
1981     int	  nTilesEmptyX;     // number of original tiles empty right of
1982                             // this tile. This counts from
1983                             // aNextTileTopLeft, i.e. the additional
1984                             // area covered by aTileSizePixel is not
1985                             // considered here. This is for
1986                             // unification purposes, as the iterative
1987                             // calculation of the next level's empty
1988                             // tiles has to be based on this value.
1989     int	  nTilesEmptyY;     // as above, for Y
1990 };
1991 
1992 
1993 bool GraphicObject::ImplRenderTempTile( VirtualDevice& rVDev, int nExponent,
1994                                         int nNumTilesX, int nNumTilesY,
1995                                         const Size& rTileSizePixel,
1996                                         const GraphicAttr* pAttr, sal_uLong nFlags )
1997 {
1998     if( nExponent <= 1 )
1999         return false;
2000 
2001     // determine MSB factor
2002     int nMSBFactor( 1 );
2003     while( nNumTilesX / nMSBFactor != 0 ||
2004            nNumTilesY / nMSBFactor != 0 )
2005     {
2006         nMSBFactor *= nExponent;
2007     }
2008 
2009     // one less
2010     nMSBFactor /= nExponent;
2011 
2012     ImplTileInfo aTileInfo;
2013 
2014     // #105229# Switch off mapping (converting to logic and back to
2015     // pixel might cause roundoff errors)
2016     sal_Bool bOldMap( rVDev.IsMapModeEnabled() );
2017     rVDev.EnableMapMode( sal_False );
2018 
2019     bool bRet( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor, nNumTilesX, nNumTilesY,
2020                                         nNumTilesX, nNumTilesY, rTileSizePixel, pAttr, nFlags, aTileInfo ) );
2021 
2022     rVDev.EnableMapMode( bOldMap );
2023 
2024     return bRet;
2025 }
2026 
2027 // -----------------------------------------------------------------------------
2028 
2029 // define for debug drawings
2030 //#define DBG_TEST
2031 
2032 // see header comment. this works similar to base conversion of a
2033 // number, i.e. if the exponent is 10, then the number for every tile
2034 // size is given by the decimal place of the corresponding decimal
2035 // representation.
2036 bool GraphicObject::ImplRenderTileRecursive( VirtualDevice& rVDev, int nExponent, int nMSBFactor,
2037                                              int nNumOrigTilesX, int nNumOrigTilesY,
2038                                              int nRemainderTilesX, int nRemainderTilesY,
2039                                              const Size& rTileSizePixel, const GraphicAttr* pAttr,
2040                                              sal_uLong nFlags, ImplTileInfo& rTileInfo )
2041 {
2042     // gets loaded with our tile bitmap
2043     GraphicObject aTmpGraphic;
2044 
2045     // stores a flag that renders the zero'th tile position
2046     // (i.e. (0,0)+rCurrPos) only if we're at the bottom of the
2047     // recursion stack. All other position already have that tile
2048     // rendered, because the lower levels painted their generated tile
2049     // there.
2050     bool bNoFirstTileDraw( false );
2051 
2052     // what's left when we're done with our tile size
2053     const int nNewRemainderX( nRemainderTilesX % nMSBFactor );
2054     const int nNewRemainderY( nRemainderTilesY % nMSBFactor );
2055 
2056     // gets filled out from the recursive call with info of what's
2057     // been generated
2058     ImplTileInfo aTileInfo;
2059 
2060     // current output position while drawing
2061     Point aCurrPos;
2062     int nX, nY;
2063 
2064     // check for recursion's end condition: LSB place reached?
2065     if( nMSBFactor == 1 )
2066     {
2067         aTmpGraphic = *this;
2068 
2069         // set initial tile size -> orig size
2070         aTileInfo.aTileSizePixel = rTileSizePixel;
2071         aTileInfo.nTilesEmptyX = nNumOrigTilesX;
2072         aTileInfo.nTilesEmptyY = nNumOrigTilesY;
2073     }
2074     else if( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor/nExponent,
2075                                       nNumOrigTilesX, nNumOrigTilesY,
2076                                       nNewRemainderX, nNewRemainderY,
2077                                       rTileSizePixel, pAttr, nFlags, aTileInfo ) )
2078     {
2079         // extract generated tile -> see comment on the first loop below
2080         BitmapEx aTileBitmap( rVDev.GetBitmap( aTileInfo.aTileTopLeft, aTileInfo.aTileSizePixel ) );
2081 
2082         aTmpGraphic = GraphicObject( aTileBitmap );
2083 
2084         // fill stripes left over from upstream levels:
2085         //
2086         //  x0000
2087         //  0
2088         //  0
2089         //  0
2090         //  0
2091         //
2092         // where x denotes the place filled by our recursive predecessors
2093 
2094         // check whether we have to fill stripes here. Although not
2095         // obvious, there is one case where we can skip this step: if
2096         // the previous recursion level (the one who filled our
2097         // aTileInfo) had zero area to fill, then there are no white
2098         // stripes left, naturally. This happens if the digit
2099         // associated to that level has a zero, and can be checked via
2100         // aTileTopLeft==aNextTileTopLeft.
2101         if( aTileInfo.aTileTopLeft != aTileInfo.aNextTileTopLeft )
2102         {
2103             // now fill one row from aTileInfo.aNextTileTopLeft.X() all
2104             // the way to the right
2105             aCurrPos.X() = aTileInfo.aNextTileTopLeft.X();
2106             aCurrPos.Y() = aTileInfo.aTileTopLeft.Y();
2107             for( nX=0; nX < aTileInfo.nTilesEmptyX; nX += nMSBFactor )
2108             {
2109                 if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) )
2110                     return false;
2111 
2112                 aCurrPos.X() += aTileInfo.aTileSizePixel.Width();
2113             }
2114 
2115 #ifdef DBG_TEST
2116 //    		rVDev.SetFillColor( COL_WHITE );
2117             rVDev.SetFillColor();
2118             rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) );
2119             rVDev.DrawEllipse( Rectangle(aTileInfo.aNextTileTopLeft.X(), aTileInfo.aTileTopLeft.Y(),
2120                                          aTileInfo.aNextTileTopLeft.X() - 1 + (aTileInfo.nTilesEmptyX/nMSBFactor)*aTileInfo.aTileSizePixel.Width(),
2121                                          aTileInfo.aTileTopLeft.Y() + aTileInfo.aTileSizePixel.Height() - 1) );
2122 #endif
2123 
2124             // now fill one column from aTileInfo.aNextTileTopLeft.Y() all
2125             // the way to the bottom
2126             aCurrPos.X() = aTileInfo.aTileTopLeft.X();
2127             aCurrPos.Y() = aTileInfo.aNextTileTopLeft.Y();
2128             for( nY=0; nY < aTileInfo.nTilesEmptyY; nY += nMSBFactor )
2129             {
2130                 if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) )
2131                     return false;
2132 
2133                 aCurrPos.Y() += aTileInfo.aTileSizePixel.Height();
2134             }
2135 
2136 #ifdef DBG_TEST
2137             rVDev.DrawEllipse( Rectangle(aTileInfo.aTileTopLeft.X(), aTileInfo.aNextTileTopLeft.Y(),
2138                                          aTileInfo.aTileTopLeft.X() + aTileInfo.aTileSizePixel.Width() - 1,
2139                                          aTileInfo.aNextTileTopLeft.Y() - 1 + (aTileInfo.nTilesEmptyY/nMSBFactor)*aTileInfo.aTileSizePixel.Height()) );
2140 #endif
2141         }
2142         else
2143         {
2144             // Thought that aTileInfo.aNextTileTopLeft tile has always
2145             // been drawn already, but that's wrong: typically,
2146             // _parts_ of that tile have been drawn, since the
2147             // previous level generated the tile there. But when
2148             // aTileInfo.aNextTileTopLeft!=aTileInfo.aTileTopLeft, the
2149             // difference between these two values is missing in the
2150             // lower right corner of this first tile. So, can do that
2151             // only here.
2152             bNoFirstTileDraw = true;
2153         }
2154     }
2155     else
2156     {
2157         return false;
2158     }
2159 
2160     // calc number of original tiles in our drawing area without
2161     // remainder
2162     nRemainderTilesX -= nNewRemainderX;
2163     nRemainderTilesY -= nNewRemainderY;
2164 
2165     // fill tile info for calling method
2166     rTileInfo.aTileTopLeft 	   = aTileInfo.aNextTileTopLeft;
2167     rTileInfo.aNextTileTopLeft = Point( rTileInfo.aTileTopLeft.X() + rTileSizePixel.Width()*nRemainderTilesX,
2168                                         rTileInfo.aTileTopLeft.Y() + rTileSizePixel.Height()*nRemainderTilesY );
2169     rTileInfo.aTileSizePixel   = Size( rTileSizePixel.Width()*nMSBFactor*nExponent,
2170                                        rTileSizePixel.Height()*nMSBFactor*nExponent );
2171     rTileInfo.nTilesEmptyX	   = aTileInfo.nTilesEmptyX - nRemainderTilesX;
2172     rTileInfo.nTilesEmptyY	   = aTileInfo.nTilesEmptyY - nRemainderTilesY;
2173 
2174     // init output position
2175     aCurrPos = aTileInfo.aNextTileTopLeft;
2176 
2177     // fill our drawing area. Fill possibly more, to create the next
2178     // bigger tile size -> see bitmap extraction above. This does no
2179     // harm, since everything right or below our actual area is
2180     // overdrawn by our caller. Just in case we're in the last level,
2181     // we don't draw beyond the right or bottom border.
2182     for( nY=0; nY < aTileInfo.nTilesEmptyY && nY < nExponent*nMSBFactor; nY += nMSBFactor )
2183     {
2184         aCurrPos.X() = aTileInfo.aNextTileTopLeft.X();
2185 
2186         for( nX=0; nX < aTileInfo.nTilesEmptyX && nX < nExponent*nMSBFactor; nX += nMSBFactor )
2187         {
2188             if( bNoFirstTileDraw )
2189                 bNoFirstTileDraw = false; // don't draw first tile position
2190             else if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) )
2191                 return false;
2192 
2193             aCurrPos.X() += aTileInfo.aTileSizePixel.Width();
2194         }
2195 
2196         aCurrPos.Y() += aTileInfo.aTileSizePixel.Height();
2197     }
2198 
2199 #ifdef DBG_TEST
2200 //  rVDev.SetFillColor( COL_WHITE );
2201     rVDev.SetFillColor();
2202     rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) );
2203     rVDev.DrawRect( Rectangle((rTileInfo.aTileTopLeft.X())*rTileSizePixel.Width(),
2204                               (rTileInfo.aTileTopLeft.Y())*rTileSizePixel.Height(),
2205                               (rTileInfo.aNextTileTopLeft.X())*rTileSizePixel.Width()-1,
2206                               (rTileInfo.aNextTileTopLeft.Y())*rTileSizePixel.Height()-1) );
2207 #endif
2208 
2209     return true;
2210 }
2211 
2212 // -----------------------------------------------------------------------------
2213 
2214 bool GraphicObject::ImplDrawTiled( OutputDevice* pOut, const Rectangle& rArea, const Size& rSizePixel,
2215                                    const Size& rOffset, const GraphicAttr* pAttr, sal_uLong nFlags, int nTileCacheSize1D )
2216 {
2217     // how many tiles to generate per recursion step
2218     enum{ SubdivisionExponent=2 };
2219 
2220     const MapMode 	aOutMapMode( pOut->GetMapMode() );
2221     const MapMode	aMapMode( aOutMapMode.GetMapUnit(), Point(), aOutMapMode.GetScaleX(), aOutMapMode.GetScaleY() );
2222     bool 			bRet( false );
2223 
2224     // #i42643# Casting to Int64, to avoid integer overflow for
2225     // huge-DPI output devices
2226     if( GetGraphic().GetType() == GRAPHIC_BITMAP &&
2227         static_cast<sal_Int64>(rSizePixel.Width()) * rSizePixel.Height() <
2228         static_cast<sal_Int64>(nTileCacheSize1D)*nTileCacheSize1D )
2229     {
2230         // First combine very small bitmaps into a larger tile
2231         // ===================================================
2232 
2233         VirtualDevice	aVDev;
2234         const int		nNumTilesInCacheX( (nTileCacheSize1D + rSizePixel.Width()-1) / rSizePixel.Width() );
2235         const int		nNumTilesInCacheY( (nTileCacheSize1D + rSizePixel.Height()-1) / rSizePixel.Height() );
2236 
2237         aVDev.SetOutputSizePixel( Size( nNumTilesInCacheX*rSizePixel.Width(),
2238                                         nNumTilesInCacheY*rSizePixel.Height() ) );
2239         aVDev.SetMapMode( aMapMode );
2240 
2241         // draw bitmap content
2242         if( ImplRenderTempTile( aVDev, SubdivisionExponent, nNumTilesInCacheX,
2243                                 nNumTilesInCacheY, rSizePixel, pAttr, nFlags ) )
2244         {
2245             BitmapEx aTileBitmap( aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ) );
2246 
2247             // draw alpha content, if any
2248             if( IsTransparent() )
2249             {
2250                 GraphicObject aAlphaGraphic;
2251 
2252                 if( GetGraphic().IsAlpha() )
2253                     aAlphaGraphic.SetGraphic( GetGraphic().GetBitmapEx().GetAlpha().GetBitmap() );
2254                 else
2255                     aAlphaGraphic.SetGraphic( GetGraphic().GetBitmapEx().GetMask() );
2256 
2257                 if( aAlphaGraphic.ImplRenderTempTile( aVDev, SubdivisionExponent, nNumTilesInCacheX,
2258                                                       nNumTilesInCacheY, rSizePixel, pAttr, nFlags ) )
2259                 {
2260                     // Combine bitmap and alpha/mask
2261                     if( GetGraphic().IsAlpha() )
2262                         aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(),
2263                                                 AlphaMask( aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ) ) );
2264                     else
2265                         aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(),
2266                                                 aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ).CreateMask( Color(COL_WHITE) ) );
2267                 }
2268             }
2269 
2270             // paint generated tile
2271             GraphicObject aTmpGraphic( aTileBitmap );
2272             bRet = aTmpGraphic.ImplDrawTiled( pOut, rArea,
2273                                               aTileBitmap.GetSizePixel(),
2274                                               rOffset, pAttr, nFlags, nTileCacheSize1D );
2275         }
2276     }
2277     else
2278     {
2279         const Size		aOutOffset( pOut->LogicToPixel( rOffset, aOutMapMode ) );
2280         const Rectangle	aOutArea( pOut->LogicToPixel( rArea, aOutMapMode ) );
2281 
2282         // number of invisible (because out-of-area) tiles
2283         int nInvisibleTilesX;
2284         int nInvisibleTilesY;
2285 
2286         // round towards -infty for negative offset
2287         if( aOutOffset.Width() < 0 )
2288             nInvisibleTilesX = (aOutOffset.Width() - rSizePixel.Width() + 1) / rSizePixel.Width();
2289         else
2290             nInvisibleTilesX = aOutOffset.Width() / rSizePixel.Width();
2291 
2292         // round towards -infty for negative offset
2293         if( aOutOffset.Height() < 0 )
2294             nInvisibleTilesY = (aOutOffset.Height() - rSizePixel.Height() + 1) / rSizePixel.Height();
2295         else
2296             nInvisibleTilesY = aOutOffset.Height() / rSizePixel.Height();
2297 
2298         // origin from where to 'virtually' start drawing in pixel
2299         const Point aOutOrigin( pOut->LogicToPixel( Point( rArea.Left() - rOffset.Width(),
2300                                                            rArea.Top() - rOffset.Height() ) ) );
2301         // position in pixel from where to really start output
2302         const Point aOutStart( aOutOrigin.X() + nInvisibleTilesX*rSizePixel.Width(),
2303                                aOutOrigin.Y() + nInvisibleTilesY*rSizePixel.Height() );
2304 
2305         pOut->Push( PUSH_CLIPREGION );
2306         pOut->IntersectClipRegion( rArea );
2307 
2308         // Paint all tiles
2309         // ===============
2310 
2311         bRet = ImplDrawTiled( *pOut, aOutStart,
2312                               (aOutArea.GetWidth() + aOutArea.Left() - aOutStart.X() + rSizePixel.Width() - 1) / rSizePixel.Width(),
2313                               (aOutArea.GetHeight() + aOutArea.Top() - aOutStart.Y() + rSizePixel.Height() - 1) / rSizePixel.Height(),
2314                               rSizePixel, pAttr, nFlags );
2315 
2316         pOut->Pop();
2317     }
2318 
2319     return bRet;
2320 }
2321 
2322 // -----------------------------------------------------------------------------
2323 
2324 bool GraphicObject::ImplDrawTiled( OutputDevice& rOut, const Point& rPosPixel,
2325                                    int nNumTilesX, int nNumTilesY,
2326                                    const Size& rTileSizePixel, const GraphicAttr* pAttr, sal_uLong nFlags )
2327 {
2328     Point 	aCurrPos( rPosPixel );
2329     Size	aTileSizeLogic( rOut.PixelToLogic( rTileSizePixel ) );
2330     int 	nX, nY;
2331 
2332     // #107607# Use logical coordinates for metafile playing, too
2333     bool	bDrawInPixel( rOut.GetConnectMetaFile() == NULL && GRAPHIC_BITMAP == GetType() );
2334     sal_Bool	bRet( sal_False );
2335 
2336     // #105229# Switch off mapping (converting to logic and back to
2337     // pixel might cause roundoff errors)
2338     sal_Bool bOldMap( rOut.IsMapModeEnabled() );
2339 
2340     if( bDrawInPixel )
2341         rOut.EnableMapMode( sal_False );
2342 
2343     for( nY=0; nY < nNumTilesY; ++nY )
2344     {
2345         aCurrPos.X() = rPosPixel.X();
2346 
2347         for( nX=0; nX < nNumTilesX; ++nX )
2348         {
2349             // #105229# work with pixel coordinates here, mapping is disabled!
2350             // #104004# don't disable mapping for metafile recordings
2351             // #108412# don't quit the loop if one draw fails
2352 
2353             // update return value. This method should return true, if
2354             // at least one of the looped Draws succeeded.
2355             bRet |= Draw( &rOut,
2356                           bDrawInPixel ? aCurrPos : rOut.PixelToLogic( aCurrPos ),
2357                           bDrawInPixel ? rTileSizePixel : aTileSizeLogic,
2358                           pAttr, nFlags );
2359 
2360             aCurrPos.X() += rTileSizePixel.Width();
2361         }
2362 
2363         aCurrPos.Y() += rTileSizePixel.Height();
2364     }
2365 
2366     if( bDrawInPixel )
2367         rOut.EnableMapMode( bOldMap );
2368 
2369     return bRet;
2370 }
2371 
2372 // -----------------------------------------------------------------------------
2373 
2374 void GraphicObject::ImplTransformBitmap( BitmapEx& 			rBmpEx,
2375                                          const GraphicAttr& rAttr,
2376                                          const Size&		rCropLeftTop,
2377                                          const Size&		rCropRightBottom,
2378                                          const Rectangle&	rCropRect,
2379                                          const Size& 		rDstSize,
2380                                          sal_Bool				bEnlarge ) const
2381 {
2382     // #107947# Extracted from svdograf.cxx
2383 
2384     // #104115# Crop the bitmap
2385     if( rAttr.IsCropped() )
2386     {
2387         rBmpEx.Crop( rCropRect );
2388 
2389         // #104115# Negative crop sizes mean: enlarge bitmap and pad
2390         if( bEnlarge && (
2391             rCropLeftTop.Width() < 0 ||
2392             rCropLeftTop.Height() < 0 ||
2393             rCropRightBottom.Width() < 0 ||
2394             rCropRightBottom.Height() < 0 ) )
2395         {
2396             Size aBmpSize( rBmpEx.GetSizePixel() );
2397             sal_Int32 nPadLeft( rCropLeftTop.Width() < 0 ? -rCropLeftTop.Width() : 0 );
2398             sal_Int32 nPadTop( rCropLeftTop.Height() < 0 ? -rCropLeftTop.Height() : 0 );
2399             sal_Int32 nPadTotalWidth( aBmpSize.Width() + nPadLeft + (rCropRightBottom.Width() < 0 ? -rCropRightBottom.Width() : 0) );
2400             sal_Int32 nPadTotalHeight( aBmpSize.Height() + nPadTop + (rCropRightBottom.Height() < 0 ? -rCropRightBottom.Height() : 0) );
2401 
2402             BitmapEx aBmpEx2;
2403 
2404             if( rBmpEx.IsTransparent() )
2405             {
2406                 if( rBmpEx.IsAlpha() )
2407                     aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetAlpha() );
2408                 else
2409                     aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetMask() );
2410             }
2411             else
2412             {
2413                 // #104115# Generate mask bitmap and init to zero
2414                 Bitmap aMask( aBmpSize, 1 );
2415                 aMask.Erase( Color(0,0,0) );
2416 
2417                 // #104115# Always generate transparent bitmap, we need the border transparent
2418                 aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), aMask );
2419 
2420                 // #104115# Add opaque mask to source bitmap, otherwise the destination remains transparent
2421                 rBmpEx = aBmpEx2;
2422             }
2423 
2424             aBmpEx2.SetSizePixel( Size(nPadTotalWidth, nPadTotalHeight) );
2425             aBmpEx2.Erase( Color(0xFF,0,0,0) );
2426             aBmpEx2.CopyPixel( Rectangle( Point(nPadLeft, nPadTop), aBmpSize ), Rectangle( Point(0, 0), aBmpSize ), &rBmpEx );
2427             rBmpEx = aBmpEx2;
2428         }
2429     }
2430 
2431     const Size 	aSizePixel( rBmpEx.GetSizePixel() );
2432 
2433     if( rAttr.GetRotation() != 0 && !IsAnimated() )
2434     {
2435         if( aSizePixel.Width() && aSizePixel.Height() && rDstSize.Width() && rDstSize.Height() )
2436         {
2437             double fSrcWH = (double) aSizePixel.Width() / aSizePixel.Height();
2438             double fDstWH = (double) rDstSize.Width() / rDstSize.Height();
2439             double fScaleX = 1.0, fScaleY = 1.0;
2440 
2441             // always choose scaling to shrink bitmap
2442             if( fSrcWH < fDstWH )
2443                 fScaleY = aSizePixel.Width() / ( fDstWH * aSizePixel.Height() );
2444             else
2445                 fScaleX = fDstWH * aSizePixel.Height() / aSizePixel.Width();
2446 
2447             rBmpEx.Scale( fScaleX, fScaleY );
2448         }
2449     }
2450 }
2451