lottie: Added support for rendering embedded image resource

Change-Id: I330ee46e7bccabbebe2b15b54d21582212c1f020
This commit is contained in:
subhransu mohanty 2019-01-23 17:20:17 +09:00 committed by Hermet Park
parent ecb0b9748c
commit 3db9cd3b15
8 changed files with 129 additions and 22 deletions

File diff suppressed because one or more lines are too long

View File

@ -23,7 +23,6 @@
#include "vdasher.h"
#include "vpainter.h"
#include "vraster.h"
#include "vimageloader.h"
/* Lottie Layer Rules
* 1. time stretch is pre calculated and applied to all the properties of the
@ -703,12 +702,9 @@ void LOTImageLayerItem::updateContent()
// load image
//@TODO find a better way to load
// so that can be shared by multiple layers
if (!mLayerData->mAsset->mImagePath.empty()) {
VBitmap img = VImageLoader::instance().load(mLayerData->mAsset->mImagePath.c_str());
VBrush brush(img);
VBrush brush(mLayerData->mAsset->bitmap());
mRenderNode->setBrush(brush);
}
}
if (flag() & DirtyFlagBit::Matrix) {
VPath path;

View File

@ -18,6 +18,7 @@
#include "lottiemodel.h"
#include "vline.h"
#include "vimageloader.h"
#include <cassert>
#include <stack>
@ -317,3 +318,15 @@ void LOTGradient::update(std::unique_ptr<VGradient> &grad, int frameNo)
grad->radial.fradius = 0;
}
}
VBitmap
LOTAsset::bitmap() const
{
if (!mImageData.empty()) {
return VImageLoader::instance().load(mImageData.c_str(), mImageData.length());
} else if (!mImagePath.empty()) {
return VImageLoader::instance().load(mImagePath.c_str());
} else {
return VBitmap();
}
}

View File

@ -339,12 +339,12 @@ public:
class LOTLayerData;
struct LOTAsset
{
enum class Type {
enum class Type : unsigned char{
Precomp,
Image,
Char
};
VBitmap bitmap() const;
Type mAssetType{Type::Precomp};
std::string mRefId; // ref id
std::vector<std::shared_ptr<LOTData>> mLayers;
@ -352,6 +352,7 @@ struct LOTAsset
int mWidth{0};
int mHeight{0};
std::string mImagePath;
std::string mImageData;
};
class LOTLayerData : public LOTGroupData

View File

@ -626,6 +626,56 @@ void LottieParserImpl::parseAssets(LOTCompositionData *composition)
// update the precomp layers with the actual layer object
}
static constexpr const unsigned char B64index[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
std::string b64decode(const void* data, const size_t len)
{
unsigned char* p = (unsigned char*)data;
int pad = len > 0 && (len % 4 || p[len - 1] == '=');
const size_t L = ((len + 3) / 4 - pad) * 4;
std::string str(L / 4 * 3 + pad, '\0');
for (size_t i = 0, j = 0; i < L; i += 4)
{
int n = B64index[p[i]] << 18 | B64index[p[i + 1]] << 12 | B64index[p[i + 2]] << 6 | B64index[p[i + 3]];
str[j++] = n >> 16;
str[j++] = n >> 8 & 0xFF;
str[j++] = n & 0xFF;
}
if (pad)
{
int n = B64index[p[L]] << 18 | B64index[p[L + 1]] << 12;
str[str.size() - 1] = n >> 16;
if (len > L + 2 && p[L + 2] != '=')
{
n |= B64index[p[L + 2]] << 6;
str.push_back(n >> 8 & 0xFF);
}
}
return str;
}
static std::string
convertFromBase64(const std::string &str)
{
// usual header look like "data:image/png;base64,"
// so need to skip till ','.
int startIndex = str.find(",", 0);
startIndex += 1; // skip ","
int length = str.length() - startIndex;
const char *b64Data = str.c_str() + startIndex;
return b64decode(b64Data, length);
}
/*
* https://github.com/airbnb/lottie-web/blob/master/docs/json/layers/shape.json
*
@ -637,6 +687,7 @@ std::shared_ptr<LOTAsset> LottieParserImpl::parseAsset()
LOTAsset * asset = sharedAsset.get();
std::string filename;
std::string relativePath;
bool embededResource = false;
EnterObject();
while (const char *key = NextObjectKey()) {
if (0 == strcmp(key, "w")) {
@ -652,6 +703,8 @@ std::shared_ptr<LOTAsset> LottieParserImpl::parseAsset()
} else if (0 == strcmp(key, "u")) { /* relative image path */
RAPIDJSON_ASSERT(PeekType() == kStringType);
relativePath = std::string(GetString());
} else if (0 == strcmp(key, "e")) { /* relative image path */
embededResource = GetInt();
} else if (0 == strcmp(key, "id")) { /* reference id*/
if (PeekType() == kStringType) {
asset->mRefId = std::string(GetString());
@ -674,8 +727,13 @@ std::shared_ptr<LOTAsset> LottieParserImpl::parseAsset()
Skip(key);
}
}
// its an image asset
if (!filename.empty()) {
if (embededResource) {
// embeder resource should start with "data:"
if (filename.find("data:", 0) == 0) {
asset->mImageData = convertFromBase64(filename);
}
} else {
asset->mImagePath = mDirPath + relativePath + filename;
}
return sharedAsset;

View File

@ -49,6 +49,13 @@ lottie_image_load(char const *filename, int *x, int *y, int *comp, int req_comp)
return stbi_load(filename, x, y, comp, req_comp);
}
LOT_EXPORT unsigned char *
lottie_image_load_from_data(const char *imageData, int len, int *x, int *y, int *comp, int req_comp)
{
unsigned char *data = (unsigned char *)imageData;
return stbi_load_from_memory(data, len, x, y, comp, req_comp);
}
LOT_EXPORT void
lottie_image_free(unsigned char *data)
{

View File

@ -3,7 +3,8 @@
#include <dlfcn.h>
#include <cstring>
using lottie_image_load_f = unsigned char* (*)(char const *filename, int *x, int *y, int *comp, int req_comp);
using lottie_image_load_f = unsigned char* (*)(const char *filename, int *x, int *y, int *comp, int req_comp);
using lottie_image_load_data_f = unsigned char* (*)(const char *data, int len, int *x, int *y, int *comp, int req_comp);
using lottie_image_free_f = void (*)(unsigned char *);
struct VImageLoader::Impl
@ -11,6 +12,7 @@ struct VImageLoader::Impl
void *dl_handle{nullptr};
lottie_image_load_f lottie_image_load{nullptr};
lottie_image_free_f lottie_image_free{nullptr};
lottie_image_load_data_f lottie_image_load_data{nullptr};
Impl()
{
@ -27,24 +29,19 @@ struct VImageLoader::Impl
lottie_image_free = (lottie_image_free_f) dlsym(dl_handle, "lottie_image_free");
if (!lottie_image_free)
vWarning<<"Failed to find symbol lottie_image_free in librlottie-image-loader library";
lottie_image_load_data = (lottie_image_load_data_f) dlsym(dl_handle, "lottie_image_load_from_data");
if (!lottie_image_load_data)
vWarning<<"Failed to find symbol lottie_image_load_data in librlottie-image-loader library";
}
~Impl()
{
if (dl_handle) dlclose(dl_handle);
}
VBitmap load(const char *fileName)
VBitmap createBitmap(unsigned char *data, int width, int height, int channel)
{
if (!lottie_image_load) return VBitmap();
int width, height, n;
unsigned char *data = lottie_image_load(fileName, &width, &height, &n, 4);
if (!data) {
return VBitmap();
}
// premultiply alpha
if (n == 4)
if (channel == 4)
convertToBGRAPremul(data, width, height);
else
convertToBGRA(data, width, height);
@ -60,6 +57,34 @@ struct VImageLoader::Impl
return result;
}
VBitmap load(const char *fileName)
{
if (!lottie_image_load) return VBitmap();
int width, height, n;
unsigned char *data = lottie_image_load(fileName, &width, &height, &n, 4);
if (!data) {
return VBitmap();
}
return createBitmap(data, width, height, n);
}
VBitmap load(const char *imageData, int len)
{
if (!lottie_image_load_data) return VBitmap();
int width, height, n;
unsigned char *data = lottie_image_load_data(imageData, len, &width, &height, &n, 4);
if (!data) {
return VBitmap();
}
return createBitmap(data, width, height, n);
}
/*
* convert from RGBA to BGRA and premultiply
*/
@ -114,3 +139,8 @@ VBitmap VImageLoader::load(const char *fileName)
return mImpl->load(fileName);
}
VBitmap VImageLoader::load(const char *data, int len)
{
return mImpl->load(data, len);
}

View File

@ -15,6 +15,7 @@ public:
}
VBitmap load(const char *fileName);
VBitmap load(const char *data, int len);
~VImageLoader();
private:
VImageLoader();