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