Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <vcl/bmpacc.hxx>
21 : #include <tools/poly.hxx>
22 : #include <vcl/outdev.hxx>
23 : #include <vcl/window.hxx>
24 : #include <vcl/gdimtf.hxx>
25 : #include <vcl/metaact.hxx>
26 : #include <vcl/metric.hxx>
27 : #include <vcl/animate.hxx>
28 : #include <vcl/alpha.hxx>
29 : #include <vcl/virdev.hxx>
30 : #include "grfcache.hxx"
31 : #include <svtools/grfmgr.hxx>
32 :
33 : // -----------
34 : // - defines -
35 : // -----------
36 :
37 : #define WATERMARK_LUM_OFFSET 50
38 : #define WATERMARK_CON_OFFSET -70
39 : #define MAP( cVal0, cVal1, nFrac ) ((sal_uInt8)((((long)(cVal0)<<20L)+nFrac*((long)(cVal1)-(cVal0)))>>20L))
40 :
41 : // ------------------
42 : // - GraphicManager -
43 : // ------------------
44 :
45 64 : GraphicManager::GraphicManager( sal_uLong nCacheSize, sal_uLong nMaxObjCacheSize ) :
46 64 : mpCache( new GraphicCache( nCacheSize, nMaxObjCacheSize ) )
47 : {
48 64 : }
49 :
50 10 : GraphicManager::~GraphicManager()
51 : {
52 5 : for( size_t i = 0, n = maObjList.size(); i < n; ++i )
53 0 : maObjList[ i ]->GraphicManagerDestroyed();
54 :
55 5 : delete mpCache;
56 5 : }
57 :
58 0 : void GraphicManager::SetMaxCacheSize( sal_uLong nNewCacheSize )
59 : {
60 0 : mpCache->SetMaxDisplayCacheSize( nNewCacheSize );
61 0 : }
62 :
63 0 : void GraphicManager::SetMaxObjCacheSize( sal_uLong nNewMaxObjSize, sal_Bool bDestroyGreaterCached )
64 : {
65 0 : mpCache->SetMaxObjDisplayCacheSize( nNewMaxObjSize, bDestroyGreaterCached );
66 0 : }
67 :
68 64 : void GraphicManager::SetCacheTimeout( sal_uLong nTimeoutSeconds )
69 : {
70 64 : mpCache->SetCacheTimeout( nTimeoutSeconds );
71 64 : }
72 :
73 0 : void GraphicManager::ReleaseFromCache( const GraphicObject& /*rObj*/ )
74 : {
75 : // !!!
76 0 : }
77 :
78 38 : sal_Bool GraphicManager::IsInCache( OutputDevice* pOut, const Point& rPt,
79 : const Size& rSz, const GraphicObject& rObj,
80 : const GraphicAttr& rAttr ) const
81 : {
82 38 : return mpCache->IsInDisplayCache( pOut, rPt, rSz, rObj, rAttr );
83 : }
84 :
85 50 : sal_Bool GraphicManager::DrawObj( OutputDevice* pOut, const Point& rPt, const Size& rSz,
86 : GraphicObject& rObj, const GraphicAttr& rAttr,
87 : const sal_uLong nFlags, sal_Bool& rCached )
88 : {
89 50 : Point aPt( rPt );
90 50 : Size aSz( rSz );
91 50 : sal_Bool bRet = sal_False;
92 :
93 50 : rCached = sal_False;
94 :
95 50 : if( ( rObj.GetType() == GRAPHIC_BITMAP ) || ( rObj.GetType() == GRAPHIC_GDIMETAFILE ) )
96 : {
97 : // create output and fill cache
98 :
99 212 : if( rObj.IsAnimated() || ( pOut->GetOutDevType() == OUTDEV_PRINTER ) ||
100 50 : ( !( nFlags & GRFMGR_DRAW_NO_SUBSTITUTE ) &&
101 : ( ( nFlags & GRFMGR_DRAW_SUBSTITUTE ) ||
102 50 : !( nFlags & GRFMGR_DRAW_CACHED ) ||
103 62 : ( pOut->GetConnectMetaFile() && !pOut->IsOutputEnabled() ) ) ) )
104 : {
105 : // simple output of transformed graphic
106 12 : const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) );
107 :
108 12 : if( aGraphic.IsSupportedGraphic() )
109 : {
110 12 : const sal_uInt16 nRot10 = rAttr.GetRotation() % 3600;
111 :
112 12 : if( nRot10 )
113 : {
114 0 : Polygon aPoly( Rectangle( aPt, aSz ) );
115 :
116 0 : aPoly.Rotate( aPt, nRot10 );
117 0 : const Rectangle aRotBoundRect( aPoly.GetBoundRect() );
118 0 : aPt = aRotBoundRect.TopLeft();
119 0 : aSz = aRotBoundRect.GetSize();
120 : }
121 :
122 12 : aGraphic.Draw( pOut, aPt, aSz );
123 : }
124 :
125 12 : bRet = sal_True;
126 : }
127 :
128 50 : if( !bRet )
129 : {
130 : // cached/direct drawing
131 38 : if( !mpCache->DrawDisplayCacheObj( pOut, aPt, aSz, rObj, rAttr ) )
132 18 : bRet = ImplDraw( pOut, aPt, aSz, rObj, rAttr, nFlags, rCached );
133 : else
134 20 : bRet = rCached = sal_True;
135 : }
136 : }
137 :
138 50 : return bRet;
139 : }
140 :
141 4298 : void GraphicManager::ImplRegisterObj( const GraphicObject& rObj, Graphic& rSubstitute,
142 : const rtl::OString* pID, const GraphicObject* pCopyObj )
143 : {
144 4298 : maObjList.push_back( (GraphicObject*)&rObj );
145 4298 : mpCache->AddGraphicObject( rObj, rSubstitute, pID, pCopyObj );
146 4298 : }
147 :
148 3681 : void GraphicManager::ImplUnregisterObj( const GraphicObject& rObj )
149 : {
150 3681 : mpCache->ReleaseGraphicObject( rObj );
151 65260 : for( GraphicObjectList_impl::iterator it = maObjList.begin(); it != maObjList.end(); ++it )
152 : {
153 65260 : if ( *it == &rObj ) {
154 3681 : maObjList.erase( it );
155 3681 : break;
156 : }
157 : }
158 3681 : }
159 :
160 38 : void GraphicManager::ImplGraphicObjectWasSwappedOut( const GraphicObject& rObj )
161 : {
162 38 : mpCache->GraphicObjectWasSwappedOut( rObj );
163 38 : }
164 :
165 272 : rtl::OString GraphicManager::ImplGetUniqueID( const GraphicObject& rObj ) const
166 : {
167 272 : return mpCache->GetUniqueID( rObj );
168 : }
169 :
170 4 : sal_Bool GraphicManager::ImplFillSwappedGraphicObject( const GraphicObject& rObj, Graphic& rSubstitute )
171 : {
172 4 : return( mpCache->FillSwappedGraphicObject( rObj, rSubstitute ) );
173 : }
174 :
175 4 : void GraphicManager::ImplGraphicObjectWasSwappedIn( const GraphicObject& rObj )
176 : {
177 4 : mpCache->GraphicObjectWasSwappedIn( rObj );
178 4 : }
179 :
180 18 : sal_Bool GraphicManager::ImplDraw( OutputDevice* pOut, const Point& rPt,
181 : const Size& rSz, GraphicObject& rObj,
182 : const GraphicAttr& rAttr,
183 : const sal_uLong nFlags, sal_Bool& rCached )
184 : {
185 18 : const Graphic& rGraphic = rObj.GetGraphic();
186 18 : sal_Bool bRet = sal_False;
187 :
188 18 : if( rGraphic.IsSupportedGraphic() && !rGraphic.IsSwapOut() )
189 : {
190 18 : if( GRAPHIC_BITMAP == rGraphic.GetType() )
191 : {
192 16 : const BitmapEx aSrcBmpEx( rGraphic.GetBitmapEx() );
193 :
194 : // #i46805# No point in caching a bitmap that is rendered
195 : // via RectFill on the OutDev
196 32 : if( !(pOut->GetDrawMode() & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP )) &&
197 16 : mpCache->IsDisplayCacheable( pOut, rPt, rSz, rObj, rAttr ) )
198 : {
199 16 : BitmapEx aDstBmpEx;
200 :
201 16 : if( ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr, nFlags, &aDstBmpEx ) )
202 : {
203 16 : rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx );
204 16 : bRet = sal_True;
205 16 : }
206 : }
207 :
208 16 : if( !bRet )
209 0 : bRet = ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr, nFlags );
210 : }
211 : else
212 : {
213 2 : const GDIMetaFile& rSrcMtf = rGraphic.GetGDIMetaFile();
214 :
215 2 : if( mpCache->IsDisplayCacheable( pOut, rPt, rSz, rObj, rAttr ) )
216 : {
217 2 : GDIMetaFile aDstMtf;
218 2 : BitmapEx aContainedBmpEx;
219 :
220 2 : if( ImplCreateOutput( pOut, rPt, rSz, rSrcMtf, rAttr, nFlags, aDstMtf, aContainedBmpEx ) )
221 : {
222 2 : if( !!aContainedBmpEx )
223 : {
224 : // Use bitmap output method, if metafile basically contains only a single
225 : // bitmap (allows caching the resulting pixmap).
226 0 : BitmapEx aDstBmpEx;
227 :
228 0 : if( ImplCreateOutput( pOut, rPt, rSz, aContainedBmpEx, rAttr, nFlags, &aDstBmpEx ) )
229 : {
230 0 : rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx );
231 0 : bRet = sal_True;
232 0 : }
233 : }
234 : else
235 : {
236 2 : rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstMtf );
237 2 : bRet = sal_True;
238 : }
239 2 : }
240 : }
241 :
242 2 : if( !bRet )
243 : {
244 0 : const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) );
245 :
246 0 : if( aGraphic.IsSupportedGraphic() )
247 : {
248 0 : aGraphic.Draw( pOut, rPt, rSz );
249 0 : bRet = sal_True;
250 0 : }
251 : }
252 : }
253 : }
254 :
255 18 : return bRet;
256 : }
257 :
258 10 : sal_Bool ImplCreateRotatedScaled( const BitmapEx& rBmpEx, const GraphicAttr& rAttributes,
259 : sal_uInt16 nRot10, const Size& rUnrotatedSzPix,
260 : long nStartX, long nEndX, long nStartY, long nEndY,
261 : BitmapEx& rOutBmpEx )
262 : {
263 10 : const long aUnrotatedWidth = rUnrotatedSzPix.Width();
264 10 : const long aUnrotatedHeight = rUnrotatedSzPix.Height();
265 10 : const long aBitmapWidth = rBmpEx.GetSizePixel().Width();
266 10 : const long aBitmapHeight = rBmpEx.GetSizePixel().Height();
267 :
268 : long nX, nY, nTmpX, nTmpY, nTmpFX, nTmpFY, nTmp;
269 : double fTmp;
270 :
271 10 : bool bHMirr = ( rAttributes.GetMirrorFlags() & BMP_MIRROR_HORZ ) != 0;
272 10 : bool bVMirr = ( rAttributes.GetMirrorFlags() & BMP_MIRROR_VERT ) != 0;
273 :
274 10 : long* pMapIX = new long[ aUnrotatedWidth ];
275 10 : long* pMapFX = new long[ aUnrotatedWidth ];
276 10 : long* pMapIY = new long[ aUnrotatedHeight ];
277 10 : long* pMapFY = new long[ aUnrotatedHeight ];
278 :
279 10 : const double fScaleX = ( aUnrotatedWidth - 1 ) / (double) ( aBitmapWidth - 1 );
280 10 : const double fScaleY = ( aUnrotatedHeight - 1 ) / (double) ( aBitmapHeight - 1 );
281 :
282 10 : const double fRevScaleX = 1.0 / fScaleX;
283 10 : const double fRevScaleY = 1.0 / fScaleY;
284 :
285 : int x,y;
286 :
287 : // create horizontal mapping table
288 1856 : for( x = 0, nTmpX = aBitmapWidth - 1L, nTmp = aBitmapWidth - 2L; x < aUnrotatedWidth; x++ )
289 : {
290 1846 : fTmp = x * fRevScaleX;
291 :
292 1846 : if( bHMirr )
293 0 : fTmp = nTmpX - fTmp;
294 :
295 1846 : pMapIX[ x ] = MinMax( fTmp, 0, nTmp );
296 1846 : pMapFX[ x ] = (long) ( ( fTmp - pMapIX[ x ] ) * 1048576.0 );
297 : }
298 :
299 : // create vertical mapping table
300 532 : for( y = 0, nTmpY = aBitmapHeight - 1L, nTmp = aBitmapHeight - 2L; y < aUnrotatedHeight; y++ )
301 : {
302 522 : fTmp = y * fRevScaleY;
303 :
304 522 : if( bVMirr )
305 0 : fTmp = nTmpY - fTmp;
306 :
307 522 : pMapIY[ y ] = MinMax( fTmp, 0, nTmp );
308 522 : pMapFY[ y ] = (long) ( ( fTmp - pMapIY[ y ] ) * 1048576.0 );
309 : }
310 :
311 :
312 10 : Bitmap aBmp( rBmpEx.GetBitmap() );
313 10 : Bitmap aOutBmp;
314 10 : BitmapReadAccess* pReadAccess = aBmp.AcquireReadAccess();
315 : BitmapWriteAccess* pWriteAccess;
316 :
317 10 : const double fCosAngle = cos( nRot10 * F_PI1800 );
318 10 : const double fSinAngle = sin( nRot10 * F_PI1800 );
319 10 : const long aTargetWidth = nEndX - nStartX + 1L;
320 10 : const long aTargetHeight = nEndY - nStartY + 1L;
321 10 : long* pCosX = new long[ aTargetWidth ];
322 10 : long* pSinX = new long[ aTargetWidth ];
323 10 : long* pCosY = new long[ aTargetHeight ];
324 10 : long* pSinY = new long[ aTargetHeight ];
325 : long nUnRotX, nUnRotY, nSinY, nCosY;
326 : sal_uInt8 cR0, cG0, cB0, cR1, cG1, cB1;
327 10 : bool bRet = false;
328 :
329 10 : Polygon aPoly( Rectangle( Point(), rUnrotatedSzPix ) );
330 10 : aPoly.Rotate( Point(), nRot10 );
331 10 : Rectangle aNewBound( aPoly.GetBoundRect() );
332 :
333 10 : bool scaleByAveraging = fScaleX < 0.6 || fScaleY < 0.6;
334 :
335 : // create horizontal mapping table
336 1856 : for( x = 0, nTmpX = aNewBound.Left() + nStartX; x < aTargetWidth; x++ )
337 : {
338 1846 : pCosX[ x ] = FRound( fCosAngle * ( fTmp = nTmpX++ << 8 ) );
339 1846 : pSinX[ x ] = FRound( fSinAngle * fTmp );
340 : }
341 :
342 : // create vertical mapping table
343 532 : for( y = 0, nTmpY = aNewBound.Top() + nStartY; y < aTargetHeight; y++ )
344 : {
345 522 : pCosY[ y ] = FRound( fCosAngle * ( fTmp = nTmpY++ << 8 ) );
346 522 : pSinY[ y ] = FRound( fSinAngle * fTmp );
347 : }
348 :
349 10 : if( pReadAccess )
350 : {
351 10 : aOutBmp = Bitmap( Size( aTargetWidth, aTargetHeight ), 24 );
352 10 : pWriteAccess = aOutBmp.AcquireWriteAccess();
353 :
354 10 : if( pWriteAccess )
355 : {
356 10 : BitmapColor aColRes;
357 :
358 10 : if ( !scaleByAveraging )
359 : {
360 8 : if( pReadAccess->HasPalette() )
361 : {
362 262 : for( y = 0; y < aTargetHeight; y++ )
363 : {
364 258 : nSinY = pSinY[ y ];
365 258 : nCosY = pCosY[ y ];
366 :
367 137802 : for( x = 0; x < aTargetWidth; x++ )
368 : {
369 137544 : nUnRotX = ( pCosX[ x ] - nSinY ) >> 8;
370 137544 : nUnRotY = ( pSinX[ x ] + nCosY ) >> 8;
371 :
372 137544 : if( ( nUnRotX >= 0L ) && ( nUnRotX < aUnrotatedWidth ) &&
373 : ( nUnRotY >= 0L ) && ( nUnRotY < aUnrotatedHeight ) )
374 : {
375 137544 : nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ];
376 137544 : nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
377 :
378 137544 : const BitmapColor& rCol0 = pReadAccess->GetPaletteColor( pReadAccess->GetPixel( nTmpY, nTmpX ) );
379 137544 : const BitmapColor& rCol1 = pReadAccess->GetPaletteColor( pReadAccess->GetPixel( nTmpY, ++nTmpX ) );
380 137544 : cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTmpFX );
381 137544 : cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTmpFX );
382 137544 : cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTmpFX );
383 :
384 137544 : const BitmapColor& rCol3 = pReadAccess->GetPaletteColor( pReadAccess->GetPixel( ++nTmpY, nTmpX ) );
385 137544 : const BitmapColor& rCol2 = pReadAccess->GetPaletteColor( pReadAccess->GetPixel( nTmpY, --nTmpX ) );
386 137544 : cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTmpFX );
387 137544 : cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTmpFX );
388 137544 : cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTmpFX );
389 :
390 137544 : aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
391 137544 : aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
392 137544 : aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
393 137544 : pWriteAccess->SetPixel( y, x, aColRes );
394 : }
395 : }
396 : }
397 : }
398 : else
399 : {
400 4 : BitmapColor aCol0, aCol1;
401 :
402 198 : for( y = 0; y < aTargetHeight; y++ )
403 : {
404 194 : nSinY = pSinY[ y ];
405 194 : nCosY = pCosY[ y ];
406 :
407 15122 : for( x = 0; x < aTargetWidth; x++ )
408 : {
409 14928 : nUnRotX = ( pCosX[ x ] - nSinY ) >> 8;
410 14928 : nUnRotY = ( pSinX[ x ] + nCosY ) >> 8;
411 :
412 14928 : if( ( nUnRotX >= 0L ) && ( nUnRotX < aUnrotatedWidth ) &&
413 : ( nUnRotY >= 0L ) && ( nUnRotY < aUnrotatedHeight ) )
414 : {
415 14928 : nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ];
416 14928 : nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
417 :
418 14928 : aCol0 = pReadAccess->GetPixel( nTmpY, nTmpX );
419 14928 : aCol1 = pReadAccess->GetPixel( nTmpY, ++nTmpX );
420 14928 : cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
421 14928 : cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
422 14928 : cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
423 :
424 14928 : aCol1 = pReadAccess->GetPixel( ++nTmpY, nTmpX );
425 14928 : aCol0 = pReadAccess->GetPixel( nTmpY, --nTmpX );
426 14928 : cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
427 14928 : cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
428 14928 : cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
429 :
430 14928 : aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
431 14928 : aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
432 14928 : aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
433 14928 : pWriteAccess->SetPixel( y, x, aColRes );
434 : }
435 : }
436 4 : }
437 : }
438 : }
439 : else
440 : {
441 : double aSumRed, aSumGreen, aSumBlue, aCount;
442 2 : BitmapColor aColor;
443 2 : BitmapColor aResultColor;
444 :
445 72 : for( y = 0; y < aTargetHeight; y++ )
446 : {
447 70 : nSinY = pSinY[ y ];
448 70 : nCosY = pCosY[ y ];
449 :
450 3570 : for( x = 0; x < aTargetWidth; x++ )
451 : {
452 3500 : double aUnrotatedX = ( pCosX[ x ] - nSinY ) / 256.0;
453 3500 : double aUnrotatedY = ( pSinX[ x ] + nCosY ) / 256.0;
454 :
455 3500 : if( ( aUnrotatedX >= 0 ) && ( aUnrotatedX < aUnrotatedWidth ) &&
456 : ( aUnrotatedY >= 0 ) && ( aUnrotatedY < aUnrotatedHeight ) )
457 : {
458 3500 : double dYStart = ((aUnrotatedY + 0.5) * fRevScaleY) - 0.5;
459 3500 : double dYEnd = ((aUnrotatedY + 1.5) * fRevScaleY) - 0.5;
460 :
461 3500 : int yStart = MinMax( dYStart, 0, aBitmapHeight - 1);
462 3500 : int yEnd = MinMax( dYEnd, 0, aBitmapHeight - 1);
463 :
464 3500 : double dXStart = ((aUnrotatedX + 0.5) * fRevScaleX) - 0.5;
465 3500 : double dXEnd = ((aUnrotatedX + 1.5) * fRevScaleX) - 0.5;
466 :
467 3500 : int xStart = MinMax( dXStart, 0, aBitmapWidth - 1);
468 3500 : int xEnd = MinMax( dXEnd, 0, aBitmapWidth - 1);
469 :
470 3500 : aSumRed = aSumGreen = aSumBlue = 0.0;
471 3500 : aCount = 0;
472 :
473 21000 : for (int yIn = yStart; yIn <= yEnd; yIn++)
474 : {
475 104300 : for (int xIn = xStart; xIn <= xEnd; xIn++)
476 : {
477 86800 : aColor = pReadAccess->GetPixel( yIn, xIn );
478 :
479 86800 : if( pReadAccess->HasPalette() )
480 86800 : aColor = pReadAccess->GetPaletteColor( aColor );
481 :
482 86800 : aSumRed += aColor.GetRed();
483 86800 : aSumGreen += aColor.GetGreen();
484 86800 : aSumBlue += aColor.GetBlue();
485 :
486 86800 : aCount++;
487 : }
488 : }
489 :
490 3500 : aResultColor.SetRed( MinMax( aSumRed / aCount, 0, 255) );
491 3500 : aResultColor.SetGreen( MinMax( aSumGreen / aCount, 0, 255) );
492 3500 : aResultColor.SetBlue( MinMax( aSumBlue / aCount, 0, 255) );
493 :
494 3500 : pWriteAccess->SetPixel( y, x, aResultColor );
495 : }
496 : }
497 2 : }
498 : }
499 :
500 10 : aOutBmp.ReleaseAccess( pWriteAccess );
501 10 : bRet = true;
502 : }
503 :
504 10 : aBmp.ReleaseAccess( pReadAccess );
505 : }
506 :
507 : // mask processing
508 10 : if( bRet && ( rBmpEx.IsTransparent() || ( nRot10 != 0 && nRot10 != 900 && nRot10 != 1800 && nRot10 != 2700 ) ) )
509 : {
510 4 : bRet = false;
511 :
512 4 : if( rBmpEx.IsAlpha() )
513 : {
514 0 : AlphaMask aAlpha( rBmpEx.GetAlpha() );
515 0 : AlphaMask aOutAlpha;
516 :
517 0 : pReadAccess = aAlpha.AcquireReadAccess();
518 :
519 0 : if( pReadAccess )
520 : {
521 0 : aOutAlpha = AlphaMask( Size( aTargetWidth, aTargetHeight ) );
522 0 : pWriteAccess = aOutAlpha.AcquireWriteAccess();
523 :
524 0 : if( pWriteAccess )
525 : {
526 0 : if( pReadAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL &&
527 0 : pWriteAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
528 : {
529 0 : if ( !scaleByAveraging )
530 : {
531 : Scanline pLine0, pLine1, pLineW;
532 :
533 0 : for( nY = 0; nY < aTargetHeight; nY++ )
534 : {
535 0 : nSinY = pSinY[ nY ], nCosY = pCosY[ nY ];
536 0 : pLineW = pWriteAccess->GetScanline( nY );
537 :
538 0 : for( nX = 0; nX < aTargetWidth; nX++ )
539 : {
540 0 : nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
541 0 : nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
542 :
543 0 : if( ( nUnRotX >= 0L ) && ( nUnRotX < aUnrotatedWidth ) &&
544 : ( nUnRotY >= 0L ) && ( nUnRotY < aUnrotatedHeight ) )
545 : {
546 0 : nTmpX = pMapIX[ nUnRotX ], nTmpFX = pMapFX[ nUnRotX ];
547 0 : nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
548 :
549 0 : pLine0 = pReadAccess->GetScanline( nTmpY++ );
550 0 : pLine1 = pReadAccess->GetScanline( nTmpY );
551 :
552 0 : const long nAlpha0 = pLine0[ nTmpX ];
553 0 : const long nAlpha2 = pLine1[ nTmpX++ ];
554 0 : const long nAlpha1 = pLine0[ nTmpX ];
555 0 : const long nAlpha3 = pLine1[ nTmpX ];
556 0 : const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX );
557 0 : const long n1 = MAP( nAlpha2, nAlpha3, nTmpFX );
558 :
559 0 : *pLineW++ = MAP( n0, n1, nTmpFY );
560 : }
561 : else
562 0 : *pLineW++ = 255;
563 : }
564 : }
565 : }
566 : else
567 : {
568 0 : const BitmapColor aTrans( pWriteAccess->GetBestMatchingColor( Color( COL_WHITE ) ) );
569 0 : BitmapColor aResultColor( 0 );
570 : double aSum, aCount;
571 :
572 0 : for( y = 0; y < aTargetHeight; y++ )
573 : {
574 0 : nSinY = pSinY[ y ];
575 0 : nCosY = pCosY[ y ];
576 :
577 0 : for( x = 0; x < aTargetWidth; x++ )
578 : {
579 :
580 0 : double aUnrotatedX = ( pCosX[ x ] - nSinY ) / 256.0;
581 0 : double aUnrotatedY = ( pSinX[ x ] + nCosY ) / 256.0;
582 :
583 0 : if( ( aUnrotatedX >= 0 ) && ( aUnrotatedX < aUnrotatedWidth ) &&
584 : ( aUnrotatedY >= 0 ) && ( aUnrotatedY < aUnrotatedHeight ) )
585 : {
586 0 : double dYStart = ((aUnrotatedY + 0.5) * fRevScaleY) - 0.5;
587 0 : double dYEnd = ((aUnrotatedY + 1.5) * fRevScaleY) - 0.5;
588 :
589 0 : int yStart = MinMax( dYStart, 0, aBitmapHeight - 1);
590 0 : int yEnd = MinMax( dYEnd, 0, aBitmapHeight - 1);
591 :
592 0 : double dXStart = ((aUnrotatedX + 0.5) * fRevScaleX) - 0.5;
593 0 : double dXEnd = ((aUnrotatedX + 1.5) * fRevScaleX) - 0.5;
594 :
595 0 : int xStart = MinMax( dXStart, 0, aBitmapWidth - 1);
596 0 : int xEnd = MinMax( dXEnd, 0, aBitmapWidth - 1);
597 :
598 0 : aSum = 0.0;
599 0 : aCount = 0;
600 :
601 0 : for (int yIn = yStart; yIn <= yEnd; yIn++)
602 : {
603 0 : for (int xIn = xStart; xIn <= xEnd; xIn++)
604 : {
605 0 : aSum += pReadAccess->GetPixel( yIn, xIn ).GetIndex();
606 0 : aCount++;
607 : }
608 : }
609 0 : aResultColor.SetIndex( MinMax( aSum / aCount, 0, 255) );
610 0 : pWriteAccess->SetPixel( y, x, aResultColor );
611 : }
612 : else
613 : {
614 0 : pWriteAccess->SetPixel( y, x, aTrans );
615 : }
616 : }
617 0 : }
618 : }
619 : }
620 : else
621 : {
622 0 : const BitmapColor aTrans( pWriteAccess->GetBestMatchingColor( Color( COL_WHITE ) ) );
623 0 : BitmapColor aAlphaVal( 0 );
624 :
625 0 : for( nY = 0; nY < aTargetHeight; nY++ )
626 : {
627 0 : nSinY = pSinY[ nY ], nCosY = pCosY[ nY ];
628 :
629 0 : for( nX = 0; nX < aTargetWidth; nX++ )
630 : {
631 0 : nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
632 0 : nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
633 :
634 0 : if( ( nUnRotX >= 0L ) && ( nUnRotX < aUnrotatedWidth ) &&
635 : ( nUnRotY >= 0L ) && ( nUnRotY < aUnrotatedHeight ) )
636 : {
637 0 : nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ];
638 0 : nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
639 :
640 0 : const long nAlpha0 = pReadAccess->GetPixel( nTmpY, nTmpX ).GetIndex();
641 0 : const long nAlpha1 = pReadAccess->GetPixel( nTmpY, ++nTmpX ).GetIndex();
642 0 : const long nAlpha3 = pReadAccess->GetPixel( ++nTmpY, nTmpX ).GetIndex();
643 0 : const long nAlpha2 = pReadAccess->GetPixel( nTmpY, --nTmpX ).GetIndex();
644 0 : const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX );
645 0 : const long n1 = MAP( nAlpha2, nAlpha3, nTmpFX );
646 :
647 0 : aAlphaVal.SetIndex( MAP( n0, n1, nTmpFY ) );
648 0 : pWriteAccess->SetPixel( nY, nX, aAlphaVal );
649 : }
650 : else
651 0 : pWriteAccess->SetPixel( nY, nX, aTrans );
652 : }
653 0 : }
654 : }
655 :
656 0 : aOutAlpha.ReleaseAccess( pWriteAccess );
657 0 : bRet = sal_True;
658 : }
659 :
660 0 : aAlpha.ReleaseAccess( pReadAccess );
661 : }
662 :
663 0 : if( bRet )
664 0 : rOutBmpEx = BitmapEx( aOutBmp, aOutAlpha );
665 : }
666 : else
667 : {
668 4 : Bitmap aOutMsk( Size( aTargetWidth, aTargetHeight ), 1 );
669 4 : pWriteAccess = aOutMsk.AcquireWriteAccess();
670 :
671 4 : if( pWriteAccess )
672 : {
673 4 : Bitmap aMsk( rBmpEx.GetMask() );
674 4 : const BitmapColor aB( pWriteAccess->GetBestMatchingColor( Color( COL_BLACK ) ) );
675 4 : const BitmapColor aW( pWriteAccess->GetBestMatchingColor( Color( COL_WHITE ) ) );
676 4 : BitmapReadAccess* pMAcc = NULL;
677 :
678 4 : if( !aMsk || ( ( pMAcc = aMsk.AcquireReadAccess() ) != NULL ) )
679 : {
680 4 : long* pMapLX = new long[ aUnrotatedWidth ];
681 4 : long* pMapLY = new long[ aUnrotatedHeight ];
682 4 : BitmapColor aTestB;
683 :
684 4 : if( pMAcc )
685 4 : aTestB = pMAcc->GetBestMatchingColor( Color( COL_BLACK ) );
686 :
687 : // create new horizontal mapping table
688 1496 : for( nX = 0UL; nX < aUnrotatedWidth; nX++ )
689 1492 : pMapLX[ nX ] = FRound( (double) pMapIX[ nX ] + pMapFX[ nX ] / 1048576. );
690 :
691 : // create new vertical mapping table
692 264 : for( nY = 0UL; nY < aUnrotatedHeight; nY++ )
693 260 : pMapLY[ nY ] = FRound( (double) pMapIY[ nY ] + pMapFY[ nY ] / 1048576. );
694 :
695 : // do mask rotation
696 264 : for( nY = 0; nY < aTargetHeight; nY++ )
697 : {
698 260 : nSinY = pSinY[ nY ];
699 260 : nCosY = pCosY[ nY ];
700 :
701 136000 : for( nX = 0; nX < aTargetWidth; nX++ )
702 : {
703 135740 : nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
704 135740 : nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
705 :
706 135740 : if( ( nUnRotX >= 0L ) && ( nUnRotX < aUnrotatedWidth ) &&
707 : ( nUnRotY >= 0L ) && ( nUnRotY < aUnrotatedHeight ) )
708 : {
709 271480 : if( pMAcc )
710 : {
711 135740 : if( pMAcc->GetPixel( pMapLY[ nUnRotY ], pMapLX[ nUnRotX ] ) == aTestB )
712 122772 : pWriteAccess->SetPixel( nY, nX, aB );
713 : else
714 12968 : pWriteAccess->SetPixel( nY, nX, aW );
715 : }
716 : else
717 0 : pWriteAccess->SetPixel( nY, nX, aB );
718 : }
719 : else
720 0 : pWriteAccess->SetPixel( nY, nX, aW );
721 : }
722 : }
723 :
724 4 : delete[] pMapLX;
725 4 : delete[] pMapLY;
726 :
727 4 : if( pMAcc )
728 4 : aMsk.ReleaseAccess( pMAcc );
729 :
730 4 : bRet = sal_True;
731 : }
732 :
733 4 : aOutMsk.ReleaseAccess( pWriteAccess );
734 : }
735 :
736 4 : if( bRet )
737 4 : rOutBmpEx = BitmapEx( aOutBmp, aOutMsk );
738 : }
739 :
740 4 : if( !bRet )
741 0 : rOutBmpEx = aOutBmp;
742 : }
743 : else
744 6 : rOutBmpEx = aOutBmp;
745 :
746 10 : delete[] pSinX;
747 10 : delete[] pCosX;
748 10 : delete[] pSinY;
749 10 : delete[] pCosY;
750 :
751 10 : delete[] pMapIX;
752 10 : delete[] pMapFX;
753 10 : delete[] pMapIY;
754 10 : delete[] pMapFY;
755 :
756 10 : return bRet;
757 : }
758 :
759 16 : sal_Bool GraphicManager::ImplCreateOutput( OutputDevice* pOutputDevice,
760 : const Point& rPoint, const Size& rSize,
761 : const BitmapEx& rBitmapEx, const GraphicAttr& rAttributes,
762 : const sal_uLong /*nFlags*/, BitmapEx* pBmpEx )
763 : {
764 16 : sal_uInt16 nRot10 = rAttributes.GetRotation() % 3600;
765 :
766 16 : Point aOutputPointPix;
767 16 : Size aOutputSizePix;
768 16 : Point aUnrotatedPointPix( pOutputDevice->LogicToPixel( rPoint ) );
769 16 : Size aUnrotatedSizePix( pOutputDevice->LogicToPixel( rSize ) );
770 :
771 16 : bool bRet = false;
772 :
773 16 : if( nRot10 )
774 : {
775 0 : Polygon aPoly( Rectangle( rPoint, rSize ) );
776 0 : aPoly.Rotate( rPoint, nRot10 );
777 0 : const Rectangle aRotBoundRect( aPoly.GetBoundRect() );
778 0 : aOutputPointPix = pOutputDevice->LogicToPixel( aRotBoundRect.TopLeft() );
779 0 : aOutputSizePix = pOutputDevice->LogicToPixel( aRotBoundRect.GetSize() );
780 : }
781 : else
782 : {
783 16 : aOutputPointPix = aUnrotatedPointPix;
784 16 : aOutputSizePix = aUnrotatedSizePix;
785 : }
786 :
787 16 : if( aUnrotatedSizePix.Width() && aUnrotatedSizePix.Height() )
788 : {
789 16 : BitmapEx aBmpEx( rBitmapEx );
790 16 : BitmapEx aOutBmpEx;
791 16 : Point aOutPoint;
792 16 : Size aOutSize;
793 16 : const Size& rBmpSzPix = rBitmapEx.GetSizePixel();
794 16 : const long nW = rBmpSzPix.Width();
795 16 : const long nH = rBmpSzPix.Height();
796 16 : long nStartX = -1, nStartY = -1, nEndX = -1, nEndY = -1;
797 16 : bool bHMirr = ( rAttributes.GetMirrorFlags() & BMP_MIRROR_HORZ ) != 0;
798 16 : bool bVMirr = ( rAttributes.GetMirrorFlags() & BMP_MIRROR_VERT ) != 0;
799 :
800 : // calculate output sizes
801 16 : if( !pBmpEx )
802 : {
803 0 : Point aPt;
804 0 : Rectangle aOutRect( aPt, pOutputDevice->GetOutputSizePixel() );
805 0 : Rectangle aBmpRect( aOutputPointPix, aOutputSizePix );
806 :
807 0 : if( pOutputDevice->GetOutDevType() == OUTDEV_WINDOW )
808 : {
809 0 : const Region aPaintRgn( ( (Window*) pOutputDevice )->GetPaintRegion() );
810 0 : if( !aPaintRgn.IsNull() )
811 0 : aOutRect.Intersection( pOutputDevice->LogicToPixel( aPaintRgn.GetBoundRect() ) );
812 : }
813 :
814 0 : aOutRect.Intersection( aBmpRect );
815 :
816 0 : if( !aOutRect.IsEmpty() )
817 : {
818 0 : aOutPoint = pOutputDevice->PixelToLogic( aOutRect.TopLeft() );
819 0 : aOutSize = pOutputDevice->PixelToLogic( aOutRect.GetSize() );
820 0 : nStartX = aOutRect.Left() - aBmpRect.Left();
821 0 : nStartY = aOutRect.Top() - aBmpRect.Top();
822 0 : nEndX = aOutRect.Right() - aBmpRect.Left();
823 0 : nEndY = aOutRect.Bottom() - aBmpRect.Top();
824 : }
825 : else
826 : {
827 0 : nStartX = -1L; // invalid
828 : }
829 : }
830 : else
831 : {
832 16 : aOutPoint = pOutputDevice->PixelToLogic( aOutputPointPix );
833 16 : aOutSize = pOutputDevice->PixelToLogic( aOutputSizePix );
834 16 : nStartX = nStartY = 0;
835 16 : nEndX = aOutputSizePix.Width() - 1L;
836 16 : nEndY = aOutputSizePix.Height() - 1L;
837 : }
838 :
839 : // do transformation
840 16 : if( nStartX >= 0L )
841 : {
842 16 : const bool bSimple = ( 1 == nW || 1 == nH );
843 :
844 16 : if( nRot10 )
845 : {
846 0 : if( bSimple )
847 : {
848 0 : bRet = ( aOutBmpEx = aBmpEx ).Scale( aUnrotatedSizePix );
849 :
850 0 : if( bRet )
851 0 : aOutBmpEx.Rotate( nRot10, COL_TRANSPARENT );
852 : }
853 : else
854 : {
855 : bRet = ImplCreateRotatedScaled( aBmpEx, rAttributes,
856 : nRot10, aUnrotatedSizePix,
857 : nStartX, nEndX, nStartY, nEndY,
858 0 : aOutBmpEx );
859 : }
860 : }
861 : else
862 : {
863 16 : if( !bHMirr && !bVMirr && aOutputSizePix == rBmpSzPix )
864 : {
865 6 : aOutPoint = pOutputDevice->PixelToLogic( aOutputPointPix );
866 6 : aOutSize = pOutputDevice->PixelToLogic( aOutputSizePix );
867 6 : aOutBmpEx = aBmpEx;
868 6 : bRet = true;
869 : }
870 : else
871 : {
872 10 : if( bSimple )
873 : {
874 0 : bRet = ( aOutBmpEx = aBmpEx ).Scale( Size( nEndX - nStartX + 1, nEndY - nStartY + 1 ) );
875 : }
876 : else
877 : {
878 : bRet = ImplCreateRotatedScaled( aBmpEx, rAttributes,
879 : nRot10, aUnrotatedSizePix,
880 : nStartX, nEndX, nStartY, nEndY,
881 10 : aOutBmpEx );
882 : }
883 : }
884 : }
885 :
886 16 : if( bRet )
887 : {
888 : // Attribute adjustment if neccessary
889 16 : if( rAttributes.IsSpecialDrawMode() || rAttributes.IsAdjusted() || rAttributes.IsTransparent() )
890 0 : ImplAdjust( aOutBmpEx, rAttributes, ADJUSTMENT_DRAWMODE | ADJUSTMENT_COLORS | ADJUSTMENT_TRANSPARENCY );
891 :
892 : // OutDev adjustment if neccessary
893 16 : if( pOutputDevice->GetOutDevType() != OUTDEV_PRINTER && pOutputDevice->GetBitCount() <= 8 && aOutBmpEx.GetBitCount() >= 8 )
894 0 : aOutBmpEx.Dither( BMP_DITHER_MATRIX );
895 : }
896 : }
897 :
898 : // Create output
899 16 : if( bRet )
900 : {
901 16 : if( !pBmpEx )
902 0 : pOutputDevice->DrawBitmapEx( aOutPoint, aOutSize, aOutBmpEx );
903 : else
904 : {
905 16 : if( !rAttributes.IsTransparent() && !aOutBmpEx.IsAlpha() )
906 14 : aOutBmpEx = BitmapEx( aOutBmpEx.GetBitmap().CreateDisplayBitmap( pOutputDevice ), aOutBmpEx.GetMask() );
907 :
908 16 : pOutputDevice->DrawBitmapEx( aOutPoint, aOutSize, *pBmpEx = aOutBmpEx );
909 : }
910 16 : }
911 : }
912 :
913 16 : return bRet;
914 : }
915 :
916 : // This function checks whether the bitmap is usable for skipping
917 : // mtf rendering by using just this one bitmap (i.e. in case the metafile
918 : // contains just this one pixmap that covers the entire metafile area).
919 0 : static BitmapEx checkMetadataBitmap( const BitmapEx& rBmpEx,
920 : Point rSrcPoint,
921 : Size rSrcSize,
922 : const Point& rDestPoint,
923 : const Size& rDestSize,
924 : const Size& rRefSize,
925 : bool& o_rbNonBitmapActionEncountered )
926 : {
927 : // NOTE: If you do changes in this function, change checkMetadataBitmap() in grfcache.cxx too.
928 0 : BitmapEx aBmpEx;
929 0 : if( rSrcSize == Size())
930 0 : rSrcSize = rBmpEx.GetSizePixel();
931 :
932 0 : if( rDestPoint != Point( 0, 0 ))
933 : { // The pixmap in the metafile has an offset (and so would not cover)
934 : // the entire result -> fall back to mtf rendering.
935 0 : o_rbNonBitmapActionEncountered = true;
936 0 : return aBmpEx;
937 : }
938 0 : if( rDestSize != rRefSize )
939 : { // The pixmap is not fullscale (does not cover the entire metafile area).
940 : // HACK: The code here should refuse to use the bitmap directly
941 : // and fall back to mtf rendering, but there seem to be metafiles
942 : // that do not specify exactly their area (the Windows API requires apps
943 : // the specify it manually, the rectangle is specified as topleft/bottomright
944 : // rather than topleft/size [which may be confusing], and the docs
945 : // on the exact meaning are somewhat confusing as well), so if it turns
946 : // out this metafile really contains just one bitmap and no other painting,
947 : // and if the sizes almost match, just use the pixmap (which will be scaled
948 : // to fit exactly the requested size, so there should not be any actual problem
949 : // caused by this small difference). This will allow caching of the resulting
950 : // (scaled) pixmap, which can make a noticeable performance difference.
951 0 : if( rBmpEx.GetSizePixel().Width() > 100 && rBmpEx.GetSizePixel().Height() > 100
952 0 : && abs( rDestSize.Width() - rRefSize.Width()) < 5
953 0 : && abs( rDestSize.Height() - rRefSize.Height()) < 5 )
954 : ; // ok, assume it's close enough
955 : else
956 : { // fall back to mtf rendering
957 0 : o_rbNonBitmapActionEncountered = true;
958 0 : return aBmpEx;
959 : }
960 : }
961 :
962 0 : aBmpEx = rBmpEx;
963 :
964 0 : if( (rSrcPoint.X() != 0 && rSrcPoint.Y() != 0) ||
965 0 : rSrcSize != rBmpEx.GetSizePixel() )
966 : {
967 : // crop bitmap to given source rectangle (no
968 : // need to copy and convert the whole bitmap)
969 : const Rectangle aCropRect( rSrcPoint,
970 0 : rSrcSize );
971 0 : aBmpEx.Crop( aCropRect );
972 : }
973 :
974 0 : return aBmpEx;
975 : }
976 :
977 2 : sal_Bool GraphicManager::ImplCreateOutput( OutputDevice* pOut,
978 : const Point& rPt, const Size& rSz,
979 : const GDIMetaFile& rMtf, const GraphicAttr& rAttr,
980 : const sal_uLong /*nFlags*/, GDIMetaFile& rOutMtf, BitmapEx& rOutBmpEx )
981 : {
982 2 : const Size aNewSize( rMtf.GetPrefSize() );
983 :
984 2 : rOutMtf = rMtf;
985 :
986 : // Count bitmap actions, and flag actions that paint, but
987 : // are no bitmaps.
988 2 : sal_Int32 nNumBitmaps(0);
989 2 : bool bNonBitmapActionEncountered(false);
990 2 : if( aNewSize.Width() && aNewSize.Height() && rSz.Width() && rSz.Height() )
991 : {
992 2 : const double fGrfWH = (double) aNewSize.Width() / aNewSize.Height();
993 2 : const double fOutWH = (double) rSz.Width() / rSz.Height();
994 :
995 2 : const double fScaleX = fOutWH / fGrfWH;
996 2 : const double fScaleY = 1.0;
997 :
998 2 : const MapMode rPrefMapMode( rMtf.GetPrefMapMode() );
999 2 : const Size rSizePix( pOut->LogicToPixel( aNewSize, rPrefMapMode ) );
1000 :
1001 : // NOTE: If you do changes in this function, check GraphicDisplayCacheEntry::IsCacheableAsBitmap
1002 : // in grfcache.cxx too.
1003 :
1004 : // Determine whether the metafile basically displays
1005 : // a single bitmap (in which case that bitmap is simply used directly
1006 : // instead of playing the metafile). Note that
1007 : // the solution, as implemented here, is quite suboptimal (the
1008 : // cases where a mtf consisting basically of a single bitmap,
1009 : // that fail to pass the test below, are probably frequent). A
1010 : // better solution would involve FSAA, but that's currently
1011 : // expensive, and might trigger bugs on display drivers, if
1012 : // VDevs get bigger than the actual screen.
1013 : sal_uInt32 nCurPos;
1014 : MetaAction* pAct;
1015 70 : for( nCurPos = 0, pAct = (MetaAction*)rOutMtf.FirstAction(); pAct;
1016 : pAct = (MetaAction*)rOutMtf.NextAction(), nCurPos++ )
1017 : {
1018 68 : MetaAction* pModAct = NULL;
1019 68 : switch( pAct->GetType() )
1020 : {
1021 : case META_FONT_ACTION:
1022 : {
1023 : // taking care of font width default if scaling metafile.
1024 2 : MetaFontAction* pA = (MetaFontAction*)pAct;
1025 2 : Font aFont( pA->GetFont() );
1026 2 : if ( !aFont.GetWidth() )
1027 : {
1028 0 : FontMetric aFontMetric( pOut->GetFontMetric( aFont ) );
1029 0 : aFont.SetWidth( aFontMetric.GetWidth() );
1030 0 : pModAct = new MetaFontAction( aFont );
1031 2 : }
1032 : }
1033 : // FALLTHROUGH intended
1034 : case META_NULL_ACTION:
1035 : // FALLTHROUGH intended
1036 :
1037 : // OutDev state changes (which don't affect bitmap
1038 : // output)
1039 : case META_LINECOLOR_ACTION:
1040 : // FALLTHROUGH intended
1041 : case META_FILLCOLOR_ACTION:
1042 : // FALLTHROUGH intended
1043 : case META_TEXTCOLOR_ACTION:
1044 : // FALLTHROUGH intended
1045 : case META_TEXTFILLCOLOR_ACTION:
1046 : // FALLTHROUGH intended
1047 : case META_TEXTALIGN_ACTION:
1048 : // FALLTHROUGH intended
1049 : case META_TEXTLINECOLOR_ACTION:
1050 : // FALLTHROUGH intended
1051 : case META_TEXTLINE_ACTION:
1052 : // FALLTHROUGH intended
1053 : case META_PUSH_ACTION:
1054 : // FALLTHROUGH intended
1055 : case META_POP_ACTION:
1056 : // FALLTHROUGH intended
1057 : case META_LAYOUTMODE_ACTION:
1058 : // FALLTHROUGH intended
1059 : case META_TEXTLANGUAGE_ACTION:
1060 : // FALLTHROUGH intended
1061 : case META_COMMENT_ACTION:
1062 60 : break;
1063 :
1064 : // bitmap output methods
1065 : case META_BMP_ACTION:
1066 0 : if( !nNumBitmaps && !bNonBitmapActionEncountered )
1067 : {
1068 0 : MetaBmpAction* pAction = (MetaBmpAction*)pAct;
1069 :
1070 : rOutBmpEx = checkMetadataBitmap(
1071 0 : BitmapEx( pAction->GetBitmap()),
1072 : Point(), Size(),
1073 0 : pOut->LogicToPixel( pAction->GetPoint(),
1074 : rPrefMapMode ),
1075 0 : pAction->GetBitmap().GetSizePixel(),
1076 : rSizePix,
1077 0 : bNonBitmapActionEncountered );
1078 : }
1079 0 : ++nNumBitmaps;
1080 0 : break;
1081 :
1082 : case META_BMPSCALE_ACTION:
1083 0 : if( !nNumBitmaps && !bNonBitmapActionEncountered )
1084 : {
1085 0 : MetaBmpScaleAction* pAction = (MetaBmpScaleAction*)pAct;
1086 :
1087 : rOutBmpEx = checkMetadataBitmap(
1088 0 : BitmapEx( pAction->GetBitmap()),
1089 : Point(), Size(),
1090 0 : pOut->LogicToPixel( pAction->GetPoint(),
1091 : rPrefMapMode ),
1092 0 : pOut->LogicToPixel( pAction->GetSize(),
1093 : rPrefMapMode ),
1094 : rSizePix,
1095 0 : bNonBitmapActionEncountered );
1096 : }
1097 0 : ++nNumBitmaps;
1098 0 : break;
1099 :
1100 : case META_BMPSCALEPART_ACTION:
1101 0 : if( !nNumBitmaps && !bNonBitmapActionEncountered )
1102 : {
1103 0 : MetaBmpScalePartAction* pAction = (MetaBmpScalePartAction*)pAct;
1104 :
1105 : rOutBmpEx = checkMetadataBitmap(
1106 0 : BitmapEx( pAction->GetBitmap() ),
1107 0 : pAction->GetSrcPoint(),
1108 0 : pAction->GetSrcSize(),
1109 0 : pOut->LogicToPixel( pAction->GetDestPoint(),
1110 : rPrefMapMode ),
1111 0 : pOut->LogicToPixel( pAction->GetDestSize(),
1112 : rPrefMapMode ),
1113 : rSizePix,
1114 0 : bNonBitmapActionEncountered );
1115 : }
1116 0 : ++nNumBitmaps;
1117 0 : break;
1118 :
1119 : case META_BMPEX_ACTION:
1120 0 : if( !nNumBitmaps && !bNonBitmapActionEncountered )
1121 : {
1122 0 : MetaBmpExAction* pAction = (MetaBmpExAction*)pAct;
1123 :
1124 : rOutBmpEx = checkMetadataBitmap(
1125 0 : pAction->GetBitmapEx(),
1126 : Point(), Size(),
1127 0 : pOut->LogicToPixel( pAction->GetPoint(),
1128 : rPrefMapMode ),
1129 0 : pAction->GetBitmapEx().GetSizePixel(),
1130 : rSizePix,
1131 0 : bNonBitmapActionEncountered );
1132 : }
1133 0 : ++nNumBitmaps;
1134 0 : break;
1135 :
1136 : case META_BMPEXSCALE_ACTION:
1137 0 : if( !nNumBitmaps && !bNonBitmapActionEncountered )
1138 : {
1139 0 : MetaBmpExScaleAction* pAction = (MetaBmpExScaleAction*)pAct;
1140 :
1141 : rOutBmpEx = checkMetadataBitmap(
1142 0 : pAction->GetBitmapEx(),
1143 : Point(), Size(),
1144 0 : pOut->LogicToPixel( pAction->GetPoint(),
1145 : rPrefMapMode ),
1146 0 : pOut->LogicToPixel( pAction->GetSize(),
1147 : rPrefMapMode ),
1148 : rSizePix,
1149 0 : bNonBitmapActionEncountered );
1150 : }
1151 0 : ++nNumBitmaps;
1152 0 : break;
1153 :
1154 : case META_BMPEXSCALEPART_ACTION:
1155 0 : if( !nNumBitmaps && !bNonBitmapActionEncountered )
1156 : {
1157 0 : MetaBmpExScalePartAction* pAction = (MetaBmpExScalePartAction*)pAct;
1158 :
1159 0 : rOutBmpEx = checkMetadataBitmap( pAction->GetBitmapEx(),
1160 0 : pAction->GetSrcPoint(),
1161 0 : pAction->GetSrcSize(),
1162 0 : pOut->LogicToPixel( pAction->GetDestPoint(),
1163 : rPrefMapMode ),
1164 0 : pOut->LogicToPixel( pAction->GetDestSize(),
1165 : rPrefMapMode ),
1166 : rSizePix,
1167 0 : bNonBitmapActionEncountered );
1168 : }
1169 0 : ++nNumBitmaps;
1170 0 : break;
1171 :
1172 : // these actions actually output something (that's
1173 : // different from a bitmap)
1174 : case META_RASTEROP_ACTION:
1175 2 : if( ((MetaRasterOpAction*)pAct)->GetRasterOp() == ROP_OVERPAINT )
1176 2 : break;
1177 : // FALLTHROUGH intended
1178 : case META_PIXEL_ACTION:
1179 : // FALLTHROUGH intended
1180 : case META_POINT_ACTION:
1181 : // FALLTHROUGH intended
1182 : case META_LINE_ACTION:
1183 : // FALLTHROUGH intended
1184 : case META_RECT_ACTION:
1185 : // FALLTHROUGH intended
1186 : case META_ROUNDRECT_ACTION:
1187 : // FALLTHROUGH intended
1188 : case META_ELLIPSE_ACTION:
1189 : // FALLTHROUGH intended
1190 : case META_ARC_ACTION:
1191 : // FALLTHROUGH intended
1192 : case META_PIE_ACTION:
1193 : // FALLTHROUGH intended
1194 : case META_CHORD_ACTION:
1195 : // FALLTHROUGH intended
1196 : case META_POLYLINE_ACTION:
1197 : // FALLTHROUGH intended
1198 : case META_POLYGON_ACTION:
1199 : // FALLTHROUGH intended
1200 : case META_POLYPOLYGON_ACTION:
1201 : // FALLTHROUGH intended
1202 :
1203 : case META_TEXT_ACTION:
1204 : // FALLTHROUGH intended
1205 : case META_TEXTARRAY_ACTION:
1206 : // FALLTHROUGH intended
1207 : case META_STRETCHTEXT_ACTION:
1208 : // FALLTHROUGH intended
1209 : case META_TEXTRECT_ACTION:
1210 : // FALLTHROUGH intended
1211 :
1212 : case META_MASK_ACTION:
1213 : // FALLTHROUGH intended
1214 : case META_MASKSCALE_ACTION:
1215 : // FALLTHROUGH intended
1216 : case META_MASKSCALEPART_ACTION:
1217 : // FALLTHROUGH intended
1218 :
1219 : case META_GRADIENT_ACTION:
1220 : // FALLTHROUGH intended
1221 : case META_HATCH_ACTION:
1222 : // FALLTHROUGH intended
1223 : case META_WALLPAPER_ACTION:
1224 : // FALLTHROUGH intended
1225 :
1226 : case META_TRANSPARENT_ACTION:
1227 : // FALLTHROUGH intended
1228 : case META_EPS_ACTION:
1229 : // FALLTHROUGH intended
1230 : case META_FLOATTRANSPARENT_ACTION:
1231 : // FALLTHROUGH intended
1232 : case META_GRADIENTEX_ACTION:
1233 : // FALLTHROUGH intended
1234 :
1235 : // OutDev state changes that _do_ affect bitmap
1236 : // output
1237 : case META_CLIPREGION_ACTION:
1238 : // FALLTHROUGH intended
1239 : case META_ISECTRECTCLIPREGION_ACTION:
1240 : // FALLTHROUGH intended
1241 : case META_ISECTREGIONCLIPREGION_ACTION:
1242 : // FALLTHROUGH intended
1243 : case META_MOVECLIPREGION_ACTION:
1244 : // FALLTHROUGH intended
1245 :
1246 : case META_MAPMODE_ACTION:
1247 : // FALLTHROUGH intended
1248 : case META_REFPOINT_ACTION:
1249 : // FALLTHROUGH intended
1250 : default:
1251 6 : bNonBitmapActionEncountered = true;
1252 6 : break;
1253 : }
1254 68 : if ( pModAct )
1255 : {
1256 0 : rOutMtf.ReplaceAction( pModAct, nCurPos );
1257 0 : pAct->Delete();
1258 : }
1259 : else
1260 : {
1261 68 : if( pAct->GetRefCount() > 1 )
1262 : {
1263 68 : rOutMtf.ReplaceAction( pModAct = pAct->Clone(), nCurPos );
1264 68 : pAct->Delete();
1265 : }
1266 : else
1267 0 : pModAct = pAct;
1268 : }
1269 68 : pModAct->Scale( fScaleX, fScaleY );
1270 : }
1271 2 : rOutMtf.SetPrefSize( Size( FRound( aNewSize.Width() * fScaleX ),
1272 4 : FRound( aNewSize.Height() * fScaleY ) ) );
1273 : }
1274 :
1275 2 : if( nNumBitmaps != 1 || bNonBitmapActionEncountered )
1276 : {
1277 2 : if( rAttr.IsSpecialDrawMode() || rAttr.IsAdjusted() || rAttr.IsMirrored() || rAttr.IsRotated() || rAttr.IsTransparent() )
1278 0 : ImplAdjust( rOutMtf, rAttr, ADJUSTMENT_ALL );
1279 :
1280 2 : ImplDraw( pOut, rPt, rSz, rOutMtf, rAttr );
1281 2 : rOutBmpEx = BitmapEx();
1282 : }
1283 :
1284 2 : return sal_True;
1285 : }
1286 :
1287 2 : void GraphicManager::ImplAdjust( BitmapEx& rBmpEx, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags )
1288 : {
1289 2 : GraphicAttr aAttr( rAttr );
1290 :
1291 2 : if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() )
1292 : {
1293 0 : switch( aAttr.GetDrawMode() )
1294 : {
1295 : case( GRAPHICDRAWMODE_MONO ):
1296 0 : rBmpEx.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
1297 0 : break;
1298 :
1299 : case( GRAPHICDRAWMODE_GREYS ):
1300 0 : rBmpEx.Convert( BMP_CONVERSION_8BIT_GREYS );
1301 0 : break;
1302 :
1303 : case( GRAPHICDRAWMODE_WATERMARK ):
1304 : {
1305 0 : aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
1306 0 : aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
1307 : }
1308 0 : break;
1309 :
1310 : default:
1311 0 : break;
1312 : }
1313 : }
1314 :
1315 2 : if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() )
1316 : {
1317 0 : rBmpEx.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
1318 0 : aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
1319 0 : aAttr.GetGamma(), aAttr.IsInvert() );
1320 : }
1321 :
1322 2 : if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() )
1323 : {
1324 2 : rBmpEx.Mirror( aAttr.GetMirrorFlags() );
1325 : }
1326 :
1327 2 : if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() )
1328 : {
1329 0 : rBmpEx.Rotate( aAttr.GetRotation(), Color( COL_TRANSPARENT ) );
1330 : }
1331 :
1332 2 : if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() )
1333 : {
1334 0 : AlphaMask aAlpha;
1335 0 : sal_uInt8 cTrans = aAttr.GetTransparency();
1336 :
1337 0 : if( !rBmpEx.IsTransparent() )
1338 0 : aAlpha = AlphaMask( rBmpEx.GetSizePixel(), &cTrans );
1339 0 : else if( !rBmpEx.IsAlpha() )
1340 : {
1341 0 : aAlpha = rBmpEx.GetMask();
1342 0 : aAlpha.Replace( 0, cTrans );
1343 : }
1344 : else
1345 : {
1346 0 : aAlpha = rBmpEx.GetAlpha();
1347 0 : BitmapWriteAccess* pA = aAlpha.AcquireWriteAccess();
1348 :
1349 0 : if( pA )
1350 : {
1351 0 : sal_uLong nTrans = cTrans, nNewTrans;
1352 0 : const long nWidth = pA->Width(), nHeight = pA->Height();
1353 :
1354 0 : if( pA->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
1355 : {
1356 0 : for( long nY = 0; nY < nHeight; nY++ )
1357 : {
1358 0 : Scanline pAScan = pA->GetScanline( nY );
1359 :
1360 0 : for( long nX = 0; nX < nWidth; nX++ )
1361 : {
1362 0 : nNewTrans = nTrans + *pAScan;
1363 0 : *pAScan++ = (sal_uInt8) ( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans );
1364 : }
1365 : }
1366 : }
1367 : else
1368 : {
1369 0 : BitmapColor aAlphaValue( 0 );
1370 :
1371 0 : for( long nY = 0; nY < nHeight; nY++ )
1372 : {
1373 0 : for( long nX = 0; nX < nWidth; nX++ )
1374 : {
1375 0 : nNewTrans = nTrans + pA->GetPixel( nY, nX ).GetIndex();
1376 0 : aAlphaValue.SetIndex( (sal_uInt8) ( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans ) );
1377 0 : pA->SetPixel( nY, nX, aAlphaValue );
1378 : }
1379 0 : }
1380 : }
1381 :
1382 0 : aAlpha.ReleaseAccess( pA );
1383 : }
1384 : }
1385 :
1386 0 : rBmpEx = BitmapEx( rBmpEx.GetBitmap(), aAlpha );
1387 2 : }
1388 2 : }
1389 :
1390 0 : void GraphicManager::ImplAdjust( GDIMetaFile& rMtf, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags )
1391 : {
1392 0 : GraphicAttr aAttr( rAttr );
1393 :
1394 0 : if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() )
1395 : {
1396 0 : switch( aAttr.GetDrawMode() )
1397 : {
1398 : case( GRAPHICDRAWMODE_MONO ):
1399 0 : rMtf.Convert( MTF_CONVERSION_1BIT_THRESHOLD );
1400 0 : break;
1401 :
1402 : case( GRAPHICDRAWMODE_GREYS ):
1403 0 : rMtf.Convert( MTF_CONVERSION_8BIT_GREYS );
1404 0 : break;
1405 :
1406 : case( GRAPHICDRAWMODE_WATERMARK ):
1407 : {
1408 0 : aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
1409 0 : aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
1410 : }
1411 0 : break;
1412 :
1413 : default:
1414 0 : break;
1415 : }
1416 : }
1417 :
1418 0 : if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() )
1419 : {
1420 0 : rMtf.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
1421 0 : aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
1422 0 : aAttr.GetGamma(), aAttr.IsInvert() );
1423 : }
1424 :
1425 0 : if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() )
1426 : {
1427 0 : rMtf.Mirror( aAttr.GetMirrorFlags() );
1428 : }
1429 :
1430 0 : if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() )
1431 : {
1432 0 : rMtf.Rotate( aAttr.GetRotation() );
1433 : }
1434 :
1435 0 : if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() )
1436 : {
1437 : OSL_FAIL( "Missing implementation: Mtf-Transparency" );
1438 0 : }
1439 0 : }
1440 :
1441 0 : void GraphicManager::ImplAdjust( Animation& rAnimation, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags )
1442 : {
1443 0 : GraphicAttr aAttr( rAttr );
1444 :
1445 0 : if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() )
1446 : {
1447 0 : switch( aAttr.GetDrawMode() )
1448 : {
1449 : case( GRAPHICDRAWMODE_MONO ):
1450 0 : rAnimation.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
1451 0 : break;
1452 :
1453 : case( GRAPHICDRAWMODE_GREYS ):
1454 0 : rAnimation.Convert( BMP_CONVERSION_8BIT_GREYS );
1455 0 : break;
1456 :
1457 : case( GRAPHICDRAWMODE_WATERMARK ):
1458 : {
1459 0 : aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
1460 0 : aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
1461 : }
1462 0 : break;
1463 :
1464 : default:
1465 0 : break;
1466 : }
1467 : }
1468 :
1469 0 : if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() )
1470 : {
1471 0 : rAnimation.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
1472 0 : aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
1473 0 : aAttr.GetGamma(), aAttr.IsInvert() );
1474 : }
1475 :
1476 0 : if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() )
1477 : {
1478 0 : rAnimation.Mirror( aAttr.GetMirrorFlags() );
1479 : }
1480 :
1481 0 : if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() )
1482 : {
1483 : OSL_FAIL( "Missing implementation: Animation-Rotation" );
1484 : }
1485 :
1486 0 : if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() )
1487 : {
1488 : OSL_FAIL( "Missing implementation: Animation-Transparency" );
1489 0 : }
1490 0 : }
1491 :
1492 4 : void GraphicManager::ImplDraw( OutputDevice* pOut, const Point& rPt, const Size& rSz,
1493 : const GDIMetaFile& rMtf, const GraphicAttr& rAttr )
1494 : {
1495 4 : sal_uInt16 nRot10 = rAttr.GetRotation() % 3600;
1496 4 : Point aOutPt( rPt );
1497 4 : Size aOutSz( rSz );
1498 :
1499 4 : if( nRot10 )
1500 : {
1501 0 : Polygon aPoly( Rectangle( aOutPt, aOutSz ) );
1502 :
1503 0 : aPoly.Rotate( aOutPt, nRot10 );
1504 0 : const Rectangle aRotBoundRect( aPoly.GetBoundRect() );
1505 0 : aOutPt = aRotBoundRect.TopLeft();
1506 0 : aOutSz = aRotBoundRect.GetSize();
1507 : }
1508 :
1509 4 : pOut->Push( PUSH_CLIPREGION );
1510 4 : pOut->IntersectClipRegion( Rectangle( aOutPt, aOutSz ) );
1511 :
1512 4 : ( (GDIMetaFile&) rMtf ).WindStart();
1513 4 : ( (GDIMetaFile&) rMtf ).Play( pOut, aOutPt, aOutSz );
1514 4 : ( (GDIMetaFile&) rMtf ).WindStart();
1515 :
1516 4 : pOut->Pop();
1517 4 : }
1518 :
1519 : struct ImplTileInfo
1520 : {
1521 0 : ImplTileInfo() : aTileTopLeft(), aNextTileTopLeft(), aTileSizePixel(), nTilesEmptyX(0), nTilesEmptyY(0) {}
1522 :
1523 : Point aTileTopLeft; // top, left position of the rendered tile
1524 : Point aNextTileTopLeft; // top, left position for next recursion
1525 : // level's tile
1526 : Size aTileSizePixel; // size of the generated tile (might
1527 : // differ from
1528 : // aNextTileTopLeft-aTileTopLeft, because
1529 : // this is nExponent*prevTileSize. The
1530 : // generated tile is always nExponent
1531 : // times the previous tile, such that it
1532 : // can be used in the next stage. The
1533 : // required area coverage is often
1534 : // less. The extraneous area covered is
1535 : // later overwritten by the next stage)
1536 : int nTilesEmptyX; // number of original tiles empty right of
1537 : // this tile. This counts from
1538 : // aNextTileTopLeft, i.e. the additional
1539 : // area covered by aTileSizePixel is not
1540 : // considered here. This is for
1541 : // unification purposes, as the iterative
1542 : // calculation of the next level's empty
1543 : // tiles has to be based on this value.
1544 : int nTilesEmptyY; // as above, for Y
1545 : };
1546 :
1547 :
1548 0 : bool GraphicObject::ImplRenderTempTile( VirtualDevice& rVDev, int nExponent,
1549 : int nNumTilesX, int nNumTilesY,
1550 : const Size& rTileSizePixel,
1551 : const GraphicAttr* pAttr, sal_uLong nFlags )
1552 : {
1553 0 : if( nExponent <= 1 )
1554 0 : return false;
1555 :
1556 : // determine MSB factor
1557 0 : int nMSBFactor( 1 );
1558 0 : while( nNumTilesX / nMSBFactor != 0 ||
1559 : nNumTilesY / nMSBFactor != 0 )
1560 : {
1561 0 : nMSBFactor *= nExponent;
1562 : }
1563 :
1564 : // one less
1565 0 : nMSBFactor /= nExponent;
1566 :
1567 0 : ImplTileInfo aTileInfo;
1568 :
1569 : // #105229# Switch off mapping (converting to logic and back to
1570 : // pixel might cause roundoff errors)
1571 0 : sal_Bool bOldMap( rVDev.IsMapModeEnabled() );
1572 0 : rVDev.EnableMapMode( sal_False );
1573 :
1574 : bool bRet( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor, nNumTilesX, nNumTilesY,
1575 0 : nNumTilesX, nNumTilesY, rTileSizePixel, pAttr, nFlags, aTileInfo ) );
1576 :
1577 0 : rVDev.EnableMapMode( bOldMap );
1578 :
1579 0 : return bRet;
1580 : }
1581 :
1582 : // define for debug drawings
1583 : //#define DBG_TEST
1584 :
1585 : // see header comment. this works similar to base conversion of a
1586 : // number, i.e. if the exponent is 10, then the number for every tile
1587 : // size is given by the decimal place of the corresponding decimal
1588 : // representation.
1589 0 : bool GraphicObject::ImplRenderTileRecursive( VirtualDevice& rVDev, int nExponent, int nMSBFactor,
1590 : int nNumOrigTilesX, int nNumOrigTilesY,
1591 : int nRemainderTilesX, int nRemainderTilesY,
1592 : const Size& rTileSizePixel, const GraphicAttr* pAttr,
1593 : sal_uLong nFlags, ImplTileInfo& rTileInfo )
1594 : {
1595 : // gets loaded with our tile bitmap
1596 0 : GraphicObject aTmpGraphic;
1597 :
1598 : // stores a flag that renders the zero'th tile position
1599 : // (i.e. (0,0)+rCurrPos) only if we're at the bottom of the
1600 : // recursion stack. All other position already have that tile
1601 : // rendered, because the lower levels painted their generated tile
1602 : // there.
1603 0 : bool bNoFirstTileDraw( false );
1604 :
1605 : // what's left when we're done with our tile size
1606 0 : const int nNewRemainderX( nRemainderTilesX % nMSBFactor );
1607 0 : const int nNewRemainderY( nRemainderTilesY % nMSBFactor );
1608 :
1609 : // gets filled out from the recursive call with info of what's
1610 : // been generated
1611 0 : ImplTileInfo aTileInfo;
1612 :
1613 : // current output position while drawing
1614 0 : Point aCurrPos;
1615 : int nX, nY;
1616 :
1617 : // check for recursion's end condition: LSB place reached?
1618 0 : if( nMSBFactor == 1 )
1619 : {
1620 0 : aTmpGraphic = *this;
1621 :
1622 : // set initial tile size -> orig size
1623 0 : aTileInfo.aTileSizePixel = rTileSizePixel;
1624 0 : aTileInfo.nTilesEmptyX = nNumOrigTilesX;
1625 0 : aTileInfo.nTilesEmptyY = nNumOrigTilesY;
1626 : }
1627 0 : else if( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor/nExponent,
1628 : nNumOrigTilesX, nNumOrigTilesY,
1629 : nNewRemainderX, nNewRemainderY,
1630 0 : rTileSizePixel, pAttr, nFlags, aTileInfo ) )
1631 : {
1632 : // extract generated tile -> see comment on the first loop below
1633 0 : BitmapEx aTileBitmap( rVDev.GetBitmap( aTileInfo.aTileTopLeft, aTileInfo.aTileSizePixel ) );
1634 :
1635 0 : aTmpGraphic = GraphicObject( aTileBitmap );
1636 :
1637 : // fill stripes left over from upstream levels:
1638 : //
1639 : // x0000
1640 : // 0
1641 : // 0
1642 : // 0
1643 : // 0
1644 : //
1645 : // where x denotes the place filled by our recursive predecessors
1646 :
1647 : // check whether we have to fill stripes here. Although not
1648 : // obvious, there is one case where we can skip this step: if
1649 : // the previous recursion level (the one who filled our
1650 : // aTileInfo) had zero area to fill, then there are no white
1651 : // stripes left, naturally. This happens if the digit
1652 : // associated to that level has a zero, and can be checked via
1653 : // aTileTopLeft==aNextTileTopLeft.
1654 0 : if( aTileInfo.aTileTopLeft != aTileInfo.aNextTileTopLeft )
1655 : {
1656 : // now fill one row from aTileInfo.aNextTileTopLeft.X() all
1657 : // the way to the right
1658 0 : aCurrPos.X() = aTileInfo.aNextTileTopLeft.X();
1659 0 : aCurrPos.Y() = aTileInfo.aTileTopLeft.Y();
1660 0 : for( nX=0; nX < aTileInfo.nTilesEmptyX; nX += nMSBFactor )
1661 : {
1662 0 : if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) )
1663 0 : return false;
1664 :
1665 0 : aCurrPos.X() += aTileInfo.aTileSizePixel.Width();
1666 : }
1667 :
1668 : #ifdef DBG_TEST
1669 : // rVDev.SetFillColor( COL_WHITE );
1670 : rVDev.SetFillColor();
1671 : rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) );
1672 : rVDev.DrawEllipse( Rectangle(aTileInfo.aNextTileTopLeft.X(), aTileInfo.aTileTopLeft.Y(),
1673 : aTileInfo.aNextTileTopLeft.X() - 1 + (aTileInfo.nTilesEmptyX/nMSBFactor)*aTileInfo.aTileSizePixel.Width(),
1674 : aTileInfo.aTileTopLeft.Y() + aTileInfo.aTileSizePixel.Height() - 1) );
1675 : #endif
1676 :
1677 : // now fill one column from aTileInfo.aNextTileTopLeft.Y() all
1678 : // the way to the bottom
1679 0 : aCurrPos.X() = aTileInfo.aTileTopLeft.X();
1680 0 : aCurrPos.Y() = aTileInfo.aNextTileTopLeft.Y();
1681 0 : for( nY=0; nY < aTileInfo.nTilesEmptyY; nY += nMSBFactor )
1682 : {
1683 0 : if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) )
1684 0 : return false;
1685 :
1686 0 : aCurrPos.Y() += aTileInfo.aTileSizePixel.Height();
1687 : }
1688 :
1689 : #ifdef DBG_TEST
1690 : rVDev.DrawEllipse( Rectangle(aTileInfo.aTileTopLeft.X(), aTileInfo.aNextTileTopLeft.Y(),
1691 : aTileInfo.aTileTopLeft.X() + aTileInfo.aTileSizePixel.Width() - 1,
1692 : aTileInfo.aNextTileTopLeft.Y() - 1 + (aTileInfo.nTilesEmptyY/nMSBFactor)*aTileInfo.aTileSizePixel.Height()) );
1693 : #endif
1694 : }
1695 : else
1696 : {
1697 : // Thought that aTileInfo.aNextTileTopLeft tile has always
1698 : // been drawn already, but that's wrong: typically,
1699 : // _parts_ of that tile have been drawn, since the
1700 : // previous level generated the tile there. But when
1701 : // aTileInfo.aNextTileTopLeft!=aTileInfo.aTileTopLeft, the
1702 : // difference between these two values is missing in the
1703 : // lower right corner of this first tile. So, can do that
1704 : // only here.
1705 0 : bNoFirstTileDraw = true;
1706 0 : }
1707 : }
1708 : else
1709 : {
1710 0 : return false;
1711 : }
1712 :
1713 : // calc number of original tiles in our drawing area without
1714 : // remainder
1715 0 : nRemainderTilesX -= nNewRemainderX;
1716 0 : nRemainderTilesY -= nNewRemainderY;
1717 :
1718 : // fill tile info for calling method
1719 0 : rTileInfo.aTileTopLeft = aTileInfo.aNextTileTopLeft;
1720 0 : rTileInfo.aNextTileTopLeft = Point( rTileInfo.aTileTopLeft.X() + rTileSizePixel.Width()*nRemainderTilesX,
1721 0 : rTileInfo.aTileTopLeft.Y() + rTileSizePixel.Height()*nRemainderTilesY );
1722 0 : rTileInfo.aTileSizePixel = Size( rTileSizePixel.Width()*nMSBFactor*nExponent,
1723 0 : rTileSizePixel.Height()*nMSBFactor*nExponent );
1724 0 : rTileInfo.nTilesEmptyX = aTileInfo.nTilesEmptyX - nRemainderTilesX;
1725 0 : rTileInfo.nTilesEmptyY = aTileInfo.nTilesEmptyY - nRemainderTilesY;
1726 :
1727 : // init output position
1728 0 : aCurrPos = aTileInfo.aNextTileTopLeft;
1729 :
1730 : // fill our drawing area. Fill possibly more, to create the next
1731 : // bigger tile size -> see bitmap extraction above. This does no
1732 : // harm, since everything right or below our actual area is
1733 : // overdrawn by our caller. Just in case we're in the last level,
1734 : // we don't draw beyond the right or bottom border.
1735 0 : for( nY=0; nY < aTileInfo.nTilesEmptyY && nY < nExponent*nMSBFactor; nY += nMSBFactor )
1736 : {
1737 0 : aCurrPos.X() = aTileInfo.aNextTileTopLeft.X();
1738 :
1739 0 : for( nX=0; nX < aTileInfo.nTilesEmptyX && nX < nExponent*nMSBFactor; nX += nMSBFactor )
1740 : {
1741 0 : if( bNoFirstTileDraw )
1742 0 : bNoFirstTileDraw = false; // don't draw first tile position
1743 0 : else if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) )
1744 0 : return false;
1745 :
1746 0 : aCurrPos.X() += aTileInfo.aTileSizePixel.Width();
1747 : }
1748 :
1749 0 : aCurrPos.Y() += aTileInfo.aTileSizePixel.Height();
1750 : }
1751 :
1752 : #ifdef DBG_TEST
1753 : // rVDev.SetFillColor( COL_WHITE );
1754 : rVDev.SetFillColor();
1755 : rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) );
1756 : rVDev.DrawRect( Rectangle((rTileInfo.aTileTopLeft.X())*rTileSizePixel.Width(),
1757 : (rTileInfo.aTileTopLeft.Y())*rTileSizePixel.Height(),
1758 : (rTileInfo.aNextTileTopLeft.X())*rTileSizePixel.Width()-1,
1759 : (rTileInfo.aNextTileTopLeft.Y())*rTileSizePixel.Height()-1) );
1760 : #endif
1761 :
1762 0 : return true;
1763 : }
1764 :
1765 0 : bool GraphicObject::ImplDrawTiled( OutputDevice* pOut, const Rectangle& rArea, const Size& rSizePixel,
1766 : const Size& rOffset, const GraphicAttr* pAttr, sal_uLong nFlags, int nTileCacheSize1D )
1767 : {
1768 : // how many tiles to generate per recursion step
1769 : enum{ SubdivisionExponent=2 };
1770 :
1771 0 : const MapMode aOutMapMode( pOut->GetMapMode() );
1772 0 : const MapMode aMapMode( aOutMapMode.GetMapUnit(), Point(), aOutMapMode.GetScaleX(), aOutMapMode.GetScaleY() );
1773 0 : bool bRet( false );
1774 :
1775 : // #i42643# Casting to Int64, to avoid integer overflow for
1776 : // huge-DPI output devices
1777 0 : if( GetGraphic().GetType() == GRAPHIC_BITMAP &&
1778 0 : static_cast<sal_Int64>(rSizePixel.Width()) * rSizePixel.Height() <
1779 : static_cast<sal_Int64>(nTileCacheSize1D)*nTileCacheSize1D )
1780 : {
1781 : // First combine very small bitmaps into a larger tile
1782 : // ===================================================
1783 :
1784 0 : VirtualDevice aVDev;
1785 0 : const int nNumTilesInCacheX( (nTileCacheSize1D + rSizePixel.Width()-1) / rSizePixel.Width() );
1786 0 : const int nNumTilesInCacheY( (nTileCacheSize1D + rSizePixel.Height()-1) / rSizePixel.Height() );
1787 :
1788 0 : aVDev.SetOutputSizePixel( Size( nNumTilesInCacheX*rSizePixel.Width(),
1789 0 : nNumTilesInCacheY*rSizePixel.Height() ) );
1790 0 : aVDev.SetMapMode( aMapMode );
1791 :
1792 : // draw bitmap content
1793 0 : if( ImplRenderTempTile( aVDev, SubdivisionExponent, nNumTilesInCacheX,
1794 : nNumTilesInCacheY, rSizePixel, pAttr, nFlags ) )
1795 : {
1796 0 : BitmapEx aTileBitmap( aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ) );
1797 :
1798 : // draw alpha content, if any
1799 0 : if( IsTransparent() )
1800 : {
1801 0 : GraphicObject aAlphaGraphic;
1802 :
1803 0 : if( GetGraphic().IsAlpha() )
1804 0 : aAlphaGraphic.SetGraphic( GetGraphic().GetBitmapEx().GetAlpha().GetBitmap() );
1805 : else
1806 0 : aAlphaGraphic.SetGraphic( GetGraphic().GetBitmapEx().GetMask() );
1807 :
1808 0 : if( aAlphaGraphic.ImplRenderTempTile( aVDev, SubdivisionExponent, nNumTilesInCacheX,
1809 : nNumTilesInCacheY, rSizePixel, pAttr, nFlags ) )
1810 : {
1811 : // Combine bitmap and alpha/mask
1812 0 : if( GetGraphic().IsAlpha() )
1813 : aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(),
1814 0 : AlphaMask( aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ) ) );
1815 : else
1816 : aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(),
1817 0 : aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ).CreateMask( Color(COL_WHITE) ) );
1818 0 : }
1819 : }
1820 :
1821 : // paint generated tile
1822 0 : GraphicObject aTmpGraphic( aTileBitmap );
1823 : bRet = aTmpGraphic.ImplDrawTiled( pOut, rArea,
1824 0 : aTileBitmap.GetSizePixel(),
1825 0 : rOffset, pAttr, nFlags, nTileCacheSize1D );
1826 0 : }
1827 : }
1828 : else
1829 : {
1830 0 : const Size aOutOffset( pOut->LogicToPixel( rOffset, aOutMapMode ) );
1831 0 : const Rectangle aOutArea( pOut->LogicToPixel( rArea, aOutMapMode ) );
1832 :
1833 : // number of invisible (because out-of-area) tiles
1834 : int nInvisibleTilesX;
1835 : int nInvisibleTilesY;
1836 :
1837 : // round towards -infty for negative offset
1838 0 : if( aOutOffset.Width() < 0 )
1839 0 : nInvisibleTilesX = (aOutOffset.Width() - rSizePixel.Width() + 1) / rSizePixel.Width();
1840 : else
1841 0 : nInvisibleTilesX = aOutOffset.Width() / rSizePixel.Width();
1842 :
1843 : // round towards -infty for negative offset
1844 0 : if( aOutOffset.Height() < 0 )
1845 0 : nInvisibleTilesY = (aOutOffset.Height() - rSizePixel.Height() + 1) / rSizePixel.Height();
1846 : else
1847 0 : nInvisibleTilesY = aOutOffset.Height() / rSizePixel.Height();
1848 :
1849 : // origin from where to 'virtually' start drawing in pixel
1850 0 : const Point aOutOrigin( pOut->LogicToPixel( Point( rArea.Left() - rOffset.Width(),
1851 0 : rArea.Top() - rOffset.Height() ) ) );
1852 : // position in pixel from where to really start output
1853 0 : const Point aOutStart( aOutOrigin.X() + nInvisibleTilesX*rSizePixel.Width(),
1854 0 : aOutOrigin.Y() + nInvisibleTilesY*rSizePixel.Height() );
1855 :
1856 0 : pOut->Push( PUSH_CLIPREGION );
1857 0 : pOut->IntersectClipRegion( rArea );
1858 :
1859 : // Paint all tiles
1860 : // ===============
1861 :
1862 : bRet = ImplDrawTiled( *pOut, aOutStart,
1863 0 : (aOutArea.GetWidth() + aOutArea.Left() - aOutStart.X() + rSizePixel.Width() - 1) / rSizePixel.Width(),
1864 0 : (aOutArea.GetHeight() + aOutArea.Top() - aOutStart.Y() + rSizePixel.Height() - 1) / rSizePixel.Height(),
1865 0 : rSizePixel, pAttr, nFlags );
1866 :
1867 0 : pOut->Pop();
1868 : }
1869 :
1870 0 : return bRet;
1871 : }
1872 :
1873 0 : bool GraphicObject::ImplDrawTiled( OutputDevice& rOut, const Point& rPosPixel,
1874 : int nNumTilesX, int nNumTilesY,
1875 : const Size& rTileSizePixel, const GraphicAttr* pAttr, sal_uLong nFlags )
1876 : {
1877 0 : Point aCurrPos( rPosPixel );
1878 0 : Size aTileSizeLogic( rOut.PixelToLogic( rTileSizePixel ) );
1879 : int nX, nY;
1880 :
1881 : // #107607# Use logical coordinates for metafile playing, too
1882 0 : bool bDrawInPixel( rOut.GetConnectMetaFile() == NULL && GRAPHIC_BITMAP == GetType() );
1883 0 : bool bRet = false;
1884 :
1885 : // #105229# Switch off mapping (converting to logic and back to
1886 : // pixel might cause roundoff errors)
1887 0 : bool bOldMap( rOut.IsMapModeEnabled() );
1888 :
1889 0 : if( bDrawInPixel )
1890 0 : rOut.EnableMapMode( sal_False );
1891 :
1892 0 : for( nY=0; nY < nNumTilesY; ++nY )
1893 : {
1894 0 : aCurrPos.X() = rPosPixel.X();
1895 :
1896 0 : for( nX=0; nX < nNumTilesX; ++nX )
1897 : {
1898 : // #105229# work with pixel coordinates here, mapping is disabled!
1899 : // #104004# don't disable mapping for metafile recordings
1900 : // #108412# don't quit the loop if one draw fails
1901 :
1902 : // update return value. This method should return true, if
1903 : // at least one of the looped Draws succeeded.
1904 : bRet |= Draw( &rOut,
1905 : bDrawInPixel ? aCurrPos : rOut.PixelToLogic( aCurrPos ),
1906 : bDrawInPixel ? rTileSizePixel : aTileSizeLogic,
1907 0 : pAttr, nFlags );
1908 :
1909 0 : aCurrPos.X() += rTileSizePixel.Width();
1910 : }
1911 :
1912 0 : aCurrPos.Y() += rTileSizePixel.Height();
1913 : }
1914 :
1915 0 : if( bDrawInPixel )
1916 0 : rOut.EnableMapMode( bOldMap );
1917 :
1918 0 : return bRet;
1919 : }
1920 :
1921 0 : void GraphicObject::ImplTransformBitmap( BitmapEx& rBmpEx,
1922 : const GraphicAttr& rAttr,
1923 : const Size& rCropLeftTop,
1924 : const Size& rCropRightBottom,
1925 : const Rectangle& rCropRect,
1926 : const Size& rDstSize,
1927 : sal_Bool bEnlarge ) const
1928 : {
1929 : // #107947# Extracted from svdograf.cxx
1930 :
1931 : // #104115# Crop the bitmap
1932 0 : if( rAttr.IsCropped() )
1933 : {
1934 0 : rBmpEx.Crop( rCropRect );
1935 :
1936 : // #104115# Negative crop sizes mean: enlarge bitmap and pad
1937 0 : if( bEnlarge && (
1938 0 : rCropLeftTop.Width() < 0 ||
1939 0 : rCropLeftTop.Height() < 0 ||
1940 0 : rCropRightBottom.Width() < 0 ||
1941 0 : rCropRightBottom.Height() < 0 ) )
1942 : {
1943 0 : Size aBmpSize( rBmpEx.GetSizePixel() );
1944 0 : sal_Int32 nPadLeft( rCropLeftTop.Width() < 0 ? -rCropLeftTop.Width() : 0 );
1945 0 : sal_Int32 nPadTop( rCropLeftTop.Height() < 0 ? -rCropLeftTop.Height() : 0 );
1946 0 : sal_Int32 nPadTotalWidth( aBmpSize.Width() + nPadLeft + (rCropRightBottom.Width() < 0 ? -rCropRightBottom.Width() : 0) );
1947 0 : sal_Int32 nPadTotalHeight( aBmpSize.Height() + nPadTop + (rCropRightBottom.Height() < 0 ? -rCropRightBottom.Height() : 0) );
1948 :
1949 0 : BitmapEx aBmpEx2;
1950 :
1951 0 : if( rBmpEx.IsTransparent() )
1952 : {
1953 0 : if( rBmpEx.IsAlpha() )
1954 0 : aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetAlpha() );
1955 : else
1956 0 : aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetMask() );
1957 : }
1958 : else
1959 : {
1960 : // #104115# Generate mask bitmap and init to zero
1961 0 : Bitmap aMask( aBmpSize, 1 );
1962 0 : aMask.Erase( Color(0,0,0) );
1963 :
1964 : // #104115# Always generate transparent bitmap, we need the border transparent
1965 0 : aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), aMask );
1966 :
1967 : // #104115# Add opaque mask to source bitmap, otherwise the destination remains transparent
1968 0 : rBmpEx = aBmpEx2;
1969 : }
1970 :
1971 0 : aBmpEx2.SetSizePixel( Size(nPadTotalWidth, nPadTotalHeight) );
1972 0 : aBmpEx2.Erase( Color(0xFF,0,0,0) );
1973 0 : aBmpEx2.CopyPixel( Rectangle( Point(nPadLeft, nPadTop), aBmpSize ), Rectangle( Point(0, 0), aBmpSize ), &rBmpEx );
1974 0 : rBmpEx = aBmpEx2;
1975 : }
1976 : }
1977 :
1978 0 : const Size aSizePixel( rBmpEx.GetSizePixel() );
1979 :
1980 0 : if( rAttr.GetRotation() != 0 && !IsAnimated() )
1981 : {
1982 0 : if( aSizePixel.Width() && aSizePixel.Height() && rDstSize.Width() && rDstSize.Height() )
1983 : {
1984 0 : double fSrcWH = (double) aSizePixel.Width() / aSizePixel.Height();
1985 0 : double fDstWH = (double) rDstSize.Width() / rDstSize.Height();
1986 0 : double fScaleX = 1.0, fScaleY = 1.0;
1987 :
1988 : // always choose scaling to shrink bitmap
1989 0 : if( fSrcWH < fDstWH )
1990 0 : fScaleY = aSizePixel.Width() / ( fDstWH * aSizePixel.Height() );
1991 : else
1992 0 : fScaleX = fDstWH * aSizePixel.Height() / aSizePixel.Width();
1993 :
1994 0 : rBmpEx.Scale( fScaleX, fScaleY );
1995 : }
1996 : }
1997 0 : }
1998 :
1999 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|