mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-27 00:25:38 +00:00
0d52738239
e0f786c9b Fix parseStyle REVERT: d13d8e521 Refactoring REVERT: 4925c87a8 Update REVERT: 794c38591 Update REVERT: 49eee9643 Update REVERT: 914aee5ea Update REVERT: 3bb00ecee Fix ParserString string-view iterator cast in windows REVERT: fabea2008 Fix string-view iterator cast in windows REVERT: bbcf0d34f Update REVERT: 081df20f2 Update REVERT: fe3101f91 Refactoring REVERT: e9a41dc83 Refactoring git-subtree-dir: external/lunasvg git-subtree-split: e0f786c9be6fae1ffabddfe56fb1e0a1a7eb775d
434 lines
14 KiB
C++
434 lines
14 KiB
C++
#include "paintelement.h"
|
|
#include "stopelement.h"
|
|
#include "parser.h"
|
|
#include "layoutcontext.h"
|
|
|
|
#include <set>
|
|
|
|
namespace lunasvg {
|
|
|
|
PaintElement::PaintElement(ElementId id)
|
|
: StyledElement(id)
|
|
{
|
|
}
|
|
|
|
GradientElement::GradientElement(ElementId id)
|
|
: PaintElement(id)
|
|
{
|
|
}
|
|
|
|
Transform GradientElement::gradientTransform() const
|
|
{
|
|
auto& value = get(PropertyId::GradientTransform);
|
|
return Parser::parseTransform(value);
|
|
}
|
|
|
|
SpreadMethod GradientElement::spreadMethod() const
|
|
{
|
|
auto& value = get(PropertyId::SpreadMethod);
|
|
return Parser::parseSpreadMethod(value);
|
|
}
|
|
|
|
Units GradientElement::gradientUnits() const
|
|
{
|
|
auto& value = get(PropertyId::GradientUnits);
|
|
return Parser::parseUnits(value, Units::ObjectBoundingBox);
|
|
}
|
|
|
|
std::string GradientElement::href() const
|
|
{
|
|
auto& value = get(PropertyId::Href);
|
|
return Parser::parseHref(value);
|
|
}
|
|
|
|
GradientStops GradientElement::buildGradientStops() const
|
|
{
|
|
GradientStops gradientStops;
|
|
double prevOffset = 0.0;
|
|
for(auto& child : children)
|
|
{
|
|
if(child->isText())
|
|
continue;
|
|
auto element = static_cast<Element*>(child.get());
|
|
if(element->id != ElementId::Stop)
|
|
continue;
|
|
auto stop = static_cast<StopElement*>(element);
|
|
auto offset = std::max(prevOffset, stop->offset());
|
|
prevOffset = offset;
|
|
gradientStops.emplace_back(offset, stop->stopColorWithOpacity());
|
|
}
|
|
|
|
return gradientStops;
|
|
}
|
|
|
|
LinearGradientElement::LinearGradientElement()
|
|
: GradientElement(ElementId::LinearGradient)
|
|
{
|
|
}
|
|
|
|
Length LinearGradientElement::x1() const
|
|
{
|
|
auto& value = get(PropertyId::X1);
|
|
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
|
}
|
|
|
|
Length LinearGradientElement::y1() const
|
|
{
|
|
auto& value = get(PropertyId::Y1);
|
|
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
|
}
|
|
|
|
Length LinearGradientElement::x2() const
|
|
{
|
|
auto& value = get(PropertyId::X2);
|
|
return Parser::parseLength(value, AllowNegativeLengths, Length::HundredPercent);
|
|
}
|
|
|
|
Length LinearGradientElement::y2() const
|
|
{
|
|
auto& value = get(PropertyId::Y2);
|
|
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
|
}
|
|
|
|
std::unique_ptr<LayoutObject> LinearGradientElement::getPainter(LayoutContext* context) const
|
|
{
|
|
LinearGradientAttributes attributes;
|
|
std::set<const GradientElement*> processedGradients;
|
|
const GradientElement* current = this;
|
|
|
|
while(true)
|
|
{
|
|
if(!attributes.hasGradientTransform() && current->has(PropertyId::GradientTransform))
|
|
attributes.setGradientTransform(current->gradientTransform());
|
|
if(!attributes.hasSpreadMethod() && current->has(PropertyId::SpreadMethod))
|
|
attributes.setSpreadMethod(current->spreadMethod());
|
|
if(!attributes.hasGradientUnits() && current->has(PropertyId::GradientUnits))
|
|
attributes.setGradientUnits(current->gradientUnits());
|
|
if(!attributes.hasGradientStops())
|
|
attributes.setGradientStops(current->buildGradientStops());
|
|
|
|
if(current->id == ElementId::LinearGradient)
|
|
{
|
|
auto element = static_cast<const LinearGradientElement*>(current);
|
|
if(!attributes.hasX1() && element->has(PropertyId::X1))
|
|
attributes.setX1(element->x1());
|
|
if(!attributes.hasY1() && element->has(PropertyId::Y1))
|
|
attributes.setY1(element->y1());
|
|
if(!attributes.hasX2() && element->has(PropertyId::X2))
|
|
attributes.setX2(element->x2());
|
|
if(!attributes.hasY2() && element->has(PropertyId::Y2))
|
|
attributes.setY2(element->y2());
|
|
}
|
|
|
|
auto ref = context->getElementById(current->href());
|
|
if(!ref || !(ref->id == ElementId::LinearGradient || ref->id == ElementId::RadialGradient))
|
|
break;
|
|
|
|
processedGradients.insert(current);
|
|
current = static_cast<const GradientElement*>(ref);
|
|
if(processedGradients.find(current) != processedGradients.end())
|
|
break;
|
|
}
|
|
|
|
auto& stops = attributes.gradientStops();
|
|
if(stops.empty())
|
|
return nullptr;
|
|
|
|
LengthContext lengthContext(this, attributes.gradientUnits());
|
|
auto x1 = lengthContext.valueForLength(attributes.x1(), LengthMode::Width);
|
|
auto y1 = lengthContext.valueForLength(attributes.y1(), LengthMode::Height);
|
|
auto x2 = lengthContext.valueForLength(attributes.x2(), LengthMode::Width);
|
|
auto y2 = lengthContext.valueForLength(attributes.y2(), LengthMode::Height);
|
|
if((x1 == x2 && y1 == y2) || stops.size() == 1)
|
|
{
|
|
auto solid = std::make_unique<LayoutSolidColor>();
|
|
solid->color = std::get<1>(stops.back());
|
|
return std::move(solid);
|
|
}
|
|
|
|
auto gradient = std::make_unique<LayoutLinearGradient>();
|
|
gradient->transform = attributes.gradientTransform();
|
|
gradient->spreadMethod = attributes.spreadMethod();
|
|
gradient->units = attributes.gradientUnits();
|
|
gradient->stops = attributes.gradientStops();
|
|
gradient->x1 = x1;
|
|
gradient->y1 = y1;
|
|
gradient->x2 = x2;
|
|
gradient->y2 = y2;
|
|
return std::move(gradient);
|
|
}
|
|
|
|
std::unique_ptr<Node> LinearGradientElement::clone() const
|
|
{
|
|
return cloneElement<LinearGradientElement>();
|
|
}
|
|
|
|
RadialGradientElement::RadialGradientElement()
|
|
: GradientElement(ElementId::RadialGradient)
|
|
{
|
|
}
|
|
|
|
Length RadialGradientElement::cx() const
|
|
{
|
|
auto& value = get(PropertyId::Cx);
|
|
return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent);
|
|
}
|
|
|
|
Length RadialGradientElement::cy() const
|
|
{
|
|
auto& value = get(PropertyId::Cy);
|
|
return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent);
|
|
}
|
|
|
|
Length RadialGradientElement::r() const
|
|
{
|
|
auto& value = get(PropertyId::R);
|
|
return Parser::parseLength(value, ForbidNegativeLengths, Length::FiftyPercent);
|
|
}
|
|
|
|
Length RadialGradientElement::fx() const
|
|
{
|
|
auto& value = get(PropertyId::Fx);
|
|
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
|
}
|
|
|
|
Length RadialGradientElement::fy() const
|
|
{
|
|
auto& value = get(PropertyId::Fy);
|
|
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
|
}
|
|
|
|
std::unique_ptr<LayoutObject> RadialGradientElement::getPainter(LayoutContext* context) const
|
|
{
|
|
RadialGradientAttributes attributes;
|
|
std::set<const GradientElement*> processedGradients;
|
|
const GradientElement* current = this;
|
|
|
|
while(true)
|
|
{
|
|
if(!attributes.hasGradientTransform() && current->has(PropertyId::GradientTransform))
|
|
attributes.setGradientTransform(current->gradientTransform());
|
|
if(!attributes.hasSpreadMethod() && current->has(PropertyId::SpreadMethod))
|
|
attributes.setSpreadMethod(current->spreadMethod());
|
|
if(!attributes.hasGradientUnits() && current->has(PropertyId::GradientUnits))
|
|
attributes.setGradientUnits(current->gradientUnits());
|
|
if(!attributes.hasGradientStops())
|
|
attributes.setGradientStops(current->buildGradientStops());
|
|
|
|
if(current->id == ElementId::RadialGradient)
|
|
{
|
|
auto element = static_cast<const RadialGradientElement*>(current);
|
|
if(!attributes.hasCx() && element->has(PropertyId::Cx))
|
|
attributes.setCx(element->cx());
|
|
if(!attributes.hasCy() && element->has(PropertyId::Cy))
|
|
attributes.setCy(element->cy());
|
|
if(!attributes.hasR() && element->has(PropertyId::R))
|
|
attributes.setR(element->r());
|
|
if(!attributes.hasFx() && element->has(PropertyId::Fx))
|
|
attributes.setFx(element->fx());
|
|
if(!attributes.hasFy() && element->has(PropertyId::Fy))
|
|
attributes.setFy(element->fy());
|
|
}
|
|
|
|
auto ref = context->getElementById(current->href());
|
|
if(!ref || !(ref->id == ElementId::LinearGradient || ref->id == ElementId::RadialGradient))
|
|
break;
|
|
|
|
processedGradients.insert(current);
|
|
current = static_cast<const GradientElement*>(ref);
|
|
if(processedGradients.find(current) != processedGradients.end())
|
|
break;
|
|
}
|
|
|
|
if(!attributes.hasFx())
|
|
attributes.setFx(attributes.cx());
|
|
if(!attributes.hasFy())
|
|
attributes.setFy(attributes.cy());
|
|
|
|
auto& stops = attributes.gradientStops();
|
|
if(stops.empty())
|
|
return nullptr;
|
|
|
|
auto& r = attributes.r();
|
|
if(r.isZero() || stops.size() == 1)
|
|
{
|
|
auto solid = std::make_unique<LayoutSolidColor>();
|
|
solid->color = std::get<1>(stops.back());
|
|
return std::move(solid);
|
|
}
|
|
|
|
auto gradient = std::make_unique<LayoutRadialGradient>();
|
|
gradient->transform = attributes.gradientTransform();
|
|
gradient->spreadMethod = attributes.spreadMethod();
|
|
gradient->units = attributes.gradientUnits();
|
|
gradient->stops = attributes.gradientStops();
|
|
|
|
LengthContext lengthContext(this, attributes.gradientUnits());
|
|
gradient->cx = lengthContext.valueForLength(attributes.cx(), LengthMode::Width);
|
|
gradient->cy = lengthContext.valueForLength(attributes.cy(), LengthMode::Height);
|
|
gradient->r = lengthContext.valueForLength(attributes.r(), LengthMode::Both);
|
|
gradient->fx = lengthContext.valueForLength(attributes.fx(), LengthMode::Width);
|
|
gradient->fy = lengthContext.valueForLength(attributes.fy(), LengthMode::Height);
|
|
return std::move(gradient);
|
|
}
|
|
|
|
std::unique_ptr<Node> RadialGradientElement::clone() const
|
|
{
|
|
return cloneElement<RadialGradientElement>();
|
|
}
|
|
|
|
PatternElement::PatternElement()
|
|
: PaintElement(ElementId::Pattern)
|
|
{
|
|
}
|
|
|
|
Length PatternElement::x() const
|
|
{
|
|
auto& value = get(PropertyId::X);
|
|
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
|
}
|
|
|
|
Length PatternElement::y() const
|
|
{
|
|
auto& value = get(PropertyId::Y);
|
|
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
|
}
|
|
|
|
Length PatternElement::width() const
|
|
{
|
|
auto& value = get(PropertyId::Width);
|
|
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
|
|
}
|
|
|
|
Length PatternElement::height() const
|
|
{
|
|
auto& value = get(PropertyId::Height);
|
|
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
|
|
}
|
|
|
|
Transform PatternElement::patternTransform() const
|
|
{
|
|
auto& value = get(PropertyId::PatternTransform);
|
|
return Parser::parseTransform(value);
|
|
}
|
|
|
|
Units PatternElement::patternUnits() const
|
|
{
|
|
auto& value = get(PropertyId::PatternUnits);
|
|
return Parser::parseUnits(value, Units::ObjectBoundingBox);
|
|
}
|
|
|
|
Units PatternElement::patternContentUnits() const
|
|
{
|
|
auto& value = get(PropertyId::PatternContentUnits);
|
|
return Parser::parseUnits(value, Units::UserSpaceOnUse);
|
|
}
|
|
|
|
Rect PatternElement::viewBox() const
|
|
{
|
|
auto& value = get(PropertyId::ViewBox);
|
|
return Parser::parseViewBox(value);
|
|
}
|
|
|
|
PreserveAspectRatio PatternElement::preserveAspectRatio() const
|
|
{
|
|
auto& value = get(PropertyId::PreserveAspectRatio);
|
|
return Parser::parsePreserveAspectRatio(value);
|
|
}
|
|
|
|
std::string PatternElement::href() const
|
|
{
|
|
auto& value = get(PropertyId::Href);
|
|
return Parser::parseHref(value);
|
|
}
|
|
|
|
std::unique_ptr<LayoutObject> PatternElement::getPainter(LayoutContext* context) const
|
|
{
|
|
if(context->hasReference(this))
|
|
return nullptr;
|
|
|
|
PatternAttributes attributes;
|
|
std::set<const PatternElement*> processedPatterns;
|
|
const PatternElement* current = this;
|
|
|
|
while(true)
|
|
{
|
|
if(!attributes.hasX() && current->has(PropertyId::X))
|
|
attributes.setX(current->x());
|
|
if(!attributes.hasY() && current->has(PropertyId::Y))
|
|
attributes.setY(current->y());
|
|
if(!attributes.hasWidth() && current->has(PropertyId::Width))
|
|
attributes.setWidth(current->width());
|
|
if(!attributes.hasHeight() && current->has(PropertyId::Height))
|
|
attributes.setHeight(current->height());
|
|
if(!attributes.hasPatternTransform() && current->has(PropertyId::PatternTransform))
|
|
attributes.setPatternTransform(current->patternTransform());
|
|
if(!attributes.hasPatternUnits() && current->has(PropertyId::PatternUnits))
|
|
attributes.setPatternUnits(current->patternUnits());
|
|
if(!attributes.hasPatternContentUnits() && current->has(PropertyId::PatternContentUnits))
|
|
attributes.setPatternContentUnits(current->patternContentUnits());
|
|
if(!attributes.hasViewBox() && current->has(PropertyId::ViewBox))
|
|
attributes.setViewBox(current->viewBox());
|
|
if(!attributes.hasPreserveAspectRatio() && current->has(PropertyId::PreserveAspectRatio))
|
|
attributes.setPreserveAspectRatio(current->preserveAspectRatio());
|
|
if(!attributes.hasPatternContentElement() && current->children.size())
|
|
attributes.setPatternContentElement(current);
|
|
|
|
auto ref = context->getElementById(current->href());
|
|
if(!ref || ref->id != ElementId::Pattern)
|
|
break;
|
|
|
|
processedPatterns.insert(current);
|
|
current = static_cast<const PatternElement*>(ref);
|
|
if(processedPatterns.find(current) != processedPatterns.end())
|
|
break;
|
|
}
|
|
|
|
auto& width = attributes.width();
|
|
auto& height = attributes.height();
|
|
auto element = attributes.patternContentElement();
|
|
if(element == nullptr || width.isZero() || height.isZero())
|
|
return nullptr;
|
|
|
|
LayoutBreaker layoutBreaker(context, this);
|
|
auto pattern = std::make_unique<LayoutPattern>();
|
|
pattern->transform = attributes.patternTransform();
|
|
pattern->units = attributes.patternUnits();
|
|
pattern->contentUnits = attributes.patternContentUnits();
|
|
pattern->viewBox = attributes.viewBox();
|
|
pattern->preserveAspectRatio = attributes.preserveAspectRatio();
|
|
|
|
LengthContext lengthContext(this, attributes.patternUnits());
|
|
pattern->x = lengthContext.valueForLength(attributes.x(), LengthMode::Width);
|
|
pattern->y = lengthContext.valueForLength(attributes.y(), LengthMode::Height);
|
|
pattern->width = lengthContext.valueForLength(attributes.width(), LengthMode::Width);
|
|
pattern->height = lengthContext.valueForLength(attributes.height(), LengthMode::Height);
|
|
element->layoutChildren(context, pattern.get());
|
|
return std::move(pattern);
|
|
}
|
|
|
|
std::unique_ptr<Node> PatternElement::clone() const
|
|
{
|
|
return cloneElement<PatternElement>();
|
|
}
|
|
|
|
SolidColorElement::SolidColorElement()
|
|
: PaintElement(ElementId::SolidColor)
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<LayoutObject> SolidColorElement::getPainter(LayoutContext*) const
|
|
{
|
|
auto solid = std::make_unique<LayoutSolidColor>();
|
|
solid->color = solid_color();
|
|
solid->color.a = solid_opacity();
|
|
return std::move(solid);
|
|
}
|
|
|
|
std::unique_ptr<Node> SolidColorElement::clone() const
|
|
{
|
|
return cloneElement<SolidColorElement>();
|
|
}
|
|
|
|
} // namespace lunasvg
|