mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
lottie: Support the Animation Segment(Marker)
A single animation might have a desinated markers with naming: 0 ~ 0.5 (sector A), 0.5 ~ 1.0 (sector B). Selecting one of them using a marker name(sector A) and could play only that part with animation controllers. usage: - `animation->segment("sectionA") // Named segment(Marker)` - `auto cnt = animation->markerCnt()` - `auto name = animation->markers(index)` - `animation->segment(0, 0.5) // Segment` - `animation->segment(&begin, &end)` Co-authored-by: Jinny You <jinny@lottiefiles.com>
This commit is contained in:
parent
b4a0933a10
commit
3af9faf116
11 changed files with 249 additions and 4 deletions
38
inc/thorvg.h
38
inc/thorvg.h
|
@ -1858,6 +1858,44 @@ public:
|
|||
*/
|
||||
float duration() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Specifies the playback segment of the animation.
|
||||
*
|
||||
* The set segment is designated as the play area of the animation.
|
||||
* This is useful for playing a specific segment within the entire animation.
|
||||
* After setting, the number of animation frames and the playback time are calculated
|
||||
* by mapping the playback segment as the entire range.
|
||||
*
|
||||
* @param[in] begin segment start.
|
||||
* @param[in] end segment end.
|
||||
*
|
||||
* @retval Result::Success When succeed.
|
||||
* @retval Result::InsufficientCondition In case the animation is not loaded.
|
||||
* @retval Result::InvalidArguments When the given parameter is invalid.
|
||||
* @retval Result::NonSupport When it's not animatable.
|
||||
*
|
||||
* @note Range from 0.0~1.0
|
||||
* @note If a marker has been specified, its range will be disregarded.
|
||||
* @see LottieAnimation::segment(const char* marker)
|
||||
* @note Experimental API
|
||||
*/
|
||||
Result segment(float begin, float end) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Gets the current segment.
|
||||
*
|
||||
* @param[out] begin segment start.
|
||||
* @param[out] end segment end.
|
||||
*
|
||||
* @retval Result::Success When succeed.
|
||||
* @retval Result::InsufficientCondition In case the animation is not loaded.
|
||||
* @retval Result::InvalidArguments When the given parameter is invalid.
|
||||
* @retval Result::NonSupport When it's not animatable.
|
||||
*
|
||||
* @note Experimental API
|
||||
*/
|
||||
Result segment(float* begin, float* end = nullptr) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Creates a new Animation object.
|
||||
*
|
||||
|
|
|
@ -35,6 +35,50 @@ public:
|
|||
*/
|
||||
Result override(const char* slot) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Specifies a segment by marker.
|
||||
*
|
||||
* Markers are used to control animation playback by specifying start and end points,
|
||||
* eliminating the need to know the exact frame numbers.
|
||||
* Generally, markers are designated at the design level,
|
||||
* meaning the callers must know the marker name in advance to use it.
|
||||
*
|
||||
* @param[in] marker The name of the segment marker.
|
||||
*
|
||||
* @retval Result::Success When successful.
|
||||
* @retval Result::InsufficientCondition If the animation is not loaded.
|
||||
* @retval Result::InvalidArguments When the given parameter is invalid.
|
||||
* @retval Result::NonSupport When it's not animatable.
|
||||
*
|
||||
* @note If a @c marker is specified, the previously set segment will be disregarded.
|
||||
* @note Set @c nullptr to reset the specified segment.
|
||||
* @see Animation::segment(float begin, float end)
|
||||
* @note Experimental API
|
||||
*/
|
||||
Result segment(const char* marker) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Gets the marker count of the animation.
|
||||
*
|
||||
* @retval The count of the markers, zero if there is no marker.
|
||||
*
|
||||
* @see LottieAnimation::marker()
|
||||
* @note Experimental API
|
||||
*/
|
||||
uint32_t markersCnt() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Gets the marker name by a given index.
|
||||
*
|
||||
* @param[in] idx The index of the animation marker, starts from 0.
|
||||
*
|
||||
* @retval The name of marker when succeed, @c nullptr otherwise.
|
||||
*
|
||||
* @see LottieAnimation::markersCnt()
|
||||
* @note Experimental API
|
||||
*/
|
||||
const char* marker(uint32_t idx) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Creates a new LottieAnimation object.
|
||||
*
|
||||
|
|
|
@ -49,6 +49,39 @@ Result LottieAnimation::override(const char* slot) noexcept
|
|||
return Result::InvalidArguments;
|
||||
}
|
||||
|
||||
Result LottieAnimation::segment(const char* marker) noexcept
|
||||
{
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
if (!loader) return Result::InsufficientCondition;
|
||||
if (!loader->animatable()) return Result::NonSupport;
|
||||
|
||||
auto lottieLoader = static_cast<LottieLoader*>(loader);
|
||||
|
||||
if (!marker) {
|
||||
lottieLoader->segment(0.0, 1.0);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
float begin, end;
|
||||
if (!lottieLoader->getSegment(begin, end, marker)) {
|
||||
return Result::InvalidArguments;
|
||||
}
|
||||
return static_cast<Animation*>(this)->segment(begin, end);
|
||||
}
|
||||
|
||||
uint32_t LottieAnimation::markersCnt() noexcept
|
||||
{
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
if (!loader || !loader->animatable()) return 0;
|
||||
return static_cast<LottieLoader*>(loader)->markerCount();
|
||||
}
|
||||
|
||||
const char* LottieAnimation::marker(uint32_t idx) noexcept
|
||||
{
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
if (!loader || !loader->animatable()) return nullptr;
|
||||
return static_cast<LottieLoader*>(loader)->markers(idx);
|
||||
}
|
||||
|
||||
unique_ptr<LottieAnimation> LottieAnimation::gen() noexcept
|
||||
{
|
||||
|
|
|
@ -320,7 +320,7 @@ bool LottieLoader::frame(float no)
|
|||
no = roundf(no);
|
||||
no *= 0.001f;
|
||||
|
||||
this->frameNo = no;
|
||||
this->frameNo = no + startFrame();
|
||||
|
||||
TaskScheduler::request(this);
|
||||
|
||||
|
@ -328,21 +328,32 @@ bool LottieLoader::frame(float no)
|
|||
}
|
||||
|
||||
|
||||
float LottieLoader::startFrame()
|
||||
{
|
||||
return frameCnt * segmentBegin;
|
||||
}
|
||||
|
||||
|
||||
float LottieLoader::totalFrame()
|
||||
{
|
||||
return frameCnt;
|
||||
return (segmentEnd - segmentBegin) * frameCnt;
|
||||
}
|
||||
|
||||
|
||||
float LottieLoader::curFrame()
|
||||
{
|
||||
return frameNo;
|
||||
return frameNo - startFrame();
|
||||
}
|
||||
|
||||
|
||||
float LottieLoader::duration()
|
||||
{
|
||||
return frameDuration;
|
||||
if (segmentBegin == 0.0f && segmentEnd == 1.0f) return frameDuration;
|
||||
|
||||
if (!comp) done();
|
||||
|
||||
auto frameNo = frameCnt * (segmentEnd - segmentBegin);
|
||||
return frameNo / comp->frameRate;
|
||||
}
|
||||
|
||||
|
||||
|
@ -350,3 +361,34 @@ void LottieLoader::sync()
|
|||
{
|
||||
this->done();
|
||||
}
|
||||
|
||||
|
||||
uint32_t LottieLoader::markerCount()
|
||||
{
|
||||
if (!comp) done();
|
||||
return comp->markers.count;
|
||||
}
|
||||
|
||||
|
||||
const char* LottieLoader::markers(uint32_t index)
|
||||
{
|
||||
if (!comp) done();
|
||||
if (index < 0 || index >= markerCount()) return nullptr;
|
||||
auto marker = comp->markers.begin() + index;
|
||||
return (*marker)->name;
|
||||
}
|
||||
|
||||
|
||||
bool LottieLoader::getSegment(float& begin, float& end, const char* marker)
|
||||
{
|
||||
if (!comp) done();
|
||||
|
||||
for (auto m = comp->markers.begin(); m < comp->markers.end(); ++m) {
|
||||
if (!strcmp(marker, (*m)->name)) {
|
||||
begin = (*m)->time / frameCnt;
|
||||
end = ((*m)->time + (*m)->duration) / frameCnt;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -63,9 +63,15 @@ public:
|
|||
float duration() override;
|
||||
void sync() override;
|
||||
|
||||
//Marker Supports
|
||||
uint32_t markerCount();
|
||||
const char* markers(uint32_t index);
|
||||
bool getSegment(float& beign, float& end, const char* marker);
|
||||
|
||||
private:
|
||||
bool header();
|
||||
void clear();
|
||||
float startFrame();
|
||||
void run(unsigned tid) override;
|
||||
};
|
||||
|
||||
|
|
|
@ -255,4 +255,8 @@ LottieComposition::~LottieComposition()
|
|||
for (auto s = slots.begin(); s < slots.end(); ++s) {
|
||||
delete(*s);
|
||||
}
|
||||
|
||||
for (auto m = markers.begin(); m < markers.end(); ++m) {
|
||||
delete(*m);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -175,6 +175,17 @@ struct LottieFont
|
|||
Origin origin = Embedded;
|
||||
};
|
||||
|
||||
struct LottieMarker
|
||||
{
|
||||
char* name = nullptr;
|
||||
float time = 0.0f;
|
||||
float duration = 0.0f;
|
||||
|
||||
~LottieMarker()
|
||||
{
|
||||
free(name);
|
||||
}
|
||||
};
|
||||
|
||||
struct LottieText : LottieObject
|
||||
{
|
||||
|
@ -765,6 +776,7 @@ struct LottieComposition
|
|||
Array<LottieInterpolator*> interpolators;
|
||||
Array<LottieFont*> fonts;
|
||||
Array<LottieSlot*> slots;
|
||||
Array<LottieMarker*> markers;
|
||||
bool initiated = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -947,6 +947,29 @@ void LottieParser::parseAssets()
|
|||
}
|
||||
}
|
||||
|
||||
LottieMarker* LottieParser::parseMarker()
|
||||
{
|
||||
enterObject();
|
||||
|
||||
auto marker = new LottieMarker;
|
||||
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "cm")) marker->name = getStringCopy();
|
||||
else if (!strcmp(key, "tm")) marker->time = getFloat();
|
||||
else if (!strcmp(key, "dr")) marker->duration = getFloat();
|
||||
else skip(key);
|
||||
}
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
void LottieParser::parseMarkers()
|
||||
{
|
||||
enterArray();
|
||||
while (nextArrayValue()) {
|
||||
comp->markers.push(parseMarker());
|
||||
}
|
||||
}
|
||||
|
||||
void LottieParser::parseChars(Array<LottieGlyph*>& glyphes)
|
||||
{
|
||||
|
@ -1314,6 +1337,7 @@ bool LottieParser::parse()
|
|||
else if (!strcmp(key, "layers")) comp->root = parseLayers();
|
||||
else if (!strcmp(key, "fonts")) parseFonts();
|
||||
else if (!strcmp(key, "chars")) parseChars(glyphes);
|
||||
else if (!strcmp(key, "markers")) parseMarkers();
|
||||
else skip(key);
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ private:
|
|||
LottieTrimpath* parseTrimpath();
|
||||
LottieRepeater* parseRepeater();
|
||||
LottieFont* parseFont();
|
||||
LottieMarker* parseMarker();
|
||||
|
||||
void parseObject(Array<LottieObject*>& parent);
|
||||
void parseShapes(Array<LottieObject*>& parent);
|
||||
|
@ -104,6 +105,7 @@ private:
|
|||
void parseAssets();
|
||||
void parseFonts();
|
||||
void parseChars(Array<LottieGlyph*>& glyphes);
|
||||
void parseMarkers();
|
||||
void postProcess(Array<LottieGlyph*>& glyphes);
|
||||
|
||||
//Current parsing context
|
||||
|
|
|
@ -93,6 +93,33 @@ float Animation::duration() const noexcept
|
|||
}
|
||||
|
||||
|
||||
Result Animation::segment(float begin, float end) noexcept
|
||||
{
|
||||
if (begin < 0.0 || end > 1.0 || begin >= end) return Result::InvalidArguments;
|
||||
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
if (!loader) return Result::InsufficientCondition;
|
||||
if (!loader->animatable()) return Result::NonSupport;
|
||||
|
||||
static_cast<FrameModule*>(loader)->segment(begin, end);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Animation::segment(float *begin, float *end) noexcept
|
||||
{
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
if (!loader) return Result::InsufficientCondition;
|
||||
if (!loader->animatable()) return Result::NonSupport;
|
||||
if (!begin && !end) return Result::InvalidArguments;
|
||||
|
||||
static_cast<FrameModule*>(loader)->segment(begin, end);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Animation> Animation::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Animation>(new Animation);
|
||||
|
|
|
@ -31,6 +31,9 @@ namespace tvg
|
|||
class FrameModule: public ImageLoader
|
||||
{
|
||||
public:
|
||||
float segmentBegin = 0.0f;
|
||||
float segmentEnd = 1.0f;
|
||||
|
||||
FrameModule(FileType type) : ImageLoader(type) {}
|
||||
virtual ~FrameModule() {}
|
||||
|
||||
|
@ -39,6 +42,16 @@ public:
|
|||
virtual float curFrame() = 0; //return the current frame number
|
||||
virtual float duration() = 0; //return the animation duration in seconds
|
||||
|
||||
void segment(float* begin, float* end) {
|
||||
if (begin) *begin = segmentBegin;
|
||||
if (end) *end = segmentEnd;
|
||||
}
|
||||
|
||||
void segment(float begin, float end) {
|
||||
segmentBegin = begin;
|
||||
segmentEnd = end;
|
||||
}
|
||||
|
||||
virtual bool animatable() override { return true; }
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue