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.GetAlphaMask(), 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.GetAlphaMask(),
278 rBitmapEx.GetAlphaMask()));
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.GetAlphaMask();
313 }
314 else if (mpAlphaVDev)
315 {
316 aAlphaBitmap = AlphaMask(rBitmapEx.GetSizePixel());
317 aAlphaBitmap.Erase(COL_ALPHA_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 nTransparency( static_cast<sal_uInt8>( ::basegfx::fround( 255.0*(1.0 - fAlpha) + .5) ) );
520 AlphaMask aAlpha( bitmapEx.GetSizePixel(), &nTransparency );
521 if( bitmapEx.IsAlpha())
522 aAlpha.BlendWith( bitmapEx.GetAlphaMask());
523 bitmapEx = BitmapEx( bitmapEx.GetBitmap(), aAlpha );
524 }
525
526 // If the backend's implementation is known to not need any optimizations here, pass to it directly.
527 // With most backends it's more performant to try to simplify to DrawBitmapEx() first.
528 if(bTryDirectPaint && mpGraphics->HasFastDrawTransformedBitmap() && DrawTransformBitmapExDirect(aFullTransform, bitmapEx))
529 return;
530
531 // decompose matrix to check rotation and shear
532 basegfx::B2DVector aScale, aTranslate;
533 double fRotate, fShearX;
534 rTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
535 const bool bRotated(!basegfx::fTools::equalZero(fRotate));
536 const bool bSheared(!basegfx::fTools::equalZero(fShearX));
537 const bool bMirroredX(basegfx::fTools::less(aScale.getX(), 0.0));
538 const bool bMirroredY(basegfx::fTools::less(aScale.getY(), 0.0));
539
540 if(!bRotated && !bSheared && !bMirroredX && !bMirroredY)
541 {
542 // with no rotation, shear or mirroring it can be mapped to DrawBitmapEx
543 // do *not* execute the mirroring here, it's done in the fallback
544 // #i124580# the correct DestSize needs to be calculated based on MaxXY values
545 Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
546 const Size aDestSize(
547 basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
548 basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
549 const Point aOrigin = GetMapMode().GetOrigin();
550 if (!bMetafile && comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel)
551 {
552 aDestPt.Move(aOrigin.getX(), aOrigin.getY());
553 EnableMapMode(false);
554 }
555
556 DrawBitmapEx(aDestPt, aDestSize, bitmapEx);
557 if (!bMetafile && comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel)
558 {
560 aDestPt.Move(-aOrigin.getX(), -aOrigin.getY());
561 }
562 return;
563 }
564
565 // Try the backend's implementation before resorting to the slower fallback here.
566 if(bTryDirectPaint && DrawTransformBitmapExDirect(aFullTransform, bitmapEx))
567 return;
568
569 // take the fallback when no rotate and shear, but mirror (else we would have done this above)
570 if(!bRotated && !bSheared)
571 {
572 // with no rotation or shear it can be mapped to DrawBitmapEx
573 // do *not* execute the mirroring here, it's done in the fallback
574 // #i124580# the correct DestSize needs to be calculated based on MaxXY values
575 const Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
576 const Size aDestSize(
577 basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
578 basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
579
580 DrawBitmapEx(aDestPt, aDestSize, bitmapEx);
581 return;
582 }
583
584 // at this point we are either sheared or rotated or both
585 assert(bSheared || bRotated);
586
587 // fallback; create transformed bitmap the hard way (back-transform
588 // the pixels) and paint
589 basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0);
590
591 // limit maximum area to something looking good for non-pixel-based targets (metafile, printer)
592 // by using a fixed minimum (allow at least, but no need to utilize) for good smoothing and an area
593 // dependent of original size for good quality when e.g. rotated/sheared. Still, limit to a maximum
594 // to avoid crashes/resource problems (ca. 1500x3000 here)
595 const Size& rOriginalSizePixel(bitmapEx.GetSizePixel());
596 const double fOrigArea(rOriginalSizePixel.Width() * rOriginalSizePixel.Height() * 0.5);
597 const double fOrigAreaScaled(fOrigArea * 1.44);
598 double fMaximumArea(std::clamp(fOrigAreaScaled, 1000000.0, 4500000.0));
599
600 if(!bMetafile)
601 {
602 if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
603 return;
604 }
605
606 if(aVisibleRange.isEmpty())
607 return;
608
609 BitmapEx aTransformed(bitmapEx);
610
611 // #122923# when the result needs an alpha channel due to being rotated or sheared
612 // and thus uncovering areas, add these channels so that the own transformer (used
613 // in getTransformed) also creates a transformed alpha channel
614 if(!aTransformed.IsAlpha() && (bSheared || bRotated))
615 {
616 // parts will be uncovered, extend aTransformed with a mask bitmap
617 const Bitmap aContent(aTransformed.GetBitmap());
618
619 AlphaMask aMaskBmp(aContent.GetSizePixel());
620 aMaskBmp.Erase(0);
621
622 aTransformed = BitmapEx(aContent, aMaskBmp);
623 }
624
625 basegfx::B2DVector aFullScale, aFullTranslate;
626 double fFullRotate, fFullShearX;
627 aFullTransform.decompose(aFullScale, aFullTranslate, fFullRotate, fFullShearX);
628
629 double fSourceRatio = 1.0;
630 if (rOriginalSizePixel.getHeight() != 0)
631 {
632 fSourceRatio = rOriginalSizePixel.getWidth() / rOriginalSizePixel.getHeight();
633 }
634 double fTargetRatio = 1.0;
635 if (aFullScale.getY() != 0)
636 {
637 fTargetRatio = aFullScale.getX() / aFullScale.getY();
638 }
639 bool bAspectRatioKept = rtl::math::approxEqual(fSourceRatio, fTargetRatio);
640 if (bSheared || !bAspectRatioKept)
641 {
642 // Not only rotation, or scaling does not keep aspect ratio.
643 aTransformed = aTransformed.getTransformed(
644 aFullTransform,
645 aVisibleRange,
646 fMaximumArea);
647 }
648 else
649 {
650 // Just rotation, can do that directly.
651 fFullRotate = fmod(fFullRotate * -1, 2 * M_PI);
652 if (fFullRotate < 0)
653 {
654 fFullRotate += 2 * M_PI;
655 }
656 Degree10 nAngle10(basegfx::fround(basegfx::rad2deg<10>(fFullRotate)));
657 aTransformed.Rotate(nAngle10, COL_TRANSPARENT);
658 }
659 basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
660
661 // get logic object target range
662 aTargetRange.transform(rTransformation);
663
664 // get from unified/relative VisibleRange to logoc one
665 aVisibleRange.transform(
667 aTargetRange.getRange(),
668 aTargetRange.getMinimum()));
669
670 // extract point and size; do not remove size, the bitmap may have been prepared reduced by purpose
671 // #i124580# the correct DestSize needs to be calculated based on MaxXY values
672 const Point aDestPt(basegfx::fround(aVisibleRange.getMinX()), basegfx::fround(aVisibleRange.getMinY()));
673 const Size aDestSize(
674 basegfx::fround(aVisibleRange.getMaxX()) - aDestPt.X(),
675 basegfx::fround(aVisibleRange.getMaxY()) - aDestPt.Y());
676
677 DrawBitmapEx(aDestPt, aDestSize, aTransformed);
678}
679
680/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
void Erase(sal_uInt8 cTransparency)
Definition: alpha.cxx:82
void BlendWith(const AlphaMask &rOther)
Definition: alpha.cxx:88
const AlphaMask & GetAlphaMask() const
Definition: bitmapex.hxx:71
sal_Int64 GetSizeBytes() const
Definition: BitmapEx.cxx:229
BitmapEx getTransformed(const basegfx::B2DHomMatrix &rTransformation, const basegfx::B2DRange &rVisibleRange, double fMaximumArea) const
Create transformed Bitmap.
Definition: BitmapEx.cxx:778
SAL_DLLPRIVATE std::shared_ptr< SalBitmap > const & ImplGetBitmapSalBitmap() const
Definition: bitmapex.hxx:451
bool IsAlpha() const
Definition: BitmapEx.cxx:207
bool Rotate(Degree10 nAngle10, const Color &rFillColor)
Rotate bitmap by the specified angle.
Definition: BitmapEx.cxx:325
bool IsEmpty() const
Definition: BitmapEx.cxx:186
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
Definition: BitmapEx.cxx:268
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:217
const Size & GetSizePixel() const
Definition: bitmapex.hxx:73
AlphaMask maAlphaMask
Definition: bitmapex.hxx:468
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:585
const Point & GetOrigin() const
Definition: mapmod.cxx:183
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:271
void EnableMapMode(bool bEnable=true)
Definition: map.cxx:589
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:225
DrawModeFlags mnDrawMode
Definition: outdev.hxx:220
bool mbOutputClipped
Definition: outdev.hxx:245
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:1110
Size GetOutputSizePixel() const
Definition: outdev.hxx:314
SAL_DLLPRIVATE bool ImplIsRecordLayout() const
Definition: outdev.cxx:708
void DrawRect(const tools::Rectangle &rRect)
Definition: rect.cxx:50
GDIMetaFile * mpMetaFile
Definition: outdev.hxx:185
SAL_DLLPRIVATE tools::Long ImplLogicHeightToDevicePixel(tools::Long nHeight) const
Convert a logical height to a height in units of device pixels.
Definition: map.cxx:288
bool IsClipRegion() const
Definition: outdev.hxx:555
SAL_DLLPRIVATE tools::Long ImplLogicWidthToDevicePixel(tools::Long nWidth) const
Convert a logical width to a width in units of device pixels.
Definition: map.cxx:280
SalGraphics * mpGraphics
Graphics context to draw on.
Definition: outdev.hxx:182
void DrawBitmap(const Point &rDestPt, const Bitmap &rBitmap)
bool mbInitClipRegion
Definition: outdev.hxx:252
RasterOp meRasterOp
Definition: outdev.hxx:232
virtual Bitmap GetBitmap(const Point &rSrcPt, const Size &rSize) const
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:481
VclPtr< VirtualDevice > mpAlphaVDev
Definition: outdev.hxx:196
const MapMode & GetMapMode() const
Definition: outdev.hxx:1557
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:870
DrawModeFlags GetDrawMode() const
Definition: outdev.hxx:487
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:579
SAL_DLLPRIVATE tools::Long ImplLogicXToDevicePixel(tools::Long nX) const
Convert a logical X coordinate to a device pixel's X coordinate.
Definition: map.cxx:262
BitmapEx GetBitmapEx(const Point &rSrcPt, const Size &rSize) const
Query extended bitmap (with alpha channel, if available).
Definition: bitmapex.cxx:149
constexpr tools::Long Y() const
void Move(tools::Long nHorzMove, tools::Long nVertMove)
constexpr tools::Long X() const
constexpr tools::Long getX() const
constexpr tools::Long getY() const
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:342
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_ALPHA_OPAQUE(0xff, 0xff, 0xff)
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:242
basegfx::B2DRange b2DRectangleFromRectangle(const ::tools::Rectangle &rRect)
BmpMirrorFlags AdjustTwoRect(SalTwoRect &rTwoRect, const Size &rSizePix)
Definition: rect.cxx:340
tools::Long mnDestY
Definition: salgtype.hxx:44
tools::Long mnSrcX
Definition: salgtype.hxx:39
tools::Long mnDestWidth
Definition: salgtype.hxx:45
tools::Long mnSrcHeight
Definition: salgtype.hxx:42
tools::Long mnSrcWidth
Definition: salgtype.hxx:41
tools::Long mnSrcY
Definition: salgtype.hxx:40
tools::Long mnDestHeight
Definition: salgtype.hxx:46
tools::Long mnDestX
Definition: salgtype.hxx:43
unsigned char sal_uInt8