blob: ef6f4518e449f31bd555c4876acf0e9957c68871 [file] [log] [blame]
/*
* Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann
* <zimmermann@kde.org>
* Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
* Copyright (C) 2007 Eric Seidel <eric@webkit.org>
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "third_party/blink/renderer/core/svg/svg_path.h"
#include <memory>
#include <utility>
#include "third_party/blink/renderer/core/svg/animation/smil_animation_effect_parameters.h"
#include "third_party/blink/renderer/core/svg/svg_path_blender.h"
#include "third_party/blink/renderer/core/svg/svg_path_byte_stream.h"
#include "third_party/blink/renderer/core/svg/svg_path_byte_stream_builder.h"
#include "third_party/blink/renderer/core/svg/svg_path_byte_stream_source.h"
#include "third_party/blink/renderer/core/svg/svg_path_utilities.h"
#include "third_party/blink/renderer/platform/graphics/path.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
namespace blink {
using cssvalue::CSSPathValue;
namespace {
std::unique_ptr<SVGPathByteStream> BlendPathByteStreams(
const SVGPathByteStream& from_stream,
const SVGPathByteStream& to_stream,
float progress) {
std::unique_ptr<SVGPathByteStream> result_stream =
std::make_unique<SVGPathByteStream>();
SVGPathByteStreamBuilder builder(*result_stream);
SVGPathByteStreamSource from_source(from_stream);
SVGPathByteStreamSource to_source(to_stream);
SVGPathBlender blender(&from_source, &to_source, &builder);
blender.BlendAnimatedPath(progress);
return result_stream;
}
std::unique_ptr<SVGPathByteStream> AddPathByteStreams(
const SVGPathByteStream& from_stream,
const SVGPathByteStream& by_stream,
unsigned repeat_count = 1) {
std::unique_ptr<SVGPathByteStream> result_stream =
std::make_unique<SVGPathByteStream>();
SVGPathByteStreamBuilder builder(*result_stream);
SVGPathByteStreamSource from_source(from_stream);
SVGPathByteStreamSource by_source(by_stream);
SVGPathBlender blender(&from_source, &by_source, &builder);
blender.AddAnimatedPath(repeat_count);
return result_stream;
}
std::unique_ptr<SVGPathByteStream> ConditionallyAddPathByteStreams(
std::unique_ptr<SVGPathByteStream> from_stream,
const SVGPathByteStream& by_stream,
unsigned repeat_count = 1) {
if (from_stream->IsEmpty() || by_stream.IsEmpty())
return from_stream;
return AddPathByteStreams(*from_stream, by_stream, repeat_count);
}
} // namespace
SVGPath::SVGPath() : path_value_(CSSPathValue::EmptyPathValue()) {}
SVGPath::SVGPath(const CSSPathValue& path_value) : path_value_(path_value) {}
SVGPath::~SVGPath() = default;
String SVGPath::ValueAsString() const {
return BuildStringFromByteStream(ByteStream(), kNoTransformation);
}
SVGPath* SVGPath::Clone() const {
return MakeGarbageCollected<SVGPath>(*path_value_);
}
SVGParsingError SVGPath::SetValueAsString(const String& string) {
std::unique_ptr<SVGPathByteStream> byte_stream =
std::make_unique<SVGPathByteStream>();
SVGParsingError parse_status =
BuildByteStreamFromString(string, *byte_stream);
path_value_ = MakeGarbageCollected<CSSPathValue>(std::move(byte_stream));
return parse_status;
}
SVGPropertyBase* SVGPath::CloneForAnimation(const String& value) const {
std::unique_ptr<SVGPathByteStream> byte_stream =
std::make_unique<SVGPathByteStream>();
BuildByteStreamFromString(value, *byte_stream);
return MakeGarbageCollected<SVGPath>(
*MakeGarbageCollected<CSSPathValue>(std::move(byte_stream)));
}
void SVGPath::Add(const SVGPropertyBase* other, const SVGElement*) {
const auto& other_path_byte_stream = To<SVGPath>(other)->ByteStream();
if (ByteStream().size() != other_path_byte_stream.size() ||
ByteStream().IsEmpty() || other_path_byte_stream.IsEmpty())
return;
path_value_ = MakeGarbageCollected<CSSPathValue>(
AddPathByteStreams(ByteStream(), other_path_byte_stream));
}
void SVGPath::CalculateAnimatedValue(
const SMILAnimationEffectParameters& parameters,
float percentage,
unsigned repeat_count,
const SVGPropertyBase* from_value,
const SVGPropertyBase* to_value,
const SVGPropertyBase* to_at_end_of_duration_value,
const SVGElement*) {
const auto& to = To<SVGPath>(*to_value);
const SVGPathByteStream& to_stream = to.ByteStream();
// If no 'to' value is given, nothing to animate.
if (!to_stream.size())
return;
const auto& from = To<SVGPath>(*from_value);
const SVGPathByteStream& from_stream = from.ByteStream();
// If the 'from' value is given and it's length doesn't match the 'to' value
// list length, fallback to a discrete animation.
if (from_stream.size() != to_stream.size() && from_stream.size()) {
// If this is a 'to' animation, the "from" value will be the same
// object as this object, so this will be a no-op but shouldn't
// clobber the object.
path_value_ = percentage < 0.5 ? from.PathValue() : to.PathValue();
return;
}
// If this is a 'to' animation, the "from" value will be the same
// object as this object, so make sure to update the state of this
// object as the last thing to avoid clobbering the result. As long
// as all intermediate results are computed into |new_stream| that
// should be unproblematic.
std::unique_ptr<SVGPathByteStream> new_stream =
BlendPathByteStreams(from_stream, to_stream, percentage);
// Handle accumulate='sum'.
if (repeat_count && parameters.is_cumulative) {
new_stream = ConditionallyAddPathByteStreams(
std::move(new_stream),
To<SVGPath>(to_at_end_of_duration_value)->ByteStream(), repeat_count);
}
// Handle additive='sum'.
if (parameters.is_additive) {
new_stream =
ConditionallyAddPathByteStreams(std::move(new_stream), ByteStream());
}
path_value_ = MakeGarbageCollected<CSSPathValue>(std::move(new_stream));
}
float SVGPath::CalculateDistance(const SVGPropertyBase* to,
const SVGElement*) const {
// FIXME: Support paced animations.
return -1;
}
void SVGPath::Trace(Visitor* visitor) const {
visitor->Trace(path_value_);
SVGPropertyBase::Trace(visitor);
}
} // namespace blink