/* * Copyright 2019 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "modules/skottie/src/SkottiePriv.h" #include "include/core/SkImage.h" #include "modules/skottie/src/SkottieJson.h" #include "modules/sksg/include/SkSGImage.h" #include "modules/sksg/include/SkSGTransform.h" namespace skottie { namespace internal { const AnimationBuilder::ImageAssetInfo* AnimationBuilder::loadImageAsset(const skjson::ObjectValue& jimage) const { const skjson::StringValue* name = jimage["p"]; const skjson::StringValue* path = jimage["u"]; const skjson::StringValue* id = jimage["id"]; if (!name || !path || !id) { return nullptr; } const SkString res_id(id->begin()); if (auto* cached_info = fImageAssetCache.find(res_id)) { return cached_info; } auto asset = fResourceProvider->loadImageAsset(path->begin(), name->begin(), id->begin()); if (!asset) { this->log(Logger::Level::kError, nullptr, "Could not load image asset: %s/%s (id: '%s').", path->begin(), name->begin(), id->begin()); return nullptr; } const auto size = SkISize::Make(ParseDefault<int>(jimage["w"], 0), ParseDefault<int>(jimage["h"], 0)); return fImageAssetCache.set(res_id, { std::move(asset), size }); } sk_sp<sksg::RenderNode> AnimationBuilder::attachImageAsset(const skjson::ObjectValue& jimage, LayerInfo* layer_info) const { const auto* asset_info = this->loadImageAsset(jimage); if (!asset_info) { return nullptr; } SkASSERT(asset_info->fAsset); auto image = asset_info->fAsset->getFrame(0); if (!image) { this->log(Logger::Level::kError, nullptr, "Could not load first image asset frame."); return nullptr; } auto image_node = sksg::Image::Make(image); image_node->setQuality(kMedium_SkFilterQuality); if (asset_info->fAsset->isMultiFrame()) { class MultiFrameAnimator final : public sksg::Animator { public: MultiFrameAnimator(sk_sp<ImageAsset> asset, sk_sp<sksg::Image> image_node, float time_bias, float time_scale) : fAsset(std::move(asset)) , fImageNode(std::move(image_node)) , fTimeBias(time_bias) , fTimeScale(time_scale) {} void onTick(float t) override { fImageNode->setImage(fAsset->getFrame((t + fTimeBias) * fTimeScale)); } private: sk_sp<ImageAsset> fAsset; sk_sp<sksg::Image> fImageNode; float fTimeBias, fTimeScale; }; fCurrentAnimatorScope->push_back(sk_make_sp<MultiFrameAnimator>(asset_info->fAsset, image_node, -layer_info->fInPoint, 1 / fFrameRate)); } const auto asset_size = SkISize::Make( asset_info->fSize.width() > 0 ? asset_info->fSize.width() : image->width(), asset_info->fSize.height() > 0 ? asset_info->fSize.height() : image->height()); // Image layers are sized explicitly. layer_info->fSize = asset_size; if (asset_size == image->bounds().size()) { // No resize needed. return image_node; } return sksg::TransformEffect::Make(std::move(image_node), SkMatrix::MakeRectToRect(SkRect::Make(image->bounds()), SkRect::Make(asset_size), SkMatrix::kCenter_ScaleToFit)); } sk_sp<sksg::RenderNode> AnimationBuilder::attachImageLayer(const skjson::ObjectValue& jlayer, LayerInfo* layer_info) const { return this->attachAssetRef(jlayer, [this, &layer_info] (const skjson::ObjectValue& jimage) { return this->attachImageAsset(jimage, layer_info); }); } } // namespace internal } // namespace skottie