diff options
Diffstat (limited to 'vendor/github.com/Benau/go_rlottie/lottie_lottiemodel.cpp')
-rw-r--r-- | vendor/github.com/Benau/go_rlottie/lottie_lottiemodel.cpp | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/vendor/github.com/Benau/go_rlottie/lottie_lottiemodel.cpp b/vendor/github.com/Benau/go_rlottie/lottie_lottiemodel.cpp new file mode 100644 index 00000000..0389e8aa --- /dev/null +++ b/vendor/github.com/Benau/go_rlottie/lottie_lottiemodel.cpp @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lottie_lottiemodel.h" +#include <cassert> +#include <iterator> +#include <stack> +#include "vector_vimageloader.h" +#include "vector_vline.h" + +using namespace rlottie::internal; + +/* + * We process the iterator objects in the children list + * by iterating from back to front. when we find a repeater object + * we remove the objects from satrt till repeater object and then place + * under a new shape group object which we add it as children to the repeater + * object. + * Then we visit the childrens of the newly created shape group object to + * process the remaining repeater object(when children list contains more than + * one repeater). + * + */ +class LottieRepeaterProcesser { +public: + void visitChildren(model::Group *obj) + { + for (auto i = obj->mChildren.rbegin(); i != obj->mChildren.rend(); + ++i) { + auto child = (*i); + if (child->type() == model::Object::Type::Repeater) { + model::Repeater *repeater = + static_cast<model::Repeater *>(child); + // check if this repeater is already processed + // can happen if the layer is an asset and referenced by + // multiple layer. + if (repeater->processed()) continue; + + repeater->markProcessed(); + + auto content = repeater->content(); + // 1. increment the reverse iterator to point to the + // object before the repeater + ++i; + // 2. move all the children till repater to the group + std::move(obj->mChildren.begin(), i.base(), + back_inserter(content->mChildren)); + // 3. erase the objects from the original children list + obj->mChildren.erase(obj->mChildren.begin(), i.base()); + + // 5. visit newly created group to process remaining repeater + // object. + visitChildren(content); + // 6. exit the loop as the current iterators are invalid + break; + } + visit(child); + } + } + + void visit(model::Object *obj) + { + switch (obj->type()) { + case model::Object::Type::Group: + case model::Object::Type::Layer: { + visitChildren(static_cast<model::Group *>(obj)); + break; + } + default: + break; + } + } +}; + +class LottieUpdateStatVisitor { + model::Composition::Stats *stat; + +public: + explicit LottieUpdateStatVisitor(model::Composition::Stats *s) : stat(s) {} + void visitChildren(model::Group *obj) + { + for (const auto &child : obj->mChildren) { + if (child) visit(child); + } + } + void visitLayer(model::Layer *layer) + { + switch (layer->mLayerType) { + case model::Layer::Type::Precomp: + stat->precompLayerCount++; + break; + case model::Layer::Type::Null: + stat->nullLayerCount++; + break; + case model::Layer::Type::Shape: + stat->shapeLayerCount++; + break; + case model::Layer::Type::Solid: + stat->solidLayerCount++; + break; + case model::Layer::Type::Image: + stat->imageLayerCount++; + break; + default: + break; + } + visitChildren(layer); + } + void visit(model::Object *obj) + { + switch (obj->type()) { + case model::Object::Type::Layer: { + visitLayer(static_cast<model::Layer *>(obj)); + break; + } + case model::Object::Type::Repeater: { + visitChildren(static_cast<model::Repeater *>(obj)->content()); + break; + } + case model::Object::Type::Group: { + visitChildren(static_cast<model::Group *>(obj)); + break; + } + default: + break; + } + } +}; + +void model::Composition::processRepeaterObjects() +{ + LottieRepeaterProcesser visitor; + visitor.visit(mRootLayer); +} + +void model::Composition::updateStats() +{ + LottieUpdateStatVisitor visitor(&mStats); + visitor.visit(mRootLayer); +} + +VMatrix model::Repeater::Transform::matrix(int frameNo, float multiplier) const +{ + VPointF scale = mScale.value(frameNo) / 100.f; + scale.setX(std::pow(scale.x(), multiplier)); + scale.setY(std::pow(scale.y(), multiplier)); + VMatrix m; + m.translate(mPosition.value(frameNo) * multiplier) + .translate(mAnchor.value(frameNo)) + .scale(scale) + .rotate(mRotation.value(frameNo) * multiplier) + .translate(-mAnchor.value(frameNo)); + + return m; +} + +VMatrix model::Transform::Data::matrix(int frameNo, bool autoOrient) const +{ + VMatrix m; + VPointF position; + if (mExtra && mExtra->mSeparate) { + position.setX(mExtra->mSeparateX.value(frameNo)); + position.setY(mExtra->mSeparateY.value(frameNo)); + } else { + position = mPosition.value(frameNo); + } + + float angle = autoOrient ? mPosition.angle(frameNo) : 0; + if (mExtra && mExtra->m3DData) { + m.translate(position) + .rotate(mExtra->m3DRz.value(frameNo) + angle) + .rotate(mExtra->m3DRy.value(frameNo), VMatrix::Axis::Y) + .rotate(mExtra->m3DRx.value(frameNo), VMatrix::Axis::X) + .scale(mScale.value(frameNo) / 100.f) + .translate(-mAnchor.value(frameNo)); + } else { + m.translate(position) + .rotate(mRotation.value(frameNo) + angle) + .scale(mScale.value(frameNo) / 100.f) + .translate(-mAnchor.value(frameNo)); + } + return m; +} + +void model::Dash::getDashInfo(int frameNo, std::vector<float> &result) const +{ + result.clear(); + + if (mData.size() <= 1) return; + + if (result.capacity() < mData.size()) result.reserve(mData.size() + 1); + + for (const auto &elm : mData) result.push_back(elm.value(frameNo)); + + // if the size is even then we are missing last + // gap information which is same as the last dash value + // copy it from the last dash value. + // NOTE: last value is the offset and last-1 is the last dash value. + auto size = result.size(); + if ((size % 2) == 0) { + // copy offset value to end. + result.push_back(result.back()); + // copy dash value to gap. + result[size - 1] = result[size - 2]; + } +} + +/** + * Both the color stops and opacity stops are in the same array. + * There are {@link #colorPoints} colors sequentially as: + * [ + * ..., + * position, + * red, + * green, + * blue, + * ... + * ] + * + * The remainder of the array is the opacity stops sequentially as: + * [ + * ..., + * position, + * opacity, + * ... + * ] + */ +void model::Gradient::populate(VGradientStops &stops, int frameNo) +{ + model::Gradient::Data gradData = mGradient.value(frameNo); + auto size = gradData.mGradient.size(); + float * ptr = gradData.mGradient.data(); + int colorPoints = mColorPoints; + if (colorPoints == -1) { // for legacy bodymovin (ref: lottie-android) + colorPoints = int(size / 4); + } + auto opacityArraySize = size - colorPoints * 4; + float *opacityPtr = ptr + (colorPoints * 4); + stops.clear(); + size_t j = 0; + for (int i = 0; i < colorPoints; i++) { + float colorStop = ptr[0]; + model::Color color = model::Color(ptr[1], ptr[2], ptr[3]); + if (opacityArraySize) { + if (j == opacityArraySize) { + // already reached the end + float stop1 = opacityPtr[j - 4]; + float op1 = opacityPtr[j - 3]; + float stop2 = opacityPtr[j - 2]; + float op2 = opacityPtr[j - 1]; + if (colorStop > stop2) { + stops.push_back( + std::make_pair(colorStop, color.toColor(op2))); + } else { + float progress = (colorStop - stop1) / (stop2 - stop1); + float opacity = op1 + progress * (op2 - op1); + stops.push_back( + std::make_pair(colorStop, color.toColor(opacity))); + } + continue; + } + for (; j < opacityArraySize; j += 2) { + float opacityStop = opacityPtr[j]; + if (opacityStop < colorStop) { + // add a color using opacity stop + stops.push_back(std::make_pair( + opacityStop, color.toColor(opacityPtr[j + 1]))); + continue; + } + // add a color using color stop + if (j == 0) { + stops.push_back(std::make_pair( + colorStop, color.toColor(opacityPtr[j + 1]))); + } else { + float progress = (colorStop - opacityPtr[j - 2]) / + (opacityPtr[j] - opacityPtr[j - 2]); + float opacity = + opacityPtr[j - 1] + + progress * (opacityPtr[j + 1] - opacityPtr[j - 1]); + stops.push_back( + std::make_pair(colorStop, color.toColor(opacity))); + } + j += 2; + break; + } + } else { + stops.push_back(std::make_pair(colorStop, color.toColor())); + } + ptr += 4; + } +} + +void model::Gradient::update(std::unique_ptr<VGradient> &grad, int frameNo) +{ + bool init = false; + if (!grad) { + if (mGradientType == 1) + grad = std::make_unique<VGradient>(VGradient::Type::Linear); + else + grad = std::make_unique<VGradient>(VGradient::Type::Radial); + grad->mSpread = VGradient::Spread::Pad; + init = true; + } + + if (!mGradient.isStatic() || init) { + populate(grad->mStops, frameNo); + } + + if (mGradientType == 1) { // linear gradient + VPointF start = mStartPoint.value(frameNo); + VPointF end = mEndPoint.value(frameNo); + grad->linear.x1 = start.x(); + grad->linear.y1 = start.y(); + grad->linear.x2 = end.x(); + grad->linear.y2 = end.y(); + } else { // radial gradient + VPointF start = mStartPoint.value(frameNo); + VPointF end = mEndPoint.value(frameNo); + grad->radial.cx = start.x(); + grad->radial.cy = start.y(); + grad->radial.cradius = + VLine::length(start.x(), start.y(), end.x(), end.y()); + /* + * Focal point is the point lives in highlight length distance from + * center along the line (start, end) and rotated by highlight angle. + * below calculation first finds the quadrant(angle) on which the point + * lives by applying inverse slope formula then adds the rotation angle + * to find the final angle. then point is retrived using circle equation + * of center, angle and distance. + */ + float progress = mHighlightLength.value(frameNo) / 100.0f; + if (vCompare(progress, 1.0f)) progress = 0.99f; + float startAngle = VLine(start, end).angle(); + float highlightAngle = mHighlightAngle.value(frameNo); + static constexpr float K_PI = 3.1415926f; + float angle = (startAngle + highlightAngle) * (K_PI / 180.0f); + grad->radial.fx = + grad->radial.cx + std::cos(angle) * progress * grad->radial.cradius; + grad->radial.fy = + grad->radial.cy + std::sin(angle) * progress * grad->radial.cradius; + // Lottie dosen't have any focal radius concept. + grad->radial.fradius = 0; + } +} + +void model::Asset::loadImageData(std::string data) +{ + if (!data.empty()) + mBitmap = VImageLoader::instance().load(data.c_str(), data.length()); +} + +void model::Asset::loadImagePath(std::string path) +{ + if (!path.empty()) mBitmap = VImageLoader::instance().load(path.c_str()); +} + +std::vector<LayerInfo> model::Composition::layerInfoList() const +{ + if (!mRootLayer || mRootLayer->mChildren.empty()) return {}; + + std::vector<LayerInfo> result; + + result.reserve(mRootLayer->mChildren.size()); + + for (auto it : mRootLayer->mChildren) { + auto layer = static_cast<model::Layer *>(it); + result.emplace_back(layer->name(), layer->mInFrame, layer->mOutFrame); + } + + return result; +} |