Revision control

Copy as Markdown

// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include <QtLottie/private/qlottierasterrenderer_p.h>
#include <QPainter>
#include <QRectF>
#include <QBrush>
#include <QTransform>
#include <QGradient>
#include <QPointer>
#include <QtLottie/private/qlottieshape_p.h>
#include <QtLottie/private/qlottiefill_p.h>
#include <QtLottie/private/qlottiegfill_p.h>
#include <QtLottie/private/qlottieimage_p.h>
#include <QtLottie/private/qlottiebasictransform_p.h>
#include <QtLottie/private/qlottieshapetransform_p.h>
#include <QtLottie/private/qlottierect_p.h>
#include <QtLottie/private/qlottieellipse_p.h>
#include <QtLottie/private/qlottiepolystar_p.h>
#include <QtLottie/private/qlottieround_p.h>
#include <QtLottie/private/qlottiefreeformshape_p.h>
#include <QtLottie/private/qlottietrimpath_p.h>
#include <QtLottie/private/qlottiefilleffect_p.h>
#include <QtLottie/private/qlottierepeater_p.h>
#include <QtLottie/private/qlottieflatlayers_p.h>
#include <QtLottie/private/qlottieprecomposition_p.h>
#include <QtLottie/private/qlottietextlayer_p.h>
QT_BEGIN_NAMESPACE
QLottieRasterRenderer::QLottieRasterRenderer(QPainter *painter)
: m_painter(painter)
{
m_painter->setPen(QPen(Qt::NoPen));
m_unitedPath.setCachingEnabled(true);
}
void QLottieRasterRenderer::saveState()
{
qCDebug(lcLottieQtLottieRender) << "Save painter state";
m_painter->save();
saveTrimmingState();
m_pathStack.push_back(m_unitedPath);
m_fillEffectStack.push_back(m_fillEffect);
m_inverseFillTransformStack.push(m_inverseFillTransform);
m_unitedPath.clear();
}
void QLottieRasterRenderer::restoreState()
{
qCDebug(lcLottieQtLottieRender) << "Restore painter state";
m_painter->restore();
restoreTrimmingState();
m_unitedPath = m_pathStack.pop();
m_renderPath.setFillRule(m_unitedPath.fillRule());
m_clipPath.setFillRule(m_unitedPath.fillRule());
m_fillEffect = m_fillEffectStack.pop();
m_inverseFillTransform = m_inverseFillTransformStack.pop();
}
void QLottieRasterRenderer::render(const QLottiePrecomposition &precomp)
{
Q_UNUSED(precomp);
// Nothing to do: no graphical properties at this level
}
void QLottieRasterRenderer::render(const QLottieLayer &layer)
{
qCDebug(lcLottieQtLottieRender) << "Layer:" << layer.name()
<< "is matte:" << layer.isMatteLayer()
<< "using matte:" << layer.isUsingMatteLayer();
if (layer.isMatteLayer())
m_buildingClipRegion = true;
else if (!m_clipPath.isEmpty()) {
QTransform inv = m_painter->transform().inverted();
if (layer.matteMode() == QLottieLayer::Alpha)
m_painter->setClipPath(inv.map(m_clipPath));
else if (layer.matteMode() == QLottieLayer::InvertedAlpha) {
QPainterPath screen;
screen.addRect(0, 0, m_painter->device()->width(),
m_painter->device()->height());
m_painter->setClipPath(inv.map(screen - m_clipPath));
}
else {
// Clipping is not applied to paths that have
// not setting clipping parameters
m_painter->setClipping(false);
}
m_buildingClipRegion = false;
m_clipPath = QPainterPath();
}
}
void QLottieRasterRenderer::render(const QLottieSolidLayer &layer)
{
render(static_cast<const QLottieLayer &>(layer));
m_painter->fillRect(QRect(QPoint(), layer.layerSize()), layer.color());
}
void QLottieRasterRenderer::render(const QLottieTextLayer &layer)
{
render(static_cast<const QLottieLayer &>(layer));
for (const QPainterPath &path : layer.buildGlyphPaths())
m_painter->fillPath(path, QBrush(layer.textDocument().fillColor));
}
void QLottieRasterRenderer::render(const QLottieRect &rect)
{
qCDebug(lcLottieQtLottieRender) << "Rect:" << rect.name()
<< rect.position() << rect.size();
m_renderPath.addPath(rect.path());
}
void QLottieRasterRenderer::render(const QLottieEllipse &ellipse)
{
qCDebug(lcLottieQtLottieRender) << "Ellipse:" << ellipse.name()
<< ellipse.position()
<< ellipse.size();
m_renderPath.addPath(ellipse.path());
}
void QLottieRasterRenderer::render(const QLottiePolyStar &star)
{
qCDebug(lcLottieQtLottieRender) << "PolyStar:" << star.name()
<< star.position() << star.pointCount()
<< star.outerRadius() << star.innerRadius();
m_renderPath.addPath(star.path());
}
void QLottieRasterRenderer::render(const QLottieImage &image)
{
const QImage img = image.image();
if (img.isNull())
return;
m_painter->save();
for (int i = 0; i < m_repeatCount; i++) {
qCDebug(lcLottieQtLottieRender) << "Image" << image.name();
applyRepeaterTransform(i);
m_painter->drawImage(QRectF(QPointF(), image.size()), img);
}
m_painter->restore();
}
void QLottieRasterRenderer::render(const QLottieRound &round)
{
Q_UNUSED(round);
// Rounded Corners modifier, not implemented
}
void QLottieRasterRenderer::render(const QLottieFill &fill)
{
qCDebug(lcLottieQtLottieRender) << "Fill:" <<fill.name()
<< fill.color();
if (m_fillEffect)
return;
float alpha = fill.color().alphaF() * fill.opacity() / 100.0f;
QColor color = fill.color();
color.setAlphaF(alpha);
m_painter->setBrush(color);
m_renderPath.setFillRule(fill.fillRule());
m_unitedPath.setFillRule(fill.fillRule());
m_clipPath.setFillRule(fill.fillRule());
}
void QLottieRasterRenderer::render(const QLottieGFill &gradient)
{
qCDebug(lcLottieQtLottieRender) << "Gradient:" << gradient.name()
<< gradient.value();
if (m_fillEffect)
return;
m_inverseFillTransform = QTransform();
if (gradient.value())
m_painter->setBrush(*gradient.value());
else
qCWarning(lcLottieQtLottieRender) << "Gradient:"
<< gradient.name()
<< "Cannot draw gradient fill";
m_renderPath.setFillRule(gradient.fillRule());
m_unitedPath.setFillRule(gradient.fillRule());
m_clipPath.setFillRule(gradient.fillRule());
}
void QLottieRasterRenderer::render(const QLottieStroke &stroke)
{
qCDebug(lcLottieQtLottieRender) << "Stroke:" << stroke.name()
<< stroke.pen() << stroke.pen().miterLimit();
if (m_fillEffect)
return;
m_painter->setPen(stroke.pen());
}
void QLottieRasterRenderer::render(const QLottieBasicTransform &transform)
{
QTransform t = m_painter->transform();
applyTransform(&t, transform);
m_painter->setTransform(t);
if (transform.hasLinkedLayerTransform())
m_painter->setOpacity(1.0);
m_painter->setOpacity(m_painter->opacity() * transform.opacity());
qCDebug(lcLottieQtLottieRender) << transform.name()
<< m_painter->transform()
<< "opacity:" << m_painter->opacity();
}
void QLottieRasterRenderer::render(const QLottieShapeTransform &transform)
{
qCDebug(lcLottieQtLottieRender) << "Shape transform:" << transform.name()
<< "of" << transform.parent()->name();
QTransform t = m_painter->transform();
applyTransform(&t, transform, true);
m_painter->setTransform(t);
m_painter->setOpacity(m_painter->opacity() * transform.opacity());
if (m_painter->brush().gradient())
applyTransform(&m_inverseFillTransform, transform, true);
qCDebug(lcLottieQtLottieRender) << transform.name()
<< m_painter->transform()
<< m_painter->opacity();
}
void QLottieRasterRenderer::render(const QLottieFreeFormShape &shape)
{
qCDebug(lcLottieQtLottieRender) << "Render shape:"
<< shape.name() << "of"
<< shape.parent()->name();
m_renderPath.addPath(shape.path());
}
void QLottieRasterRenderer::render(const QLottieTrimPath &trimPath)
{
// TODO: Move "Sequential" trimming to the prerendering thread
// Now it is done in the GUI thread
m_painter->save();
for (int i = 0; i < m_repeatCount; i ++) {
qCDebug(lcLottieQtLottieRender) << "Render shape:"
<< trimPath.name() << "of"
<< trimPath.parent()->name();
applyRepeaterTransform(i);
if (!trimPath.isParallel() && !qFuzzyCompare(qreal(0.0), m_unitedPath.length())) {
qCDebug(lcLottieQtLottieRender) << "Render trim path in the GUI thread";
QPainterPath tr = trimPath.trim(m_unitedPath);
// Do not use the applied transform, as the transform
// is already included in m_unitedPath
m_painter->setTransform(QTransform());
m_painter->drawPath(tr);
}
}
m_painter->restore();
}
void QLottieRasterRenderer::render(const QLottieFillEffect &effect)
{
qCDebug(lcLottieQtLottieRender) << "Fill:" <<effect.name()
<< effect.color();
m_fillEffect = &effect;
m_painter->setBrush(m_fillEffect->color());
m_painter->setOpacity(m_painter->opacity() * m_fillEffect->opacity());
}
void QLottieRasterRenderer::render(const QLottieRepeater &repeater)
{
qCDebug(lcLottieQtLottieRender) << "Repeater:" <<repeater.name()
<< "count:" << repeater.copies();
if (m_repeaterTransform) {
qCWarning(lcLottieQtLottieRender) << "Only one Repeater can be active at a time!";
return;
}
m_repeatCount = repeater.copies();
m_repeatOffset = repeater.offset();
// Can store pointer to transform, although the transform
// is managed by another thread. The object will be available
// until the frame has been rendered
m_repeaterTransform = &repeater.transform();
m_painter->translate(m_repeatOffset * m_repeaterTransform->position().x(),
m_repeatOffset * m_repeaterTransform->position().y());
}
void QLottieRasterRenderer::renderPathElements(const QList<QLottieBase *> &pathElements)
{
qCDebug(lcLottieQtLottieRender) << "PathElements: count:" << pathElements.size();
m_painter->save();
m_renderPath.clear();
for (const QLottieBase *element : pathElements)
element->render(*this); // Accumulates element paths in m_renderPath
for (int i = 0; i < m_repeatCount; i++) {
if (i > 0)
qCDebug(lcLottieQtLottieRender) << "Render repeated, combined path, iteration" << i + 1;
applyRepeaterTransform(i);
if (trimmingState() == QLottieRenderer::Sequential) {
QTransform t = m_painter->transform();
QPainterPath tp = t.map(m_renderPath);
tp.addPath(m_unitedPath);
m_unitedPath = tp;
} else if (m_buildingClipRegion) {
QTransform t = m_painter->transform();
QPainterPath tp = t.map(m_renderPath);
tp.addPath(m_clipPath);
m_clipPath = tp;
} else {
if (!m_inverseFillTransform.isIdentity() && m_painter->brush().gradient()) {
bool fill_ok = false;
const QTransform fillTransform = m_inverseFillTransform.inverted(&fill_ok);
if (fill_ok) {
QBrush adjustedBrush = m_painter->brush();
adjustedBrush.setTransform(fillTransform);
m_painter->save();
m_painter->setBrush(adjustedBrush);
m_painter->drawPath(m_renderPath);
m_painter->restore();
} else {
m_painter->drawPath(m_renderPath);
}
} else {
m_painter->drawPath(m_renderPath);
}
}
}
m_painter->restore();
}
void QLottieRasterRenderer::applyRepeaterTransform(int instance)
{
if (!m_repeaterTransform || instance == 0)
return;
QTransform t = m_painter->transform();
QPointF anchors = -m_repeaterTransform->anchorPoint();
QPointF position = m_repeaterTransform->position();
QPointF anchoredCenter = anchors + position;
t.translate(anchoredCenter.x(), anchoredCenter.y());
t.rotate(m_repeaterTransform->rotation());
t.scale(m_repeaterTransform->scale().x(),
m_repeaterTransform->scale().y());
m_painter->setTransform(t);
qreal o =m_repeaterTransform->opacityAtInstance(instance);
m_painter->setOpacity(m_painter->opacity() * o);
}
QT_END_NAMESPACE