slideshow: fix para. rendering when using SdrBlockTextPrimitive2D

If using an empty ViewInformation, it is possible that the prim.
decomposition is redone, when a different ViewInformation is used.
This removes all visibility flags for the paragraphs, so the whole
text is rendered. This fixes the issue by using the same instance
of the ViewInformation that is also used later for rendering the
primitives to the VirtualDevice, so the buffered decomposition is
used.

Also a test is added - it checks the hash of the buffer, which
should be different for all 3 layers. For this the tostring has
to be moved to the common hash.hxx, so we can reuse it in out
tests.

Includes:

	Author:     Marco Cecchetti <marco.cecchetti@collabora.com>
	AuthorDate: Wed Mar 5 23:15:33 2025 +0100

	    slideshow: regression: layer with paragraph has not the expected content

	    Change-Id: I850c6922cdd358eee35074fbff04f238994b2c76

Otherwise 'make -C sd -sr CppunitTest_sd_tiledrendering
CPPUNIT_TEST_NAME=testSlideshowLayeredRendering_Animation_DifferentKindOfTextBox'
would fail.

Change-Id: I4c951215a52e302d3b7b60a30c1b995002e53a4b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182833
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
This commit is contained in:
Tomaž Vajngerl
2025-03-12 18:30:54 +01:00
committed by Miklos Vajna
parent 3cf71eec36
commit cde8bf6bcf
5 changed files with 104 additions and 14 deletions
@@ -62,7 +62,8 @@ namespace drawinglayer::processor2d
{
for (const primitive2d::Primitive2DReference& rCandidate : rSource)
{
if (rCandidate)
// Skip, if not visible
if (rCandidate && rCandidate->getVisible())
processBasePrimitive2D(*rCandidate);
}
}
@@ -192,10 +192,6 @@ bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(
void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
{
// Skip, if not visible
if (!rCandidate.getVisible())
return;
switch (rCandidate.getPrimitive2DID())
{
case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D:
@@ -18,6 +18,7 @@
#include <comphelper/propertysequence.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/string.hxx>
#include <comphelper/hash.hxx>
#include <editeng/eeitem.hxx>
#include <editeng/editids.hrc>
#include <editeng/editobj.hxx>
@@ -3422,6 +3423,91 @@ CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testSlideshowLayeredRendering_Animati
pXImpressDocument->postSlideshowCleanup();
}
namespace
{
template <typename T>
bool is_unique(std::vector<T> vec)
{
std::sort(vec.begin(), vec.end());
return std::unique(vec.begin(), vec.end()) == vec.end();
}
}
CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testSlideshowLayeredRendering_Animation_DifferentKindOfTextBox)
{
SdXImpressDocument* pXImpressDocument = createDoc("SlideRenderingTest_Animations_DifferentKindOfTextBox.odp");
pXImpressDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
CPPUNIT_ASSERT(pViewShell);
SdPage* pPage = pViewShell->GetActualPage();
CPPUNIT_ASSERT(pPage);
sal_Int32 nViewWidth = 2000;
sal_Int32 nViewHeight = 2000;
std::string sHash = GetInterfaceHash(GetXDrawPageForSdrPage(pPage));
CPPUNIT_ASSERT(pXImpressDocument->createSlideRenderer(sHash.c_str(), 0, nViewWidth, nViewHeight, true, true));
CPPUNIT_ASSERT_EQUAL(2000, nViewWidth);
CPPUNIT_ASSERT_EQUAL(1500, nViewHeight);
std::vector<std::string> aHashes;
{
std::vector<sal_uInt8> pBuffer(nViewWidth * nViewHeight * 4);
bool bIsBitmapLayer = false;
OUString rJsonMsg;
double dScale = 1.0;
CPPUNIT_ASSERT(!pXImpressDocument->renderNextSlideLayer(pBuffer.data(), bIsBitmapLayer, dScale, rJsonMsg));
// Remember the hash of the buffer for uniqueness check
auto aBufferHash = comphelper::Hash::calculateHash(pBuffer.data(), pBuffer.size(), comphelper::HashType::SHA1);
aHashes.push_back(comphelper::hashToString(aBufferHash));
debugWriteImageToFile(1, pBuffer, nViewWidth, nViewHeight, rJsonMsg.toUtf8().getStr());
}
{
std::vector<sal_uInt8> pBuffer(nViewWidth * nViewHeight * 4);
bool bIsBitmapLayer = false;
OUString rJsonMsg;
double dScale = 1.0;
CPPUNIT_ASSERT(!pXImpressDocument->renderNextSlideLayer(pBuffer.data(), bIsBitmapLayer, dScale, rJsonMsg));
// Remember the hash of the buffer for uniqueness check
auto aBufferHash = comphelper::Hash::calculateHash(pBuffer.data(), pBuffer.size(), comphelper::HashType::SHA1);
aHashes.push_back(comphelper::hashToString(aBufferHash));
debugWriteImageToFile(2, pBuffer, nViewWidth, nViewHeight, rJsonMsg.toUtf8().getStr());
}
{
std::vector<sal_uInt8> pBuffer(nViewWidth * nViewHeight * 4);
bool bIsBitmapLayer = false;
OUString rJsonMsg;
double dScale = 1.0;
CPPUNIT_ASSERT(!pXImpressDocument->renderNextSlideLayer(pBuffer.data(), bIsBitmapLayer, dScale, rJsonMsg));
// Remember the hash of the buffer for uniqueness check
auto aBufferHash = comphelper::Hash::calculateHash(pBuffer.data(), pBuffer.size(), comphelper::HashType::SHA1);
aHashes.push_back(comphelper::hashToString(aBufferHash));
debugWriteImageToFile(3, pBuffer, nViewWidth, nViewHeight, rJsonMsg.toUtf8().getStr());
}
// check if all hashes are unique
CPPUNIT_ASSERT(is_unique(aHashes));
// Check we are done
{
std::vector<sal_uInt8> pBuffer(nViewWidth * nViewHeight * 4);
bool bIsBitmapLayer = false;
OUString rJsonMsg;
double dScale = 1.0;
CPPUNIT_ASSERT(pXImpressDocument->renderNextSlideLayer(pBuffer.data(), bIsBitmapLayer, dScale, rJsonMsg));
}
pXImpressDocument->postSlideshowCleanup();
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+16 -9
View File
@@ -138,7 +138,8 @@ void changeBackground(drawinglayer::primitive2d::Primitive2DContainer const& rCo
}
drawinglayer::primitive2d::TextHierarchyBlockPrimitive2D*
findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const& rContainer)
findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const& rContainer,
drawinglayer::geometry::ViewInformation2D const& rViewInformation2D)
{
for (size_t i = 0; i < rContainer.size(); i++)
{
@@ -157,7 +158,8 @@ findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const& rContainer)
= dynamic_cast<drawinglayer::primitive2d::GroupPrimitive2D*>(pBasePrimitive);
if (pGroupPrimitive)
{
auto* pTextBlock = findTextBlock(pGroupPrimitive->getChildren());
auto* pTextBlock
= findTextBlock(pGroupPrimitive->getChildren(), rViewInformation2D);
if (pTextBlock)
return pTextBlock;
}
@@ -169,9 +171,8 @@ findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const& rContainer)
{
// try to decompose
drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
drawinglayer::geometry::ViewInformation2D());
auto* pTextBlock = findTextBlock(aPrimitiveContainer);
pBasePrimitive->get2DDecomposition(aPrimitiveContainer, rViewInformation2D);
auto* pTextBlock = findTextBlock(aPrimitiveContainer, rViewInformation2D);
if (pTextBlock)
return pTextBlock;
}
@@ -181,9 +182,10 @@ findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const& rContainer)
}
void modifyParagraphs(drawinglayer::primitive2d::Primitive2DContainer& rContainer,
drawinglayer::geometry::ViewInformation2D const& rViewInformation2D,
std::deque<sal_Int32> const& rPreserveIndices, bool bRenderObject)
{
auto* pTextBlock = findTextBlock(rContainer);
auto* pTextBlock = findTextBlock(rContainer, rViewInformation2D);
if (pTextBlock)
{
@@ -243,6 +245,10 @@ public:
return;
SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
drawinglayer::geometry::ViewInformation2D const& rViewInformation2D
= rOriginal.GetObjectContact().getViewInformation2D();
// Check if we are rendering an object that is valid to render (exists, and not empty)
if (pObject == nullptr || pObject->IsEmptyPresObj())
return;
@@ -338,7 +344,8 @@ public:
if (bRenderOtherParagraphs)
{
modifyParagraphs(rContainer, nOtherParagraphs, true); // render the object
modifyParagraphs(rContainer, rViewInformation2D, nOtherParagraphs,
true); // render the object
mrRenderState.mnCurrentTargetParagraph = -1;
}
else
@@ -348,8 +355,8 @@ public:
std::deque<sal_Int32> aPreserveParagraphs{ nParagraph };
mrRenderState.mnCurrentTargetParagraph = nParagraph;
modifyParagraphs(rContainer, aPreserveParagraphs,
false); // render only the paragraphs
// render only the paragraphs
modifyParagraphs(rContainer, rViewInformation2D, aPreserveParagraphs, false);
}
}