summaryrefslogtreecommitdiffstats
path: root/canvas/source/vcl/spritehelper.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /canvas/source/vcl/spritehelper.cxx
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'canvas/source/vcl/spritehelper.cxx')
-rw-r--r--canvas/source/vcl/spritehelper.cxx352
1 files changed, 352 insertions, 0 deletions
diff --git a/canvas/source/vcl/spritehelper.cxx b/canvas/source/vcl/spritehelper.cxx
new file mode 100644
index 000000000..df8550c6c
--- /dev/null
+++ b/canvas/source/vcl/spritehelper.cxx
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <rtl/math.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/alpha.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/BitmapMonochromeFilter.hxx>
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <vcl/skia/SkiaHelper.hxx>
+
+#include <canvas/canvastools.hxx>
+#include <config_features.h>
+
+#include "impltools.hxx"
+#include "spritehelper.hxx"
+
+using namespace ::com::sun::star;
+
+
+namespace vclcanvas
+{
+ SpriteHelper::SpriteHelper() :
+ mpBackBuffer(),
+ mpBackBufferMask(),
+ maContent(),
+ mbShowSpriteBounds(false)
+ {
+ }
+
+ void SpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
+ const ::canvas::SpriteSurface::Reference& rOwningSpriteCanvas,
+ const BackBufferSharedPtr& rBackBuffer,
+ const BackBufferSharedPtr& rBackBufferMask,
+ bool bShowSpriteBounds )
+ {
+ ENSURE_OR_THROW( rOwningSpriteCanvas && rBackBuffer && rBackBufferMask,
+ "SpriteHelper::init(): Invalid sprite canvas or back buffer" );
+
+ mpBackBuffer = rBackBuffer;
+ mpBackBufferMask = rBackBufferMask;
+ mbShowSpriteBounds = bShowSpriteBounds;
+
+ init( rSpriteSize, rOwningSpriteCanvas );
+ }
+
+ void SpriteHelper::disposing()
+ {
+ mpBackBuffer.reset();
+ mpBackBufferMask.reset();
+
+ // forward to parent
+ CanvasCustomSpriteHelper::disposing();
+ }
+
+ void SpriteHelper::redraw( OutputDevice& rTargetSurface,
+ const ::basegfx::B2DPoint& rPos,
+ bool& io_bSurfacesDirty,
+ bool bBufferedUpdate ) const
+ {
+ (void)bBufferedUpdate; // not used on every platform
+
+ if( !mpBackBuffer ||
+ !mpBackBufferMask )
+ {
+ return; // we're disposed
+ }
+
+ // log output pos in device pixel
+ SAL_INFO("canvas.vcl", "SpriteHelper::redraw(): output pos is (" <<
+ rPos.getX() << "," << rPos.getY() << ")");
+
+ const double fAlpha( getAlpha() );
+
+ if( !(isActive() &&
+ !::basegfx::fTools::equalZero( fAlpha )) )
+ return;
+
+ const Point aEmptyPoint;
+ const ::basegfx::B2DVector& rOrigOutputSize( getSizePixel() );
+
+ // might get changed below (e.g. adapted for
+ // transformations). IMPORTANT: both position and size are
+ // rounded to integer values. From now on, only those
+ // rounded values are used, to keep clip and content in
+ // sync.
+ ::Size aOutputSize( vcl::unotools::sizeFromB2DSize( rOrigOutputSize ) );
+ ::Point aOutPos( vcl::unotools::pointFromB2DPoint( rPos ) );
+
+
+ // TODO(F3): Support for alpha-VDev
+
+ // Do we have to update our bitmaps (necessary if virdev
+ // was painted to, or transformation changed)?
+ const bool bNeedBitmapUpdate( io_bSurfacesDirty ||
+ hasTransformChanged() ||
+ maContent->IsEmpty() );
+
+ // updating content of sprite cache - surface is no
+ // longer dirty in relation to our cache
+ io_bSurfacesDirty = false;
+ transformUpdated();
+
+ if( bNeedBitmapUpdate )
+ {
+ BitmapEx aBmp( mpBackBuffer->getOutDev().GetBitmapEx( aEmptyPoint,
+ aOutputSize ) );
+
+ if( isContentFullyOpaque() )
+ {
+ // optimized case: content canvas is fully
+ // opaque. Note: since we retrieved aBmp directly
+ // from an OutDev, it's already a 'display bitmap'
+ // on windows.
+ maContent = aBmp;
+ }
+ else
+ {
+ // sprite content might contain alpha, create
+ // BmpEx, then.
+ BitmapEx aMask( mpBackBufferMask->getOutDev().GetBitmapEx( aEmptyPoint,
+ aOutputSize ) );
+
+ // bitmasks are much faster than alphamasks on some platforms
+ // so convert to bitmask if useful
+ bool convertTo1Bpp = aMask.GetBitCount() != 1;
+#ifdef MACOSX
+ convertTo1Bpp = false;
+#endif
+ if( SkiaHelper::isVCLSkiaEnabled())
+ convertTo1Bpp = false;
+
+ if( convertTo1Bpp )
+ {
+ OSL_FAIL("CanvasCustomSprite::redraw(): Mask bitmap is not "
+ "monochrome (performance!)");
+ BitmapEx aMaskEx(aMask);
+ BitmapFilter::Filter(aMaskEx, BitmapMonochromeFilter(255));
+ aMask = aMaskEx.GetBitmap();
+ }
+
+ // Note: since we retrieved aBmp and aMask
+ // directly from an OutDev, it's already a
+ // 'display bitmap' on windows.
+ if( aMask.GetBitCount() == 1 )
+ maContent = BitmapEx( aBmp.GetBitmap(), aMask.GetBitmap() );
+ else
+ maContent = BitmapEx( aBmp.GetBitmap(), AlphaMask( aMask.GetBitmap()) );
+ }
+ }
+
+ ::basegfx::B2DHomMatrix aTransform( getTransformation() );
+
+ // check whether matrix is "easy" to handle - pure
+ // translations or scales are handled by OutputDevice
+ // alone
+ const bool bIdentityTransform( aTransform.isIdentity() );
+
+ // make transformation absolute (put sprite to final
+ // output position). Need to happen here, as we also have
+ // to translate the clip polygon
+ aTransform.translate( aOutPos.X(),
+ aOutPos.Y() );
+
+ if( !bIdentityTransform )
+ {
+ // Avoid the trick with the negative width in the OpenGL case,
+ // OutputDevice::DrawDeviceAlphaBitmap() doesn't like it.
+ if (!::basegfx::fTools::equalZero( aTransform.get(0,1) ) ||
+ !::basegfx::fTools::equalZero( aTransform.get(1,0) )
+#if HAVE_FEATURE_UI
+ || OpenGLHelper::isVCLOpenGLEnabled()
+#endif
+ || SkiaHelper::isVCLSkiaEnabled()
+ )
+ {
+ // "complex" transformation, employ affine
+ // transformator
+
+ // modify output position, to account for the fact
+ // that transformBitmap() always normalizes its output
+ // bitmap into the smallest enclosing box.
+ ::basegfx::B2DRectangle aDestRect;
+ ::canvas::tools::calcTransformedRectBounds( aDestRect,
+ ::basegfx::B2DRectangle(0,
+ 0,
+ rOrigOutputSize.getX(),
+ rOrigOutputSize.getY()),
+ aTransform );
+
+ aOutPos.setX( ::basegfx::fround( aDestRect.getMinX() ) );
+ aOutPos.setY( ::basegfx::fround( aDestRect.getMinY() ) );
+
+ // TODO(P3): Use optimized bitmap transformation here.
+
+ // actually re-create the bitmap ONLY if necessary
+ if( bNeedBitmapUpdate )
+ maContent = tools::transformBitmap( *maContent,
+ aTransform );
+
+ aOutputSize = maContent->GetSizePixel();
+ }
+ else
+ {
+ // relatively 'simplistic' transformation -
+ // retrieve scale and translational offset
+ aOutputSize.setWidth (
+ ::basegfx::fround( rOrigOutputSize.getX() * aTransform.get(0,0) ) );
+ aOutputSize.setHeight(
+ ::basegfx::fround( rOrigOutputSize.getY() * aTransform.get(1,1) ) );
+
+ aOutPos.setX( ::basegfx::fround( aTransform.get(0,2) ) );
+ aOutPos.setY( ::basegfx::fround( aTransform.get(1,2) ) );
+ }
+ }
+
+ // transformBitmap() might return empty bitmaps, for tiny
+ // scales.
+ if( !(*maContent) )
+ return;
+
+ rTargetSurface.Push( PushFlags::CLIPREGION );
+
+ // apply clip (if any)
+ if( getClip().is() )
+ {
+ ::basegfx::B2DPolyPolygon aClipPoly(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
+ getClip() ));
+
+ if( aClipPoly.count() )
+ {
+ // aTransform already contains the
+ // translational component, moving the clip to
+ // the final sprite output position.
+ aClipPoly.transform( aTransform );
+
+ if( mbShowSpriteBounds )
+ {
+ // Paint green sprite clip area
+ rTargetSurface.SetLineColor( Color( 0,255,0 ) );
+ rTargetSurface.SetFillColor();
+
+ rTargetSurface.DrawPolyPolygon(::tools::PolyPolygon(aClipPoly)); // #i76339#
+ }
+
+ vcl::Region aClipRegion( aClipPoly );
+ rTargetSurface.SetClipRegion( aClipRegion );
+ }
+ }
+
+ if( ::rtl::math::approxEqual(fAlpha, 1.0) )
+ {
+ // no alpha modulation -> just copy to output
+ if( maContent->IsTransparent() )
+ rTargetSurface.DrawBitmapEx( aOutPos, aOutputSize, *maContent );
+ else
+ rTargetSurface.DrawBitmap( aOutPos, aOutputSize, maContent->GetBitmap() );
+ }
+ else
+ {
+ // TODO(P3): Switch to OutputDevice::DrawTransparent()
+ // here
+
+ // draw semi-transparent
+ sal_uInt8 nColor( static_cast<sal_uInt8>( ::basegfx::fround( 255.0*(1.0 - fAlpha) + .5) ) );
+ AlphaMask aAlpha( maContent->GetSizePixel(),
+ &nColor );
+
+ // mask out fully transparent areas
+ if( maContent->IsTransparent() )
+ aAlpha.Replace( maContent->GetMask(), 255 );
+
+ // alpha-blend to output
+ rTargetSurface.DrawBitmapEx( aOutPos, aOutputSize,
+ BitmapEx( maContent->GetBitmap(),
+ aAlpha ) );
+ }
+
+ rTargetSurface.Pop();
+
+ if( !mbShowSpriteBounds )
+ return;
+
+ ::tools::PolyPolygon aMarkerPoly(
+ ::canvas::tools::getBoundMarksPolyPolygon(
+ ::basegfx::B2DRectangle(aOutPos.X(),
+ aOutPos.Y(),
+ aOutPos.X() + aOutputSize.Width()-1,
+ aOutPos.Y() + aOutputSize.Height()-1) ) );
+
+ // Paint little red sprite area markers
+ rTargetSurface.SetLineColor( COL_RED );
+ rTargetSurface.SetFillColor();
+
+ for( int i=0; i<aMarkerPoly.Count(); ++i )
+ {
+ rTargetSurface.DrawPolyLine( aMarkerPoly.GetObject(static_cast<sal_uInt16>(i)) );
+ }
+
+ // paint sprite prio
+ vcl::Font aVCLFont;
+ aVCLFont.SetFontHeight( std::min(long(20),aOutputSize.Height()) );
+ aVCLFont.SetColor( COL_RED );
+
+ rTargetSurface.SetTextAlign(ALIGN_TOP);
+ rTargetSurface.SetTextColor( COL_RED );
+ rTargetSurface.SetFont( aVCLFont );
+
+ OUString text( ::rtl::math::doubleToUString( getPriority(),
+ rtl_math_StringFormat_F,
+ 2,'.',nullptr,' ') );
+
+ rTargetSurface.DrawText( aOutPos+Point(2,2), text );
+ SAL_INFO( "canvas.vcl",
+ "sprite " << this << " has prio " << getPriority());
+ }
+
+ ::basegfx::B2DPolyPolygon SpriteHelper::polyPolygonFromXPolyPolygon2D( uno::Reference< rendering::XPolyPolygon2D >& xPoly ) const
+ {
+ return ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( xPoly );
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */