/* -*- 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/.
*/
#ifdef IOS #include <vcl/sysdata.hxx> namespace CoreGraphics
{ // Namespace here because iOS redefines Point and Size #import <CoreGraphics/CoreGraphics.h>
SystemGraphicsData getiOSGraphicsData(unsignedchar* pBuffer, long width, long height)
{ double fDPIScale = 1.0;
// Online uses the LOK_TILEMODE_RGBA by default so flip the normal flags // to kCGImageAlphaPremultipliedLast | kCGImageByteOrder32Big
CGContextRef pCGContext
= CGBitmapContextCreate(pBuffer, width, height, 8, width * 4, CGColorSpaceCreateDeviceRGB(),
kCGImageAlphaPremultipliedLast | kCGImageByteOrder32Big);
/// Analyze the rendering and create rendering passes class AnalyzeRenderingRedirector : public sdr::contact::ViewObjectContactRedirector
{ private:
RenderState& mrRenderState; bool mbRenderMasterPage;
// Adds a new rendering pass to the list and returns it
RenderPass* newRenderPass()
{
mrRenderState.maRenderPasses.emplace_back(); return &mrRenderState.maRenderPasses.back();
}
// Closes current rendering pass, and creates a new empty current one void closeRenderPass()
{ if (mpCurrentRenderPass->maObjectsAndParagraphs.empty()) return;
void finalizeRenderPasses()
{ // Last rendering pass might be empty - delete if (mrRenderState.maRenderPasses.back().isEmpty())
mrRenderState.maRenderPasses.pop_back();
// Merge text field render passes into the main render pass list. // We prepend them, so that they are rendered and sent to the client first. // So, when the client try to draw a master page layer corresponding // to a text field placeholder the content is already available. for (auto& rRenderWork : mrRenderState.maTextFields)
{
mrRenderState.maRenderPasses.push_front(rRenderWork);
}
}
// Does the object have a page if (pPage == nullptr) return;
// is the object visible and not hidden by any option constbool bVisible
= pObject->getSdrPageFromSdrObject()->checkVisibility(rOriginal, rDisplayInfo, true);
// Determine the current stage, depending on the page
RenderStage eCurrentStage
= pPage->IsMasterPage() ? RenderStage::Master : RenderStage::Slide;
// Check if the object has slide number, footer, date/time if (eCurrentStage == RenderStage::Master && !sTextFieldType.isEmpty())
{ // it's not possible to set visibility for non-presentation text fields bool bIsTextFieldVisible
= !isPresentationTextField || isTextFieldVisible(sTextFieldType);
// A placeholder always needs to be exported even if the content is hidden // since it could be visible on another slide and master page layers should be cached // on the client if (mbRenderMasterPage)
{
closeRenderPass();
mpCurrentRenderPass->maObjectsAndParagraphs.emplace(pObject,
std::deque<sal_Int32>());
mpCurrentRenderPass->meStage = eCurrentStage;
mpCurrentRenderPass->mbPlaceholder = true;
mpCurrentRenderPass->maFieldType = sTextFieldType;
mpCurrentRenderPass->mpObject = pObject;
closeRenderPass();
} // Collect text field content if it's visible // Both checks are needed! if (bVisible && bIsTextFieldVisible)
{
RenderPass aTextFieldPass;
aTextFieldPass.maObjectsAndParagraphs.emplace(pObject, std::deque<sal_Int32>());
aTextFieldPass.meStage = RenderStage::TextFields;
aTextFieldPass.maFieldType = sTextFieldType;
aTextFieldPass.mpObject = pObject;
if (!mbRenderMasterPage && eCurrentStage == RenderStage::Master) return;
if (!bVisible) return;
// We switched from master objects to slide objects if (eCurrentStage == RenderStage::Slide && mePreviousStage == RenderStage::Master)
closeRenderPass();
// check if object is in an animation auto aIterator = mrRenderState.maAnimationRenderInfoList.find(pObject); if (aIterator != mrRenderState.maAnimationRenderInfoList.end())
{
closeRenderPass();
AnimationRenderInfo aInfo = aIterator->second;
if (!aInfo.maParagraphs.empty()) // we need to render paragraphs
{ auto* pTextObject = dynamic_cast<SdrTextObj*>(pObject); if (pTextObject)
{
sal_Int32 nNumberOfParagraphs = pTextObject->GetOutlinerParaObject()->Count();
std::deque<sal_Int32> nOtherParagraphs; for (sal_Int32 nParagraph = 0; nParagraph < nNumberOfParagraphs; ++nParagraph)
{ if (std::find(aInfo.maParagraphs.begin(), aInfo.maParagraphs.end(),
nParagraph)
== aInfo.maParagraphs.end())
nOtherParagraphs.push_back(nParagraph);
} // Add the non-animated part of the object that has animated paragraphs
mpCurrentRenderPass->maObjectsAndParagraphs.emplace(pObject, nOtherParagraphs);
mpCurrentRenderPass->meStage = eCurrentStage;
mpCurrentRenderPass->mbRenderObjectBackground = true;
mpCurrentRenderPass->mbAnimation = true;
mpCurrentRenderPass->mpObject = pObject;
closeRenderPass();
// Add all the animated paragraphs for (sal_Int32 nParagraph : aInfo.maParagraphs)
{
mpCurrentRenderPass->maObjectsAndParagraphs.emplace(
pObject, std::deque<sal_Int32>{ nParagraph });
mpCurrentRenderPass->meStage = eCurrentStage;
mpCurrentRenderPass->mbAnimation = true;
mpCurrentRenderPass->mpObject = pObject;
mpCurrentRenderPass->mnParagraph = nParagraph;
closeRenderPass();
}
}
} else
{ // Add the animated object - paragraphs are not animated
mpCurrentRenderPass->maObjectsAndParagraphs.emplace(pObject,
std::deque<sal_Int32>());
mpCurrentRenderPass->meStage = eCurrentStage;
mpCurrentRenderPass->mbAnimation = true;
mpCurrentRenderPass->mpObject = pObject;
closeRenderPass();
}
} // check if object is part of an animated group elseif (SdrObject* pAncestor = getAnimatedAncestor(pObject))
{ // a new animated group is started ? if (mpCurrentRenderPass->mpObject && mpCurrentRenderPass->mpObject != pAncestor)
closeRenderPass();
// Add the animated object
mpCurrentRenderPass->maObjectsAndParagraphs.emplace(pObject, std::deque<sal_Int32>());
mpCurrentRenderPass->meStage = eCurrentStage;
mpCurrentRenderPass->mbAnimation = true;
mpCurrentRenderPass->mpObject = pAncestor;
} // No special handling is needed, just add the object to the current rendering pass else
{ // an animated group is complete ? if (mpCurrentRenderPass->mpObject)
closeRenderPass();
// check if object is in animation auto aIterator = mrRenderPass.maObjectsAndParagraphs.find(pObject); if (aIterator == mrRenderPass.maObjectsAndParagraphs.end()) return;
// render the object
sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
rOriginal, rDisplayInfo, rVisitor);
autoconst& rParagraphs = aIterator->second;
// A render pass for the non-animated part of a text shapes whose paragraphs are all animated // has no paragraphs (rParagraphs.empty()) anyway it still needs to be modified in order to // render the text shape background only; on the contrary it will render all paragraphs. if (!rParagraphs.empty() || mrRenderPass.mbRenderObjectBackground)
{ autoconst& rViewInformation2D = rOriginal.GetObjectContact().getViewInformation2D(); auto rContainer
= static_cast<drawinglayer::primitive2d::Primitive2DContainer&>(rVisitor);
if ((aTargetAny >>= xShape) && xShape.is())
{
pObject = getObjectForShape(xShape);
} else// if target is not a shape - could be paragraph target containing a shape
{
presentation::ParagraphTarget aParagraphTarget; if ((aTargetAny >>= aParagraphTarget) && aParagraphTarget.Shape.is())
{
nParagraph = aParagraphTarget.Paragraph;
pObject = getObjectForShape(aParagraphTarget.Shape);
}
}
if (!pObject) return;
// afaics, when a shape is part of a group any applied effect is ignored, // so no layer should be created if (pObject->getParentSdrObjectFromSdrObject()) return;
// some kind of effect, like the ones based on color animations, // is ignored when applied to a group if (isGroup(pObject))
{ if (constNonValidEffectsForGroupSet.find(rEffect->getPresetId().toUtf8())
!= constNonValidEffectsForGroupSet.end()) return;
}
AnimationRenderInfo aAnimationInfo; auto aIterator = maRenderState.maAnimationRenderInfoList.find(pObject); if (aIterator != maRenderState.maAnimationRenderInfoList.end())
aAnimationInfo = aIterator->second;
AnimationLayerInfo aLayerInfo;
std::optional<bool> bVisible;
uno::Any aAny
= rEffect->getProperty(animations::AnimationNodeType::SET, u"Visibility", EValue::To); if (aAny.hasValue())
{ // if initial anim sets shape visible, set it // to invisible. If we're asked for the final // state, don't do anything obviously
bVisible = !anim::getVisibilityPropertyForAny(aAny);
}
aLayerInfo.moInitiallyVisible = bVisible;
// We have paragraphs if (nParagraph >= 0)
{ auto aParagraphIterator = std::find(aAnimationInfo.maParagraphs.begin(),
aAnimationInfo.maParagraphs.end(), nParagraph);
// Check if paragraph already exists if (aParagraphIterator == aAnimationInfo.maParagraphs.end())
{ // We have a paragraph, so write the hash for the paragraph
aAnimationInfo.maParagraphs.push_back(nParagraph);
aAnimationInfo.maParagraphInfos.emplace(nParagraph, aLayerInfo);
}
} else
{ if (!aAnimationInfo.moObjectInfo)
aAnimationInfo.moObjectInfo = aLayerInfo;
}
// Rendering of a text shape in edit mode is performed by decomposing the TextHierarchyEditPrimitive2D instance. // Usually such kind of primitive doesn't decompose on primitive processing, so we need to signal through a flag // that a slideshow rendering is going to be performed in order to enable the decomposition. // Using TextHierarchyEditPrimitive2D decomposition in place of TextEditDrawing for rendering a text object // in edit mode allows to animate a single paragraph even when the related text object is in edit mode.
comphelper::LibreOfficeKit::setSlideshowRendering(true); // Redraw slide but skip EndCompleteRedraw() which uses TextEditDrawing for rendering text when a text object is // in edit mode. TextEditDrawing was causing to have artifacts displayed while playing the slideshow such as // a tiny rectangle around the edited text shape.
SdrPaintWindow* pPaintWindow = aView.BeginCompleteRedraw(rRenderContext.maVirtualDevice);
assert(pPaintWindow && "SlideshowLayerRenderer::createViewAndDraw: No OutDev (!)");
aView.DoCompleteRedraw(*pPaintWindow, aRegion, pRedirector);
comphelper::LibreOfficeKit::setSlideshowRendering(false);
}
// a group of object has no such property if (!isGroup(pObject))
{ if (nParagraph < 0)
{
drawing::FillStyle aFillStyle
= pObject->GetProperties().GetItem(XATTR_FILLSTYLE).GetValue(); if (aFillStyle == drawing::FillStyle::FillStyle_SOLID)
{ auto aFillColor
= pObject->GetProperties().GetItem(XATTR_FILLCOLOR).GetColorValue();
aJsonWriter.put("fillColor", "#" + aFillColor.AsRGBHEXString());
}
drawing::LineStyle aLineStyle
= pObject->GetProperties().GetItem(XATTR_LINESTYLE).GetValue(); if (aLineStyle == drawing::LineStyle::LineStyle_SOLID)
{ auto aLineColor
= pObject->GetProperties().GetItem(XATTR_LINECOLOR).GetColorValue();
aJsonWriter.put("lineColor", "#" + aLineColor.AsRGBHEXString());
}
} else
{ if (rFontColor == COL_AUTO)
{
SAL_WARN("sd", "SlideshowLayerRenderer: on writing JSON info for an animated " "paragraph layer: font color is set to auto.");
}
aJsonWriter.put("fontColor", "#" + rFontColor.AsRGBHEXString());
}
}
}
}
// Render Background and analyze other passes if (maRenderState.meStage == RenderStage::Background)
{ // Render no objects, just the background, but analyze and create rendering passes
AnalyzeRenderingRedirector aRedirector(maRenderState, mbRenderMasterPage);
createViewAndDraw(aRenderContext, &aRedirector);
aRedirector.finalizeRenderPasses();
if (mbRenderBackground)
{
bIsBitmapLayer = true;
// Write JSON for the Background layer
writeBackgroundJSON(rJsonMsg);
}
// We need to return a valid layer, so if background has to be skipped // render the next layer if (!mbRenderBackground)
render(pBuffer, bIsBitmapLayer, rScale, rJsonMsg);
} else
{ if (maRenderState.maRenderPasses.empty())
{
cleanup(); returnfalse;
} auto& rRenderPass = maRenderState.maRenderPasses.front();
maRenderState.meStage = rRenderPass.meStage;
bIsBitmapLayer = !rRenderPass.mbPlaceholder; if (bIsBitmapLayer) // no need to render if placeholder
{
RenderPassObjectRedirector aRedirector(maRenderState, rRenderPass);
createViewAndDraw(aRenderContext, &aRedirector);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.