LibreOffice Module vcl (master) 1
bitmapex.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <config_features.h>
21
22#include <rtl/math.hxx>
23#include <comphelper/lok.hxx>
25
26#include <vcl/canvastools.hxx>
27#include <vcl/metaact.hxx>
28#include <vcl/virdev.hxx>
29
30#include <drawmode.hxx>
31#include <salgdi.hxx>
32
33void OutputDevice::DrawBitmapEx( const Point& rDestPt,
34 const BitmapEx& rBitmapEx )
35{
37
38 if( ImplIsRecordLayout() )
39 return;
40
41 if( !rBitmapEx.IsAlpha() )
42 {
43 DrawBitmap( rDestPt, rBitmapEx.GetBitmap() );
44 }
45 else
46 {
47 const Size aSizePix( rBitmapEx.GetSizePixel() );
48 DrawBitmapEx( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmapEx, MetaActionType::BMPEX );
49 }
50}
51
52void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
53 const BitmapEx& rBitmapEx )
54{
56
57 if( ImplIsRecordLayout() )
58 return;
59
60 if ( !rBitmapEx.IsAlpha() )
61 {
62 DrawBitmap( rDestPt, rDestSize, rBitmapEx.GetBitmap() );
63 }
64 else
65 {
66 DrawBitmapEx( rDestPt, rDestSize, Point(), rBitmapEx.GetSizePixel(), rBitmapEx, MetaActionType::BMPEXSCALE );
67 }
68}
69
70void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
71 const Point& rSrcPtPixel, const Size& rSrcSizePixel,
72 const BitmapEx& rBitmapEx)
73{
75
76 if( ImplIsRecordLayout() )
77 return;
78
79 if ( !rBitmapEx.IsAlpha() )
80 {
81 DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx.GetBitmap() );
82 }
83 else
84 {
85 DrawBitmapEx( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx, MetaActionType::BMPEXSCALEPART );
86 }
87}
88
89void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
90 const Point& rSrcPtPixel, const Size& rSrcSizePixel,
91 const BitmapEx& rBitmapEx, const MetaActionType nAction )
92{
94
95 if( ImplIsRecordLayout() )
96 return;
97
98 if( !rBitmapEx.IsAlpha() )
99 {
100 DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx.GetBitmap() );
101 }
102 else
103 {
105 {
106 DrawRect( tools::Rectangle( rDestPt, rDestSize ) );
107 return;
108 }
109
110 BitmapEx aBmpEx(vcl::drawmode::GetBitmapEx(rBitmapEx, GetDrawMode()));
111
112 if ( mpMetaFile )
113 {
114 switch( nAction )
115 {
117 mpMetaFile->AddAction( new MetaBmpExAction( rDestPt, aBmpEx ) );
118 break;
119
121 mpMetaFile->AddAction( new MetaBmpExScaleAction( rDestPt, rDestSize, aBmpEx ) );
122 break;
123
125 mpMetaFile->AddAction( new MetaBmpExScalePartAction( rDestPt, rDestSize,
126 rSrcPtPixel, rSrcSizePixel, aBmpEx ) );
127 break;
128
129 default: break;
130 }
131 }
132
134 return;
135
136 if ( !mpGraphics && !AcquireGraphics() )
137 return;
138
139 if ( mbInitClipRegion )
141
142 if ( mbOutputClipped )
143 return;
144
145 DrawDeviceBitmapEx( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmpEx );
146 }
147}
148
149BitmapEx OutputDevice::GetBitmapEx( const Point& rSrcPt, const Size& rSize ) const
150{
151
152 // #110958# Extract alpha value from VDev, if any
153 if( mpAlphaVDev )
154 {
155 Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( rSrcPt, rSize ) );
156
157 // ensure 8 bit alpha
158 if (aAlphaBitmap.getPixelFormat() > vcl::PixelFormat::N8_BPP)
160
161 return BitmapEx(GetBitmap( rSrcPt, rSize ), AlphaMask( aAlphaBitmap ) );
162 }
163 else
164 return BitmapEx(GetBitmap( rSrcPt, rSize ));
165}
166
167void OutputDevice::DrawDeviceBitmapEx( const Point& rDestPt, const Size& rDestSize,
168 const Point& rSrcPtPixel, const Size& rSrcSizePixel,
169 BitmapEx& rBitmapEx )
170{
171 assert(!is_double_buffered_window());
172
173 if (rBitmapEx.IsAlpha())
174 {
175 DrawDeviceAlphaBitmap(rBitmapEx.GetBitmap(), rBitmapEx.GetAlpha(), rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel);
176 }
177 else if (!rBitmapEx.IsEmpty())
178 {
179 SalTwoRect aPosAry(rSrcPtPixel.X(), rSrcPtPixel.Y(), rSrcSizePixel.Width(), rSrcSizePixel.Height(),
180 ImplLogicXToDevicePixel(rDestPt.X()), ImplLogicYToDevicePixel(rDestPt.Y()),
183
184 const BmpMirrorFlags nMirrFlags = AdjustTwoRect(aPosAry, rBitmapEx.GetSizePixel());
185
186 if (aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight)
187 {
188
189 if (nMirrFlags != BmpMirrorFlags::NONE)
190 rBitmapEx.Mirror(nMirrFlags);
191
192 const SalBitmap* pSalSrcBmp = rBitmapEx.ImplGetBitmapSalBitmap().get();
193 std::shared_ptr<SalBitmap> xMaskBmp = rBitmapEx.maAlphaMask.ImplGetSalBitmap();
194
195 if (xMaskBmp)
196 {
197 bool bTryDirectPaint(pSalSrcBmp);
198
199 if (bTryDirectPaint && mpGraphics->DrawAlphaBitmap(aPosAry, *pSalSrcBmp, *xMaskBmp, *this))
200 {
201 // tried to paint as alpha directly. If this worked, we are done (except
202 // alpha, see below)
203 }
204 else
205 {
206 // #4919452# reduce operation area to bounds of
207 // cliprect. since masked transparency involves
208 // creation of a large vdev and copying the screen
209 // content into that (slooow read from framebuffer),
210 // that should considerably increase performance for
211 // large bitmaps and small clippings.
212
213 // Note that this optimization is a workaround for a
214 // Writer peculiarity, namely, to decompose background
215 // graphics into myriads of disjunct, tiny
216 // rectangles. That otherwise kills us here, since for
217 // transparent output, SAL always prepares the whole
218 // bitmap, if aPosAry contains the whole bitmap (and
219 // it's _not_ to blame for that).
220
221 // Note the call to ImplPixelToDevicePixel(), since
222 // aPosAry already contains the mnOutOff-offsets, they
223 // also have to be applied to the region
224 tools::Rectangle aClipRegionBounds( ImplPixelToDevicePixel(maRegion).GetBoundRect() );
225
226 // TODO: Also respect scaling (that's a bit tricky,
227 // since the source points have to move fractional
228 // amounts (which is not possible, thus has to be
229 // emulated by increases copy area)
230 // const double nScaleX( aPosAry.mnDestWidth / aPosAry.mnSrcWidth );
231 // const double nScaleY( aPosAry.mnDestHeight / aPosAry.mnSrcHeight );
232
233 // for now, only identity scales allowed
234 if (!aClipRegionBounds.IsEmpty() &&
235 aPosAry.mnDestWidth == aPosAry.mnSrcWidth &&
236 aPosAry.mnDestHeight == aPosAry.mnSrcHeight)
237 {
238 // now intersect dest rect with clip region
239 aClipRegionBounds.Intersection(tools::Rectangle(aPosAry.mnDestX,
240 aPosAry.mnDestY,
241 aPosAry.mnDestX + aPosAry.mnDestWidth - 1,
242 aPosAry.mnDestY + aPosAry.mnDestHeight - 1));
243
244 // Note: I could theoretically optimize away the
245 // DrawBitmap below, if the region is empty
246 // here. Unfortunately, cannot rule out that
247 // somebody relies on the side effects.
248 if (!aClipRegionBounds.IsEmpty())
249 {
250 aPosAry.mnSrcX += aClipRegionBounds.Left() - aPosAry.mnDestX;
251 aPosAry.mnSrcY += aClipRegionBounds.Top() - aPosAry.mnDestY;
252 aPosAry.mnSrcWidth = aClipRegionBounds.GetWidth();
253 aPosAry.mnSrcHeight = aClipRegionBounds.GetHeight();
254
255 aPosAry.mnDestX = aClipRegionBounds.Left();
256 aPosAry.mnDestY = aClipRegionBounds.Top();
257 aPosAry.mnDestWidth = aClipRegionBounds.GetWidth();
258 aPosAry.mnDestHeight = aClipRegionBounds.GetHeight();
259 }
260 }
261
262 mpGraphics->DrawBitmap(aPosAry, *pSalSrcBmp, *xMaskBmp, *this);
263 }
264
265 // #110958# Paint mask to alpha channel. Luckily, the
266 // black and white representation of the mask maps to
267 // the alpha channel
268
269 // #i25167# Restrict mask painting to _opaque_ areas
270 // of the mask, otherwise we spoil areas where no
271 // bitmap content was ever visible. Interestingly
272 // enough, this can be achieved by taking the mask as
273 // the transparency mask of itself
274 if (mpAlphaVDev)
275 mpAlphaVDev->DrawBitmapEx(rDestPt,
276 rDestSize,
277 BitmapEx(rBitmapEx.GetAlpha(),
278 rBitmapEx.GetAlpha()));
279 }
280 else
281 {
282 mpGraphics->DrawBitmap(aPosAry, *pSalSrcBmp, *this);
283
284 if (mpAlphaVDev)
285 {
286 // #i32109#: Make bitmap area opaque
288 }
289 }
290 }
291 }
292}
293
295 const basegfx::B2DHomMatrix& aFullTransform,
296 const BitmapEx& rBitmapEx,
297 double fAlpha)
298{
299 assert(!is_double_buffered_window());
300
301 bool bDone = false;
302
303 // try to paint directly
304 const basegfx::B2DPoint aNull(aFullTransform * basegfx::B2DPoint(0.0, 0.0));
305 const basegfx::B2DPoint aTopX(aFullTransform * basegfx::B2DPoint(1.0, 0.0));
306 const basegfx::B2DPoint aTopY(aFullTransform * basegfx::B2DPoint(0.0, 1.0));
307 SalBitmap* pSalSrcBmp = rBitmapEx.GetBitmap().ImplGetSalBitmap().get();
308 Bitmap aAlphaBitmap;
309
310 if(rBitmapEx.IsAlpha())
311 {
312 aAlphaBitmap = rBitmapEx.GetAlpha();
313 }
314 else if (mpAlphaVDev)
315 {
316 aAlphaBitmap = AlphaMask(rBitmapEx.GetSizePixel());
317 aAlphaBitmap.Erase(COL_BLACK); // opaque
318 }
319
320 SalBitmap* pSalAlphaBmp = aAlphaBitmap.ImplGetSalBitmap().get();
321
323 aNull,
324 aTopX,
325 aTopY,
326 *pSalSrcBmp,
327 pSalAlphaBmp,
328 fAlpha,
329 *this);
330
331 if (mpAlphaVDev)
332 {
333 // Merge bitmap alpha to alpha device
334 AlphaMask aAlpha(rBitmapEx.GetSizePixel());
335 aAlpha.Erase( ( 1 - fAlpha ) * 255 );
336 mpAlphaVDev->DrawTransformBitmapExDirect(aFullTransform, BitmapEx(aAlpha, aAlphaBitmap));
337 }
338
339 return bDone;
340};
341
343 const basegfx::B2DHomMatrix& aFullTransform,
344 basegfx::B2DRange &aVisibleRange,
345 double &fMaximumArea)
346{
347 // limit TargetRange to existing pixels (if pixel device)
348 // first get discrete range of object
349 basegfx::B2DRange aFullPixelRange(aVisibleRange);
350
351 aFullPixelRange.transform(aFullTransform);
352
353 if(basegfx::fTools::equalZero(aFullPixelRange.getWidth()) || basegfx::fTools::equalZero(aFullPixelRange.getHeight()))
354 {
355 // object is outside of visible area
356 return false;
357 }
358
359 // now get discrete target pixels; start with OutDev pixel size and evtl.
360 // intersect with active clipping area
361 basegfx::B2DRange aOutPixel(
362 0.0,
363 0.0,
366
367 if(IsClipRegion())
368 {
369 tools::Rectangle aRegionRectangle(GetActiveClipRegion().GetBoundRect());
370
371 // caution! Range from rectangle, one too much (!)
372 aRegionRectangle.AdjustRight(-1);
373 aRegionRectangle.AdjustBottom(-1);
374 aOutPixel.intersect( vcl::unotools::b2DRectangleFromRectangle(aRegionRectangle) );
375 }
376
377 if(aOutPixel.isEmpty())
378 {
379 // no active output area
380 return false;
381 }
382
383 // if aFullPixelRange is not completely inside of aOutPixel,
384 // reduction of target pixels is possible
385 basegfx::B2DRange aVisiblePixelRange(aFullPixelRange);
386
387 if(!aOutPixel.isInside(aFullPixelRange))
388 {
389 aVisiblePixelRange.intersect(aOutPixel);
390
391 if(aVisiblePixelRange.isEmpty())
392 {
393 // nothing in visible part, reduces to nothing
394 return false;
395 }
396
397 // aVisiblePixelRange contains the reduced output area in
398 // discrete coordinates. To make it useful everywhere, make it relative to
399 // the object range
400 basegfx::B2DHomMatrix aMakeVisibleRangeRelative;
401
402 aVisibleRange = aVisiblePixelRange;
403 aMakeVisibleRangeRelative.translate(
404 -aFullPixelRange.getMinX(),
405 -aFullPixelRange.getMinY());
406 aMakeVisibleRangeRelative.scale(
407 1.0 / aFullPixelRange.getWidth(),
408 1.0 / aFullPixelRange.getHeight());
409 aVisibleRange.transform(aMakeVisibleRangeRelative);
410 }
411
412 // for pixel devices, do *not* limit size, else OutputDevice::DrawDeviceAlphaBitmap
413 // will create another, badly scaled bitmap to do the job. Nonetheless, do a
414 // maximum clipping of something big (1600x1280x2). Add 1.0 to avoid rounding
415 // errors in rough estimations
416 const double fNewMaxArea(aVisiblePixelRange.getWidth() * aVisiblePixelRange.getHeight());
417
418 fMaximumArea = std::min(4096000.0, fNewMaxArea + 1.0);
419
420 return true;
421}
422
423// MM02 add some test class to get a simple timer-based output to be able
424// to check if it gets faster - and how much. Uncomment next line or set
425// DO_TIME_TEST for compile time if you want to use it
426// #define DO_TIME_TEST
427#ifdef DO_TIME_TEST
428#include <tools/time.hxx>
429struct LocalTimeTest
430{
431 const sal_uInt64 nStartTime;
432 LocalTimeTest() : nStartTime(tools::Time::GetSystemTicks()) {}
433 ~LocalTimeTest()
434 {
435 const sal_uInt64 nEndTime(tools::Time::GetSystemTicks());
436 const sal_uInt64 nDiffTime(nEndTime - nStartTime);
437
438 if(nDiffTime > 0)
439 {
440 OStringBuffer aOutput("Time: ");
441 OString aNumber(OString::number(nDiffTime));
442 aOutput.append(aNumber);
443 OSL_FAIL(aOutput.getStr());
444 }
445 }
446};
447#endif
448
450 const basegfx::B2DHomMatrix& rTransformation,
451 const BitmapEx& rBitmapEx,
452 double fAlpha)
453{
454 assert(!is_double_buffered_window());
455
456 if( ImplIsRecordLayout() )
457 return;
458
459 if(rBitmapEx.IsEmpty())
460 return;
461
462 if(rtl::math::approxEqual( fAlpha, 0.0 ))
463 return;
464
465 // MM02 compared to other public methods of OutputDevice
466 // this test was missing and led to zero-ptr-accesses
467 if ( !mpGraphics && !AcquireGraphics() )
468 return;
469
470 if ( mbInitClipRegion )
472
473 const bool bMetafile(nullptr != mpMetaFile);
474 /*
475 tdf#135325 typically in these OutputDevice methods, for the in
476 record-to-metafile case the MetaFile is already written to before the
477 test against mbOutputClipped to determine that output to the current
478 device would result in no visual output. In this case the metafile is
479 written after the test, so we must continue past mbOutputClipped if
480 recording to a metafile. It's typical to record with a device of nominal
481 size and play back later against something of a totally different size.
482 */
483 if (mbOutputClipped && !bMetafile)
484 return;
485
486#ifdef DO_TIME_TEST
487 // MM02 start time test when some data (not for trivial stuff). Will
488 // trigger and show data when leaving this method by destructing helper
489 static const char* pEnableBitmapDrawTimerTimer(getenv("SAL_ENABLE_TIMER_BITMAPDRAW"));
490 static bool bUseTimer(nullptr != pEnableBitmapDrawTimerTimer);
491 std::unique_ptr<LocalTimeTest> aTimeTest(
492 bUseTimer && rBitmapEx.GetSizeBytes() > 10000
493 ? new LocalTimeTest()
494 : nullptr);
495#endif
496
497 BitmapEx bitmapEx = rBitmapEx;
498
499 const bool bInvert(RasterOp::Invert == meRasterOp);
501 const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile);
502 // tdf#130768 CAUTION(!) using GetViewTransformation() is *not* enough here, it may
503 // be that mnOutOffX/mnOutOffY is used - see AOO bug 75163, mentioned at
504 // ImplGetDeviceTransformation declaration
505 basegfx::B2DHomMatrix aFullTransform(ImplGetDeviceTransformation() * rTransformation);
506
507 // First try to handle additional alpha blending, either directly, or modify the bitmap.
508 if(!rtl::math::approxEqual( fAlpha, 1.0 ))
509 {
510 if(bTryDirectPaint)
511 {
512 if(DrawTransformBitmapExDirect(aFullTransform, bitmapEx, fAlpha))
513 {
514 // we are done
515 return;
516 }
517 }
518 // Apply the alpha manually.
519 sal_uInt8 nColor( static_cast<sal_uInt8>( ::basegfx::fround( 255.0*(1.0 - fAlpha) + .5) ) );
520 AlphaMask aAlpha( bitmapEx.GetSizePixel(), &nColor );
521 if( bitmapEx.IsAlpha())
522 aAlpha.BlendWith( bitmapEx.GetAlpha());
523 bitmapEx = BitmapEx( bitmapEx.GetBitmap(), aAlpha );
524 }
525 if(rtl::math::approxEqual( fAlpha, 1.0 ))
526 fAlpha = 1.0; // avoid the need for approxEqual in backends
527
528 // If the backend's implementation is known to not need any optimizations here, pass to it directly.
529 // With most backends it's more performant to try to simplify to DrawBitmapEx() first.
530 if(bTryDirectPaint && mpGraphics->HasFastDrawTransformedBitmap() && DrawTransformBitmapExDirect(aFullTransform, bitmapEx))
531 return;
532
533 // decompose matrix to check rotation and shear
534 basegfx::B2DVector aScale, aTranslate;
535 double fRotate, fShearX;
536 rTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
537 const bool bRotated(!basegfx::fTools::equalZero(fRotate));
538 const bool bSheared(!basegfx::fTools::equalZero(fShearX));
539 const bool bMirroredX(basegfx::fTools::less(aScale.getX(), 0.0));
540 const bool bMirroredY(basegfx::fTools::less(aScale.getY(), 0.0));
541
542 if(!bRotated && !bSheared && !bMirroredX && !bMirroredY)
543 {
544 // with no rotation, shear or mirroring it can be mapped to DrawBitmapEx
545 // do *not* execute the mirroring here, it's done in the fallback
546 // #i124580# the correct DestSize needs to be calculated based on MaxXY values
547 Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
548 const Size aDestSize(
549 basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
550 basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
551 const Point aOrigin = GetMapMode().GetOrigin();
552 if (!bMetafile && comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel)
553 {
554 aDestPt.Move(aOrigin.getX(), aOrigin.getY());
555 EnableMapMode(false);
556 }
557
558 DrawBitmapEx(aDestPt, aDestSize, bitmapEx);
559 if (!bMetafile && comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel)
560 {
562 aDestPt.Move(-aOrigin.getX(), -aOrigin.getY());
563 }
564 return;
565 }
566
567 // Try the backend's implementation before resorting to the slower fallback here.
568 if(bTryDirectPaint && DrawTransformBitmapExDirect(aFullTransform, bitmapEx))
569 return;
570
571 // take the fallback when no rotate and shear, but mirror (else we would have done this above)
572 if(!bRotated && !bSheared)
573 {
574 // with no rotation or shear it can be mapped to DrawBitmapEx
575 // do *not* execute the mirroring here, it's done in the fallback
576 // #i124580# the correct DestSize needs to be calculated based on MaxXY values
577 const Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
578 const Size aDestSize(
579 basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
580 basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
581
582 DrawBitmapEx(aDestPt, aDestSize, bitmapEx);
583 return;
584 }
585
586 // at this point we are either sheared or rotated or both
587 assert(bSheared || bRotated);
588
589 // fallback; create transformed bitmap the hard way (back-transform
590 // the pixels) and paint
591 basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0);
592
593 // limit maximum area to something looking good for non-pixel-based targets (metafile, printer)
594 // by using a fixed minimum (allow at least, but no need to utilize) for good smoothing and an area
595 // dependent of original size for good quality when e.g. rotated/sheared. Still, limit to a maximum
596 // to avoid crashes/resource problems (ca. 1500x3000 here)
597 const Size& rOriginalSizePixel(bitmapEx.GetSizePixel());
598 const double fOrigArea(rOriginalSizePixel.Width() * rOriginalSizePixel.Height() * 0.5);
599 const double fOrigAreaScaled(fOrigArea * 1.44);
600 double fMaximumArea(std::clamp(fOrigAreaScaled, 1000000.0, 4500000.0));
601
602 if(!bMetafile)
603 {
604 if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
605 return;
606 }
607
608 if(aVisibleRange.isEmpty())
609 return;
610
611 BitmapEx aTransformed(bitmapEx);
612
613 // #122923# when the result needs an alpha channel due to being rotated or sheared
614 // and thus uncovering areas, add these channels so that the own transformer (used
615 // in getTransformed) also creates a transformed alpha channel
616 if(!aTransformed.IsAlpha() && (bSheared || bRotated))
617 {
618 // parts will be uncovered, extend aTransformed with a mask bitmap
619 const Bitmap aContent(aTransformed.GetBitmap());
620
621 AlphaMask aMaskBmp(aContent.GetSizePixel());
622 aMaskBmp.Erase(0);
623
624 aTransformed = BitmapEx(aContent, aMaskBmp);
625 }
626
627 basegfx::B2DVector aFullScale, aFullTranslate;
628 double fFullRotate, fFullShearX;
629 aFullTransform.decompose(aFullScale, aFullTranslate, fFullRotate, fFullShearX);
630
631 double fSourceRatio = 1.0;
632 if (rOriginalSizePixel.getHeight() != 0)
633 {
634 fSourceRatio = rOriginalSizePixel.getWidth() / rOriginalSizePixel.getHeight();
635 }
636 double fTargetRatio = 1.0;
637 if (aFullScale.getY() != 0)
638 {
639 fTargetRatio = aFullScale.getX() / aFullScale.getY();
640 }
641 bool bAspectRatioKept = rtl::math::approxEqual(fSourceRatio, fTargetRatio);
642 if (bSheared || !bAspectRatioKept)
643 {
644 // Not only rotation, or scaling does not keep aspect ratio.
645 aTransformed = aTransformed.getTransformed(
646 aFullTransform,
647 aVisibleRange,
648 fMaximumArea);
649 }
650 else
651 {
652 // Just rotation, can do that directly.
653 fFullRotate = fmod(fFullRotate * -1, 2 * M_PI);
654 if (fFullRotate < 0)
655 {
656 fFullRotate += 2 * M_PI;
657 }
658 Degree10 nAngle10(basegfx::fround(basegfx::rad2deg<10>(fFullRotate)));
659 aTransformed.Rotate(nAngle10, COL_TRANSPARENT);
660 }
661 basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
662
663 // get logic object target range
664 aTargetRange.transform(rTransformation);
665
666 // get from unified/relative VisibleRange to logoc one
667 aVisibleRange.transform(
669 aTargetRange.getRange(),
670 aTargetRange.getMinimum()));
671
672 // extract point and size; do not remove size, the bitmap may have been prepared reduced by purpose
673 // #i124580# the correct DestSize needs to be calculated based on MaxXY values
674 const Point aDestPt(basegfx::fround(aVisibleRange.getMinX()), basegfx::fround(aVisibleRange.getMinY()));
675 const Size aDestSize(
676 basegfx::fround(aVisibleRange.getMaxX()) - aDestPt.X(),
677 basegfx::fround(aVisibleRange.getMaxY()) - aDestPt.Y());
678
679 DrawBitmapEx(aDestPt, aDestSize, aTransformed);
680}
681
682/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
void BlendWith(const Bitmap &rOther)
Definition: alpha.cxx:83
void Erase(sal_uInt8 cTransparency)
Definition: alpha.cxx:78
AlphaMask GetAlpha() const
Definition: BitmapEx.cxx:215
sal_Int64 GetSizeBytes() const
Definition: BitmapEx.cxx:220
BitmapEx getTransformed(const basegfx::B2DHomMatrix &rTransformation, const basegfx::B2DRange &rVisibleRange, double fMaximumArea) const
Create transformed Bitmap.
Definition: BitmapEx.cxx:775
SAL_DLLPRIVATE std::shared_ptr< SalBitmap > const & ImplGetBitmapSalBitmap() const
Definition: bitmapex.hxx:450
bool IsAlpha() const
Definition: BitmapEx.cxx:193
bool Rotate(Degree10 nAngle10, const Color &rFillColor)
Rotate bitmap by the specified angle.
Definition: BitmapEx.cxx:316
bool IsEmpty() const
Definition: BitmapEx.cxx:177
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
Definition: BitmapEx.cxx:259
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:203
Bitmap maAlphaMask
Definition: bitmapex.hxx:465
const Size & GetSizePixel() const
Definition: bitmapex.hxx:72
const std::shared_ptr< SalBitmap > & ImplGetSalBitmap() const
bool Convert(BmpConversion eConversion)
Convert bitmap format.
Size GetSizePixel() const
bool Erase(const Color &rFillColor)
Fill the entire bitmap with the given color.
Definition: bitmappaint.cxx:34
vcl::PixelFormat getPixelFormat() const
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:581
const Point & GetOrigin() const
Definition: mapmod.cxx:152
virtual void InitClipRegion()
virtual void DrawDeviceBitmapEx(const Point &rDestPt, const Size &rDestSize, const Point &rSrcPtPixel, const Size &rSrcSizePixel, BitmapEx &rBitmapEx)
Definition: bitmapex.cxx:167
virtual vcl::Region GetActiveClipRegion() const
SAL_DLLPRIVATE tools::Long ImplLogicYToDevicePixel(tools::Long nY) const
Convert a logical Y coordinate to a device pixel's Y coordinate.
Definition: map.cxx:328
void EnableMapMode(bool bEnable=true)
Definition: map.cxx:646
void DrawBitmapEx(const Point &rDestPt, const BitmapEx &rBitmapEx)
Definition: bitmapex.cxx:33
void DrawTransformedBitmapEx(const basegfx::B2DHomMatrix &rTransformation, const BitmapEx &rBitmapEx, double fAlpha=1.0)
Draw BitmapEx transformed.
Definition: bitmapex.cxx:449
SAL_DLLPRIVATE bool is_double_buffered_window() const
vcl::Region maRegion
Definition: outdev.hxx:226
DrawModeFlags mnDrawMode
Definition: outdev.hxx:221
bool mbOutputClipped
Definition: outdev.hxx:246
SAL_DLLPRIVATE void DrawDeviceAlphaBitmap(const Bitmap &rBmp, const AlphaMask &rAlpha, const Point &rDestPt, const Size &rDestSize, const Point &rSrcPtPixel, const Size &rSrcSizePixel)
virtual bool AcquireGraphics() const =0
Acquire a graphics device that the output device uses to draw on.
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1169
Size GetOutputSizePixel() const
Definition: outdev.hxx:315
SAL_DLLPRIVATE bool ImplIsRecordLayout() const
Definition: outdev.cxx:709
void DrawRect(const tools::Rectangle &rRect)
Definition: rect.cxx:51
GDIMetaFile * mpMetaFile
Definition: outdev.hxx:186
SAL_DLLPRIVATE tools::Long ImplLogicHeightToDevicePixel(tools::Long nHeight) const
Convert a logical height to a height in units of device pixels.
Definition: map.cxx:345
bool IsClipRegion() const
Definition: outdev.hxx:556
SAL_DLLPRIVATE tools::Long ImplLogicWidthToDevicePixel(tools::Long nWidth) const
Convert a logical width to a width in units of device pixels.
Definition: map.cxx:337
SalGraphics * mpGraphics
Graphics context to draw on.
Definition: outdev.hxx:183
void DrawBitmap(const Point &rDestPt, const Bitmap &rBitmap)
bool mbInitClipRegion
Definition: outdev.hxx:253
RasterOp meRasterOp
Definition: outdev.hxx:233
virtual Bitmap GetBitmap(const Point &rSrcPt, const Size &rSize) const
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:482
VclPtr< VirtualDevice > mpAlphaVDev
Definition: outdev.hxx:197
const MapMode & GetMapMode() const
Definition: outdev.hxx:1558
virtual bool TransformAndReduceBitmapExToTargetRange(const basegfx::B2DHomMatrix &aFullTransform, basegfx::B2DRange &aVisibleRange, double &fMaximumArea)
Transform and reduce the area that needs to be drawn of the bitmap and return the new visible range a...
Definition: bitmapex.cxx:342
virtual bool DrawTransformBitmapExDirect(const basegfx::B2DHomMatrix &aFullTransform, const BitmapEx &rBitmapEx, double fAlpha=1.0)
Transform and draw a bitmap directly.
Definition: bitmapex.cxx:294
SAL_DLLPRIVATE basegfx::B2DHomMatrix ImplGetDeviceTransformation() const
Get device transformation.
Definition: map.cxx:929
DrawModeFlags GetDrawMode() const
Definition: outdev.hxx:488
SAL_DLLPRIVATE vcl::Region ImplPixelToDevicePixel(const vcl::Region &rRegion) const
Convert a region in pixel units to a region in device pixel units and coords.
Definition: map.cxx:636
SAL_DLLPRIVATE tools::Long ImplLogicXToDevicePixel(tools::Long nX) const
Convert a logical X coordinate to a device pixel's X coordinate.
Definition: map.cxx:319
BitmapEx GetBitmapEx(const Point &rSrcPt, const Size &rSize) const
Query extended bitmap (with alpha channel, if available).
Definition: bitmapex.cxx:149
bool DrawTransformedBitmap(const basegfx::B2DPoint &rNull, const basegfx::B2DPoint &rX, const basegfx::B2DPoint &rY, const SalBitmap &rSourceBitmap, const SalBitmap *pAlphaBitmap, double fAlpha, const OutputDevice &rOutDev)
bool HasFastDrawTransformedBitmap() const
void DrawBitmap(const SalTwoRect &rPosAry, const SalBitmap &rSalBitmap, const OutputDevice &rOutDev)
bool DrawAlphaBitmap(const SalTwoRect &, const SalBitmap &rSourceBitmap, const SalBitmap &rAlphaBitmap, const OutputDevice &rOutDev)
constexpr tools::Long getHeight() const
constexpr tools::Long Height() const
constexpr tools::Long getWidth() const
constexpr tools::Long Width() const
SAL_DLLPRIVATE void ImplFillOpaqueRectangle(const tools::Rectangle &rRect)
Used for alpha VDev, to set areas to opaque.
Definition: virdev.cxx:345
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void translate(double fX, double fY)
void scale(double fX, double fY)
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
B2DVector getRange() const
B2DPoint getMinimum() const
TYPE getMaxX() const
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getMaxY() const
void intersect(const Range2D &rRange)
bool isInside(const Tuple2D< TYPE > &rTuple) const
bool isEmpty() const
TYPE getHeight() const
TYPE getX() const
TYPE getY() const
constexpr tools::Long GetWidth() const
constexpr tools::Long Top() const
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
constexpr tools::Long GetHeight() const
tools::Long AdjustBottom(tools::Long nVertMoveDelta)
tools::Rectangle & Intersection(const tools::Rectangle &rRect)
constexpr tools::Long Left() const
constexpr bool IsEmpty() const
static sal_uInt64 GetSystemTicks()
constexpr ::Color COL_BLACK(0x00, 0x00, 0x00)
constexpr ::Color COL_TRANSPARENT(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
BmpMirrorFlags
MetaActionType
MapUnit GetMapUnit()
bool equalZero(const T &rfVal)
bool less(const T &rfValA, const T &rfValB)
B2DHomMatrix createScaleTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fTranslateX, double fTranslateY)
B2IRange fround(const B2DRange &rRange)
BitmapEx GetBitmapEx(BitmapEx const &rBitmapEx, DrawModeFlags nDrawMode)
Definition: drawmode.cxx:224
basegfx::B2DRange b2DRectangleFromRectangle(const ::tools::Rectangle &rRect)
BmpMirrorFlags AdjustTwoRect(SalTwoRect &rTwoRect, const Size &rSizePix)
Definition: rect.cxx:341
tools::Long mnDestY
Definition: salgtype.hxx:45
tools::Long mnSrcX
Definition: salgtype.hxx:40
tools::Long mnDestWidth
Definition: salgtype.hxx:46
tools::Long mnSrcHeight
Definition: salgtype.hxx:43
tools::Long mnSrcWidth
Definition: salgtype.hxx:42
tools::Long mnSrcY
Definition: salgtype.hxx:41
tools::Long mnDestHeight
Definition: salgtype.hxx:47
tools::Long mnDestX
Definition: salgtype.hxx:44
unsigned char sal_uInt8